diff options
author | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-02-22 12:09:27 +0000 |
---|---|---|
committer | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-02-22 12:09:27 +0000 |
commit | 011ddb51b40897bc13a75d78c9e992f559cbde48 (patch) | |
tree | d2f25aa76ab1466ecca82d2cf07e3594b8d7addc | |
parent | a75cce1febf0403d66631841fff3bfbeefbfe6e3 (diff) | |
parent | eb421c88ee2a57a437b9b14ba7447a04720354ac (diff) | |
download | gitlab-ce-011ddb51b40897bc13a75d78c9e992f559cbde48.tar.gz |
Merge branch 'master' into 'backstage/gb/build-stages-catch-up-migration'
# Conflicts:
# db/schema.rb
654 files changed, 7402 insertions, 8676 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml index ecac24b68d7..b02fe54a4ff 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -15,7 +15,7 @@ engines: enabled: false rubocop: enabled: true - channel: "gitlab-rubocop-0-52" + channel: "gitlab-rubocop-0-52-1" ratings: paths: - Gemfile.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c3556f5cce..ae762e7aa6e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -323,69 +323,69 @@ setup-test-env: - tmp/tests - config/secrets.yml -rspec-pg 0 27: *rspec-metadata-pg -rspec-pg 1 27: *rspec-metadata-pg -rspec-pg 2 27: *rspec-metadata-pg -rspec-pg 3 27: *rspec-metadata-pg -rspec-pg 4 27: *rspec-metadata-pg -rspec-pg 5 27: *rspec-metadata-pg -rspec-pg 6 27: *rspec-metadata-pg -rspec-pg 7 27: *rspec-metadata-pg -rspec-pg 8 27: *rspec-metadata-pg -rspec-pg 9 27: *rspec-metadata-pg -rspec-pg 10 27: *rspec-metadata-pg -rspec-pg 11 27: *rspec-metadata-pg -rspec-pg 12 27: *rspec-metadata-pg -rspec-pg 13 27: *rspec-metadata-pg -rspec-pg 14 27: *rspec-metadata-pg -rspec-pg 15 27: *rspec-metadata-pg -rspec-pg 16 27: *rspec-metadata-pg -rspec-pg 17 27: *rspec-metadata-pg -rspec-pg 18 27: *rspec-metadata-pg -rspec-pg 19 27: *rspec-metadata-pg -rspec-pg 20 27: *rspec-metadata-pg -rspec-pg 21 27: *rspec-metadata-pg -rspec-pg 22 27: *rspec-metadata-pg -rspec-pg 23 27: *rspec-metadata-pg -rspec-pg 24 27: *rspec-metadata-pg -rspec-pg 25 27: *rspec-metadata-pg -rspec-pg 26 27: *rspec-metadata-pg - -rspec-mysql 0 27: *rspec-metadata-mysql -rspec-mysql 1 27: *rspec-metadata-mysql -rspec-mysql 2 27: *rspec-metadata-mysql -rspec-mysql 3 27: *rspec-metadata-mysql -rspec-mysql 4 27: *rspec-metadata-mysql -rspec-mysql 5 27: *rspec-metadata-mysql -rspec-mysql 6 27: *rspec-metadata-mysql -rspec-mysql 7 27: *rspec-metadata-mysql -rspec-mysql 8 27: *rspec-metadata-mysql -rspec-mysql 9 27: *rspec-metadata-mysql -rspec-mysql 10 27: *rspec-metadata-mysql -rspec-mysql 11 27: *rspec-metadata-mysql -rspec-mysql 12 27: *rspec-metadata-mysql -rspec-mysql 13 27: *rspec-metadata-mysql -rspec-mysql 14 27: *rspec-metadata-mysql -rspec-mysql 15 27: *rspec-metadata-mysql -rspec-mysql 16 27: *rspec-metadata-mysql -rspec-mysql 17 27: *rspec-metadata-mysql -rspec-mysql 18 27: *rspec-metadata-mysql -rspec-mysql 19 27: *rspec-metadata-mysql -rspec-mysql 20 27: *rspec-metadata-mysql -rspec-mysql 21 27: *rspec-metadata-mysql -rspec-mysql 22 27: *rspec-metadata-mysql -rspec-mysql 23 27: *rspec-metadata-mysql -rspec-mysql 24 27: *rspec-metadata-mysql -rspec-mysql 25 27: *rspec-metadata-mysql -rspec-mysql 26 27: *rspec-metadata-mysql - -spinach-pg 0 3: *spinach-metadata-pg -spinach-pg 1 3: *spinach-metadata-pg -spinach-pg 2 3: *spinach-metadata-pg - -spinach-mysql 0 3: *spinach-metadata-mysql -spinach-mysql 1 3: *spinach-metadata-mysql -spinach-mysql 2 3: *spinach-metadata-mysql +rspec-pg 0 28: *rspec-metadata-pg +rspec-pg 1 28: *rspec-metadata-pg +rspec-pg 2 28: *rspec-metadata-pg +rspec-pg 3 28: *rspec-metadata-pg +rspec-pg 4 28: *rspec-metadata-pg +rspec-pg 5 28: *rspec-metadata-pg +rspec-pg 6 28: *rspec-metadata-pg +rspec-pg 7 28: *rspec-metadata-pg +rspec-pg 8 28: *rspec-metadata-pg +rspec-pg 9 28: *rspec-metadata-pg +rspec-pg 10 28: *rspec-metadata-pg +rspec-pg 11 28: *rspec-metadata-pg +rspec-pg 12 28: *rspec-metadata-pg +rspec-pg 13 28: *rspec-metadata-pg +rspec-pg 14 28: *rspec-metadata-pg +rspec-pg 15 28: *rspec-metadata-pg +rspec-pg 16 28: *rspec-metadata-pg +rspec-pg 17 28: *rspec-metadata-pg +rspec-pg 18 28: *rspec-metadata-pg +rspec-pg 19 28: *rspec-metadata-pg +rspec-pg 20 28: *rspec-metadata-pg +rspec-pg 21 28: *rspec-metadata-pg +rspec-pg 22 28: *rspec-metadata-pg +rspec-pg 23 28: *rspec-metadata-pg +rspec-pg 24 28: *rspec-metadata-pg +rspec-pg 25 28: *rspec-metadata-pg +rspec-pg 26 28: *rspec-metadata-pg +rspec-pg 27 28: *rspec-metadata-pg + +rspec-mysql 0 28: *rspec-metadata-mysql +rspec-mysql 1 28: *rspec-metadata-mysql +rspec-mysql 2 28: *rspec-metadata-mysql +rspec-mysql 3 28: *rspec-metadata-mysql +rspec-mysql 4 28: *rspec-metadata-mysql +rspec-mysql 5 28: *rspec-metadata-mysql +rspec-mysql 6 28: *rspec-metadata-mysql +rspec-mysql 7 28: *rspec-metadata-mysql +rspec-mysql 8 28: *rspec-metadata-mysql +rspec-mysql 9 28: *rspec-metadata-mysql +rspec-mysql 10 28: *rspec-metadata-mysql +rspec-mysql 11 28: *rspec-metadata-mysql +rspec-mysql 12 28: *rspec-metadata-mysql +rspec-mysql 13 28: *rspec-metadata-mysql +rspec-mysql 14 28: *rspec-metadata-mysql +rspec-mysql 15 28: *rspec-metadata-mysql +rspec-mysql 16 28: *rspec-metadata-mysql +rspec-mysql 17 28: *rspec-metadata-mysql +rspec-mysql 18 28: *rspec-metadata-mysql +rspec-mysql 19 28: *rspec-metadata-mysql +rspec-mysql 20 28: *rspec-metadata-mysql +rspec-mysql 21 28: *rspec-metadata-mysql +rspec-mysql 22 28: *rspec-metadata-mysql +rspec-mysql 23 28: *rspec-metadata-mysql +rspec-mysql 24 28: *rspec-metadata-mysql +rspec-mysql 25 28: *rspec-metadata-mysql +rspec-mysql 26 28: *rspec-metadata-mysql +rspec-mysql 27 28: *rspec-metadata-mysql + +spinach-pg 0 2: *spinach-metadata-pg +spinach-pg 1 2: *spinach-metadata-pg + +spinach-mysql 0 2: *spinach-metadata-mysql +spinach-mysql 1 2: *spinach-metadata-mysql # Static analysis jobs .ruby-static-analysis: &ruby-static-analysis @@ -607,22 +607,24 @@ karma: codequality: <<: *except-docs <<: *pull-cache - before_script: [] - image: docker:latest stage: test - variables: - SETUP_DB: "false" - DOCKER_DRIVER: overlay + image: docker:latest + before_script: [] services: - docker:dind + variables: + SETUP_DB: "false" + DOCKER_DRIVER: overlay2 + CODECLIMATE_FORMAT: json + cache: {} + dependencies: [] script: - - cp .rubocop.yml .rubocop.yml.bak - - grep -v "rubocop-gitlab-security" .rubocop.yml.bak > .rubocop.yml - - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-v2 analyze -f json > raw_codeclimate.json - - cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,fingerprint,location})' > codeclimate.json - - mv .rubocop.yml.bak .rubocop.yml + - ./scripts/codequality analyze -f json > raw_codeclimate.json || true + # The following line keeps only the fields used in the MR widget, reducing the JSON artifact size + - cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,description,fingerprint,location})' > codeclimate.json artifacts: paths: [codeclimate.json] + expire_in: 1 week sast: <<: *except-docs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad603fdc75..81858cad1ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,217 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.5.0 (2018-02-22) + +### Security (3 changes, 1 of them is from the community) + +- Update marked from 0.3.6 to 0.3.12. !16480 (Takuya Noguchi) +- Update nokogiri to 1.8.2. !16807 +- Add verification for GitLab Pages custom domains. + +### Fixed (77 changes, 25 of them are from the community) + +- Fix the Projects API with_issues_enabled filter behaving incorrectly any user. !12724 (Jan Christophersen) +- Hide pipeline schedule take ownership for current owner. !12986 +- Handle special characters on API request of issuable templates. !15323 (Takuya Noguchi) +- Shows signin tab after new user email confirmation. !16174 (Jacopo Beschi @jacopo-beschi) +- Make project README containers wider on fixed layout. !16181 (Takuya Noguchi) +- Fix dashboard projects nav links height. !16204 (George Tsiolis) +- Fix error on empty query for Members API. !16235 +- Issue board: fix for dragging an issue to the very bottom in long lists. !16250 (David Kuri) +- Make rich blob viewer wider for PC. !16262 (Takuya Noguchi) +- Substitute deprecated ui_charcoal with new default ui_indigo. !16271 (Takuya Noguchi) +- Generate HTTP URLs for custom Pages domains when appropriate. !16279 +- Make modal dialog common for Groups tree app. !16311 +- Allow moving wiki pages from the UI. !16313 +- Filter groups and projects dropdowns of search page on backend. !16336 +- Adjust layout width for fixed layout. !16337 (George Tsiolis) +- Fix custom header logo design nitpick: Remove unneeded margin on empty logo text. !16383 (Markus Doits) +- File Upload UI can create LFS pointers based on .gitattributes. !16412 +- Fix Ctrl+Enter keyboard shortcut saving comment/note edit. !16415 +- Fix file search results when they match file contents with a number between two colons. !16462 +- Fix tooltip displayed for running manual actions. !16489 +- Allow trailing + on labels in board filters. !16490 +- Prevent JIRA issue identifier from being humanized. !16491 (Andrew McCallum) +- Add horizontal scroll to wiki tables. !16527 (George Tsiolis) +- Fix a bug calculating artifact size for project statistics. !16539 +- Stop loading spinner on error of issuable templates. !16600 (Takuya Noguchi) +- Allows html text in commits atom feed. !16603 (Jacopo Beschi @jacopo-beschi) +- Disable MR check out button when source branch is deleted. !16631 (Jacopo Beschi @jacopo-beschi) +- Fix export removal for hashed-storage projects within a renamed or deleted namespace. !16658 +- Default to HTTPS for all Gravatar URLs. !16666 +- Login via OAuth now only marks new users as external. !16672 +- Fix default avatar icon missing when Gravatar is disabled. !16681 (Felix Geyer) +- Change button group width on mobile. !16726 (George Tsiolis) +- Fix version information not showing on help page if commercial content display was disabled. !16743 +- Adds spacing between edit and delete tag btn in tag list. !16757 (Jacopo Beschi @jacopo-beschi) +- Fix 500 error when loading a merge request with an invalid comment. !16795 +- Deleting an upload will correctly clean up the filesystem. !16799 +- Cleanup new branch/merge request form in issues. !16854 +- Fix GitLab import leaving group_id on ProjectLabel. !16877 +- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881 +- Trigger change event on filename input when file template is applied. !16911 (Sebastian Klingler) +- Fixes different margins between buttons in tag list. !16927 (Jacopo Beschi @jacopo-beschi) +- Close low level rugged repository in project cache worker. !16930 (Bastian Blank) +- Override group sidebar links. !16942 (George Tsiolis) +- Avoid running `PopulateForkNetworksRange`-migration multiple times. !16988 +- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019 +- Fix monaco editor features which were incompatable with GitLab CDN settings. !17021 +- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054 +- Fix cnacel edit note button reverting changes. !42462 +- For issues display time of last edit of title or description instead of time of any attribute change. +- Handle all Psych YAML parser exceptions (fixes #41209). +- Fix validation of environment scope of variables. +- Display user friendly error message if rebase fails. +- Hide new branch and tag links for projects with an empty repo. +- Fix protected branches API to accept name parameter with dot. +- Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check rake task. +- Keep subscribers when promoting labels to group labels. +- Replace verified badge icons and uniform colors. +- Fix error on changes tab when merge request cannot be created. +- Ignore leading slashes when searching for files within context of repository. (Andrew McCallum) +- Close and do not reload MR diffs when source branch is deleted. +- Bypass commits title markdown on notes. +- Reload MRs memoization after diffs creation. +- Return more consistent values for merge_status on MR APIs. +- Contribution calendar label was cut off. (Branka Martinovic) +- LDAP Person no longer throws exception on invalid entry. +- Fix bug where award emojis would be lost when moving issues between projects. +- Fix not all events being shown in group dashboard. +- Fix JIRA not working when a trailing slash is included. +- Fix squash not working when diff contained non-ASCII data. +- Remove erroneous text in shared runners page that suggested more runners available. +- Execute system hooks after-commit when executing project hooks. +- Makes forking protect default branch on completion. +- Validate user, group and project paths consistently, and only once. +- Validate user namespace before saving so that errors persist on model. +- Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController. +- Fix duplicate item in protected branch/tag dropdown. +- Open visibility level help in a new tab. (Jussi Räsänen) + +### Deprecated (1 change) + +- Add note within ux documentation that further changes should be made within the design.gitlab project. + +### Changed (20 changes, 7 of them are from the community) + +- Show coverage to two decimal points in coverage badge. !10083 (Jeff Stubler) +- Update 'removed assignee' note to include old assignee reference. !16301 (Maurizio De Santis) +- Move row containing Projects, Users and Groups count to the top in admin dashboard. !16421 +- Add Auto DevOps Domain application setting. !16604 +- Changes Revert this merge request text. !16611 (Jacopo Beschi @jacopo-beschi) +- Link Auto DevOps settings to Clusters page. !16641 +- Internationalize charts page. !16687 (selrahman) +- Internationalize graph page selrahman. !16688 (Shah El-Rahman) +- Save traces as artifacts. !16702 +- Hide variable values on pipeline schedule edit page. !16729 +- Update runner info on all authenticated requests. !16756 +- Improve issue note dropdown and mr button. !16758 (George Tsiolis) +- Replace "cluster" with "Kubernetes cluster". !16778 +- Enable Prometheus metrics for deployed Ingresses. !16866 (joshlambert) +- Rename button to enable CI/CD configuration to "Set up CI/CD". !16870 +- Double padding for file-content wiki class on larger screens. +- Improve wording about additional costs for Ingress on custom clusters. +- Last push widget will show banner for new pushes to previously merged branch. +- Save user ID and username in Grape API log (api_json.log). +- Include subgroup issues and merge requests on the group page. + +### Performance (14 changes, 1 of them is from the community) + +- Fix double query execution on groups page. !16314 +- Speed up loading merged merge requests when they contained a lot of commits before merging. !16320 +- Properly memoize some predicate methods. !16329 +- Reduce the number of Prometheus metrics. !16443 +- Only highlight search results under the highlighting size limit. !16462 +- Add fast-blank. !16468 +- Move BoardList vue component to vue file. !16888 (George Tsiolis) +- Fix N+1 query problem for snippets dashboard. !16944 +- Optimize search queries on the search page by setting a limit for matching records. +- Store number of commits in merge_request_diffs table. +- Improve performance of target branch dropdown. +- Remove duplicate calls of MergeRequest#can_be_reverted?. +- Stop checking if discussions are in a mergeable state if the MR isn't. +- Remove N+1 queries with /projects/:project_id/{access_requests,members} API endpoints. + +### Added (28 changes, 10 of them are from the community) + +- Add link on commit page to merge request that introduced that commit. !13713 (Hiroyuki Sato) +- System hooks for Merge Requests. !14387 (Alexis Reigel) +- Add `pipelines` endpoint to merge requests API. !15454 (Tony Rom <thetonyrom@gmail.com>) +- Adds Rubocop rule for line break around conditionals. !15739 (Jacopo Beschi @jacopo-beschi) +- Add Colors to GitLab Flavored Markdown. !16095 (Tony Rom <thetonyrom@gmail.com>) +- Initial work to add notification reason to emails. !16160 (Mario de la Ossa) +- Implement multi server support and use kube proxy to connect to Prometheus servers inside K8S cluster. !16182 +- Add ability to transfer a group into another group. !16302 +- Add blue dot feature highlight to make GKE Clusters more visible to users. !16379 +- Add section headers to plus button dropdown. !16394 (George Tsiolis) +- Support PostgreSQL 10. !16471 +- Enables Project Milestone Deletion via the API. !16478 (Jacopo Beschi @jacopo-beschi) +- Add realtime ci status for the repository -> files view. !16523 +- User can now git push to create a new project. !16547 +- Improve empty project overview. !16617 (George Tsiolis) +- Added uploader metadata to the uploads. !16779 +- Added ldap config setting to lower case the username. !16791 +- Add search support into the API. !16878 +- Backport of LFS File Locking API. !16935 +- Add a link to documentation on how to get external ip in the Kubernetes cluster details page. !16937 +- Add sorting options for /users API (admin only). !16945 +- Adds sorting to deployments API. (Jacopo Beschi @jacopo-beschi) +- Add rake task to check integrity of uploaded files. +- Add backend for persistently dismissably callouts. +- Track and act upon the number of executed queries. +- Add a gRPC health check to ensure Gitaly is up. +- Log and send a system hook if a blocked user attempts to login. +- Add Gitaly Servers admin dashboard. + +### Other (25 changes, 7 of them are from the community) + +- Updated the katex library. !15864 +- Add modal for deleting a milestone. !16229 +- Remove unused CSS selectors for Cycle Analytics. !16270 (Takuya Noguchi) +- Add reason to keep postgresql 9.2 for CI. !16277 (Takuya Noguchi) +- Adjust modal style to new design. !16310 +- Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH pull. !16586 +- Set timezone for karma to UTC. !16602 (Takuya Noguchi) +- Make Gitaly RepositoryExists opt-out. !16680 +- Update minimum git version to 2.9.5. !16683 +- Disable throwOnError in KaTeX to reveal user where is the problem. !16684 (Jakub Jirutka) +- fix documentation about node version. !16720 (Tobias Gurtzick) +- Enable RuboCop Style/RegexpLiteral. !16752 (Takuya Noguchi) +- Add confirmation-input component. !16816 +- Add unique constraint to trending_projects#project_id. !16846 +- Add foreign key and NOT NULL constraints to todos table. !16849 +- Include branch in mobile view for pipelines. !16910 (George Tsiolis) +- Downgrade google-protobuf gem. !16941 +- Refactors mr widget components into vue files and adds i18n. +- increase-readability-of-colored-text-in-job-output-log. +- Finish any remaining jobs for issues.closed_at. +- Translate issuable sidebar. +- Set standard disabled state for all buttons. +- Upgrade GitLab Workhorse to v3.6.0. +- Improve readability of underlined links for dyslexic users. +- Adds empty state illustration for pending job. + + +## 10.4.4 (2018-02-16) + +### Security (1 change) + +- Update nokogiri to 1.8.2. !16807 + +### Fixed (9 changes) + +- Fix 500 error when loading a merge request with an invalid comment. !16795 +- Cleanup new branch/merge request form in issues. !16854 +- Fix GitLab import leaving group_id on ProjectLabel. !16877 +- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881 +- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019 +- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054 +- Validate user namespace before saving so that errors persist on model. +- LDAP Person no longer throws exception on invalid entry. +- Fix JIRA not working when a trailing slash is included. + + ## 10.4.3 (2018-02-05) ### Security (4 changes) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 9a55e28031d..92fc430ae8f 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.81.0 +0.82.0 @@ -81,7 +81,7 @@ gem 'gollum-lib', '~> 4.2', require: false gem 'gollum-rugged_adapter', '~> 0.4.4', require: false # Language detection -gem 'github-linguist', '~> 4.7.0', require: 'linguist' +gem 'github-linguist', '~> 5.3.3', require: 'linguist' # API gem 'grape', '~> 1.0' @@ -401,6 +401,7 @@ gem 'sys-filesystem', '~> 1.1.6' # SSH host key support gem 'net-ssh', '~> 4.1.0' +gem 'sshkey', '~> 1.9.0' # Required for ED25519 SSH host key support group :ed25519 do @@ -414,7 +415,7 @@ gem 'gitaly-proto', '~> 0.84.0', require: 'gitaly' # Locked until https://github.com/google/protobuf/issues/4210 is closed gem 'google-protobuf', '= 3.5.1' -gem 'toml-rb', '~> 0.3.15', require: false +gem 'toml-rb', '~> 1.0.0', require: false # Feature toggles gem 'flipper', '~> 0.11.0' diff --git a/Gemfile.lock b/Gemfile.lock index 22c4fc0ef28..57ff086f0b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -288,11 +288,11 @@ GEM gitaly-proto (0.84.0) google-protobuf (~> 3.1) grpc (~> 1.0) - github-linguist (4.7.6) - charlock_holmes (~> 0.7.3) + github-linguist (5.3.3) + charlock_holmes (~> 0.7.5) escape_utils (~> 1.1.0) mime-types (>= 1.19) - rugged (>= 0.23.0b) + rugged (>= 0.25.1) github-markup (1.6.1) gitlab-flowdock-git-hook (1.0.1) flowdock (~> 0.7) @@ -895,6 +895,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.13) + sshkey (1.9.0) stackprof (0.2.10) state_machines (0.4.0) state_machines-activemodel (0.4.0) @@ -923,7 +924,7 @@ GEM timfel-krb5-auth (0.8.3) toml (0.1.2) parslet (~> 1.5.0) - toml-rb (0.3.15) + toml-rb (1.0.0) citrus (~> 3.0, > 3.0) truncato (0.7.10) htmlentities (~> 4.3.1) @@ -1057,7 +1058,7 @@ DEPENDENCIES gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) gitaly-proto (~> 0.84.0) - github-linguist (~> 4.7.0) + github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) gitlab-styles (~> 2.3) @@ -1192,6 +1193,7 @@ DEPENDENCIES spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.1.0) sprockets (~> 3.7.0) + sshkey (~> 1.9.0) stackprof (~> 0.2.10) state_machines-activerecord (~> 0.4.0) sys-filesystem (~> 1.1.6) @@ -1199,7 +1201,7 @@ DEPENDENCIES test_after_commit (~> 1.1) thin (~> 1.7.0) timecop (~> 0.8.0) - toml-rb (~> 0.3.15) + toml-rb (~> 1.0.0) truncato (~> 0.7.9) u2f (~> 0.2.1) uglifier (~> 2.7.2) @@ -1 +1 @@ -10.5.0-pre +10.6.0-pre diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 3283ce5ec36..9456edebccb 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -312,7 +312,7 @@ class AwardsHandler { } getAwardUrl() { - return this.getVotesBlock().data('award-url'); + return this.getVotesBlock().data('awardUrl'); } checkMutuality(votesBlock, emoji) { diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js index cdea625fc8c..b669b63d23c 100644 --- a/app/assets/javascripts/behaviors/copy_to_clipboard.js +++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js @@ -2,7 +2,7 @@ import Clipboard from 'clipboard'; function showTooltip(target, title) { const $target = $(target); - const originalTitle = $target.data('original-title'); + const originalTitle = $target.data('originalTitle'); if (!$target.data('hideTooltip')) { $target diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js index 2cf8f4fa935..312edc0cd69 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js +++ b/app/assets/javascripts/behaviors/quick_submit.js @@ -43,7 +43,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => { const $form = $(e.target).closest('form'); const $submitButton = $form.find('input[type=submit], button[type=submit]').first(); - if (!$submitButton.attr('disabled')) { + if (!$submitButton.prop('disabled')) { $submitButton.trigger('click', [e]); if (!isInIssuePage()) { diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js index 035a7e5c431..e10cb2e3dc4 100644 --- a/app/assets/javascripts/behaviors/requires_input.js +++ b/app/assets/javascripts/behaviors/requires_input.js @@ -40,7 +40,7 @@ $.fn.requiresInput = function requiresInput() { // based on the option selected function hideOrShowHelpBlock(form) { const selected = $('.js-select-namespace option:selected'); - if (selected.length && selected.data('options-parent') === 'groups') { + if (selected.length && selected.data('optionsParent') === 'groups') { form.find('.help-block').hide(); } else if (selected.length) { form.find('.help-block').show(); diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 6b06344f5ba..931ed042dfd 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -4,16 +4,16 @@ import NewCommitForm from '../new_commit_form'; import EditBlob from './edit_blob'; import BlobFileDropzone from '../blob/blob_file_dropzone'; -$(() => { +export default () => { const editBlobForm = $('.js-edit-blob-form'); const uploadBlobForm = $('.js-upload-blob-form'); const deleteBlobForm = $('.js-delete-blob-form'); if (editBlobForm.length) { - const urlRoot = editBlobForm.data('relative-url-root'); - const assetsPath = editBlobForm.data('assets-prefix'); - const blobLanguage = editBlobForm.data('blob-language'); - const currentAction = $('.js-file-title').data('current-action'); + const urlRoot = editBlobForm.data('relativeUrlRoot'); + const assetsPath = editBlobForm.data('assetsPrefix'); + const blobLanguage = editBlobForm.data('blobLanguage'); + const currentAction = $('.js-file-title').data('currentAction'); new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction); new NewCommitForm(editBlobForm); @@ -34,4 +34,4 @@ $(() => { if (deleteBlobForm.length) { new NewCommitForm(deleteBlobForm); } -}); +}; diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index a25f7fb3dcd..d4f6adaccbc 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -59,7 +59,7 @@ export default class EditBlob { if (paneId === '#preview') { this.$toggleButton.hide(); - axios.post(currentLink.data('preview-url'), { + axios.post(currentLink.data('previewUrl'), { content: this.editor.getValue(), }) .then(({ data }) => { diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js index cf0bb5f5376..362ef43e6f7 100644 --- a/app/assets/javascripts/boards/components/new_list_dropdown.js +++ b/app/assets/javascripts/boards/components/new_list_dropdown.js @@ -25,7 +25,7 @@ $(document).off('created.label').on('created.label', (e, label) => { gl.issueBoards.newListDropdownInit = () => { $('.js-new-board-list').each(function () { const $this = $(this); - new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path')); + new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath')); $this.glDropdown({ data(term, callback) { diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 184665f395c..0df1f7a6f82 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -1,7 +1,8 @@ /* eslint-disable class-methods-use-this */ import FilteredSearchContainer from '../filtered_search/container'; +import FilteredSearchManager from '../filtered_search/filtered_search_manager'; -export default class FilteredSearchBoards extends gl.FilteredSearchManager { +export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { super('boards'); diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js index c11b7d5f340..db96da4ccba 100644 --- a/app/assets/javascripts/commons/bootstrap.js +++ b/app/assets/javascripts/commons/bootstrap.js @@ -13,6 +13,6 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; // custom jQuery functions $.fn.extend({ - disable() { return $(this).attr('disabled', 'disabled').addClass('disabled'); }, - enable() { return $(this).removeAttr('disabled').removeClass('disabled'); }, + disable() { return $(this).prop('disabled', true).addClass('disabled'); }, + enable() { return $(this).prop('disabled', false).removeClass('disabled'); }, }); diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index e2a008e8904..d5a35ed81a6 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -13,7 +13,7 @@ export default class Compare { $dropdown = $(dropdown); return $dropdown.glDropdown({ selectable: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), filterable: true, id: function(obj, $el) { return $el.data('id'); diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 59899e97be1..fa341918fc1 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -9,7 +9,7 @@ export default function initCompareAutocomplete() { $dropdown = $(this); selected = $dropdown.data('selected'); const $dropdownContainer = $dropdown.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); + const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer); $dropdown.glDropdown({ data: function(term, callback) { @@ -25,7 +25,7 @@ export default function initCompareAutocomplete() { selectable: true, filterable: true, filterRemote: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), filterInput: 'input[type="search"]', renderRow: function(ref) { var link; diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 3ab8f3ab7ad..3df082e8c0c 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -68,7 +68,7 @@ export default class Diff { } const file = $target.parents('.diff-file'); - const link = file.data('blob-diff-path'); + const link = file.data('blobDiffPath'); const view = file.data('view'); const params = { since, to, bottom, offset, unfold, view }; @@ -121,7 +121,7 @@ export default class Diff { } // eslint-disable-next-line class-methods-use-this diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); + return $('.inline-parallel-buttons a.active').data('viewType'); } // eslint-disable-next-line class-methods-use-this lineNumbers(line) { diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index e0422057090..38c42a11b4e 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js @@ -15,7 +15,7 @@ import './components/resolve_discussion_btn'; import './components/diff_note_avatars'; import './components/new_issue_for_discussion'; -$(() => { +export default () => { const projectPathHolder = document.querySelector('.merge-request') || document.querySelector('.commit-box'); const projectPath = projectPathHolder.dataset.projectPath; const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn'; @@ -75,4 +75,4 @@ $(() => { }); $(window).trigger('resize.nav'); -}); +}; diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 8f708dde063..80ee41d3bbb 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -43,345 +43,24 @@ var Dispatcher; }); switch (page) { - case 'projects:environments:metrics': - import('./pages/projects/environments/metrics') - .then(callDefault) - .catch(fail); - break; case 'projects:merge_requests:index': case 'projects:issues:index': case 'projects:issues:show': - shortcut_handler = true; - break; - case 'projects:milestones:index': - import('./pages/projects/milestones/index') - .then(callDefault) - .catch(fail); - break; - case 'projects:milestones:show': - import('./pages/projects/milestones/show') - .then(callDefault) - .catch(fail); - break; - case 'groups:milestones:show': - import('./pages/groups/milestones/show') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:milestones:show': - import('./pages/dashboard/milestones/show') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:issues': - import('./pages/dashboard/issues') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:merge_requests': - import('./pages/dashboard/merge_requests') - .then(callDefault) - .catch(fail); - break; - case 'groups:issues': - import('./pages/groups/issues') - .then(callDefault) - .catch(fail); - break; - case 'groups:merge_requests': - import('./pages/groups/merge_requests') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:todos:index': - import('./pages/dashboard/todos/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:jobs:index': - import('./pages/admin/jobs/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:projects:index': - import('./pages/admin/projects/index/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:index': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:show': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:projects:index': - case 'dashboard:projects:starred': - import('./pages/dashboard/projects') - .then(callDefault) - .catch(fail); - break; - case 'explore:projects:index': - case 'explore:projects:trending': - case 'explore:projects:starred': - import('./pages/explore/projects') - .then(callDefault) - .catch(fail); - break; - case 'explore:groups:index': - import('./pages/explore/groups') - .then(callDefault) - .catch(fail); - break; - case 'projects:milestones:new': - case 'projects:milestones:create': - import('./pages/projects/milestones/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:milestones:edit': - case 'projects:milestones:update': - import('./pages/projects/milestones/edit') - .then(callDefault) - .catch(fail); - break; - case 'groups:milestones:new': - case 'groups:milestones:create': - import('./pages/groups/milestones/new') - .then(callDefault) - .catch(fail); - break; - case 'groups:milestones:edit': - case 'groups:milestones:update': - import('./pages/groups/milestones/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:compare:show': - import('./pages/projects/compare/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:branches:new': - import('./pages/projects/branches/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:branches:create': - import('./pages/projects/branches/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:branches:index': - import('./pages/projects/branches/index') - .then(callDefault) - .catch(fail); - break; case 'projects:issues:new': - import('./pages/projects/issues/new') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:issues:edit': - import('./pages/projects/issues/edit') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:merge_requests:creations:new': - import('./pages/projects/merge_requests/creations/new') - .then(callDefault) - .catch(fail); case 'projects:merge_requests:creations:diffs': - import('./pages/projects/merge_requests/creations/diffs') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:merge_requests:edit': - import('./pages/projects/merge_requests/edit') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'projects:tags:new': - import('./pages/projects/tags/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:show': - import('./pages/projects/snippets/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:new': - case 'projects:snippets:create': - import('./pages/projects/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:services:edit': - import('./pages/projects/services/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:edit': - case 'projects:snippets:update': - import('./pages/projects/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'snippets:new': - import('./pages/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'snippets:edit': - import('./pages/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'snippets:create': - import('./pages/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'snippets:update': - import('./pages/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:releases:edit': - import('./pages/projects/releases/edit') - .then(callDefault) - .catch(fail); - break; case 'projects:merge_requests:show': - import('./pages/projects/merge_requests/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'dashboard:activity': - import('./pages/dashboard/activity') - .then(callDefault) - .catch(fail); - break; case 'projects:commit:show': - import('./pages/projects/commit/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'projects:commit:pipelines': - import('./pages/projects/commit/pipelines') - .then(callDefault) - .catch(fail); - break; case 'projects:activity': - import('./pages/projects/activity') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:commits:show': - import('./pages/projects/commits/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:show': - shortcut_handler = true; - break; - case 'projects:edit': - import('./pages/projects/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:imports:show': - import('./pages/projects/imports/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:pipelines:new': - case 'projects:pipelines:create': - import('./pages/projects/pipelines/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:pipelines:builds': - case 'projects:pipelines:failures': - case 'projects:pipelines:show': - import('./pages/projects/pipelines/builds') - .then(callDefault) - .catch(fail); - break; - case 'groups:activity': - import('./pages/groups/activity') - .then(callDefault) - .catch(fail); - break; case 'groups:show': - shortcut_handler = true; - break; - case 'groups:group_members:index': - import('./pages/groups/group_members/index') - .then(callDefault) - .catch(fail); - break; - case 'projects:project_members:index': - import('./pages/projects/project_members') - .then(callDefault) - .catch(fail); - break; - case 'groups:create': - case 'groups:new': - import('./pages/groups/new') - .then(callDefault) - .catch(fail); - break; - case 'groups:edit': - import('./pages/groups/edit') - .then(callDefault) - .catch(fail); - break; - case 'admin:groups:create': - case 'admin:groups:new': - import('./pages/admin/groups/new') - .then(callDefault) - .catch(fail); - break; - case 'admin:groups:edit': - import('./pages/admin/groups/edit') - .then(callDefault) - .catch(fail); - break; case 'projects:tree:show': - import('./pages/projects/tree/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:find_file:show': - import('./pages/projects/find_file/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:blob:show': - import('./pages/projects/blob/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:blame:show': - import('./pages/projects/blame/show') - .then(callDefault) - .catch(fail); shortcut_handler = true; break; case 'groups:labels:new': @@ -436,11 +115,6 @@ var Dispatcher; .catch(fail); shortcut_handler = true; break; - case 'help:index': - import('./pages/help') - .then(callDefault) - .catch(fail); - break; case 'search:show': import('./pages/search/show') .then(callDefault) @@ -472,11 +146,6 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'snippets:show': - import('./pages/snippets/show') - .then(callDefault) - .catch(fail); - break; case 'import:fogbugz:new_user_map': import('./pages/import/fogbugz/new_user_map') .then(callDefault) @@ -492,18 +161,6 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'projects:clusters:show': - case 'projects:clusters:update': - case 'projects:clusters:destroy': - import('./pages/projects/clusters/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:clusters:index': - import('./pages/projects/clusters/index') - .then(callDefault) - .catch(fail); - break; case 'dashboard:groups:index': import('./pages/dashboard/groups/index') .then(callDefault) @@ -511,20 +168,7 @@ var Dispatcher; break; } switch (path[0]) { - case 'sessions': - import('./pages/sessions') - .then(callDefault) - .catch(fail); - break; - case 'omniauth_callbacks': - import('./pages/omniauth_callbacks') - .then(callDefault) - .catch(fail); - break; case 'admin': - import('./pages/admin') - .then(callDefault) - .catch(fail); switch (path[1]) { case 'broadcast_messages': import('./pages/admin/broadcast_messages') diff --git a/app/assets/javascripts/docs/docs_bundle.js b/app/assets/javascripts/docs/docs_bundle.js index a32bd6d0fc7..897439f56b0 100644 --- a/app/assets/javascripts/docs/docs_bundle.js +++ b/app/assets/javascripts/docs/docs_bundle.js @@ -4,10 +4,7 @@ function addMousetrapClick(el, key) { el.addEventListener('click', () => Mousetrap.trigger(key)); } -function domContentLoaded() { +export default () => { addMousetrapClick(document.querySelector('.js-trigger-shortcut'), '?'); addMousetrapClick(document.querySelector('.js-trigger-search-bar'), 's'); -} - -document.addEventListener('DOMContentLoaded', domContentLoaded); - +}; diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index bd4c58b7cb1..417258e0092 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -17,9 +17,9 @@ class DueDateSelect { this.$value = $block.find('.value'); this.$valueContent = $block.find('.value-content'); this.$sidebarValue = $('.js-due-date-sidebar-value', $block); - this.fieldName = $dropdown.data('field-name'); - this.abilityName = $dropdown.data('ability-name'); - this.issueUpdateURL = $dropdown.data('issue-update'); + this.fieldName = $dropdown.data('fieldName'); + this.abilityName = $dropdown.data('abilityName'); + this.issueUpdateURL = $dropdown.data('issueUpdate'); this.rawSelectedDate = null; this.displayedDate = null; diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 90020344748..6a4874e1ab8 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -25,7 +25,7 @@ export default { if (!this.userCanCreateNote) { // data-can-create-note is an empty string when true, otherwise undefined - this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === ''; + this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === ''; } this.isParallelView = Cookies.get('diff_view') === 'parallel'; diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js index c51d4b056af..b693084e434 100644 --- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js +++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js @@ -1,4 +1,5 @@ import eventHub from '../event_hub'; +import FilteredSearchTokenizer from '../filtered_search_tokenizer'; export default { name: 'RecentSearchesDropdownContent', @@ -23,7 +24,7 @@ export default { processedItems() { return this.items.map((item) => { const { tokens, searchToken } - = gl.FilteredSearchTokenizer.processTokens(item, this.allowedKeys); + = FilteredSearchTokenizer.processTokens(item, this.allowedKeys); const resultantTokens = tokens.map(token => ({ prefix: `${token.key}:`, diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js index a6cc079d720..5ddd0e5e690 100644 --- a/app/assets/javascripts/filtered_search/dropdown_emoji.js +++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js @@ -1,9 +1,10 @@ import Flash from '../flash'; import Ajax from '../droplab/plugins/ajax'; import Filter from '../droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; -class DropdownEmoji extends gl.FilteredSearchDropdown { +export default class DropdownEmoji extends FilteredSearchDropdown { constructor(options = {}) { super(options); this.config = { @@ -49,7 +50,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { itemClicked(e) { super.itemClicked(e, (selected) => { const name = selected.querySelector('.js-data-value').innerText.trim(); - return gl.DropdownUtils.getEscapedText(name); + return DropdownUtils.getEscapedText(name); }); } @@ -76,6 +77,3 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownEmoji = DropdownEmoji; diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 23040cd9eb8..184b34b7b5e 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -1,14 +1,17 @@ import Filter from '~/droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; -class DropdownHint extends gl.FilteredSearchDropdown { +export default class DropdownHint extends FilteredSearchDropdown { constructor(options = {}) { const { input, tokenKeys } = options; super(options); this.config = { Filter: { template: 'hint', - filterFunction: gl.DropdownUtils.filterHint.bind(null, { + filterFunction: DropdownUtils.filterHint.bind(null, { input, allowedKeys: tokenKeys.getKeys(), }), @@ -45,10 +48,10 @@ class DropdownHint extends gl.FilteredSearchDropdown { }); if (searchTerms.length > 0) { - gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); + FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); } - gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); + FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); } this.dismissDropdown(); this.dispatchInputEvent(); @@ -73,6 +76,3 @@ class DropdownHint extends gl.FilteredSearchDropdown { this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownHint = DropdownHint; diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js index 788fb1dc614..2ffda7e2037 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js @@ -1,9 +1,10 @@ import Flash from '../flash'; import Ajax from '../droplab/plugins/ajax'; import Filter from '../droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; -class DropdownNonUser extends gl.FilteredSearchDropdown { +export default class DropdownNonUser extends FilteredSearchDropdown { constructor(options = {}) { const { input, endpoint, symbol, preprocessing } = options; super(options); @@ -21,7 +22,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { }, }, Filter: { - filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input), + filterFunction: DropdownUtils.filterWithSymbol.bind(null, this.symbol, input), template: 'title', }, }; @@ -30,7 +31,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { itemClicked(e) { super.itemClicked(e, (selected) => { const title = selected.querySelector('.js-data-value').innerText.trim(); - return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`; + return `${this.symbol}${DropdownUtils.getEscapedText(title)}`; }); } @@ -45,6 +46,3 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownNonUser = DropdownNonUser; diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index a9e2b65def0..22421fc4868 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -1,9 +1,11 @@ import Flash from '../flash'; import AjaxFilter from '../droplab/plugins/ajax_filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; +import DropdownUtils from './dropdown_utils'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; -class DropdownUser extends gl.FilteredSearchDropdown { +export default class DropdownUser extends FilteredSearchDropdown { constructor(options = {}) { const { tokenKeys } = options; super(options); @@ -56,8 +58,8 @@ class DropdownUser extends gl.FilteredSearchDropdown { } getSearchInput() { - const query = gl.DropdownUtils.getSearchInput(this.input); - const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get()); + const query = DropdownUtils.getSearchInput(this.input); + const { lastToken } = FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get()); let value = lastToken || ''; @@ -78,6 +80,3 @@ class DropdownUser extends gl.FilteredSearchDropdown { this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownUser = DropdownUser; diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index cf8a9b0402b..9bc36c1f9b6 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -1,7 +1,10 @@ import _ from 'underscore'; import FilteredSearchContainer from './container'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; -class DropdownUtils { +export default class DropdownUtils { static getEscapedText(text) { let escapedText = text; const hasSpace = text.indexOf(' ') !== -1; @@ -24,7 +27,7 @@ class DropdownUtils { static filterWithSymbol(filterSymbol, input, item) { const updatedItem = item; - const searchInput = gl.DropdownUtils.getSearchInput(input); + const searchInput = DropdownUtils.getSearchInput(input); const title = updatedItem.title.toLowerCase(); let value = searchInput.toLowerCase(); @@ -114,9 +117,9 @@ class DropdownUtils { static filterHint(config, item) { const { input, allowedKeys } = config; const updatedItem = item; - const searchInput = gl.DropdownUtils.getSearchQuery(input); + const searchInput = DropdownUtils.getSearchQuery(input); const { lastToken, tokens } = - gl.FilteredSearchTokenizer.processTokens(searchInput, allowedKeys); + FilteredSearchTokenizer.processTokens(searchInput, allowedKeys); const lastKey = lastToken.key || lastToken || ''; const allowMultiple = item.type === 'array'; const itemInExistingTokens = tokens.some(t => t.key === item.hint); @@ -140,7 +143,7 @@ class DropdownUtils { const dataValue = selected.getAttribute('data-value'); if (dataValue) { - gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true); + FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true); } // Return boolean based on whether it was set @@ -190,7 +193,7 @@ class DropdownUtils { } } else if (token.classList.contains('input-token')) { const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); const input = FilteredSearchContainer.container.querySelector('.filtered-search'); const inputValue = input && input.value; @@ -211,7 +214,7 @@ class DropdownUtils { static getSearchInput(filteredSearchInput) { const inputValue = filteredSearchInput.value; - const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput); + const { right } = DropdownUtils.getInputSelectionPosition(filteredSearchInput); return inputValue.slice(0, right); } @@ -252,6 +255,3 @@ class DropdownUtils { }; } } - -window.gl = window.gl || {}; -gl.DropdownUtils = DropdownUtils; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js index 9e9a9ef74be..cfdd3380fc7 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js @@ -1,6 +1,9 @@ +import DropdownUtils from './dropdown_utils'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; + const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; -class FilteredSearchDropdown { +export default class FilteredSearchDropdown { constructor({ droplab, dropdown, input, filter }) { this.droplab = droplab; this.hookId = input && input.id; @@ -30,11 +33,11 @@ class FilteredSearchDropdown { const { selected } = e.detail; if (selected.tagName === 'LI' && selected.innerHTML) { - const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected); + const dataValueSet = DropdownUtils.setDataValueIfSelected(this.filter, selected); if (!dataValueSet) { const value = getValueFunction(selected); - gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); + FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); } this.resetFilters(); @@ -117,6 +120,3 @@ class FilteredSearchDropdown { } } } - -window.gl = window.gl || {}; -gl.FilteredSearchDropdown = FilteredSearchDropdown; 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 b2add862051..c64553a1b92 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -1,13 +1,20 @@ import _ from 'underscore'; import DropLab from '~/droplab/drop_lab'; import FilteredSearchContainer from './container'; - -class FilteredSearchDropdownManager { +import FilteredSearchTokenKeys from './filtered_search_token_keys'; +import DropdownUtils from './dropdown_utils'; +import DropdownHint from './dropdown_hint'; +import DropdownEmoji from './dropdown_emoji'; +import DropdownNonUser from './dropdown_non_user'; +import DropdownUser from './dropdown_user'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; + +export default class FilteredSearchDropdownManager { constructor(baseEndpoint = '', tokenizer, page, isGroup, filteredSearchTokenKeys) { this.container = FilteredSearchContainer.container; this.baseEndpoint = baseEndpoint.replace(/\/$/, ''); this.tokenizer = tokenizer; - this.filteredSearchTokenKeys = filteredSearchTokenKeys; + this.filteredSearchTokenKeys = filteredSearchTokenKeys || FilteredSearchTokenKeys; this.filteredSearchInput = this.container.querySelector('.filtered-search'); this.page = page; @@ -33,24 +40,24 @@ class FilteredSearchDropdownManager { const allowedMappings = { hint: { reference: null, - gl: 'DropdownHint', + gl: DropdownHint, element: this.container.querySelector('#js-dropdown-hint'), }, }; const availableMappings = { author: { reference: null, - gl: 'DropdownUser', + gl: DropdownUser, element: this.container.querySelector('#js-dropdown-author'), }, assignee: { reference: null, - gl: 'DropdownUser', + gl: DropdownUser, element: this.container.querySelector('#js-dropdown-assignee'), }, milestone: { reference: null, - gl: 'DropdownNonUser', + gl: DropdownNonUser, extraArguments: { endpoint: `${this.baseEndpoint}/milestones.json`, symbol: '%', @@ -59,17 +66,17 @@ class FilteredSearchDropdownManager { }, label: { reference: null, - gl: 'DropdownNonUser', + gl: DropdownNonUser, extraArguments: { endpoint: `${this.baseEndpoint}/labels.json`, symbol: '~', - preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing, + preprocessing: DropdownUtils.duplicateLabelPreprocessing, }, element: this.container.querySelector('#js-dropdown-label'), }, 'my-reaction': { reference: null, - gl: 'DropdownEmoji', + gl: DropdownEmoji, element: this.container.querySelector('#js-dropdown-my-reaction'), }, }; @@ -86,11 +93,11 @@ class FilteredSearchDropdownManager { static addWordToInput(tokenName, tokenValue = '', clicked = false) { const input = FilteredSearchContainer.container.querySelector('.filtered-search'); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue); input.value = ''; if (clicked) { - gl.FilteredSearchVisualTokens.moveInputToTheRight(); + FilteredSearchVisualTokens.moveInputToTheRight(); } } @@ -131,9 +138,9 @@ class FilteredSearchDropdownManager { const extraArguments = mappingKey.extraArguments || {}; const glArguments = Object.assign({}, defaultArguments, extraArguments); - // Passing glArguments to `new gl[glClass](<arguments>)` + // Passing glArguments to `new glClass(<arguments>)` mappingKey.reference = - new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))(); + new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); } if (firstLoad) { @@ -171,7 +178,7 @@ class FilteredSearchDropdownManager { } setDropdown() { - const query = gl.DropdownUtils.getSearchQuery(true); + const query = DropdownUtils.getSearchQuery(true); const { lastToken, searchToken } = this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys()); @@ -216,6 +223,3 @@ class FilteredSearchDropdownManager { this.droplab.destroy(); } } - -window.gl = window.gl || {}; -gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager; diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 532a5fe1090..e294b629bd0 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -1,15 +1,23 @@ import _ from 'underscore'; +import { + getParameterByName, + getUrlParamsArray, +} from '~/lib/utils/common_utils'; import { visitUrl } from '../lib/utils/url_utility'; import Flash from '../flash'; import FilteredSearchContainer from './container'; -import RecentSearchesRoot from './recent_searches_root'; import FilteredSearchTokenKeys from './filtered_search_token_keys'; +import RecentSearchesRoot from './recent_searches_root'; import RecentSearchesStore from './stores/recent_searches_store'; import RecentSearchesService from './services/recent_searches_service'; import eventHub from './event_hub'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; +import DropdownUtils from './dropdown_utils'; -class FilteredSearchManager { +export default class FilteredSearchManager { constructor({ page, filteredSearchTokenKeys = FilteredSearchTokenKeys, @@ -66,8 +74,8 @@ class FilteredSearchManager { }); if (this.filteredSearchInput) { - this.tokenizer = gl.FilteredSearchTokenizer; - this.dropdownManager = new gl.FilteredSearchDropdownManager( + this.tokenizer = FilteredSearchTokenizer; + this.dropdownManager = new FilteredSearchDropdownManager( this.filteredSearchInput.getAttribute('data-base-endpoint') || '', this.tokenizer, this.page, @@ -85,7 +93,6 @@ class FilteredSearchManager { this.bindEvents(); this.loadSearchParamsFromURL(); this.dropdownManager.setDropdown(); - this.cleanupWrapper = this.cleanup.bind(this); document.addEventListener('beforeunload', this.cleanupWrapper); } @@ -197,8 +204,8 @@ class FilteredSearchManager { // 8 = Backspace Key // 46 = Delete Key if (e.keyCode === 8 || e.keyCode === 46) { - const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); - const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken); + const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(lastVisualToken); const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue); if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { @@ -206,8 +213,8 @@ class FilteredSearchManager { if (backspaceCount === 2) { backspaceCount = 0; - this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); - gl.FilteredSearchVisualTokens.removeLastTokenPartial(); + this.filteredSearchInput.value = FilteredSearchVisualTokens.getLastTokenPartial(); + FilteredSearchVisualTokens.removeLastTokenPartial(); } } @@ -275,7 +282,7 @@ class FilteredSearchManager { e.stopImmediatePropagation(); const button = e.target.closest('.selectable'); - gl.FilteredSearchVisualTokens.selectToken(button, true); + FilteredSearchVisualTokens.selectToken(button, true); this.removeSelectedToken(); } } @@ -287,7 +294,7 @@ class FilteredSearchManager { const isElementTokensContainer = e.target.classList.contains('tokens-container'); if ((!isElementInFilteredSearch && !isElementInFilterDropdown) || isElementTokensContainer) { - gl.FilteredSearchVisualTokens.moveInputToTheRight(); + FilteredSearchVisualTokens.moveInputToTheRight(); this.dropdownManager.resetDropdowns(); } } @@ -300,13 +307,13 @@ class FilteredSearchManager { if (token && canEdit) { e.preventDefault(); e.stopPropagation(); - gl.FilteredSearchVisualTokens.editToken(token); + FilteredSearchVisualTokens.editToken(token); this.tokenChange(); } } toggleClearSearchButton() { - const query = gl.DropdownUtils.getSearchQuery(); + const query = DropdownUtils.getSearchQuery(); const hidden = 'hidden'; const hasHidden = this.clearSearchButton.classList.contains(hidden); @@ -318,7 +325,7 @@ class FilteredSearchManager { } handleInputPlaceholder() { - const query = gl.DropdownUtils.getSearchQuery(); + const query = DropdownUtils.getSearchQuery(); const placeholder = 'Search or filter results...'; const currentPlaceholder = this.filteredSearchInput.placeholder; @@ -338,7 +345,7 @@ class FilteredSearchManager { } removeSelectedToken() { - gl.FilteredSearchVisualTokens.removeSelectedToken(); + FilteredSearchVisualTokens.removeSelectedToken(); this.handleInputPlaceholder(); this.toggleClearSearchButton(); this.dropdownManager.updateCurrentDropdownOffset(); @@ -358,7 +365,7 @@ class FilteredSearchManager { let canClearToken = t.classList.contains('js-visual-token'); if (canClearToken) { - const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(t); + const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(t); canClearToken = this.canEdit && this.canEdit(tokenName, tokenValue); } @@ -386,12 +393,12 @@ class FilteredSearchManager { const { tokens, searchToken } = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys()); const { isLastVisualTokenValid } - = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (isLastVisualTokenValid) { tokens.forEach((t) => { input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, ''); - gl.FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`); + FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`); }); const fragments = searchToken.split(':'); @@ -404,10 +411,10 @@ class FilteredSearchManager { const searchTerms = inputValues.join(' '); input.value = input.value.replace(searchTerms, ''); - gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms); + FilteredSearchVisualTokens.addSearchVisualToken(searchTerms); } - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenKey); + FilteredSearchVisualTokens.addFilterVisualToken(tokenKey); input.value = input.value.replace(`${tokenKey}:`, ''); } } else { @@ -415,7 +422,7 @@ class FilteredSearchManager { const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g; if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') { - gl.FilteredSearchVisualTokens.addFilterVisualToken(searchToken); + FilteredSearchVisualTokens.addFilterVisualToken(searchToken); // Trim the last space as seen in the if statement above input.value = input.value.replace(searchToken, '').trim(); @@ -431,7 +438,7 @@ class FilteredSearchManager { saveCurrentSearchQuery() { // Don't save before we have fetched the already saved searches this.fetchingRecentSearchesPromise.then(() => { - const searchQuery = gl.DropdownUtils.getSearchQuery(); + const searchQuery = DropdownUtils.getSearchQuery(); if (searchQuery.length > 0) { const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery); this.recentSearchesService.save(resultantSearches); @@ -447,7 +454,7 @@ class FilteredSearchManager { } loadSearchParamsFromURL() { - const urlParams = gl.utils.getUrlParamsArray(); + const urlParams = getUrlParamsArray(); const params = this.getAllParams(urlParams); const usernameParams = this.getUsernameParams(); let hasFilteredSearch = false; @@ -463,7 +470,7 @@ class FilteredSearchManager { if (condition) { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(condition.tokenKey); - gl.FilteredSearchVisualTokens.addFilterVisualToken( + FilteredSearchVisualTokens.addFilterVisualToken( condition.tokenKey, condition.value, canEdit, @@ -492,7 +499,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue); - gl.FilteredSearchVisualTokens.addFilterVisualToken( + FilteredSearchVisualTokens.addFilterVisualToken( sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`, canEdit, @@ -503,7 +510,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'assignee'; const canEdit = this.canEdit && this.canEdit(tokenName); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); } } else if (!match && keyParam === 'author_id') { const id = parseInt(value, 10); @@ -511,7 +518,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'author'; const canEdit = this.canEdit && this.canEdit(tokenName); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); } } else if (!match && keyParam === 'search') { hasFilteredSearch = true; @@ -543,13 +550,13 @@ class FilteredSearchManager { search(state = null) { const paths = []; - const searchQuery = gl.DropdownUtils.getSearchQuery(); + const searchQuery = DropdownUtils.getSearchQuery(); this.saveCurrentSearchQuery(); const { tokens, searchToken } = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys()); - const currentState = state || gl.utils.getParameterByName('state') || 'opened'; + const currentState = state || getParameterByName('state') || 'opened'; paths.push(`state=${currentState}`); tokens.forEach((token) => { @@ -628,6 +635,3 @@ class FilteredSearchManager { return true; } } - -window.gl = window.gl || {}; -gl.FilteredSearchManager = FilteredSearchManager; diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js index f2e66503e5e..d75610f6d68 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js +++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js @@ -1,6 +1,6 @@ import './filtered_search_token_keys'; -class FilteredSearchTokenizer { +export default class FilteredSearchTokenizer { static processTokens(input, allowedKeys) { // Regex extracts `(token):(symbol)(value)` // Values that start with a double quote must end in a double quote (same for single) @@ -50,6 +50,3 @@ class FilteredSearchTokenizer { }; } } - -window.gl = window.gl || {}; -gl.FilteredSearchTokenizer = FilteredSearchTokenizer; 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 2e859d2de3a..a19bb882410 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -3,8 +3,9 @@ import AjaxCache from '../lib/utils/ajax_cache'; import Flash from '../flash'; import FilteredSearchContainer from './container'; import UsersCache from '../lib/utils/users_cache'; +import DropdownUtils from './dropdown_utils'; -class FilteredSearchVisualTokens { +export default class FilteredSearchVisualTokens { static getLastVisualTokenBeforeInput() { const inputLi = FilteredSearchContainer.container.querySelector('.input-token'); const lastVisualToken = inputLi && inputLi.previousElementSibling; @@ -74,7 +75,7 @@ class FilteredSearchVisualTokens { let processed = labels; if (!labels.preprocessed) { - processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels); + processed = DropdownUtils.duplicateLabelPreprocessing(labels); AjaxCache.override(labelsEndpoint, processed); processed.preprocessed = true; } @@ -90,7 +91,7 @@ class FilteredSearchVisualTokens { return AjaxCache.retrieve(labelsEndpoint) .then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint)) .then((labels) => { - const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue); + const matchingLabel = (labels || []).find(label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue); if (!matchingLabel) { return; @@ -259,11 +260,11 @@ class FilteredSearchVisualTokens { static tokenizeInput() { const input = FilteredSearchContainer.container.querySelector('.filtered-search'); const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (input.value) { if (isLastVisualTokenValid) { - gl.FilteredSearchVisualTokens.addSearchVisualToken(input.value); + FilteredSearchVisualTokens.addSearchVisualToken(input.value); } else { FilteredSearchVisualTokens.addValueToPreviousVisualTokenElement(input.value); } @@ -324,12 +325,12 @@ class FilteredSearchVisualTokens { if (!tokenContainer.lastElementChild.isEqualNode(inputLi)) { const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (!isLastVisualTokenValid) { - const lastPartial = gl.FilteredSearchVisualTokens.getLastTokenPartial(); - gl.FilteredSearchVisualTokens.removeLastTokenPartial(); - gl.FilteredSearchVisualTokens.addSearchVisualToken(lastPartial); + const lastPartial = FilteredSearchVisualTokens.getLastTokenPartial(); + FilteredSearchVisualTokens.removeLastTokenPartial(); + FilteredSearchVisualTokens.addSearchVisualToken(lastPartial); } tokenContainer.removeChild(inputLi); @@ -337,6 +338,3 @@ class FilteredSearchVisualTokens { } } } - -window.gl = window.gl || {}; -gl.FilteredSearchVisualTokens = FilteredSearchVisualTokens; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 15df7a7f989..e322756f256 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -485,7 +485,7 @@ GitLabDropdown = (function() { $target = $(e.target); if ($target && !$target.hasClass('dropdown-menu-close') && !$target.hasClass('dropdown-menu-close-icon') && - !$target.data('is-link')) { + !$target.data('isLink')) { e.stopPropagation(); return false; } else { diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index d200044b79f..2d40856e038 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -12,7 +12,7 @@ export default class GLForm { this.destroy(); // Setup the form this.setupForm(); - this.form.data('gl-form', this); + this.form.data('glForm', this); } destroy() { @@ -21,7 +21,7 @@ export default class GLForm { if (this.autoComplete) { this.autoComplete.destroy(); } - this.form.data('gl-form', null); + this.form.data('glForm', null); } setupForm() { diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index b33165f9402..6bf21f4f27d 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -11,7 +11,7 @@ export default class GpgBadges { badges.html('<i class="fa fa-spinner fa-spin"></i>'); const params = parseQueryStringIntoObject(form.serialize()); - return axios.get(form.data('signatures-path'), { params }) + return axios.get(form.data('signaturesPath'), { params }) .then(({ data }) => { data.signatures.forEach((signature) => { badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js deleted file mode 100644 index 534bc535bb6..00000000000 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ /dev/null @@ -1,4 +0,0 @@ -import Chart from 'vendor/Chart'; - -// export to global scope -window.Chart = Chart; diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index 65a2395fe29..12fc5f9b5c9 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -7,8 +7,8 @@ export default function groupsSelect() { window.GROUP_SELECT_PER_PAGE = 20; $('.ajax-groups-select').each(function setAjaxGroupsSelect2() { const $select = $(this); - const allAvailable = $select.data('all-available'); - const skipGroups = $select.data('skip-groups') || []; + const allAvailable = $select.data('allAvailable'); + const skipGroups = $select.data('skipGroups') || []; $select.select2({ placeholder: 'Search for a group', multiple: $select.hasClass('multiselect'), diff --git a/app/assets/javascripts/help/help.js b/app/assets/javascripts/help/help.js index 4a22ebf187d..d02477b19a2 100644 --- a/app/assets/javascripts/help/help.js +++ b/app/assets/javascripts/help/help.js @@ -1,6 +1,8 @@ // We will render the icons list here -if ($('#user-content-gitlab-icons').length > 0) { - const $iconsHeader = $('#user-content-gitlab-icons'); - const $iconsList = $('<div id="iconsList">ICONS</div>'); - $($iconsList).insertAfter($iconsHeader.parent()); -} +export default () => { + if ($('#user-content-gitlab-icons').length > 0) { + const $iconsHeader = $('#user-content-gitlab-icons'); + const $iconsList = $('<div id="iconsList">ICONS</div>'); + $($iconsList).insertAfter($iconsHeader.parent()); + } +}; diff --git a/app/assets/javascripts/how_to_merge.js b/app/assets/javascripts/how_to_merge.js index 19f4a946f73..12e6f24595a 100644 --- a/app/assets/javascripts/how_to_merge.js +++ b/app/assets/javascripts/how_to_merge.js @@ -1,12 +1,13 @@ -document.addEventListener('DOMContentLoaded', () => { - const modal = $('#modal_merge_info').modal({ - modal: true, - show: false, - }); - $('.how_to_merge_link').on('click', () => { - modal.show(); - }); - $('.modal-header .close').on('click', () => { - modal.hide(); - }); -}); +export default () => { + const modal = $('#modal_merge_info'); + + if (modal) { + modal.modal({ + modal: true, + show: false, + }); + + $('.how_to_merge_link').on('click', modal.show); + $('.modal-header .close').on('click', modal.hide); + } +}; diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js index af83a1ec0b4..142a220097b 100644 --- a/app/assets/javascripts/ide/monaco_loader.js +++ b/app/assets/javascripts/ide/monaco_loader.js @@ -6,6 +6,11 @@ monacoContext.require.config({ }, }); +// ignore CDN config and use local assets path for service worker which cannot be cross-domain +const relativeRootPath = (gon && gon.relative_url_root) || ''; +const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`; +window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` }; + // eslint-disable-next-line no-underscore-dangle window.__monaco_context__ = monacoContext; export default monacoContext.require; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 3f27cfc2f6d..2848fe003cb 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -6,8 +6,8 @@ export default class IntegrationSettingsForm { this.$form = $(formSelector); // Form Metadata - this.canTestService = this.$form.data('can-test'); - this.testEndPoint = this.$form.data('test-url'); + this.canTestService = this.$form.data('canTest'); + this.testEndPoint = this.$form.data('testUrl'); // Form Child Elements this.$serviceToggle = this.$form.find('#service_active'); diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 9afa9dea126..1338be0ec4b 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -78,6 +78,7 @@ taskListUpdateSuccess(data) { try { this.checkForSpam(data); + this.closeRecaptcha(); } catch (error) { if (error && error.name === 'SpamError') this.openRecaptcha(); } diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js index 03546f61d1f..71c0f894389 100644 --- a/app/assets/javascripts/issue_status_select.js +++ b/app/assets/javascripts/issue_status_select.js @@ -1,6 +1,6 @@ export default function issueStatusSelect() { $('.js-issue-status').each((i, el) => { - const fieldName = $(el).data('field-name'); + const fieldName = $(el).data('fieldName'); return $(el).glDropdown({ selectable: true, fieldName, diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index db53b04de0e..85a88ae409b 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -3,7 +3,7 @@ import JobMediator from './job_details_mediator'; import jobHeader from './components/header.vue'; import detailsBlock from './components/sidebar_details_block.vue'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { const dataset = document.getElementById('js-job-details-vue').dataset; const mediator = new JobMediator({ endpoint: dataset.endpoint }); @@ -55,4 +55,4 @@ document.addEventListener('DOMContentLoaded', () => { }); }, }); -}); +}; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 5ecf81ad11d..dc1930a997f 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -25,19 +25,19 @@ export default class LabelsSelect { $dropdown = $(dropdown); $dropdownContainer = $dropdown.closest('.labels-filter'); $toggleText = $dropdown.find('.dropdown-toggle-text'); - namespacePath = $dropdown.data('namespace-path'); - projectPath = $dropdown.data('project-path'); + namespacePath = $dropdown.data('namespacePath'); + projectPath = $dropdown.data('projectPath'); labelUrl = $dropdown.data('labels'); issueUpdateURL = $dropdown.data('issueUpdate'); selectedLabel = $dropdown.data('selected'); if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) { selectedLabel = selectedLabel.split(','); } - showNo = $dropdown.data('show-no'); - showAny = $dropdown.data('show-any'); + showNo = $dropdown.data('showNo'); + showAny = $dropdown.data('showAny'); showMenuAbove = $dropdown.data('showMenuAbove'); - defaultLabel = $dropdown.data('default-label'); - abilityName = $dropdown.data('ability-name'); + defaultLabel = $dropdown.data('defaultLabel'); + abilityName = $dropdown.data('abilityName'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); $form = $dropdown.closest('form, .js-issuable-update'); @@ -45,11 +45,11 @@ export default class LabelsSelect { $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); $value = $block.find('.value'); $loading = $block.find('.block-loading').fadeOut(); - fieldName = $dropdown.data('field-name'); + fieldName = $dropdown.data('fieldName'); useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown'); propertyName = useId ? 'id' : 'title'; initialSelected = $selectbox - .find('input[name="' + $dropdown.data('field-name') + '"]') + .find('input[name="' + $dropdown.data('fieldName') + '"]') .map(function () { return this.value; }).get(); @@ -268,7 +268,7 @@ export default class LabelsSelect { return defaultLabel; } }, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), id: function(label) { if (label.id <= 0) return label.title; diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index ab3cc29146a..1b4900827b8 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -4,7 +4,7 @@ import initFlyOutNav from './fly_out_nav'; function hideEndFade($scrollingTabs) { $scrollingTabs.each(function scrollTabsLoop() { const $this = $(this); - $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); + $this.siblings('.fade-right').toggleClass('scrolling', Math.round($this.width()) < $this.prop('scrollWidth')); }); } diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 2dc9cf0cc29..5dc98b4a920 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -138,7 +138,7 @@ textUtils.init = function(form) { return $('.js-md', form).off('click').on('click', function() { var $this; $this = $(this); - return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend')); + return self.updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend')); }); }; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index fbd381d8ff7..e5c1fce3db9 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -83,7 +83,7 @@ LineHighlighter.prototype.clickHandler = function(event) { var current, lineNumber, range; event.preventDefault(); this.clearHighlight(); - lineNumber = $(event.target).closest('a').data('line-number'); + lineNumber = $(event.target).closest('a').data('lineNumber'); current = this.hashToRange(this._hash); if (!(current[0] && event.shiftKey)) { // If there's no current selection, or there is but Shift wasn't held, diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index b99cb257ce3..dc9e5bb03f4 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -61,7 +61,7 @@ gl.lazyLoader = new LazyLoader({ observerNode: '#content-body', }); -$(() => { +document.addEventListener('DOMContentLoaded', () => { const $body = $('body'); const $document = $(document); const $window = $(window); @@ -220,7 +220,7 @@ $(() => { $document.on('click', '.js-confirm-danger', (e) => { const btn = $(e.target); const form = btn.closest('form'); - const text = btn.data('confirm-danger-message'); + const text = btn.data('confirmDangerMessage'); e.preventDefault(); // eslint-disable-next-line no-new diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index 52315e969d1..330ebed5f73 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -19,7 +19,7 @@ export default class Members { isSelectable(selected, $el) { return !$el.hasClass('is-active'); }, - fieldName: $btn.data('field-name'), + fieldName: $btn.data('fieldName'), id(selected, $el) { return $el.data('id'); }, @@ -51,7 +51,7 @@ export default class Members { } // eslint-disable-next-line class-methods-use-this getMemberListItems($el) { - const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); + const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('elId')}`); return { $memberListItem, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 3e97a8c758d..41971e92ec0 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -361,7 +361,7 @@ export default class MergeRequestTabs { } diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); + return $('.inline-parallel-buttons a.active').data('viewType'); } isDiffAction(action) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 6581be606eb..2841ecb558b 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -24,19 +24,19 @@ export default class MilestoneSelect { $els.each((i, dropdown) => { let collapsedSidebarLabelTemplate, milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; const $dropdown = $(dropdown); - const projectId = $dropdown.data('project-id'); + const projectId = $dropdown.data('projectId'); const milestonesUrl = $dropdown.data('milestones'); const issueUpdateURL = $dropdown.data('issueUpdate'); - const showNo = $dropdown.data('show-no'); - const showAny = $dropdown.data('show-any'); + const showNo = $dropdown.data('showNo'); + const showAny = $dropdown.data('showAny'); const showMenuAbove = $dropdown.data('showMenuAbove'); - const showUpcoming = $dropdown.data('show-upcoming'); - const showStarted = $dropdown.data('show-started'); - const useId = $dropdown.data('use-id'); - const defaultLabel = $dropdown.data('default-label'); - const defaultNo = $dropdown.data('default-no'); - const issuableId = $dropdown.data('issuable-id'); - const abilityName = $dropdown.data('ability-name'); + const showUpcoming = $dropdown.data('showUpcoming'); + const showStarted = $dropdown.data('showStarted'); + const useId = $dropdown.data('useId'); + const defaultLabel = $dropdown.data('defaultLabel'); + const defaultNo = $dropdown.data('defaultNo'); + const issuableId = $dropdown.data('issuableId'); + const abilityName = $dropdown.data('abilityName'); const $selectBox = $dropdown.closest('.selectbox'); const $block = $selectBox.closest('.block'); const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); @@ -114,7 +114,7 @@ export default class MilestoneSelect { } }, defaultLabel: defaultLabel, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), text: milestone => _.escape(milestone.title), id: (milestone) => { if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { @@ -166,7 +166,7 @@ export default class MilestoneSelect { } if (boardsStore) { - boardsStore[$dropdown.data('field-name')] = selected.name; + boardsStore[$dropdown.data('fieldName')] = selected.name; e.preventDefault(); } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { return Issuable.filterResults($dropdown.closest('form')); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 8efb8ac5320..f17b432cffd 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -219,7 +219,7 @@ export default class Notes { } editNote = $textarea.closest('.note'); if (editNote.length) { - originalText = $textarea.closest('form').data('original-note'); + originalText = $textarea.closest('form').data('originalNote'); newText = $textarea.val(); if (originalText !== newText) { if (!confirm('Are you sure you want to cancel editing this comment?')) { @@ -609,9 +609,9 @@ export default class Notes { */ addDiscussionNote($form, note, isNewDiffComment) { if ($form.attr('data-resolve-all') != null) { - var projectPath = $form.data('project-path'); - var discussionId = $form.data('discussion-id'); - var mergeRequestId = $form.data('noteable-iid'); + var projectPath = $form.data('projectPath'); + var discussionId = $form.data('discussionId'); + var mergeRequestId = $form.data('noteableIid'); if (ResolveService != null) { ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId); @@ -751,7 +751,7 @@ export default class Notes { form.removeClass('current-note-edit-form'); form.find('.js-finish-edit-warning').hide(); // Replace markdown textarea text with original note text. - return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note')); + return form.find('.js-note-text').val(form.find('form.edit-note').data('originalNote')); } /** @@ -776,7 +776,7 @@ export default class Notes { var $note, $notes; $note = $(el); $notes = $note.closest('.discussion-notes'); - const discussionId = $('.notes', $notes).data('discussion-id'); + const discussionId = $('.notes', $notes).data('discussionId'); if (typeof gl.diffNotesCompileComponents !== 'undefined') { if (gl.diffNoteApps[noteElId]) { @@ -897,7 +897,7 @@ export default class Notes { // DiffNote form.find('#note_position').val(dataHolder.attr('data-position')); - form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancel-text')); + form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancelText')); form.find('.js-note-target-close').remove(); form.find('.js-note-new-discussion').remove(); this.setupNoteForm(form); @@ -1037,7 +1037,7 @@ export default class Notes { removeDiscussionNoteForm(form) { var glForm, row; row = form.closest('tr'); - glForm = form.data('gl-form'); + glForm = form.data('glForm'); glForm.destroy(); form.find('.js-note-text').data('autosave').reset(); // show the reply button (will only work for replies) @@ -1122,8 +1122,8 @@ export default class Notes { return discardbtn.show(); } } else { - reopentext = reopenbtn.data('original-text'); - closetext = closebtn.data('original-text'); + reopentext = reopenbtn.data('originalText'); + closetext = closebtn.data('originalText'); if (reopenbtn.text() !== reopentext) { reopenbtn.text(reopentext); } @@ -1150,9 +1150,9 @@ export default class Notes { var $originalContentEl = $note.find('.original-note-content'); var originalContent = $originalContentEl.text().trim(); - var postUrl = $originalContentEl.data('post-url'); - var targetId = $originalContentEl.data('target-id'); - var targetType = $originalContentEl.data('target-type'); + var postUrl = $originalContentEl.data('postUrl'); + var targetId = $originalContentEl.data('targetId'); + var targetType = $originalContentEl.data('targetType'); this.glForm = new GLForm($editForm.find('form'), this.enableGFM); @@ -1513,9 +1513,9 @@ export default class Notes { // If comment intends to resolve discussion, do the same. if (isDiscussionResolve) { $form - .attr('data-discussion-id', $submitBtn.data('discussion-id')) + .attr('data-discussion-id', $submitBtn.data('discussionId')) .attr('data-resolve-all', 'true') - .attr('data-project-path', $submitBtn.data('project-path')); + .attr('data-project-path', $submitBtn.data('projectPath')); } // Show final note element on UI @@ -1587,7 +1587,7 @@ export default class Notes { this.addNoteError($form); }); - return $closeBtn.text($closeBtn.data('original-text')); + return $closeBtn.text($closeBtn.data('originalText')); } /** @@ -1642,7 +1642,7 @@ export default class Notes { this.updateNoteError(); }); - return $closeBtn.text($closeBtn.data('original-text')); + return $closeBtn.text($closeBtn.data('originalText')); } } diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index 9570d1c00aa..479a512ed65 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -3,11 +3,11 @@ import Flash from './flash'; export default function notificationsDropdown() { $(document).on('click', '.update-notification', function updateNotificationCallback(e) { e.preventDefault(); - if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { + if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') { return; } - const notificationLevel = $(this).data('notification-level'); + const notificationLevel = $(this).data('notificationLevel'); const form = $(this).parents('.notification-form:first'); form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index fd3105b1960..7e85bce0d73 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -56,7 +56,7 @@ export default { }, initLoadMore() { - $(document).unbind('scroll'); + $(document).off('scroll'); $(document).endlessScroll({ bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, diff --git a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js index d87e6304a24..66702ec4ca0 100644 --- a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js +++ b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js @@ -15,21 +15,21 @@ export default class AbuseReports { const $messageCellElement = $(this); const reportMessage = $messageCellElement.text(); if (reportMessage.length > MAX_MESSAGE_LENGTH) { - $messageCellElement.data('original-message', reportMessage); - $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.data('originalMessage', reportMessage); + $messageCellElement.data('messageTruncated', 'true'); $messageCellElement.text(truncate(reportMessage, MAX_MESSAGE_LENGTH)); } } toggleMessageTruncation() { const $messageCellElement = $(this); - const originalMessage = $messageCellElement.data('original-message'); + const originalMessage = $messageCellElement.data('originalMessage'); if (!originalMessage) return; - if ($messageCellElement.data('message-truncated') === 'true') { - $messageCellElement.data('message-truncated', 'false'); + if ($messageCellElement.data('messageTruncated') === 'true') { + $messageCellElement.data('messageTruncated', 'false'); $messageCellElement.text(originalMessage); } else { - $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.data('messageTruncated', 'true'); $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); } } diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js index 135c15c346b..45e05f111a7 100644 --- a/app/assets/javascripts/pages/admin/admin.js +++ b/app/assets/javascripts/pages/admin/admin.js @@ -16,9 +16,9 @@ export default function adminInit() { $('input#user_force_random_password').on('change', function randomPasswordClick() { const $elems = $('#user_password, #user_password_confirmation'); if ($(this).attr('checked')) { - $elems.val('').attr('disabled', true); + $elems.val('').prop('disabled', true); } else { - $elems.removeAttr('disabled'); + $elems.prop('disabled', false); } }); diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js index 885acfac6d0..b68ce5d32d8 100644 --- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js +++ b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js @@ -14,7 +14,7 @@ export default function initBroadcastMessagesForm() { $('div.broadcast-message-preview').css('color', previewColor); }); - const previewPath = $('textarea#broadcast_message_message').data('preview-path'); + const previewPath = $('textarea#broadcast_message_message').data('previewPath'); $('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() { const message = $(this).val(); diff --git a/app/assets/javascripts/pages/admin/groups/edit/index.js b/app/assets/javascripts/pages/admin/groups/edit/index.js index ff9ef8d2449..d3d125a1859 100644 --- a/app/assets/javascripts/pages/admin/groups/edit/index.js +++ b/app/assets/javascripts/pages/admin/groups/edit/index.js @@ -1,3 +1,3 @@ -import groupAvatar from '../../../../group_avatar'; +import groupAvatar from '~/group_avatar'; -export default () => groupAvatar(); +document.addEventListener('DOMContentLoaded', groupAvatar); diff --git a/app/assets/javascripts/pages/admin/groups/new/index.js b/app/assets/javascripts/pages/admin/groups/new/index.js index fb5c46e4729..21f1ce222ac 100644 --- a/app/assets/javascripts/pages/admin/groups/new/index.js +++ b/app/assets/javascripts/pages/admin/groups/new/index.js @@ -2,8 +2,8 @@ import BindInOut from '../../../../behaviors/bind_in_out'; import Group from '../../../../group'; import groupAvatar from '../../../../group_avatar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { BindInOut.initAll(); new Group(); // eslint-disable-line no-new groupAvatar(); -}; +}); diff --git a/app/assets/javascripts/pages/admin/index.js b/app/assets/javascripts/pages/admin/index.js index 8b843037d85..e50b61f09e2 100644 --- a/app/assets/javascripts/pages/admin/index.js +++ b/app/assets/javascripts/pages/admin/index.js @@ -1,3 +1,3 @@ import initAdmin from './admin'; -export default () => initAdmin(); +document.addEventListener('DOMContentLoaded', initAdmin); diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index 31d58eabaaf..5a4f8c6e745 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -1,12 +1,10 @@ import Vue from 'vue'; - import Translate from '~/vue_shared/translate'; - import stopJobsModal from './components/stop_jobs_modal.vue'; Vue.use(Translate); -export default () => { +document.addEventListener('DOMContentLoaded', () => { const stopJobsButton = document.getElementById('stop-jobs-button'); if (stopJobsButton) { // eslint-disable-next-line no-new @@ -27,4 +25,4 @@ export default () => { }, }); } -}; +}); diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js index a87b27090a8..3c597a1093e 100644 --- a/app/assets/javascripts/pages/admin/projects/index/index.js +++ b/app/assets/javascripts/pages/admin/projects/index/index.js @@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; import deleteProjectModal from './components/delete_project_modal.vue'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { Vue.use(Translate); const deleteProjectModalEl = document.getElementById('delete-project-modal'); @@ -34,4 +34,4 @@ export default () => { deleteModal.projectName = buttonProps.projectName; } }); -}; +}); diff --git a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue index 7b5e333011e..7b5e333011e 100644 --- a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue diff --git a/app/assets/javascripts/pages/admin/users/shared/index.js b/app/assets/javascripts/pages/admin/users/index.js index d2a0f82fa2b..4f5d6b55031 100644 --- a/app/assets/javascripts/pages/admin/users/shared/index.js +++ b/app/assets/javascripts/pages/admin/users/index.js @@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; import deleteUserModal from './components/delete_user_modal.vue'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { Vue.use(Translate); const deleteUserModalEl = document.getElementById('delete-user-modal'); @@ -40,4 +40,4 @@ export default () => { deleteModal.username = buttonProps.username; } }); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/activity/index.js b/app/assets/javascripts/pages/dashboard/activity/index.js index 95faf1f1e98..1b887cad496 100644 --- a/app/assets/javascripts/pages/dashboard/activity/index.js +++ b/app/assets/javascripts/pages/dashboard/activity/index.js @@ -1,3 +1,3 @@ import Activities from '~/activities'; -export default () => new Activities(); +document.addEventListener('DOMContentLoaded', () => new Activities()); diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js index 8a2aae706c0..9f235ed6a98 100644 --- a/app/assets/javascripts/pages/dashboard/groups/index/index.js +++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js @@ -1,5 +1,3 @@ -import initGroupsList from '../../../../groups'; +import initGroupsList from '~/groups'; -export default () => { - initGroupsList(); -}; +export default initGroupsList; diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/issues/index.js +++ b/app/assets/javascripts/pages/dashboard/issues/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/milestones/show/index.js b/app/assets/javascripts/pages/dashboard/milestones/show/index.js index 2e7a08a369c..397149aaa9e 100644 --- a/app/assets/javascripts/pages/dashboard/milestones/show/index.js +++ b/app/assets/javascripts/pages/dashboard/milestones/show/index.js @@ -1,7 +1,9 @@ import Milestone from '~/milestone'; import Sidebar from '~/right_sidebar'; +import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Milestone(); // eslint-disable-line no-new new Sidebar(); // eslint-disable-line no-new -}; + new MountMilestoneSidebar(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/dashboard/projects/index.js +++ b/app/assets/javascripts/pages/dashboard/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); diff --git a/app/assets/javascripts/pages/dashboard/todos/index/index.js b/app/assets/javascripts/pages/dashboard/todos/index/index.js index 77c23685943..9d2c2f2994f 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/index.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/index.js @@ -1,3 +1,3 @@ import Todos from './todos'; -export default () => new Todos(); +document.addEventListener('DOMContentLoaded', () => new Todos()); diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js index e59c38b8bc4..3c7edbdd7c7 100644 --- a/app/assets/javascripts/pages/explore/groups/index.js +++ b/app/assets/javascripts/pages/explore/groups/index.js @@ -2,7 +2,7 @@ import GroupsList from '~/groups_list'; import Landing from '~/landing'; import initGroupsList from '../../../groups'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new GroupsList(); // eslint-disable-line no-new initGroupsList(); const landingElement = document.querySelector('.js-explore-groups-landing'); @@ -13,4 +13,4 @@ export default function () { 'explore_groups_landing_dismissed', ); exploreGroupsLanding.toggle(); -} +}); diff --git a/app/assets/javascripts/pages/explore/projects/index.js b/app/assets/javascripts/pages/explore/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/explore/projects/index.js +++ b/app/assets/javascripts/pages/explore/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); diff --git a/app/assets/javascripts/pages/groups/activity/index.js b/app/assets/javascripts/pages/groups/activity/index.js index 95faf1f1e98..1b887cad496 100644 --- a/app/assets/javascripts/pages/groups/activity/index.js +++ b/app/assets/javascripts/pages/groups/activity/index.js @@ -1,3 +1,3 @@ import Activities from '~/activities'; -export default () => new Activities(); +document.addEventListener('DOMContentLoaded', () => new Activities()); diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index 1aeec55a4be..d44874c8741 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -1,7 +1,7 @@ import groupAvatar from '~/group_avatar'; import TransferDropdown from '~/groups/transfer_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { groupAvatar(); new TransferDropdown(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/groups/group_members/index/index.js b/app/assets/javascripts/pages/groups/group_members/index/index.js index 29319b97ae2..c22a164cd4e 100644 --- a/app/assets/javascripts/pages/groups/group_members/index/index.js +++ b/app/assets/javascripts/pages/groups/group_members/index/index.js @@ -4,8 +4,8 @@ import memberExpirationDate from '~/member_expiration_date'; import Members from '~/members'; import UsersSelect from '~/users_select'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { memberExpirationDate(); new Members(); new UsersSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js index fbdfabd1e95..d149b307e7f 100644 --- a/app/assets/javascripts/pages/groups/issues/index.js +++ b/app/assets/javascripts/pages/groups/issues/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.ISSUES, }); projectSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index f6d284bf9ef..a5cc1f34b63 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, }); projectSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/edit/index.js +++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/new/index.js +++ b/app/assets/javascripts/pages/groups/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js index c9a18353f2e..88f40b5278e 100644 --- a/app/assets/javascripts/pages/groups/milestones/show/index.js +++ b/app/assets/javascripts/pages/groups/milestones/show/index.js @@ -1,3 +1,3 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; -export default initMilestonesShow; +document.addEventListener('DOMContentLoaded', initMilestonesShow); diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js index 7850b90d3d2..b2f275dc5ea 100644 --- a/app/assets/javascripts/pages/groups/new/index.js +++ b/app/assets/javascripts/pages/groups/new/index.js @@ -2,8 +2,8 @@ import BindInOut from '~/behaviors/bind_in_out'; import Group from '~/group'; import groupAvatar from '~/group_avatar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { BindInOut.initAll(); new Group(); // eslint-disable-line no-new groupAvatar(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js index 5c763986da3..d7b35d2b26b 100644 --- a/app/assets/javascripts/pages/groups/show/index.js +++ b/app/assets/javascripts/pages/groups/show/index.js @@ -5,7 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown'; import NotificationsForm from '~/notifications_form'; import ProjectsList from '~/projects_list'; import ShortcutsNavigation from '~/shortcuts_navigation'; -import initGroupsList from '../../../groups'; +import initGroupsList from '~/groups'; document.addEventListener('DOMContentLoaded', () => { const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); diff --git a/app/assets/javascripts/pages/help/index.js b/app/assets/javascripts/pages/help/index.js deleted file mode 100644 index 4cf8afc4b7e..00000000000 --- a/app/assets/javascripts/pages/help/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import VersionCheckImage from '../../version_check_image'; - -export default () => VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); diff --git a/app/assets/javascripts/pages/help/index/index.js b/app/assets/javascripts/pages/help/index/index.js new file mode 100644 index 00000000000..05c81fc618b --- /dev/null +++ b/app/assets/javascripts/pages/help/index/index.js @@ -0,0 +1,7 @@ +import VersionCheckImage from '~/version_check_image'; +import docs from '~/docs/docs_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + docs(); + VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); +}); diff --git a/app/assets/javascripts/pages/help/show/index.js b/app/assets/javascripts/pages/help/show/index.js new file mode 100644 index 00000000000..ec426a850b6 --- /dev/null +++ b/app/assets/javascripts/pages/help/show/index.js @@ -0,0 +1,3 @@ +import initHelp from '~/help/help'; + +document.addEventListener('DOMContentLoaded', initHelp); diff --git a/app/assets/javascripts/pages/help/ui/index.js b/app/assets/javascripts/pages/help/ui/index.js new file mode 100644 index 00000000000..709ca2f3828 --- /dev/null +++ b/app/assets/javascripts/pages/help/ui/index.js @@ -0,0 +1,3 @@ +import initUIKit from '~/ui_development_kit'; + +document.addEventListener('DOMContentLoaded', initUIKit); diff --git a/app/assets/javascripts/pages/import/gitlab_projects/new/index.js b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js new file mode 100644 index 00000000000..bb86f72b95b --- /dev/null +++ b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js @@ -0,0 +1,3 @@ +import initGitLabImportProject from '~/projects/project_import_gitlab_project'; + +document.addEventListener('DOMContentLoaded', initGitLabImportProject); diff --git a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js index 7aa5be0d5b9..b2a896a3265 100644 --- a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js +++ b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js @@ -2,8 +2,10 @@ import Milestone from '~/milestone'; import Sidebar from '~/right_sidebar'; +import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar'; export default () => { new Milestone(); new Sidebar(); + new MountMilestoneSidebar(); }; diff --git a/app/assets/javascripts/pages/omniauth_callbacks/index.js b/app/assets/javascripts/pages/omniauth_callbacks/index.js index 54f4e56359a..c2c069d1ca8 100644 --- a/app/assets/javascripts/pages/omniauth_callbacks/index.js +++ b/app/assets/javascripts/pages/omniauth_callbacks/index.js @@ -1,5 +1,3 @@ import initU2F from '../../shared/sessions/u2f'; -export default () => { - initU2F(); -}; +document.addEventListener('DOMContentLoaded', initU2F); diff --git a/app/assets/javascripts/pages/profiles/accounts/show/index.js b/app/assets/javascripts/pages/profiles/accounts/show/index.js new file mode 100644 index 00000000000..96c3d725780 --- /dev/null +++ b/app/assets/javascripts/pages/profiles/accounts/show/index.js @@ -0,0 +1,3 @@ +import initProfileAccount from '~/profile/account'; + +document.addEventListener('DOMContentLoaded', initProfileAccount); diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js index 7af95127fd5..5543ad82428 100644 --- a/app/assets/javascripts/pages/projects/activity/index.js +++ b/app/assets/javascripts/pages/projects/activity/index.js @@ -1,7 +1,7 @@ import Activities from '~/activities'; import ShortcutsNavigation from '~/shortcuts_navigation'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new Activities(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new -} +}); diff --git a/app/assets/javascripts/pages/projects/blame/show/index.js b/app/assets/javascripts/pages/projects/blame/show/index.js index 480357a309c..80d0bff92fa 100644 --- a/app/assets/javascripts/pages/projects/blame/show/index.js +++ b/app/assets/javascripts/pages/projects/blame/show/index.js @@ -1,3 +1,3 @@ import initBlob from '~/pages/projects/init_blob'; -export default initBlob; +document.addEventListener('DOMContentLoaded', initBlob); diff --git a/app/assets/javascripts/pages/projects/blob/edit/index.js b/app/assets/javascripts/pages/projects/blob/edit/index.js new file mode 100644 index 00000000000..189053f3ed7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/blob/edit/index.js @@ -0,0 +1,3 @@ +import initBlobBundle from '~/blob_edit/blob_bundle'; + +document.addEventListener('DOMContentLoaded', initBlobBundle); diff --git a/app/assets/javascripts/pages/projects/blob/new/index.js b/app/assets/javascripts/pages/projects/blob/new/index.js new file mode 100644 index 00000000000..189053f3ed7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/blob/new/index.js @@ -0,0 +1,3 @@ +import initBlobBundle from '~/blob_edit/blob_bundle'; + +document.addEventListener('DOMContentLoaded', initBlobBundle); diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index a3eeb1cefb6..26cbb279d4a 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -1,7 +1,7 @@ import BlobViewer from '~/blob/viewer/index'; import initBlob from '~/pages/projects/init_blob'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new initBlob(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js index cee0f19bf2a..8fa266a37ce 100644 --- a/app/assets/javascripts/pages/projects/branches/index/index.js +++ b/app/assets/javascripts/pages/projects/branches/index/index.js @@ -1,7 +1,7 @@ import AjaxLoadingSpinner from '~/ajax_loading_spinner'; import DeleteModal from '~/branches/branches_delete_modal'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { AjaxLoadingSpinner.init(); new DeleteModal(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index ae5e033e97e..d32d5c6cb29 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,3 +1,5 @@ import NewBranchForm from '~/new_branch_form'; -export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); +document.addEventListener('DOMContentLoaded', () => ( + new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)) +)); diff --git a/app/assets/javascripts/pages/projects/clusters/destroy/index.js b/app/assets/javascripts/pages/projects/clusters/destroy/index.js new file mode 100644 index 00000000000..8001d2dd1da --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/destroy/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + new ClustersBundle(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js index d531ab81dc7..e4b8baede58 100644 --- a/app/assets/javascripts/pages/projects/clusters/index/index.js +++ b/app/assets/javascripts/pages/projects/clusters/index/index.js @@ -1,5 +1,5 @@ import ClustersIndex from '~/clusters/clusters_index'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ClustersIndex(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js index 0458c02a66f..8001d2dd1da 100644 --- a/app/assets/javascripts/pages/projects/clusters/show/index.js +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -1,5 +1,5 @@ import ClustersBundle from '~/clusters/clusters_bundle'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ClustersBundle(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/clusters/update/index.js b/app/assets/javascripts/pages/projects/clusters/update/index.js new file mode 100644 index 00000000000..8001d2dd1da --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/update/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + new ClustersBundle(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js index 523ad567021..7889704a324 100644 --- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -1,8 +1,8 @@ import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); -}; +}); diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js index 5ac38e6f278..460a54ab504 100644 --- a/app/assets/javascripts/pages/projects/commit/show/index.js +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -7,7 +7,7 @@ import initNotes from '~/init_notes'; import initChangesDropdown from '~/init_changes_dropdown'; import { fetchCommitMergeRequests } from '~/commit_merge_requests'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); new ZenMode(); new ShortcutsNavigation(); @@ -19,4 +19,4 @@ export default () => { initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop); $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); fetchCommitMergeRequests(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js index 6110fda17de..3682020579b 100644 --- a/app/assets/javascripts/pages/projects/commits/show/index.js +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -2,8 +2,8 @@ import CommitsList from '~/commits'; import GpgBadges from '~/gpg_badges'; import ShortcutsNavigation from '~/shortcuts_navigation'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new GpgBadges.fetch(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js index 6b8d4503568..2b4fd3c47c0 100644 --- a/app/assets/javascripts/pages/projects/compare/show/index.js +++ b/app/assets/javascripts/pages/projects/compare/show/index.js @@ -1,8 +1,8 @@ import Diff from '~/diff'; import initChangesDropdown from '~/init_changes_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); // eslint-disable-line no-new const paddingTop = 16; initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); -}; +}); diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index 9edf36d66b1..064de22dfd6 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -4,11 +4,11 @@ import ProjectNew from '../shared/project_new'; import projectAvatar from '../shared/project_avatar'; import initProjectPermissionsSettings from '../shared/permissions'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ProjectNew(); // eslint-disable-line no-new setupProjectEdit(); // Initialize expandable settings panels initSettingsPanels(); projectAvatar(); initProjectPermissionsSettings(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js index f4760cb2720..0b644780ad4 100644 --- a/app/assets/javascripts/pages/projects/environments/metrics/index.js +++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js @@ -1,3 +1,3 @@ import monitoringBundle from '~/monitoring/monitoring_bundle'; -export default monitoringBundle; +document.addEventListener('DOMContentLoaded', monitoringBundle); diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js index 42bde0ff779..23d857d69ec 100644 --- a/app/assets/javascripts/pages/projects/find_file/show/index.js +++ b/app/assets/javascripts/pages/projects/find_file/show/index.js @@ -1,7 +1,7 @@ import ProjectFindFile from '~/project_find_file'; import ShortcutsFindFile from '~/shortcuts_find_file'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { const findElement = document.querySelector('.js-file-finder'); const projectFindFile = new ProjectFindFile($('.file-finder-holder'), { url: findElement.dataset.fileFindUrl, @@ -9,4 +9,4 @@ export default () => { blobUrlTemplate: findElement.dataset.blobUrlTemplate, }); new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js index ec6eab34989..42df19c2968 100644 --- a/app/assets/javascripts/graphs/graphs_charts.js +++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js @@ -1,4 +1,4 @@ -import Chart from 'vendor/Chart'; +import Chart from 'chart.js'; import _ from 'underscore'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/pages/projects/graphs/show/index.js index b670e907a5c..f516ff20995 100644 --- a/app/assets/javascripts/graphs/graphs_show.js +++ b/app/assets/javascripts/pages/projects/graphs/show/index.js @@ -1,6 +1,6 @@ -import flash from '../flash'; -import { __ } from '../locale'; -import axios from '../lib/utils/axios_utils'; +import flash from '~/flash'; +import { __ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; import ContributorsStatGraph from './stat_graph_contributors'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js index 151a4ce012c..9ac0b4c07e5 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js @@ -1,9 +1,9 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ import _ from 'underscore'; +import { n__, s__, createDateTimeFormat, sprintf } from '~/locale'; import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; import ContributorsStatGraphUtil from './stat_graph_contributors_util'; -import { n__, s__, createDateTimeFormat, sprintf } from '../locale'; export default (function() { function ContributorsStatGraph() { diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js index 9a4012232a0..6ffaa277a0a 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js @@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis'; import { area } from 'd3-shape'; import { brushX } from 'd3-brush'; import { timeParse } from 'd3-time-format'; -import { dateTickFormat } from '../lib/utils/tick_formats'; +import { dateTickFormat } from '~/lib/utils/tick_formats'; const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse }; diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js index 77135ad1f0e..77135ad1f0e 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js index 378f7b3f38b..d5f92baf054 100644 --- a/app/assets/javascripts/pages/projects/imports/show/index.js +++ b/app/assets/javascripts/pages/projects/imports/show/index.js @@ -1,5 +1,5 @@ import ProjectImport from '~/project_import'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ProjectImport(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js index 26f0ad46114..82143fa875a 100644 --- a/app/assets/javascripts/pages/projects/init_blob.js +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -3,6 +3,7 @@ import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater'; import ShortcutsNavigation from '~/shortcuts_navigation'; import ShortcutsBlob from '~/shortcuts_blob'; import BlobForkSuggestion from '~/blob/blob_fork_suggestion'; +import initBlobBundle from '~/blob_edit/blob_bundle'; export default () => { new LineHighlighter(); // eslint-disable-line no-new @@ -30,4 +31,6 @@ export default () => { suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'), actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), }).init(); + + initBlobBundle(); }; diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/edit/index.js +++ b/app/assets/javascripts/pages/projects/issues/edit/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/new/index.js +++ b/app/assets/javascripts/pages/projects/issues/new/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js index da312c1f1b7..db064e3f801 100644 --- a/app/assets/javascripts/pages/projects/issues/show/index.js +++ b/app/assets/javascripts/pages/projects/issues/show/index.js @@ -1,13 +1,12 @@ -/* eslint-disable no-new */ - import initIssuableSidebar from '~/init_issuable_sidebar'; import Issue from '~/issue'; import ShortcutsIssuable from '~/shortcuts_issuable'; import ZenMode from '~/zen_mode'; +import '~/notes/index'; document.addEventListener('DOMContentLoaded', () => { - new Issue(); - new ShortcutsIssuable(); - new ZenMode(); + new Issue(); // eslint-disable-line no-new + new ShortcutsIssuable(); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new initIssuableSidebar(); }); diff --git a/app/assets/javascripts/pages/projects/jobs/show/index.js b/app/assets/javascripts/pages/projects/jobs/show/index.js new file mode 100644 index 00000000000..3626f3ffec6 --- /dev/null +++ b/app/assets/javascripts/pages/projects/jobs/show/index.js @@ -0,0 +1,3 @@ +import initJobDetails from '~/jobs/job_details_bundle'; + +document.addEventListener('DOMContentLoaded', initJobDetails); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js index ccd0b54c5ed..1d5aec4001d 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js @@ -1,7 +1,7 @@ import Compare from '~/compare'; import MergeRequest from '~/merge_request'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); if (mrNewCompareNode) { new Compare({ // eslint-disable-line no-new @@ -15,4 +15,4 @@ export default () => { action: mrNewSubmitNode.dataset.mrSubmitAction, }); } -}; +}); diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js index c3463c266e3..07f3e579c97 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js @@ -2,16 +2,19 @@ import MergeRequest from '~/merge_request'; import ZenMode from '~/zen_mode'; import initNotes from '~/init_notes'; import initIssuableSidebar from '~/init_issuable_sidebar'; +import initDiffNotes from '~/diff_notes/diff_notes_bundle'; import ShortcutsIssuable from '~/shortcuts_issuable'; import Diff from '~/diff'; import { handleLocationHash } from '~/lib/utils/common_utils'; +import howToMerge from '~/how_to_merge'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new - initIssuableSidebar(); // eslint-disable-line no-new - initNotes(); // eslint-disable-line no-new + initIssuableSidebar(); + initNotes(); + initDiffNotes(); const mrShowNode = document.querySelector('.merge-request'); @@ -21,4 +24,5 @@ export default () => { new ShortcutsIssuable(true); // eslint-disable-line no-new handleLocationHash(); -}; + howToMerge(); +}); diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/edit/index.js +++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); diff --git a/app/assets/javascripts/pages/projects/milestones/index/index.js b/app/assets/javascripts/pages/projects/milestones/index/index.js index 8fb4d83d8a3..38789365a67 100644 --- a/app/assets/javascripts/pages/projects/milestones/index/index.js +++ b/app/assets/javascripts/pages/projects/milestones/index/index.js @@ -1,3 +1,3 @@ import milestones from '~/pages/milestones/shared'; -export default milestones; +document.addEventListener('DOMContentLoaded', milestones); diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/new/index.js +++ b/app/assets/javascripts/pages/projects/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); diff --git a/app/assets/javascripts/pages/projects/milestones/show/index.js b/app/assets/javascripts/pages/projects/milestones/show/index.js index 35b5c9c2ced..84a52421598 100644 --- a/app/assets/javascripts/pages/projects/milestones/show/index.js +++ b/app/assets/javascripts/pages/projects/milestones/show/index.js @@ -1,7 +1,7 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import milestones from '~/pages/milestones/shared'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initMilestonesShow(); milestones(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/pipelines/builds/index.js b/app/assets/javascripts/pages/projects/pipelines/builds/index.js index 060a78b427e..fbe9824c34b 100644 --- a/app/assets/javascripts/pages/projects/pipelines/builds/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/builds/index.js @@ -1,16 +1,3 @@ -import Pipelines from '../../../../pipelines'; +import initPipelines from '../init_pipelines'; -export default () => { - const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; - const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; - - new Pipelines({ // eslint-disable-line no-new - initTabs: true, - pipelineStatusUrl, - tabsOptions: { - action: controllerAction, - defaultAction: 'pipelines', - parentEl: '.pipelines-tabs', - }, - }); -}; +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js index c1dafda0e24..bb92f4e1459 100644 --- a/app/assets/javascripts/pages/projects/pipelines/charts/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js @@ -1,4 +1,4 @@ -import Chart from 'vendor/Chart'; +import Chart from 'chart.js'; const options = { scaleOverlay: true, diff --git a/app/assets/javascripts/pages/projects/pipelines/failures/index.js b/app/assets/javascripts/pages/projects/pipelines/failures/index.js new file mode 100644 index 00000000000..fbe9824c34b --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/failures/index.js @@ -0,0 +1,3 @@ +import initPipelines from '../init_pipelines'; + +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js new file mode 100644 index 00000000000..94dfeb96e8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js @@ -0,0 +1,16 @@ +import Pipelines from '~/pipelines'; + +export default () => { + const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; + const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; + + new Pipelines({ // eslint-disable-line no-new + initTabs: true, + pipelineStatusUrl, + tabsOptions: { + action: controllerAction, + defaultAction: 'pipelines', + parentEl: '.pipelines-tabs', + }, + }); +}; diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js index c54cc62bf05..da20bd995e9 100644 --- a/app/assets/javascripts/pages/projects/pipelines/new/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/new/index.js @@ -1,5 +1,5 @@ -import NewBranchForm from '../../../../new_branch_form'; +import NewBranchForm from '~/new_branch_form'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js new file mode 100644 index 00000000000..fbe9824c34b --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -0,0 +1,3 @@ +import initPipelines from '../init_pipelines'; + +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index 863dac0d20e..6e48d207571 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -71,7 +71,7 @@ export default class Project { selected = $dropdown.data('selected'); return $dropdown.glDropdown({ data(term, callback) { - axios.get($dropdown.data('refs-url'), { + axios.get($dropdown.data('refsUrl'), { params: { ref: $dropdown.data('ref'), search: term, @@ -84,8 +84,8 @@ export default class Project { filterable: true, filterRemote: true, filterByText: true, - inputFieldName: $dropdown.data('input-field-name'), - fieldName: $dropdown.data('field-name'), + inputFieldName: $dropdown.data('inputFieldName'), + fieldName: $dropdown.data('fieldName'), renderRow: function(ref) { var li = refListItem.cloneNode(false); diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index f4643e7dba0..adbe744290a 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -3,10 +3,10 @@ import UsersSelect from '../../../users_select'; import groupsSelect from '../../../groups_select'; import Members from '../../../members'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { memberExpirationDate('.js-access-expiration-date-groups'); groupsSelect(); memberExpirationDate(); new Members(); // eslint-disable-line no-new new UsersSelect(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/releases/edit/index.js b/app/assets/javascripts/pages/projects/releases/edit/index.js index 3d997cdfff0..0bf53a8de09 100644 --- a/app/assets/javascripts/pages/projects/releases/edit/index.js +++ b/app/assets/javascripts/pages/projects/releases/edit/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.release-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.release-form'))); diff --git a/app/assets/javascripts/pages/projects/services/edit/index.js b/app/assets/javascripts/pages/projects/services/edit/index.js index 434a7e44277..ba4b271f09e 100644 --- a/app/assets/javascripts/pages/projects/services/edit/index.js +++ b/app/assets/javascripts/pages/projects/services/edit/index.js @@ -1,6 +1,13 @@ import IntegrationSettingsForm from '~/integrations/integration_settings_form'; +import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { + const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring'); const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); integrationSettingsForm.init(); -}; + + if (prometheusSettingsWrapper) { + const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); + prometheusMetrics.loadActiveMetrics(); + } +}); diff --git a/app/assets/javascripts/pages/projects/snippets/edit/index.js b/app/assets/javascripts/pages/projects/snippets/edit/index.js index 9edb16dc73b..caf9ee9b398 100644 --- a/app/assets/javascripts/pages/projects/snippets/edit/index.js +++ b/app/assets/javascripts/pages/projects/snippets/edit/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.snippet-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form'))); diff --git a/app/assets/javascripts/pages/projects/snippets/new/index.js b/app/assets/javascripts/pages/projects/snippets/new/index.js index 9edb16dc73b..caf9ee9b398 100644 --- a/app/assets/javascripts/pages/projects/snippets/new/index.js +++ b/app/assets/javascripts/pages/projects/snippets/new/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.snippet-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form'))); diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js index a3cf75c385b..a134599cb04 100644 --- a/app/assets/javascripts/pages/projects/snippets/show/index.js +++ b/app/assets/javascripts/pages/projects/snippets/show/index.js @@ -3,9 +3,9 @@ import ZenMode from '~/zen_mode'; import LineHighlighter from '../../../../line_highlighter'; import BlobViewer from '../../../../blob/viewer'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new initNotes(); new ZenMode(); // eslint-disable-line no-new -} +}); diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js index dacc2875c8c..191c98b36bb 100644 --- a/app/assets/javascripts/pages/projects/tags/new/index.js +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -2,8 +2,8 @@ import RefSelectDropdown from '../../../../ref_select_dropdown'; import ZenMode from '../../../../zen_mode'; import GLForm from '../../../../gl_form'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ZenMode(); // eslint-disable-line no-new new GLForm($('.tag-form'), true); // eslint-disable-line no-new new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index cba57058380..ed7d3f1747c 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; @@ -6,7 +7,7 @@ import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; import { ajaxGet } from '../../../../lib/utils/common_utils'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ShortcutsNavigation(); // eslint-disable-line no-new new TreeView(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new @@ -14,6 +15,7 @@ export default () => { $('#tree-slider').waitForImages(() => ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); + initBlob(); const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); const statusLink = document.querySelector('.commit-actions .ci-status-link'); if (statusLink != null) { @@ -33,5 +35,4 @@ export default () => { }, }); } -}; - +}); diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js index 250f9d992ab..de8d4168d71 100644 --- a/app/assets/javascripts/pages/search/init_filtered_search.js +++ b/app/assets/javascripts/pages/search/init_filtered_search.js @@ -1,7 +1,9 @@ +import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; + export default ({ page }) => { - const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search'); if (filteredSearchEnabled) { - const filteredSearchManager = new gl.FilteredSearchManager({ page }); + const filteredSearchManager = new FilteredSearchManager({ page }); filteredSearchManager.setup(); } }; diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js index dc621bc87c0..cf44e291199 100644 --- a/app/assets/javascripts/pages/search/show/search.js +++ b/app/assets/javascripts/pages/search/show/search.js @@ -9,7 +9,7 @@ export default class Search { this.searchInput = '.js-search-input'; this.searchClear = '.js-search-clear'; - this.groupId = $groupDropdown.data('group-id'); + this.groupId = $groupDropdown.data('groupId'); this.eventListeners(); $groupDropdown.glDropdown({ @@ -36,7 +36,7 @@ export default class Search { return obj.full_name; }, toggleLabel(obj) { - return `${($groupDropdown.data('default-label'))} ${obj.full_name}`; + return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`; }, clicked: () => Search.submitSearch(), }); @@ -69,7 +69,7 @@ export default class Search { return obj.name_with_namespace; }, toggleLabel(obj) { - return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`; + return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`; }, clicked: () => Search.submitSearch(), }); diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js index 54f4e56359a..c2c069d1ca8 100644 --- a/app/assets/javascripts/pages/sessions/index.js +++ b/app/assets/javascripts/pages/sessions/index.js @@ -1,5 +1,3 @@ import initU2F from '../../shared/sessions/u2f'; -export default () => { - initU2F(); -}; +document.addEventListener('DOMContentLoaded', initU2F); diff --git a/app/assets/javascripts/pages/snippets/edit/index.js b/app/assets/javascripts/pages/snippets/edit/index.js index 9c664b5f1ff..2ee38b64ca1 100644 --- a/app/assets/javascripts/pages/snippets/edit/index.js +++ b/app/assets/javascripts/pages/snippets/edit/index.js @@ -1,3 +1,3 @@ import form from '../form'; -export default form; +document.addEventListener('DOMContentLoaded', form); diff --git a/app/assets/javascripts/pages/snippets/new/index.js b/app/assets/javascripts/pages/snippets/new/index.js index 9c664b5f1ff..2ee38b64ca1 100644 --- a/app/assets/javascripts/pages/snippets/new/index.js +++ b/app/assets/javascripts/pages/snippets/new/index.js @@ -1,3 +1,3 @@ import form from '../form'; -export default form; +document.addEventListener('DOMContentLoaded', form); diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js index 04c9562bfbb..f548b9fad65 100644 --- a/app/assets/javascripts/pages/snippets/show/index.js +++ b/app/assets/javascripts/pages/snippets/show/index.js @@ -1,12 +1,11 @@ -/* eslint-disable no-new */ import LineHighlighter from '../../../line_highlighter'; import BlobViewer from '../../../blob/viewer'; import ZenMode from '../../../zen_mode'; import initNotes from '../../../init_notes'; -export default () => { - new LineHighlighter(); - new BlobViewer(); +document.addEventListener('DOMContentLoaded', () => { + new LineHighlighter(); // eslint-disable-line no-new + new BlobViewer(); // eslint-disable-line no-new initNotes(); - new ZenMode(); -}; + new ZenMode(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue index a5f22c4ec80..0cdffbde05b 100644 --- a/app/assets/javascripts/pipelines/components/async_button.vue +++ b/app/assets/javascripts/pipelines/components/async_button.vue @@ -31,10 +31,14 @@ type: String, required: true, }, - id: { + pipelineId: { type: Number, required: true, }, + type: { + type: String, + required: true, + }, }, data() { return { @@ -46,17 +50,27 @@ return `btn ${this.cssClass}`; }, }, + created() { + // We're using eventHub to listen to the modal here instead of + // using props because it would would make the parent components + // much more complex to keep track of the loading state of each button + eventHub.$on('postAction', this.setLoading); + }, + beforeDestroy() { + eventHub.$off('postAction', this.setLoading); + }, methods: { onClick() { - eventHub.$emit('actionConfirmationModal', { - id: this.id, - callback: this.makeRequest, + eventHub.$emit('openConfirmationModal', { + pipelineId: this.pipelineId, + endpoint: this.endpoint, + type: this.type, }); }, - makeRequest() { - this.isLoading = true; - - eventHub.$emit('postAction', this.endpoint); + setLoading(endpoint) { + if (endpoint === this.endpoint) { + this.isLoading = true; + } }, }, }; diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index a1f58580318..ab84711d4a2 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -52,7 +52,7 @@ </script> <template> <div class="build-content middle-block js-pipeline-graph"> - <div class="pipeline-visualization pipeline-graph"> + <div class="pipeline-visualization pipeline-graph pipeline-tab-content"> <div class="text-center"> <loading-icon v-if="isLoading" diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index 62fe479fdf4..c9028952ddd 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -1,7 +1,8 @@ <script> + import modal from '~/vue_shared/components/modal.vue'; + import { s__, sprintf } from '~/locale'; import pipelinesTableRowComponent from './pipelines_table_row.vue'; - import stopConfirmationModal from './stop_confirmation_modal.vue'; - import retryConfirmationModal from './retry_confirmation_modal.vue'; + import eventHub from '../event_hub'; /** * Pipelines Table Component. @@ -11,8 +12,7 @@ export default { components: { pipelinesTableRowComponent, - stopConfirmationModal, - retryConfirmationModal, + modal, }, props: { pipelines: { @@ -33,6 +33,52 @@ required: true, }, }, + data() { + return { + pipelineId: '', + endpoint: '', + type: '', + }; + }, + computed: { + modalTitle() { + return this.type === 'stop' ? + sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), { + pipelineId: `'${this.pipelineId}'`, + }, false) : + sprintf(s__('Pipeline|Retry pipeline #%{pipelineId}?'), { + pipelineId: `'${this.pipelineId}'`, + }, false); + }, + modalText() { + return this.type === 'stop' ? + sprintf(s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), { + pipelineId: `<strong>#${this.pipelineId}</strong>`, + }, false) : + sprintf(s__('Pipeline|You’re about to retry pipeline %{pipelineId}.'), { + pipelineId: `<strong>#${this.pipelineId}</strong>`, + }, false); + }, + primaryButtonLabel() { + return this.type === 'stop' ? s__('Pipeline|Stop pipeline') : s__('Pipeline|Retry pipeline'); + }, + }, + created() { + eventHub.$on('openConfirmationModal', this.setModalData); + }, + beforeDestroy() { + eventHub.$off('openConfirmationModal', this.setModalData); + }, + methods: { + setModalData(data) { + this.pipelineId = data.pipelineId; + this.endpoint = data.endpoint; + this.type = data.type; + }, + onSubmit() { + eventHub.$emit('postAction', this.endpoint); + }, + }, }; </script> <template> @@ -74,7 +120,20 @@ :auto-devops-help-path="autoDevopsHelpPath" :view-type="viewType" /> - <stop-confirmation-modal /> - <retry-confirmation-modal /> + <modal + id="confirmation-modal" + :title="modalTitle" + :text="modalText" + kind="danger" + :primary-button-label="primaryButtonLabel" + @submit="onSubmit" + > + <template + slot="body" + slot-scope="props" + > + <p v-html="props.text"></p> + </template> + </modal> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 0e3a10ed7f4..33d441e573e 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -305,9 +305,10 @@ css-class="js-pipelines-retry-button btn-default btn-retry" title="Retry" icon="repeat" - :id="pipeline.id" + :pipeline-id="pipeline.id" data-toggle="modal" - data-target="#retry-confirmation-modal" + data-target="#confirmation-modal" + type="retry" /> <async-button-component @@ -316,9 +317,10 @@ css-class="js-pipelines-cancel-button btn-remove" title="Cancel" icon="close" - :id="pipeline.id" + :pipeline-id="pipeline.id" data-toggle="modal" - data-target="#stop-confirmation-modal" + data-target="#confirmation-modal" + type="stop" /> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue deleted file mode 100644 index e2ac08d67bc..00000000000 --- a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> - import modal from '~/vue_shared/components/modal.vue'; - import { s__, sprintf } from '~/locale'; - import eventHub from '../event_hub'; - - export default { - components: { - modal, - }, - data() { - return { - id: '', - callback: () => {}, - }; - }, - computed: { - title() { - return sprintf(s__('Pipeline|Retry pipeline #%{id}?'), { - id: `'${this.id}'`, - }, false); - }, - text() { - return sprintf(s__('Pipeline|You’re about to retry pipeline %{id}.'), { - id: `<strong>#${this.id}</strong>`, - }, false); - }, - primaryButtonLabel() { - return s__('Pipeline|Retry pipeline'); - }, - }, - created() { - eventHub.$on('actionConfirmationModal', this.updateModal); - }, - beforeDestroy() { - eventHub.$off('actionConfirmationModal', this.updateModal); - }, - methods: { - updateModal(action) { - this.id = action.id; - this.callback = action.callback; - }, - onSubmit() { - this.callback(); - }, - }, - }; -</script> - -<template> - <modal - id="retry-confirmation-modal" - :title="title" - :text="text" - kind="danger" - :primary-button-label="primaryButtonLabel" - @submit="onSubmit" - > - <template - slot="body" - slot-scope="props" - > - <p v-html="props.text"></p> - </template> - </modal> -</template> diff --git a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue deleted file mode 100644 index d737d567787..00000000000 --- a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> - import modal from '~/vue_shared/components/modal.vue'; - import { s__, sprintf } from '~/locale'; - import eventHub from '../event_hub'; - - export default { - components: { - modal, - }, - data() { - return { - id: '', - callback: () => {}, - }; - }, - computed: { - title() { - return sprintf(s__('Pipeline|Stop pipeline #%{id}?'), { - id: `'${this.id}'`, - }, false); - }, - text() { - return sprintf(s__('Pipeline|You’re about to stop pipeline %{id}.'), { - id: `<strong>#${this.id}</strong>`, - }, false); - }, - primaryButtonLabel() { - return s__('Pipeline|Stop pipeline'); - }, - }, - created() { - eventHub.$on('actionConfirmationModal', this.updateModal); - }, - beforeDestroy() { - eventHub.$off('actionConfirmationModal', this.updateModal); - }, - methods: { - updateModal(action) { - this.id = action.id; - this.callback = action.callback; - }, - onSubmit() { - this.callback(); - }, - }, - }; -</script> - -<template> - <modal - id="stop-confirmation-modal" - :title="title" - :text="text" - kind="danger" - :primary-button-label="primaryButtonLabel" - @submit="onSubmit" - > - <template - slot="body" - slot-scope="props" - > - <p v-html="props.text"></p> - </template> - </modal> -</template> diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index d88d280cb3f..705a60b3ba2 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -1,10 +1,14 @@ import Vue from 'vue'; -import Flash from '../flash'; -import PipelinesMediator from './pipeline_details_mediatior'; +import Flash from '~/flash'; +import Translate from '~/vue_shared/translate'; +import { __ } from '~/locale'; +import PipelinesMediator from './pipeline_details_mediator'; import pipelineGraph from './components/graph/graph_component.vue'; import pipelineHeader from './components/header_component.vue'; import eventHub from './event_hub'; +Vue.use(Translate); + document.addEventListener('DOMContentLoaded', () => { const dataset = document.querySelector('.js-pipeline-details-vue').dataset; @@ -54,7 +58,7 @@ document.addEventListener('DOMContentLoaded', () => { postAction(action) { this.mediator.service.postAction(action.path) .then(() => this.mediator.refreshPipeline()) - .catch(() => new Flash('An error occurred while making the request.')); + .catch(() => Flash(__('An error occurred while making the request.'))); }, }, render(createElement) { diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js index 823ccd849f4..10f238fe73b 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js +++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js @@ -1,6 +1,7 @@ import Visibility from 'visibilityjs'; import Flash from '../flash'; import Poll from '../lib/utils/poll'; +import { __ } from '../locale'; import PipelineStore from './stores/pipeline_store'; import PipelineService from './services/pipeline_service'; @@ -47,7 +48,7 @@ export default class pipelinesMediator { errorCallback() { this.state.isLoading = false; - return new Flash('An error occurred while fetching the pipeline.'); + Flash(__('An error occurred while fetching the pipeline.')); } refreshPipeline() { diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js index a93bc935dd0..84049a1f0b7 100644 --- a/app/assets/javascripts/profile/account/index.js +++ b/app/assets/javascripts/profile/account/index.js @@ -1,29 +1,29 @@ import Vue from 'vue'; - import Translate from '~/vue_shared/translate'; - import deleteAccountModal from './components/delete_account_modal.vue'; -Vue.use(Translate); +export default () => { + Vue.use(Translate); -const deleteAccountButton = document.getElementById('delete-account-button'); -const deleteAccountModalEl = document.getElementById('delete-account-modal'); -// eslint-disable-next-line no-new -new Vue({ - el: deleteAccountModalEl, - components: { - deleteAccountModal, - }, - mounted() { - deleteAccountButton.classList.remove('disabled'); - }, - render(createElement) { - return createElement('delete-account-modal', { - props: { - actionUrl: deleteAccountModalEl.dataset.actionUrl, - confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword, - username: deleteAccountModalEl.dataset.username, - }, - }); - }, -}); + const deleteAccountButton = document.getElementById('delete-account-button'); + const deleteAccountModalEl = document.getElementById('delete-account-modal'); + // eslint-disable-next-line no-new + new Vue({ + el: deleteAccountModalEl, + components: { + deleteAccountModal, + }, + mounted() { + deleteAccountButton.classList.remove('disabled'); + }, + render(createElement) { + return createElement('delete-account-modal', { + props: { + actionUrl: deleteAccountModalEl.dataset.actionUrl, + confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword, + username: deleteAccountModalEl.dataset.username, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index 07a49d1506c..412aca7bfed 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -5,13 +5,13 @@ import ProjectSelectComboButton from './project_select_combo_button'; export default function projectSelect() { $('.ajax-project-select').each(function(i, select) { var placeholder; - const simpleFilter = $(select).data('simple-filter') || false; - this.groupId = $(select).data('group-id'); - this.includeGroups = $(select).data('include-groups'); - this.allProjects = $(select).data('all-projects') || false; - this.orderBy = $(select).data('order-by') || 'id'; - this.withIssuesEnabled = $(select).data('with-issues-enabled'); - this.withMergeRequestsEnabled = $(select).data('with-merge-requests-enabled'); + const simpleFilter = $(select).data('simpleFilter') || false; + this.groupId = $(select).data('groupId'); + this.includeGroups = $(select).data('includeGroups'); + this.allProjects = $(select).data('allProjects') || false; + this.orderBy = $(select).data('orderBy') || 'id'; + this.withIssuesEnabled = $(select).data('withIssuesEnabled'); + this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled'); placeholder = "Search for project"; if (this.includeGroups) { diff --git a/app/assets/javascripts/projects/project_import_gitlab_project.js b/app/assets/javascripts/projects/project_import_gitlab_project.js index cec6f0dd5a3..d2c7d77bb2d 100644 --- a/app/assets/javascripts/projects/project_import_gitlab_project.js +++ b/app/assets/javascripts/projects/project_import_gitlab_project.js @@ -1,14 +1,8 @@ import { getParameterValues } from '../lib/utils/url_utility'; -const bindEvents = () => { +export default () => { const path = getParameterValues('path')[0]; // get the path url and append it in the inputS $('.js-path-name').val(path); }; - -document.addEventListener('DOMContentLoaded', bindEvents); - -export default { - bindEvents, -}; diff --git a/app/assets/javascripts/prometheus_metrics/index.js b/app/assets/javascripts/prometheus_metrics/index.js deleted file mode 100644 index a0c43c5abe1..00000000000 --- a/app/assets/javascripts/prometheus_metrics/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import PrometheusMetrics from './prometheus_metrics'; - -$(() => { - const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - prometheusMetrics.loadActiveMetrics(); -}); diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js index 59ad5b45855..e8126ac573d 100644 --- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js +++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js @@ -19,7 +19,7 @@ export default class PrometheusMetrics { this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count'); this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list'); - this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('active-metrics'); + this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('activeMetrics'); this.$panelToggle.on('click', e => this.handlePanelToggle(e)); } diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js index 38b1406a99f..40a873833e1 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js @@ -9,8 +9,8 @@ export default class ProtectedBranchAccessDropdown { $dropdown.glDropdown({ data, selectable: true, - inputId: $dropdown.data('input-id'), - fieldName: $dropdown.data('field-name'), + inputId: $dropdown.data('inputId'), + fieldName: $dropdown.data('fieldName'), toggleLabel(item, $el) { if ($el.is('.is-active')) { return item.text; diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 2948baeab11..8fc87633e18 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -59,7 +59,7 @@ export default class ProtectedBranchCreate { ); this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val()); - this.$form.find('input[type="submit"]').attr('disabled', completedForm); + this.$form.find('input[type="submit"]').prop('disabled', completedForm); } static getProtectedBranches(term, callback) { diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index b51b3e9a6ff..54560d08ad7 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -41,11 +41,11 @@ export default class ProtectedBranchEdit { axios.patch(this.$wrap.data('url'), { protected_branch: { merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('access-level-id'), + id: this.$allowedToMergeDropdown.data('accessLevelId'), access_level: $allowedToMergeInput.val(), }], push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('access-level-id'), + id: this.$allowedToPushDropdown.data('accessLevelId'), access_level: $allowedToPushInput.val(), }], }, diff --git a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js index d4c9a91a74a..b803da798d5 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js +++ b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js @@ -9,8 +9,8 @@ export default class ProtectedTagAccessDropdown { this.options.$dropdown.glDropdown({ data: this.options.data, selectable: true, - inputId: this.options.$dropdown.data('input-id'), - fieldName: this.options.$dropdown.data('field-name'), + inputId: this.options.$dropdown.data('inputId'), + fieldName: this.options.$dropdown.data('fieldName'), toggleLabel(item, $el) { if ($el.is('.is-active')) { return item.text; diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js index d1e4a75c17b..2f94ffe2507 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_create.js +++ b/app/assets/javascripts/protected_tags/protected_tag_create.js @@ -39,7 +39,7 @@ export default class ProtectedTagCreate { const $tagInput = this.$form.find('input[name="protected_tag[name]"]'); const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes'); - this.$form.find('input[type="submit"]').attr('disabled', !($tagInput.val() && $allowedToCreateInput.length)); + this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length)); } static getProtectedTags(term, callback) { diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js index 21a258cf93c..8687b2a4044 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_edit.js +++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js @@ -31,7 +31,7 @@ export default class ProtectedTagEdit { axios.patch(this.$wrap.data('url'), { protected_tag: { create_access_levels_attributes: [{ - id: this.$allowedToCreateDropdownButton.data('access-level-id'), + id: this.$allowedToCreateDropdownButton.data('accessLevelId'), access_level: $allowedToCreateInput.val(), }], }, diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js index 65e4101352c..56c25a35e6d 100644 --- a/app/assets/javascripts/ref_select_dropdown.js +++ b/app/assets/javascripts/ref_select_dropdown.js @@ -6,7 +6,7 @@ class RefSelectDropdown { filterable: true, filterByText: true, remote: false, - fieldName: $dropdownButton.data('field-name'), + fieldName: $dropdownButton.data('fieldName'), filterInput: 'input[type="search"]', selectable: true, isSelectable(branch, $el) { @@ -24,7 +24,7 @@ class RefSelectDropdown { }); const $dropdownContainer = $dropdownButton.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdownButton.data('field-name')}"]`, $dropdownContainer); + const $fieldInput = $(`input[name="${$dropdownButton.data('fieldName')}"]`, $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer); $filterInput.on('keyup', (e) => { diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 01c3be5411f..8d3cc849f81 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -76,8 +76,8 @@ Sidebar.prototype.toggleTodo = function(e) { $('.js-issuable-todo').disable().addClass('is-loading'); axios[ajaxType](url, { - issuable_id: $this.data('issuable-id'), - issuable_type: $this.data('issuable-type'), + issuable_id: $this.data('issuableId'), + issuable_type: $this.data('issuableType'), }).then(({ data }) => { this.todoUpdateDone(data); }).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`)); @@ -96,18 +96,18 @@ Sidebar.prototype.todoUpdateDone = function(data) { $el.removeClass('is-loading') .enable() - .attr('aria-label', $el.data(`${attrPrefix}-text`)) + .attr('aria-label', $el.data(`${attrPrefix}Text`)) .attr('data-delete-path', deletePath) - .attr('title', $el.data(`${attrPrefix}-text`)); + .attr('title', $el.data(`${attrPrefix}Text`)); if ($el.hasClass('has-tooltip')) { $el.tooltip('fixTitle'); } - if ($el.data(`${attrPrefix}-icon`)) { - $elText.html($el.data(`${attrPrefix}-icon`)); + if ($el.data(`${attrPrefix}Icon`)) { + $elText.html($el.data(`${attrPrefix}Icon`)); } else { - $elText.text($el.data(`${attrPrefix}-text`)); + $elText.text($el.data(`${attrPrefix}Text`)); } }); }; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 8f4a8704c3b..fdfa4f28aba 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -1,5 +1,6 @@ /* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */ import axios from './lib/utils/axios_utils'; +import DropdownUtils from './filtered_search/dropdown_utils'; import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils'; /** @@ -25,32 +26,32 @@ function setSearchOptions() { if ($projectOptionsDataEl.length) { gl.projectOptions = gl.projectOptions || {}; - var projectPath = $projectOptionsDataEl.data('project-path'); + var projectPath = $projectOptionsDataEl.data('projectPath'); gl.projectOptions[projectPath] = { name: $projectOptionsDataEl.data('name'), - issuesPath: $projectOptionsDataEl.data('issues-path'), - issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), - mrPath: $projectOptionsDataEl.data('mr-path'), + issuesPath: $projectOptionsDataEl.data('issuesPath'), + issuesDisabled: $projectOptionsDataEl.data('issuesDisabled'), + mrPath: $projectOptionsDataEl.data('mrPath'), }; } if ($groupOptionsDataEl.length) { gl.groupOptions = gl.groupOptions || {}; - var groupPath = $groupOptionsDataEl.data('group-path'); + var groupPath = $groupOptionsDataEl.data('groupPath'); gl.groupOptions[groupPath] = { name: $groupOptionsDataEl.data('name'), - issuesPath: $groupOptionsDataEl.data('issues-path'), - mrPath: $groupOptionsDataEl.data('mr-path'), + issuesPath: $groupOptionsDataEl.data('issuesPath'), + mrPath: $groupOptionsDataEl.data('mrPath'), }; } if ($dashboardOptionsDataEl.length) { gl.dashboardOptions = { - issuesPath: $dashboardOptionsDataEl.data('issues-path'), - mrPath: $dashboardOptionsDataEl.data('mr-path'), + issuesPath: $dashboardOptionsDataEl.data('issuesPath'), + mrPath: $dashboardOptionsDataEl.data('mrPath'), }; } } @@ -61,9 +62,9 @@ export default class SearchAutocomplete { this.bindEventContext(); this.wrap = wrap || $('.search'); this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); - this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); - this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); - this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); + this.autocompletePath = autocompletePath || this.optsEl.data('autocompletePath'); + this.projectId = projectId || (this.optsEl.data('autocompleteProjectId') || ''); + this.projectRef = projectRef || (this.optsEl.data('autocompleteProjectRef') || ''); this.dropdown = this.wrap.find('.dropdown'); this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); this.dropdownContent = this.dropdown.find('.dropdown-content'); diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js index 977dd83a7ea..b10e2cc60ef 100644 --- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js +++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js @@ -50,7 +50,7 @@ class SidebarMoveIssue { const selectedProjectId = options.isMarking ? project.id : 0; this.mediator.setMoveToProjectId(selectedProjectId); - this.$confirmButton.attr('disabled', !isValidProjectId(selectedProjectId)); + this.$confirmButton.prop('disabled', !isValidProjectId(selectedProjectId)); }, }); } diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js new file mode 100644 index 00000000000..b15ad0e5586 --- /dev/null +++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js @@ -0,0 +1,27 @@ +import Vue from 'vue'; +import timeTracker from './components/time_tracking/time_tracker.vue'; + +export default class SidebarMilestone { + constructor() { + const el = document.getElementById('issuable-time-tracker'); + + if (!el) return; + + // eslint-disable-next-line no-new + new Vue({ + el, + components: { + timeTracker, + }, + render: createElement => createElement('timeTracker', { + props: { + time_estimate: parseInt(el.dataset.timeEstimate, 10), + time_spent: parseInt(el.dataset.timeSpent, 10), + human_time_estimate: el.dataset.humanTimeEstimate, + human_time_spent: el.dataset.humanTimeSpent, + rootPath: '/', + }, + }), + }); + } +} diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 48dd91bdf06..6142ce6c6a3 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -18,7 +18,7 @@ export default class SingleFileDiff { this.toggleDiff = this.toggleDiff.bind(this); this.content = $('.diff-content', this.file); this.$toggleIcon = $('.diff-toggle-caret', this.file); - this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path'); + this.diffForPath = this.content.find('[data-diff-for-path]').data('diffForPath'); this.isOpen = !this.diffForPath; if (this.diffForPath) { this.collapsedContent = this.content; @@ -88,6 +88,8 @@ export default class SingleFileDiff { if (cb) cb(); }) - .catch(createFlash(__('An error occurred while retrieving diff'))); + .catch(() => { + createFlash(__('An error occurred while retrieving diff')); + }); } } diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index d5606e153f6..3deb629d5f2 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,17 +1,20 @@ import Flash from './flash'; import { __, s__ } from './locale'; import { spriteIcon } from './lib/utils/common_utils'; +import axios from './lib/utils/axios_utils'; export default class Star { constructor() { - $('.project-home-panel .toggle-star') - .on('ajax:success', function handleSuccess(e, data) { - const $this = $(this); - const $starSpan = $this.find('span'); - const $startIcon = $this.find('svg'); + $('.project-home-panel .toggle-star').on('click', function toggleStarClickCallback() { + const $this = $(this); + const $starSpan = $this.find('span'); + const $startIcon = $this.find('svg'); - function toggleStar(isStarred) { + axios.post($this.data('endpoint')) + .then(({ data }) => { + const isStarred = $starSpan.hasClass('starred'); $this.parent().find('.star-count').text(data.star_count); + if (isStarred) { $starSpan.removeClass('starred').text(s__('StarProject|Star')); $startIcon.remove(); @@ -21,12 +24,8 @@ export default class Star { $startIcon.remove(); $this.prepend(spriteIcon('star')); } - } - - toggleStar($starSpan.hasClass('starred')); - }) - .on('ajax:error', () => { - Flash('Star toggle failed. Try again later.', 'alert'); - }); + }) + .catch(() => Flash('Star toggle failed. Try again later.')); + }); } } diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index 1ab4c2229ca..3ed064f87a9 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,6 +1,6 @@ export default function subscriptionSelect() { $('.js-subscription-event').each((i, element) => { - const fieldName = $(element).data('field-name'); + const fieldName = $(element).data('fieldName'); return $(element).glDropdown({ selectable: true, diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 129a551cbcd..8fa78b636f8 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -40,7 +40,7 @@ export default class TaskList { [this.fieldName]: $target.val(), }; - return axios.patch($target.data('update-url') || $('form.js-issuable-update').attr('action'), patchData) + return axios.patch($target.data('updateUrl') || $('form.js-issuable-update').attr('action'), patchData) .then(({ data }) => this.onSuccess(data)) .catch(err => this.onError(err)); } diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js index 4cc1c96b870..b5b64f44a11 100644 --- a/app/assets/javascripts/templates/issuable_template_selector.js +++ b/app/assets/javascripts/templates/issuable_template_selector.js @@ -6,9 +6,9 @@ import TemplateSelector from '../blob/template_selector'; export default class IssuableTemplateSelector extends TemplateSelector { constructor(...args) { super(...args); - this.projectPath = this.dropdown.data('project-path'); - this.namespacePath = this.dropdown.data('namespace-path'); - this.issuableType = this.$dropdownContainer.data('issuable-type'); + this.projectPath = this.dropdown.data('projectPath'); + this.namespacePath = this.dropdown.data('namespacePath'); + this.issuableType = this.$dropdownContainer.data('issuableType'); this.titleInput = $(`#${this.issuableType}_title`); const initialQuery = { diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js index f503076715c..78dda172ee6 100644 --- a/app/assets/javascripts/ui_development_kit.js +++ b/app/assets/javascripts/ui_development_kit.js @@ -1,6 +1,6 @@ import Api from './api'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { $('#js-project-dropdown').glDropdown({ data: (term, callback) => { Api.projects(term, { @@ -19,4 +19,4 @@ document.addEventListener('DOMContentLoaded', () => { id: data => data.id, isSelected: data => (data.id === 2), }); -}); +}; diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js index a45b22f3084..a783122d500 100644 --- a/app/assets/javascripts/user_callout.js +++ b/app/assets/javascripts/user_callout.js @@ -22,7 +22,7 @@ export default class UserCallout { const $currentTarget = $(e.currentTarget); if (this.options.setCalloutPerProject) { - Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('project-path') }); + Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('projectPath') }); } else { Cookies.set(this.cookieName, 'true', { expires: 365 }); } diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index eaed81cf79e..8958534689c 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -34,23 +34,23 @@ function UsersSelect(currentUser, els, options = {}) { var options = {}; var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove; $dropdown = $(dropdown); - options.projectId = $dropdown.data('project-id'); - options.groupId = $dropdown.data('group-id'); - options.showCurrentUser = $dropdown.data('current-user'); - options.todoFilter = $dropdown.data('todo-filter'); - options.todoStateFilter = $dropdown.data('todo-state-filter'); - options.perPage = $dropdown.data('per-page'); - showNullUser = $dropdown.data('null-user'); - defaultNullUser = $dropdown.data('null-user-default'); + options.projectId = $dropdown.data('projectId'); + options.groupId = $dropdown.data('groupId'); + options.showCurrentUser = $dropdown.data('currentUser'); + options.todoFilter = $dropdown.data('todoFilter'); + options.todoStateFilter = $dropdown.data('todoStateFilter'); + options.perPage = $dropdown.data('perPage'); + showNullUser = $dropdown.data('nullUser'); + defaultNullUser = $dropdown.data('nullUserDefault'); showMenuAbove = $dropdown.data('showMenuAbove'); - showAnyUser = $dropdown.data('any-user'); - firstUser = $dropdown.data('first-user'); - options.authorId = $dropdown.data('author-id'); - defaultLabel = $dropdown.data('default-label'); + showAnyUser = $dropdown.data('anyUser'); + firstUser = $dropdown.data('firstUser'); + options.authorId = $dropdown.data('authorId'); + defaultLabel = $dropdown.data('defaultLabel'); issueURL = $dropdown.data('issueUpdate'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); - abilityName = $dropdown.data('ability-name'); + abilityName = $dropdown.data('abilityName'); $value = $block.find('.value'); $collapsedSidebar = $block.find('.sidebar-collapsed-user'); $loading = $block.find('.block-loading').fadeOut(); @@ -63,7 +63,7 @@ function UsersSelect(currentUser, els, options = {}) { const assignYourself = function () { const unassignedSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=0]`); + .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`); if (unassignedSelected) { unassignedSelected.remove(); @@ -72,7 +72,7 @@ function UsersSelect(currentUser, els, options = {}) { // Save current selected user to the DOM const input = document.createElement('input'); input.type = 'hidden'; - input.name = $dropdown.data('field-name'); + input.name = $dropdown.data('fieldName'); const currentUserInfo = $dropdown.data('currentUserInfo'); @@ -96,7 +96,7 @@ function UsersSelect(currentUser, els, options = {}) { const getSelectedUserInputs = function() { return $selectbox - .find(`input[name="${$dropdown.data('field-name')}"]`); + .find(`input[name="${$dropdown.data('fieldName')}"]`); }; const getSelected = function() { @@ -106,14 +106,14 @@ function UsersSelect(currentUser, els, options = {}) { }; const checkMaxSelect = function() { - const maxSelect = $dropdown.data('max-select'); + const maxSelect = $dropdown.data('maxSelect'); if (maxSelect) { const selected = getSelected(); if (selected.length > maxSelect) { const firstSelectedId = selected[0]; const firstSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`); + .find(`input[name='${$dropdown.data('fieldName')}'][value=${firstSelectedId}]`); firstSelected.remove(); emitSidebarEvent('sidebar.removeAssignee', { @@ -158,7 +158,7 @@ function UsersSelect(currentUser, els, options = {}) { const currentUserInfo = $dropdown.data('currentUserInfo'); $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default'); } else { - const $input = $(`input[name="${$dropdown.data('field-name')}"]`); + const $input = $(`input[name="${$dropdown.data('fieldName')}"]`); $input.val(gon.current_user_id); selectedId = $input.val(); $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default'); @@ -293,10 +293,10 @@ function UsersSelect(currentUser, els, options = {}) { const selected = getSelected().filter(i => i !== 0); if (selected.length > 0) { - if ($dropdown.data('dropdown-header')) { + if ($dropdown.data('dropdownHeader')) { showDivider += 1; users.splice(showDivider, 0, { - header: $dropdown.data('dropdown-header'), + header: $dropdown.data('dropdownHeader'), }); } @@ -327,7 +327,7 @@ function UsersSelect(currentUser, els, options = {}) { fields: ['name', 'username'] }, selectable: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), toggleLabel: function(selected, el, glDropdown) { const inputValue = glDropdown.filterInput.val(); @@ -362,7 +362,7 @@ function UsersSelect(currentUser, els, options = {}) { emitSidebarEvent('sidebar.saveAssignees'); } - if (!$dropdown.data('always-show-selectbox')) { + if (!$dropdown.data('alwaysShowSelectbox')) { $selectbox.hide(); // Recalculate where .value is because vue might have changed it @@ -373,7 +373,7 @@ function UsersSelect(currentUser, els, options = {}) { } }, multiSelect: $dropdown.hasClass('js-multiselect'), - inputMeta: $dropdown.data('input-meta'), + inputMeta: $dropdown.data('inputMeta'), clicked: function(options) { const { $el, e, isMarking } = options; const user = options.selectedObj; @@ -381,7 +381,7 @@ function UsersSelect(currentUser, els, options = {}) { if ($dropdown.hasClass('js-multiselect')) { const isActive = $el.hasClass('is-active'); const previouslySelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]"); + .find("input[name='" + ($dropdown.data('fieldName')) + "'][value!=0]"); // Enables support for limiting the number of users selected // Automatically removes the first on the list if more users are selected @@ -400,7 +400,7 @@ function UsersSelect(currentUser, els, options = {}) { // Remove unassigned selection (if it was previously selected) const unassignedSelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]"); + .find("input[name='" + ($dropdown.data('fieldName')) + "'][value=0]"); if (unassignedSelected) { unassignedSelected.remove(); @@ -408,7 +408,7 @@ function UsersSelect(currentUser, els, options = {}) { } else { if (previouslySelected.length === 0) { // Select unassigned because there is no more selected users - this.addInput($dropdown.data('field-name'), 0, {}); + this.addInput($dropdown.data('fieldName'), 0, {}); } // User unselected @@ -440,7 +440,7 @@ function UsersSelect(currentUser, els, options = {}) { return; } if ($el.closest('.add-issues-modal').length) { - gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; + gl.issueBoards.ModalStore.store.filter[$dropdown.data('fieldName')] = user.id; } else if (handleClick) { e.preventDefault(); handleClick(user, isMarking); @@ -449,15 +449,15 @@ function UsersSelect(currentUser, els, options = {}) { } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); } else if (!$dropdown.hasClass('js-multiselect')) { - selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); + selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('fieldName')) + "']").val(); return assignTo(selected); } // Automatically close dropdown after assignee is selected // since CE has no multiple assignees // EE does not have a max-select - if ($dropdown.data('max-select') && - getSelected().length === $dropdown.data('max-select')) { + if ($dropdown.data('maxSelect') && + getSelected().length === $dropdown.data('maxSelect')) { // Close the dropdown $dropdown.dropdown('toggle'); } @@ -469,7 +469,7 @@ function UsersSelect(currentUser, els, options = {}) { const $el = $(e.currentTarget); const selected = getSelected(); if ($dropdown.hasClass('js-issue-board-sidebar') && selected.length === 0) { - this.addInput($dropdown.data('field-name'), 0, {}); + this.addInput($dropdown.data('fieldName'), 0, {}); } $el.find('.is-active').removeClass('is-active'); @@ -485,7 +485,7 @@ function UsersSelect(currentUser, els, options = {}) { highlightSelected(selectedId); } }, - updateLabel: $dropdown.data('dropdown-title'), + updateLabel: $dropdown.data('dropdownTitle'), renderRow: function(user) { var avatar, img, listClosingTags, listWithName, listWithUserName, username; username = user.username ? "@" + user.username : ""; @@ -533,15 +533,15 @@ function UsersSelect(currentUser, els, options = {}) { var firstUser, showAnyUser, showEmailUser, showNullUser; var options = {}; options.skipLdap = $(select).hasClass('skip_ldap'); - options.projectId = $(select).data('project-id'); - options.groupId = $(select).data('group-id'); - options.showCurrentUser = $(select).data('current-user'); - options.authorId = $(select).data('author-id'); - options.skipUsers = $(select).data('skip-users'); - showNullUser = $(select).data('null-user'); - showAnyUser = $(select).data('any-user'); - showEmailUser = $(select).data('email-user'); - firstUser = $(select).data('first-user'); + options.projectId = $(select).data('projectId'); + options.groupId = $(select).data('groupId'); + options.showCurrentUser = $(select).data('currentUser'); + options.authorId = $(select).data('authorId'); + options.skipUsers = $(select).data('skipUsers'); + showNullUser = $(select).data('nullUser'); + showAnyUser = $(select).data('anyUser'); + showEmailUser = $(select).data('emailUser'); + firstUser = $(select).data('firstUser'); return $(select).select2({ placeholder: "Search for a user", multiple: $(select).hasClass('multiselect'), diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue index 40c3cb500bb..ebaf2b972eb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue @@ -44,7 +44,10 @@ type="button" class="btn btn-xs btn-default" > - <loading-icon v-if="isRefreshing" /> + <loading-icon + v-if="isRefreshing" + :inline="true" + /> {{ s__("mrWidget|Refresh") }} </button> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 1371dca0c35..d2e968a8419 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -64,7 +64,7 @@ return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete); }, beforeDestroy() { - const glForm = $(this.$refs['gl-form']).data('gl-form'); + const glForm = $(this.$refs['gl-form']).data('glForm'); if (glForm) { glForm.destroy(); } diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index a2ea155a10e..2c30311b1c1 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -167,7 +167,7 @@ label { .input-group { .select2-container { display: table-cell; - width: 200px !important; + max-width: 180px; } .input-group-addon { diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index dbee7073975..b40dcf93969 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -8,7 +8,7 @@ .select2-choice { background: $white-light; border-color: $input-border; - height: 35px; + height: 34px; padding: $gl-vert-padding $gl-input-padding; font-size: $gl-font-size; line-height: 1.42857143; diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss index 4869cda73e5..528ba53a48b 100644 --- a/app/assets/stylesheets/framework/stacked_progress_bar.scss +++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss @@ -10,7 +10,7 @@ .status-neutral, .status-red, { height: 100%; - min-width: 25px; + min-width: 30px; padding: 0 5px; font-size: $tooltip-font-size; font-weight: normal; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 25ee081ea9c..54e13f9d95c 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -558,6 +558,7 @@ $jq-ui-default-color: #777; /* * Label */ +$label-font-size: 12px; $label-padding: 7px; $label-padding-modal: 10px; $label-gray-bg: #f8fafc; diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index da096354b5a..8871a069d5d 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -72,7 +72,6 @@ .label { color: $gl-text-color; - font-size: inherit; } iframe.twitter-share-button { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3fe95b34e01..0cf67734237 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -102,12 +102,11 @@ .issuable-show-labels { a { - margin-right: 5px; margin-bottom: 5px; display: inline-block; .color-label { - padding: 6px 10px; + padding: 4px $grid-size; border-radius: $label-border-radius; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index a72e654824e..0f49d15203b 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -105,13 +105,16 @@ } .label { - padding: 8px 12px; - font-size: 14px; + padding: 4px $grid-size; + font-size: $label-font-size; + position: relative; + top: ($grid-size / 2); } } .color-label { - padding: 3px $label-padding; + padding: 0 $grid-size; + line-height: 16px; border-radius: $label-border-radius; } @@ -302,10 +305,11 @@ } .label-link { - display: inline-block; + display: inline-flex; vertical-align: top; .label { vertical-align: inherit; + font-size: $label-font-size; } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index ae8fa45a2d7..e5afa8fffcb 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -115,6 +115,10 @@ display: block; margin-top: 7px; + .issue-link { + display: inline-block; + } + .issuable-number { color: $gl-text-color-secondary; margin-right: 5px; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index f10908c3630..42772f13155 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -320,14 +320,17 @@ } } -// Pipeline graph -.pipeline-graph { +.pipeline-tab-content { width: 100%; background-color: $gray-light; padding: $gl-padding; + overflow: auto; +} + +// Pipeline graph +.pipeline-graph { white-space: nowrap; transition: max-height 0.3s, padding 0.3s; - overflow: auto; .stage-column-list, .builds-container > ul { diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index a79772ea37b..4b9824fab0c 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -128,7 +128,6 @@ .label { color: $gl-text-color; - font-size: inherit; } p { diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 0d7ee06deb6..f7ba305a59f 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -103,7 +103,7 @@ module IssuableCollections # @filter_params[:authorized_only] = true end - @filter_params.permit(IssuableFinder::VALID_PARAMS) + @filter_params.permit(finder_type.valid_params) end # rubocop:enable Gitlab/ModuleWithInstanceVariables @@ -146,7 +146,7 @@ module IssuableCollections def finder strong_memoize(:finder) do - issuable_finder_for(@finder_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables + issuable_finder_for(finder_type) end end diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index 3ba1235cee0..3b11a373368 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -4,7 +4,6 @@ module IssuesAction # rubocop:disable Gitlab/ModuleWithInstanceVariables def issues - @finder_type = IssuesFinder @issues = issuables_collection .non_archived .page(params[:page]) @@ -17,4 +16,11 @@ module IssuesAction end end # rubocop:enable Gitlab/ModuleWithInstanceVariables + + private + + def finder_type + (super if defined?(super)) || + (IssuesFinder if action_name == 'issues') + end end diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index a9cc13038bf..b70db99b157 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -4,8 +4,6 @@ module MergeRequestsAction # rubocop:disable Gitlab/ModuleWithInstanceVariables def merge_requests - @finder_type = MergeRequestsFinder - @merge_requests = issuables_collection.page(params[:page]) @issuable_meta_data = issuable_meta_data(@merge_requests, collection_type) @@ -14,6 +12,11 @@ module MergeRequestsAction private + def finder_type + (super if defined?(super)) || + (MergeRequestsFinder if action_name == 'merge_requests') + end + def filter_params super.merge(non_archived: true) end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 33fced99132..73806454525 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -243,9 +243,8 @@ class Projects::IssuesController < Projects::ApplicationController Issues::UpdateService.new(project, current_user, update_params) end - def set_issuables_index - @finder_type = IssuesFinder - super + def finder_type + IssuesFinder end def whitelist_query_limiting diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 8eed957d9fe..a1af125547c 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -323,9 +323,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @target_branches = @merge_request.target_project.repository.branch_names end - def set_issuables_index - @finder_type = MergeRequestsFinder - super + def finder_type + MergeRequestsFinder end def check_user_can_push_to_source_branch! diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 72573e0765d..0370edc6e20 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -279,7 +279,6 @@ class ProjectsController < Projects::ApplicationController @project_wiki = @project.wiki @wiki_home = @project_wiki.find_page('home', params[:version_id]) elsif @project.feature_available?(:issues, current_user) - @finder_type = IssuesFinder @issues = issuables_collection.page(params[:page]) @collection_type = 'Issue' @issuable_meta_data = issuable_meta_data(@issues, @collection_type) @@ -289,6 +288,10 @@ class ProjectsController < Projects::ApplicationController end end + def finder_type + IssuesFinder + end + def determine_layout if [:new, :create].include?(action_name.to_sym) 'application' diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 63e5fdb1da5..8acefd58e77 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -13,7 +13,10 @@ class RootController < Dashboard::ProjectsController before_action :redirect_logged_user, if: -> { current_user.present? } def index - super + # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37434 + Gitlab::GitalyClient.allow_n_plus_1_calls do + super + end end private diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 0fe3000ca01..384a336e2bb 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -25,32 +25,38 @@ class IssuableFinder NONE = '0'.freeze - SCALAR_PARAMS = %i[ - assignee_id - assignee_username - author_id - author_username - authorized_only - due_date - group_id - iids - label_name - milestone_title - my_reaction_emoji - non_archived - project_id - scope - search - sort - state - include_subgroups - ].freeze - ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze - - VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze - attr_accessor :current_user, :params + def self.scalar_params + @scalar_params ||= %i[ + assignee_id + assignee_username + author_id + author_username + authorized_only + group_id + iids + label_name + milestone_title + my_reaction_emoji + non_archived + project_id + scope + search + sort + state + include_subgroups + ] + end + + def self.array_params + @array_params ||= { label_name: [], iids: [], assignee_username: [] } + end + + def self.valid_params + @valid_params ||= scalar_params + [array_params] + end + def initialize(current_user, params = {}) @current_user = current_user @params = params @@ -58,6 +64,15 @@ class IssuableFinder def execute items = init_collection + items = filter_items(items) + + # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far + items = by_project(items) + + sort(items) + end + + def filter_items(items) items = by_scope(items) items = by_created_at(items) items = by_state(items) @@ -65,16 +80,11 @@ class IssuableFinder items = by_search(items) items = by_assignee(items) items = by_author(items) - items = by_due_date(items) items = by_non_archived(items) items = by_iids(items) items = by_milestone(items) items = by_label(items) - items = by_my_reaction_emoji(items) - - # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far - items = by_project(items) - sort(items) + by_my_reaction_emoji(items) end def find(*params) @@ -396,42 +406,6 @@ class IssuableFinder items end - def by_due_date(items) - if due_date? - if filter_by_no_due_date? - items = items.without_due_date - elsif filter_by_overdue? - items = items.due_before(Date.today) - elsif filter_by_due_this_week? - items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) - elsif filter_by_due_this_month? - items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) - end - end - - items - end - - def filter_by_no_due_date? - due_date? && params[:due_date] == Issue::NoDueDate.name - end - - def filter_by_overdue? - due_date? && params[:due_date] == Issue::Overdue.name - end - - def filter_by_due_this_week? - due_date? && params[:due_date] == Issue::DueThisWeek.name - end - - def filter_by_due_this_month? - due_date? && params[:due_date] == Issue::DueThisMonth.name - end - - def due_date? - params[:due_date].present? && klass.column_names.include?('due_date') - end - def label_names if labels? params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name] diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 83245aadf6e..d65c620e75a 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -16,10 +16,15 @@ # sort: string # my_reaction_emoji: string # public_only: boolean +# due_date: date or '0', '', 'overdue', 'week', or 'month' # class IssuesFinder < IssuableFinder CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER + def self.scalar_params + @scalar_params ||= super + [:due_date] + end + def klass Issue.includes(:author) end @@ -52,6 +57,46 @@ class IssuesFinder < IssuableFinder params.fetch(:public_only, false) end + def filter_items(items) + by_due_date(super) + end + + def by_due_date(items) + if due_date? + if filter_by_no_due_date? + items = items.without_due_date + elsif filter_by_overdue? + items = items.due_before(Date.today) + elsif filter_by_due_this_week? + items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) + elsif filter_by_due_this_month? + items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) + end + end + + items + end + + def filter_by_no_due_date? + due_date? && params[:due_date] == Issue::NoDueDate.name + end + + def filter_by_overdue? + due_date? && params[:due_date] == Issue::Overdue.name + end + + def filter_by_due_this_week? + due_date? && params[:due_date] == Issue::DueThisWeek.name + end + + def filter_by_due_this_month? + due_date? && params[:due_date] == Issue::DueThisMonth.name + end + + def due_date? + params[:due_date].present? + end + def user_can_see_all_confidential_issues? return @user_can_see_all_confidential_issues if defined?(@user_can_see_all_confidential_issues) diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index af24045886e..4734d97b8c7 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -10,26 +10,59 @@ class MembersFinder def execute project_members = project.project_members project_members = project_members.non_invite unless can?(current_user, :admin_project, project) - wheres = ["members.id IN (#{project_members.select(:id).to_sql})"] if group - # We need `.where.not(user_id: nil)` here otherwise when a group has an - # invitee, it would make the following query return 0 rows since a NULL - # user_id would be present in the subquery - # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values - non_null_user_ids = project_members.where.not(user_id: nil).select(:user_id) - group_members = GroupMembersFinder.new(group).execute - group_members = group_members.where.not(user_id: non_null_user_ids) - group_members = group_members.non_invite unless can?(current_user, :admin_group, group) + group_members = group_members.non_invite - wheres << "members.id IN (#{group_members.select(:id).to_sql})" - end + union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) - Member.where(wheres.join(' OR ')) + sql = distinct_on(union) + + Member.includes(:user).from("(#{sql}) AS #{Member.table_name}") + else + project_members + end end def can?(*args) Ability.allowed?(*args) end + + private + + def distinct_on(union) + # We're interested in a list of members without duplicates by user_id. + # We prefer project members over group members, project members should go first. + if Gitlab::Database.postgresql? + <<~SQL + SELECT DISTINCT ON (user_id, invite_email) member_union.* + FROM (#{union.to_sql}) AS member_union + ORDER BY user_id, + invite_email, + CASE + WHEN type = 'ProjectMember' THEN 1 + WHEN type = 'GroupMember' THEN 2 + ELSE 3 + END + SQL + else + # Older versions of MySQL do not support window functions (and DISTINCT ON is postgres-specific). + <<~SQL + SELECT t1.* + FROM (#{union.to_sql}) AS t1 + JOIN ( + SELECT + COALESCE(user_id, -1) AS user_id, + COALESCE(invite_email, 'NULL') AS invite_email, + MIN(CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END) AS type_number + FROM + (#{union.to_sql}) AS t3 + GROUP BY COALESCE(user_id, -1), COALESCE(invite_email, 'NULL') + ) AS t2 ON COALESCE(t1.user_id, -1) = t2.user_id + AND COALESCE(t1.invite_email, 'NULL') = t2.invite_email + AND CASE WHEN t1.type = 'ProjectMember' THEN 1 WHEN t1.type = 'GroupMember' THEN 2 ELSE 3 END = t2.type_number + SQL + end + end end diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index 33359fa1efb..ec61fe1892e 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -56,8 +56,10 @@ class SnippetsFinder < UnionFinder end def feature_available_projects - projects = Project.public_or_visible_to_user(current_user) - .with_feature_available_for_user(:snippets, current_user).select(:id) + projects = Project.public_or_visible_to_user(current_user, use_where_in: false) do |part| + part.with_feature_available_for_user(:snippets, current_user) + end.select(:id) + arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql) table[:project_id].in(arel_query) end diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb index 9d071f2d59a..8bcced70d63 100644 --- a/app/helpers/webpack_helper.rb +++ b/app/helpers/webpack_helper.rb @@ -7,17 +7,24 @@ module WebpackHelper def webpack_controller_bundle_tags bundles = [] - segments = [*controller.controller_path.split('/'), controller.action_name].compact - until segments.empty? + action = case controller.action_name + when 'create' then 'new' + when 'update' then 'edit' + else controller.action_name + end + + route = [*controller.controller_path.split('/'), action].compact + + until route.empty? begin - asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js') + asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js') bundles.unshift(*asset_paths) rescue Webpack::Rails::Manifest::EntryPointMissingError # no bundle exists for this path end - segments.pop + route.pop end javascript_include_tag(*bundles) diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb index d76c61c369f..75cf56a51f2 100644 --- a/app/mailers/emails/members.rb +++ b/app/mailers/emails/members.rb @@ -7,18 +7,11 @@ module Emails helper_method :member_source, :member end - def member_access_requested_email(member_source_type, member_id) + def member_access_requested_email(member_source_type, member_id, recipient_notification_email) @member_source_type = member_source_type @member_id = member_id - admins = member_source.members.owners_and_masters.pluck(:notification_email) - # A project in a group can have no explicit owners/masters, in that case - # we fallbacks to the group's owners/masters. - if admins.empty? && member_source.respond_to?(:group) && member_source.group - admins = member_source.group.members.owners_and_masters.pluck(:notification_email) - end - - mail(to: admins, + mail(to: recipient_notification_email, subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) end diff --git a/app/models/blob.rb b/app/models/blob.rb index 19ad110db58..71c974b4c09 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -160,7 +160,7 @@ class Blob < SimpleDelegator if stored_externally? if rich_viewer rich_viewer.binary? - elsif Linguist::Language.find_by_filename(name).any? + elsif Linguist::Language.find_by_extension(name).any? false elsif _mime_type _mime_type.binary? diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 490edf4ac57..ee987949080 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -41,41 +41,12 @@ module Ci scope :unstarted, ->() { where(runner_id: nil) } scope :ignore_failures, ->() { where(allow_failure: false) } - - # This convoluted mess is because we need to handle two cases of - # artifact files during the migration. And a simple OR clause - # makes it impossible to optimize. - - # Instead we want to use UNION ALL and do two carefully - # constructed disjoint queries. But Rails cannot handle UNION or - # UNION ALL queries so we do the query in a subquery and wrap it - # in an otherwise redundant WHERE IN query (IN is fine for - # non-null columns). - - # This should all be ripped out when the migration is finished and - # replaced with just the new storage to avoid the extra work. - scope :with_artifacts, ->() do - old = Ci::Build.select(:id).where(%q[artifacts_file <> '']) - new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)], - Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id')) - where('ci_builds.id IN (? UNION ALL ?)', old, new) + where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)', + '', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id')) end - - scope :with_artifacts_not_expired, ->() do - old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND (artifacts_expire_at IS NULL OR artifacts_expire_at > ?)], Time.now) - new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)], - Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND (expire_at IS NULL OR expire_at > ?)', Time.now)) - where('ci_builds.id IN (? UNION ALL ?)', old, new) - end - - scope :with_expired_artifacts, ->() do - old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND artifacts_expire_at < ?], Time.now) - new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)], - Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND expire_at < ?', Time.now)) - where('ci_builds.id IN (? UNION ALL ?)', old, new) - end - + scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) } + scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) } scope :ref_protected, -> { where(protected: true) } diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 2abe90dd181..a72a815bfe8 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -13,7 +13,7 @@ module Ci belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule' has_many :stages - has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id + has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent has_many :variables, class_name: 'Ci::PipelineVariable' diff --git a/app/models/commit.rb b/app/models/commit.rb index 8c960389652..add5fcf0e79 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -417,6 +417,10 @@ class Commit !!(title =~ WIP_REGEX) end + def merged_merge_request?(user) + !!merged_merge_request(user) + end + private def commit_reference(from, referable_commit_id, full: false) @@ -445,10 +449,6 @@ class Commit changes end - def merged_merge_request?(user) - !!merged_merge_request(user) - end - def merged_merge_request_no_cache(user) MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index fd6703831e4..caf8afa97f9 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -94,6 +94,14 @@ module Milestoneish Gitlab::TimeTrackingFormatter.output(total_issue_time_spent) end + def total_issue_time_estimate + @total_issue_time_estimate ||= issues.sum(:time_estimate) + end + + def human_total_issue_time_estimate + Gitlab::TimeTrackingFormatter.output(total_issue_time_estimate) + end + private def count_issues_by_state(user) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 3aed071dd49..b6cf168d60e 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -1,8 +1,8 @@ class Deployment < ActiveRecord::Base include InternalId - belongs_to :project, required: true, validate: true - belongs_to :environment, required: true, validate: true + belongs_to :project, required: true + belongs_to :environment, required: true belongs_to :user belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/environment.rb b/app/models/environment.rb index 2f6eae605ee..f78c21aebe5 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -4,7 +4,7 @@ class Environment < ActiveRecord::Base NUMBERS = '0'..'9' SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a - belongs_to :project, required: true, validate: true + belongs_to :project, required: true has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent diff --git a/app/models/key.rb b/app/models/key.rb index 7406c98c99e..ae5769c0627 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -33,9 +33,8 @@ class Key < ActiveRecord::Base after_destroy :refresh_user_cache def key=(value) - value&.delete!("\n\r") - value.strip! unless value.blank? - write_attribute(:key, value) + write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil) + @public_key = nil end @@ -97,7 +96,7 @@ class Key < ActiveRecord::Base def generate_fingerprint self.fingerprint = nil - return unless self.key.present? + return unless public_key.valid? self.fingerprint = public_key.fingerprint end diff --git a/app/models/project.rb b/app/models/project.rb index 2ba6a863500..79058d51af8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -316,18 +316,42 @@ class Project < ActiveRecord::Base # Returns a collection of projects that is either public or visible to the # logged in user. - def self.public_or_visible_to_user(user = nil) - if user - authorized = user - .project_authorizations - .select(1) - .where('project_authorizations.project_id = projects.id') - - levels = Gitlab::VisibilityLevel.levels_for_user(user) - - where('EXISTS (?) OR projects.visibility_level IN (?)', authorized, levels) + # + # A caller may pass in a block to modify individual parts of + # the query, e.g. to apply .with_feature_available_for_user on top of it. + # This is useful for performance as we can stick those additional filters + # at the bottom of e.g. the UNION. + # + # Optionally, turning `use_where_in` off leads to returning a + # relation using #from instead of #where. This can perform much better + # but leads to trouble when used in conjunction with AR's #merge method. + def self.public_or_visible_to_user(user = nil, use_where_in: true, &block) + # If we don't get a block passed, use identity to avoid if/else repetitions + block = ->(part) { part } unless block_given? + + return block.call(public_to_user) unless user + + # If the user is allowed to see all projects, + # we can shortcut and just return. + return block.call(all) if user.full_private_access? + + authorized = user + .project_authorizations + .select(1) + .where('project_authorizations.project_id = projects.id') + authorized_projects = block.call(where('EXISTS (?)', authorized)) + + levels = Gitlab::VisibilityLevel.levels_for_user(user) + visible_projects = block.call(where(visibility_level: levels)) + + # We use a UNION here instead of OR clauses since this results in better + # performance. + union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')]) + + if use_where_in + where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection else - public_to_user + from("(#{union.to_sql}) AS #{table_name}") end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 4f754b11da4..299a3f32a85 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -492,12 +492,8 @@ class Repository end def root_ref - if raw_repository - raw_repository.root_ref - else - # When the repo does not exist we raise this error so no data is cached. - raise Gitlab::Git::Repository::NoRepository - end + # When the repo does not exist, or there is no root ref, we raise this error so no data is cached. + raw_repository&.root_ref or raise Gitlab::Git::Repository::NoRepository # rubocop:disable Style/AndOr end cache_method :root_ref diff --git a/app/models/user.rb b/app/models/user.rb index 5e84d2da805..f5eeba27572 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -59,6 +59,8 @@ class User < ActiveRecord::Base # Override Devise::Models::Trackable#update_tracked_fields! # to limit database writes to at most once every hour def update_tracked_fields!(request) + return if Gitlab::Database.read_only? + update_tracked_fields(request) lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i) diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 299b9c6215f..7140890d201 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -48,7 +48,8 @@ module Issues new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, milestone_id: cloneable_milestone_id, project: @new_project, author: @old_issue.author, - description: rewrite_content(@old_issue.description) } + description: rewrite_content(@old_issue.description), + assignee_ids: @old_issue.assignee_ids } new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params) CreateService.new(@new_project, @current_user, new_params).execute diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 8c84ccfcc92..56e941d90ff 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -208,7 +208,12 @@ class NotificationService def new_access_request(member) return true unless member.notifiable?(:subscription) - mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later + recipients = member.source.members.owners_and_masters + if fallback_to_group_owners_masters?(recipients, member) + recipients = member.source.group.members.owners_and_masters + end + + recipients.each { |recipient| deliver_access_request_email(recipient, member) } end def decline_access_request(member) @@ -435,4 +440,14 @@ class NotificationService def notifiable_users(*args) NotificationRecipientService.notifiable_users(*args) end + + def deliver_access_request_email(recipient, member) + mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later + end + + def fallback_to_group_owners_masters?(recipients, member) + return false if recipients.present? + + member.source.respond_to?(:group) && member.source.group + end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 60f12030f98..938185b6eba 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -647,8 +647,11 @@ = f.label :version_check_enabled do = f.check_box :version_check_enabled Version check enabled + = link_to icon('question-circle'), help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check") .help-block - Let GitLab inform you when an update is available. + Let GitLab inform you when an update is available. When + enabled, GitLab Inc. will collect info about your hostname + and version. .form-group .col-sm-offset-2.col-sm-10 - can_be_configured = @application_setting.usage_ping_can_be_configured? diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index a039756c7e2..56ec1b3db0d 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -1,6 +1,6 @@ - if inject_u2f_api? - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('u2f') + = webpack_bundle_tag('u2f') %div = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication' diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 00909982d59..f2ae7c52031 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -5,7 +5,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' - if group_issues_exists .top-area diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 694292aa7c1..046b92bd9fb 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -2,7 +2,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' - if @group_merge_requests.empty? = render 'shared/empty_states/merge_requests', project_select_button: true diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 63811ea1c81..bf2725dc328 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,5 +1,3 @@ -= webpack_bundle_tag 'docs' - %div - if Gitlab::CurrentSettings.help_page_text.present? = markdown_field(Gitlab::CurrentSettings.current_application_settings, :help_page_text) diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml index d6789baea28..c07c148a12a 100644 --- a/app/views/help/show.html.haml +++ b/app/views/help/show.html.haml @@ -1,5 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'help' - page_title @path.split("/").reverse.map(&:humanize) .documentation.wiki.prepend-top-default = markdown @markdown diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index 5d68e1e2156..df5841d1911 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -1,7 +1,5 @@ - page_title "GitLab Import" - header_title "Projects", root_path -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'project_import_gl' %h3.page-title = icon('gitlab') diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml index 83ae9129807..a8eb66ca13c 100644 --- a/app/views/profiles/_head.html.haml +++ b/app/views/profiles/_head.html.haml @@ -1,2 +1,2 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('profile') + = webpack_bundle_tag('profile') diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 79e197ad08b..0f849f6f8b7 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -102,6 +102,3 @@ %p = s_("Profiles|You don't have access to delete this user.") .append-bottom-default - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('account') diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 5207dac3ac2..e58cd20402c 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -6,8 +6,8 @@ - content_for :page_specific_javascripts do - if inject_u2f_api? - = page_specific_javascript_bundle_tag('u2f') - = page_specific_javascript_bundle_tag('two_factor_auth') + = webpack_bundle_tag('u2f') + = webpack_bundle_tag('two_factor_auth') .js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } .row.prepend-top-default diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 21b6aa4bad9..b3afd16f900 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -2,7 +2,7 @@ .modal-dialog.modal-lg .modal-content .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title= title .modal-body = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal', data: { method: method } do @@ -29,4 +29,4 @@ = commit_in_fork_help - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('blob') + = webpack_bundle_tag('blob') diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 626cbc9e41d..9d90251ab66 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -3,7 +3,6 @@ - page_title "Edit", @blob.path, @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('blob') %div{ class: container_class } - if @conflict diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index a4263774dfd..fa091d8f6ef 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -2,7 +2,6 @@ - page_title "New File", @path.presence, @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('blob') .editor-title-row %h3.page-title.blob-new-page-title New file diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 2ed454131af..efb8175398b 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,10 +3,6 @@ - page_title @blob.path, @ref -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'blob' - - %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml index 1e7c461f02e..15349387eb2 100644 --- a/app/views/projects/blob/viewers/_balsamiq.html.haml +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -1,4 +1,4 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('balsamiq_viewer') + = webpack_bundle_tag('balsamiq_viewer') .file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_notebook.html.haml b/app/views/projects/blob/viewers/_notebook.html.haml index 8a41bc53004..d1ffaca35b9 100644 --- a/app/views/projects/blob/viewers/_notebook.html.haml +++ b/app/views/projects/blob/viewers/_notebook.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('notebook_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('notebook_viewer') .file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_pdf.html.haml b/app/views/projects/blob/viewers/_pdf.html.haml index ec2b18bd4ab..fc3f0d922b1 100644 --- a/app/views/projects/blob/viewers/_pdf.html.haml +++ b/app/views/projects/blob/viewers/_pdf.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('pdf_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('pdf_viewer') .file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_sketch.html.haml b/app/views/projects/blob/viewers/_sketch.html.haml index 775e4584f77..8fb67c819c1 100644 --- a/app/views/projects/blob/viewers/_sketch.html.haml +++ b/app/views/projects/blob/viewers/_sketch.html.haml @@ -1,6 +1,6 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('sketch_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('sketch_viewer') .file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } } .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' } diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml index 6578d826ace..e58809ec008 100644 --- a/app/views/projects/blob/viewers/_stl.html.haml +++ b/app/views/projects/blob/viewers/_stl.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('stl_viewer') + = webpack_bundle_tag('stl_viewer') .file-content.is-stl-loading .text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 0a54c736761..d8b4266143e 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do + %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } } - if current_user.starred?(@project) = sprite_icon('star') %span.starred= _('Unstar') diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 49b0b314e1d..3f699882c5f 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -8,5 +8,5 @@ } } - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('commit_pipelines') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('commit_pipelines') diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 2890e9d2b65..4058e61eb9a 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -7,8 +7,8 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('diff_notes') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('diff_notes') .container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 71d30da14a9..d98e0564da4 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,8 +1,8 @@ - @no_container = true - page_title "Cycle Analytics" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('cycle_analytics') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('cycle_analytics') #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index d9c9f0ed546..eca10d99908 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -2,8 +2,8 @@ - page_title "Environments" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag("environments_folder") + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag("environments_folder") #environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), "folder-name" => @folder, diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 88f1348da47..31cf173fa9c 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -3,8 +3,8 @@ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag("common_vue") - = page_specific_javascript_bundle_tag("environments") + = webpack_bundle_tag("common_vue") + = webpack_bundle_tag("environments") #environments-list-view{ data: { environments_data: environments_list_data, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 10812f67cbe..91b3743e9e7 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -2,7 +2,6 @@ - page_title "Metrics for environment", @environment.name - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'common_d3' .prometheus-container{ class: container_class } .top-area diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index a073a164f11..7be4ef39117 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -3,7 +3,7 @@ - content_for :page_specific_javascripts do = stylesheet_link_tag "xterm/xterm" - = page_specific_javascript_bundle_tag("terminal") + = webpack_bundle_tag("terminal") %div{ class: container_class } .top-area diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 300a39fe257..d4b4a6203f3 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -1,9 +1,5 @@ - @no_container = true - page_title "Charts" -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_d3') - = webpack_bundle_tag('graphs') - = webpack_bundle_tag('graphs_charts') .repo-charts{ class: container_class } %h4.sub-header diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index cce16bc58b3..c81ee6874e3 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,9 +1,5 @@ - @no_container = true - page_title _('Contributors') -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_d3') - = webpack_bundle_tag('graphs') - = webpack_bundle_tag('graphs_show') .js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .sub-header-block diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 193111b4cee..fb06ba58c27 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -6,7 +6,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' = content_for :meta_tags do = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues") diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 1f28d8acff6..91f68d8c419 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -5,10 +5,6 @@ - page_description @issue.description - page_card_attributes @issue.card_attributes -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'notes' - - can_update_issue = can?(current_user, :update_issue, @issue) - can_report_spam = @issue.submittable_as_spam_by?(current_user) @@ -87,5 +83,5 @@ = render 'shared/issuable/sidebar', issuable: @issue -= page_specific_javascript_bundle_tag('common_vue') -= page_specific_javascript_bundle_tag('issue_show') += webpack_bundle_tag('common_vue') += webpack_bundle_tag('issue_show') diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 93efa7e8e86..849c273db8c 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -112,7 +112,3 @@ .js-build-options{ data: javascript_build_options } #js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json) } } - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('job_details') diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index 917ec7fdbda..54a661040ea 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('how_to_merge') - #modal_merge_info.modal .modal-dialog .modal-content diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml index 454bc359b6b..2a2e57027be 100644 --- a/app/views/projects/merge_requests/conflicts.html.haml +++ b/app/views/projects/merge_requests/conflicts.html.haml @@ -1,7 +1,7 @@ - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('merge_conflicts') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('merge_conflicts') = page_specific_javascript_tag('lib/ace.js') = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index 454bc359b6b..2a2e57027be 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -1,7 +1,7 @@ - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('merge_conflicts') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('merge_conflicts') = page_specific_javascript_tag('lib/ace.js') = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 640d2791dc1..720ba236434 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -8,7 +8,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 8740c6895df..e29f21b3bec 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -6,7 +6,6 @@ - page_card_attributes @merge_request.card_attributes - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('diff_notes') .merge-request{ data: { mr_action: j(params[:tab].presence || 'show'), url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project) } } = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 2efb7fc719f..97be8950db0 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title "Graph" - page_title "Graph", @ref - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('network') + = webpack_bundle_tag('network') = render "head" %div{ class: container_class } .project-network diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 5de17977d5a..852143ecb2a 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -3,16 +3,16 @@ .tabs-holder %ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator %li.js-pipeline-tab-link - = link_to project_pipeline_path(@project, @pipeline), data: { target: 'div#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do - Pipeline + = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do + = _("Pipeline") %li.js-builds-tab-link - = link_to builds_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do - Jobs + = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do + = _("Jobs") %span.badge.js-builds-counter= pipeline.total_size - if failed_builds.present? %li.js-failures-tab-link - = link_to failures_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do - Failed Jobs + = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do + = _("Failed Jobs") %span.badge.js-failures-counter= failed_builds.count .tab-content diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index f8555f11aab..fdcc60f48a5 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -13,5 +13,5 @@ "ci-lint-path" => ci_lint_path, "reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } } - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('pipelines') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('pipelines') diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml index 2f30fe33a97..127a338e413 100644 --- a/app/views/projects/protected_branches/_index.html.haml +++ b/app/views/projects/protected_branches/_index.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('protected_branches') + = webpack_bundle_tag('protected_branches') - content_for :create_protected_branch do = render 'projects/protected_branches/create_protected_branch' diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml index 955220562a6..74f7f63c941 100644 --- a/app/views/projects/protected_tags/_index.html.haml +++ b/app/views/projects/protected_tags/_index.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('protected_tags') + = webpack_bundle_tag('protected_tags') - content_for :create_protected_tag do = render 'projects/protected_tags/create_protected_tag' diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index 36ea5e013e4..744b88760bc 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -14,8 +14,8 @@ .col-lg-12 #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } } - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('registry_list') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('registry_list') .row.prepend-top-10 .col-lg-12 diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index b0cb5ce5e8f..5f38ecd6820 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('prometheus_metrics') - .row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring .col-lg-3 %h4.prepend-top-0 diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 517d51993d2..3077203c2a6 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -3,8 +3,8 @@ - @content_class = "limit-container-width" unless fluid_layout - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('deploy_keys') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('deploy_keys') -# Protected branches & tags use a lot of nested partials. -# The shared parts of the views can be found in the `shared` directory. diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index ee8ad8e3999..a10fc42b82d 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -6,7 +6,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' = webpack_bundle_tag 'boards' %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 15fd01c8429..dc583d3eb3b 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,7 +1,7 @@ - todo = issuable_todo(issuable) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('sidebar') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('sidebar') %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } } diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 479b7270b28..129f6ab604e 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -17,7 +17,7 @@ = confidential_icon(issuable) = link_to issuable.title, issuable_url_args, title: issuable.title .issuable-detail - = link_to [namespace, project, issuable] do + = link_to [namespace, project, issuable], class: 'issue-link' do %span.issuable-number= issuable.to_reference - labels.each do |label| diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index 4f51455c26e..cd4188daf5b 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -1,5 +1,7 @@ - affix_offset = local_assigns.fetch(:affix_offset, "50") - project = local_assigns[:project] +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('common_vue') %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar.milestone-sidebar @@ -85,21 +87,12 @@ Closed: = milestone.issues_visible_to_user(current_user).closed.count - .block.time_spent - .sidebar-collapsed-icon - = custom_icon('icon_hourglass') - %span.collapsed-milestone-total-time-spent - - if milestone.human_total_issue_time_spent - = milestone.human_total_issue_time_spent - - else - = _("None") - .title.hide-collapsed - = _("Total issue time spent") - .value.hide-collapsed - - if milestone.human_total_issue_time_spent - %span.bold= milestone.human_total_issue_time_spent - - else - %span.no-value= _("No time spent") + .block + #issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate, time_spent: @milestone.total_issue_time_spent, human_time_estimate: @milestone.human_total_issue_time_estimate, human_time_spent: @milestone.human_total_issue_time_spent } } + // Fallback while content is loading + .title.hide-collapsed + = _('Time tracking') + = icon('spinner spin') .block.merge-requests .sidebar-collapsed-icon diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 43322978749..2726a4934fb 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -1,6 +1,6 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('snippet') + = webpack_bundle_tag('snippet') .snippet-form-holder = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f| diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb index 52eebe475ec..5b25d980bdb 100644 --- a/app/workers/process_commit_worker.rb +++ b/app/workers/process_commit_worker.rb @@ -23,27 +23,25 @@ class ProcessCommitWorker return unless user commit = build_commit(project, commit_hash) - author = commit.author || user process_commit_message(project, commit, user, author, default) - update_issue_metrics(commit, author) end def process_commit_message(project, commit, user, author, default = false) - closed_issues = default ? commit.closes_issues(user) : [] + # this is a GitLab generated commit message, ignore it. + return if commit.merged_merge_request?(user) - unless closed_issues.empty? - close_issues(project, user, author, commit, closed_issues) - end + closed_issues = default ? commit.closes_issues(user) : [] + close_issues(project, user, author, commit, closed_issues) if closed_issues.any? commit.create_cross_references!(author, closed_issues) end def close_issues(project, user, author, commit, issues) # We don't want to run permission related queries for every single issue, - # therefor we use IssueCollection here and skip the authorization check in + # therefore we use IssueCollection here and skip the authorization check in # Issues::CloseService#execute. IssueCollection.new(issues).updatable_by_user(user).each do |issue| Issues::CloseService.new(project, author) diff --git a/bin/changelog b/bin/changelog index efe25032ba1..9b60f53ce40 100755 --- a/bin/changelog +++ b/bin/changelog @@ -214,9 +214,10 @@ class ChangelogEntry end def unreleased_path - File.join('changelogs', 'unreleased').tap do |path| - path << '-ee' if ee? - end + path = File.join('changelogs', 'unreleased') + path = File.join('ee', path) if ee? + + path end def ee? diff --git a/changelogs/unreleased/14256-upload-destroy-removes-file.yml b/changelogs/unreleased/14256-upload-destroy-removes-file.yml deleted file mode 100644 index d97188e23f1..00000000000 --- a/changelogs/unreleased/14256-upload-destroy-removes-file.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Deleting an upload will correctly clean up the filesystem. -merge_request: 16799 -author: -type: fixed diff --git a/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml b/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml deleted file mode 100644 index e94b4f8bb26..00000000000 --- a/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Update 'removed assignee' note to include old assignee reference" -merge_request: 16301 -author: Maurizio De Santis -type: changed diff --git a/changelogs/unreleased/16468-add-fast-blank.yml b/changelogs/unreleased/16468-add-fast-blank.yml deleted file mode 100644 index ef68888ae33..00000000000 --- a/changelogs/unreleased/16468-add-fast-blank.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Add fast-blank" -merge_request: 16468 -author: -type: performance diff --git a/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml b/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml new file mode 100644 index 00000000000..efd936ca104 --- /dev/null +++ b/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml @@ -0,0 +1,5 @@ +--- + title: Add missing pagination on the commit diff endpoint + merge_request: 17203 + author: Maxime Roussin-Bélanger + type: fixed diff --git a/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml b/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml new file mode 100644 index 00000000000..a94e6153a05 --- /dev/null +++ b/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml @@ -0,0 +1,5 @@ +--- +title: Update issue closing pattern to allow variations in punctuation +merge_request: 17198 +author: Vicky Chijwani +type: changed diff --git a/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml b/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml deleted file mode 100644 index 447c65a3764..00000000000 --- a/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds Rubocop rule for line break around conditionals -merge_request: 15739 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml b/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml deleted file mode 100644 index 962f918e9db..00000000000 --- a/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Makes forking protect default branch on completion -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/21554-mark-new-user-as-external.yml b/changelogs/unreleased/21554-mark-new-user-as-external.yml deleted file mode 100644 index fb0826fc176..00000000000 --- a/changelogs/unreleased/21554-mark-new-user-as-external.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Login via OAuth now only marks new users as external -merge_request: 16672 -author: -type: fixed diff --git a/changelogs/unreleased/24035-api-create-application.yml b/changelogs/unreleased/24035-api-create-application.yml deleted file mode 100644 index c583a020d9d..00000000000 --- a/changelogs/unreleased/24035-api-create-application.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add application create API -merge_request: 8160 -author: Nicolas Merelli @PNSalocin diff --git a/changelogs/unreleased/24167__color_label.yml b/changelogs/unreleased/24167__color_label.yml deleted file mode 100644 index 68c6c731163..00000000000 --- a/changelogs/unreleased/24167__color_label.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Colors to GitLab Flavored Markdown -merge_request: 16095 -author: Tony Rom <thetonyrom@gmail.com> -type: added diff --git a/changelogs/unreleased/25327-coverage-badge-rounding.yml b/changelogs/unreleased/25327-coverage-badge-rounding.yml deleted file mode 100644 index ea985689484..00000000000 --- a/changelogs/unreleased/25327-coverage-badge-rounding.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show coverage to two decimal points in coverage badge -merge_request: 10083 -author: Jeff Stubler -type: changed diff --git a/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml b/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml new file mode 100644 index 00000000000..0f1cb2fef9d --- /dev/null +++ b/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml @@ -0,0 +1,5 @@ +--- +title: Update to github-linguist 5.3.x +merge_request: 17241 +author: Ken Ding +type: other diff --git a/changelogs/unreleased/26296-update-styling-disabled-buttons.yml b/changelogs/unreleased/26296-update-styling-disabled-buttons.yml deleted file mode 100644 index 5fa109d75e0..00000000000 --- a/changelogs/unreleased/26296-update-styling-disabled-buttons.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Set standard disabled state for all buttons -merge_request: -author: -type: other diff --git a/changelogs/unreleased/26388-push-to-create-a-new-project.yml b/changelogs/unreleased/26388-push-to-create-a-new-project.yml deleted file mode 100644 index f641fcced37..00000000000 --- a/changelogs/unreleased/26388-push-to-create-a-new-project.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: User can now git push to create a new project -merge_request: 16547 -author: -type: added diff --git a/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml b/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml deleted file mode 100644 index a2c81f6c995..00000000000 --- a/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Sort by Recent Sign-in in Admin Area -merge_request: 13852 -author: Poornima M diff --git a/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml b/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml deleted file mode 100644 index edd63c4ea9c..00000000000 --- a/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Generate HTTP URLs for custom Pages domains when appropriate -merge_request: 16279 -author: -type: fixed diff --git a/changelogs/unreleased/30106-group-issues.yml b/changelogs/unreleased/30106-group-issues.yml deleted file mode 100644 index d24996e6087..00000000000 --- a/changelogs/unreleased/30106-group-issues.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Include subgroup issues and merge requests on the group page -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml b/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml deleted file mode 100644 index d2a5802af64..00000000000 --- a/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add ability to transfer a group into another group -merge_request: 16302 -author: -type: added diff --git a/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml b/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml deleted file mode 100644 index e74c2a8b9ff..00000000000 --- a/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add foreign key and NOT NULL constraints to todos table. -merge_request: 16849 -author: -type: other diff --git a/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml b/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml deleted file mode 100644 index 4fd6b6fddc4..00000000000 --- a/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add unique constraint to trending_projects#project_id. -merge_request: 16846 -author: -type: other diff --git a/changelogs/unreleased/32564-fix-double-system-closing-notes.yml b/changelogs/unreleased/32564-fix-double-system-closing-notes.yml new file mode 100644 index 00000000000..e6e1ef8c76d --- /dev/null +++ b/changelogs/unreleased/32564-fix-double-system-closing-notes.yml @@ -0,0 +1,5 @@ +--- +title: Fix duplicate system notes when merging a merge request. +merge_request: 17035 +author: +type: fixed diff --git a/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml b/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml deleted file mode 100644 index 09e2af1e4d3..00000000000 --- a/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix the Projects API with_issues_enabled filter behaving incorrectly - any user -merge_request: 12724 -author: Jan Christophersen -type: fixed diff --git a/changelogs/unreleased/34252-trailing-plus.yml b/changelogs/unreleased/34252-trailing-plus.yml deleted file mode 100644 index fce17cb6ab9..00000000000 --- a/changelogs/unreleased/34252-trailing-plus.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow trailing + on labels in board filters -merge_request: 16490 -author: -type: fixed diff --git a/changelogs/unreleased/34416-issue-i18n.yml b/changelogs/unreleased/34416-issue-i18n.yml deleted file mode 100644 index 523073ee43b..00000000000 --- a/changelogs/unreleased/34416-issue-i18n.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Translate issuable sidebar -merge_request: -author: -type: other diff --git a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml deleted file mode 100644 index 0791847b64d..00000000000 --- a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix default avatar icon missing when Gravatar is disabled -merge_request: 16681 -author: Felix Geyer -type: fixed diff --git a/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml b/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml deleted file mode 100644 index f3a04469884..00000000000 --- a/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hide pipeline schedule take ownership for current owner -merge_request: 12986 -author: -type: fixed diff --git a/changelogs/unreleased/35418-remove-underline-for-avatar.yml b/changelogs/unreleased/35418-remove-underline-for-avatar.yml new file mode 100644 index 00000000000..034365e1137 --- /dev/null +++ b/changelogs/unreleased/35418-remove-underline-for-avatar.yml @@ -0,0 +1,5 @@ +--- +title: remove avater underline +merge_request: 17219 +author: Ken Ding +type: fixed diff --git a/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml b/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml deleted file mode 100644 index 82df00fe631..00000000000 --- a/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add realtime ci status for the repository -> files view -merge_request: 16523 -author: -type: added diff --git a/changelogs/unreleased/35856-implement-file-locking-api.yml b/changelogs/unreleased/35856-implement-file-locking-api.yml deleted file mode 100644 index fa848ad9ed8..00000000000 --- a/changelogs/unreleased/35856-implement-file-locking-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Backport of LFS File Locking API -merge_request: 16935 -author: -type: added diff --git a/changelogs/unreleased/36571-ignore-root-in-repo.yml b/changelogs/unreleased/36571-ignore-root-in-repo.yml deleted file mode 100644 index 396e82be51b..00000000000 --- a/changelogs/unreleased/36571-ignore-root-in-repo.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Ignore leading slashes when searching for files within context of repository. -merge_request: -author: Andrew McCallum -type: fixed diff --git a/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml b/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml new file mode 100644 index 00000000000..74eaf57c056 --- /dev/null +++ b/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml @@ -0,0 +1,5 @@ +--- +title: update toml-rb to 1.0.0 +merge_request: 17259 +author: Ken Ding +type: other diff --git a/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml b/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml deleted file mode 100644 index 0ab765a43b7..00000000000 --- a/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Issue board: fix for dragging an issue to the very bottom in long lists" -merge_request: 16250 -author: David Kuri -type: fixed
\ No newline at end of file diff --git a/changelogs/unreleased/37199-labels-fix.yml b/changelogs/unreleased/37199-labels-fix.yml deleted file mode 100644 index bd70babb73d..00000000000 --- a/changelogs/unreleased/37199-labels-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Keep subscribers when promoting labels to group labels -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml b/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml deleted file mode 100644 index 813b9ab81fa..00000000000 --- a/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: increase-readability-of-colored-text-in-job-output-log -merge_request: -author: -type: other diff --git a/changelogs/unreleased/38068-commits-count.yml b/changelogs/unreleased/38068-commits-count.yml deleted file mode 100644 index 3fbf554c98c..00000000000 --- a/changelogs/unreleased/38068-commits-count.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Store number of commits in merge_request_diffs table. -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml b/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml deleted file mode 100644 index 475e1dc12b5..00000000000 --- a/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Auto DevOps Domain application setting -merge_request: 16604 -author: -type: changed diff --git a/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml b/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml deleted file mode 100644 index 4d8e6acfcb7..00000000000 --- a/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update runner info on all authenticated requests -merge_request: 16756 -author: -type: changed diff --git a/changelogs/unreleased/38540-ssh-env-file.yml b/changelogs/unreleased/38540-ssh-env-file.yml deleted file mode 100644 index 5ada0ede76d..00000000000 --- a/changelogs/unreleased/38540-ssh-env-file.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: 'Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check - rake task' -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/39214__pipeline_api.yml b/changelogs/unreleased/39214__pipeline_api.yml deleted file mode 100644 index 18ee2e43798..00000000000 --- a/changelogs/unreleased/39214__pipeline_api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add `pipelines` endpoint to merge requests API -merge_request: 15454 -author: Tony Rom <thetonyrom@gmail.com> -type: added diff --git a/changelogs/unreleased/39917-revert-this-merge-request-text.yml b/changelogs/unreleased/39917-revert-this-merge-request-text.yml deleted file mode 100644 index 9a27be1f9c6..00000000000 --- a/changelogs/unreleased/39917-revert-this-merge-request-text.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Changes Revert this merge request text -merge_request: 16611 -author: Jacopo Beschi @jacopo-beschi -type: changed diff --git a/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml b/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml deleted file mode 100644 index 5c45d0db602..00000000000 --- a/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable Prometheus metrics for deployed Ingresses -merge_request: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16866 -author: joshlambert -type: changed diff --git a/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml b/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml deleted file mode 100644 index 4f2c87c44b3..00000000000 --- a/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hide new branch and tag links for projects with an empty repo -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml b/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml deleted file mode 100644 index ffab28acbd5..00000000000 --- a/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Handle special characters on API request of issuable templates -merge_request: 15323 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml b/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml deleted file mode 100644 index 519f411d642..00000000000 --- a/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Stop loading spinner on error of issuable templates -merge_request: 16600 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/4020-rebase-message.yml b/changelogs/unreleased/4020-rebase-message.yml deleted file mode 100644 index 4793f3d9cb9..00000000000 --- a/changelogs/unreleased/4020-rebase-message.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Display user friendly error message if rebase fails. -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml b/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml deleted file mode 100644 index 2416b15b6d5..00000000000 --- a/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move row containing Projects, Users and Groups count to the top in admin dashboard -merge_request: 16421 -author: -type: changed diff --git a/changelogs/unreleased/40540-use-limit-for-global-search.yml b/changelogs/unreleased/40540-use-limit-for-global-search.yml deleted file mode 100644 index 7d9612c16df..00000000000 --- a/changelogs/unreleased/40540-use-limit-for-global-search.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Optimize search queries on the search page by setting a limit for matching records. -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/40552-sanitize-extra-blank-spaces-used-when-uploading-a-ssh-key.yml b/changelogs/unreleased/40552-sanitize-extra-blank-spaces-used-when-uploading-a-ssh-key.yml new file mode 100644 index 00000000000..9e4811ca308 --- /dev/null +++ b/changelogs/unreleased/40552-sanitize-extra-blank-spaces-used-when-uploading-a-ssh-key.yml @@ -0,0 +1,5 @@ +--- +title: Sanitize extra blank spaces used when uploading a SSH key +merge_request: 40552 +author: +type: fixed diff --git a/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml b/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml new file mode 100644 index 00000000000..d77572d6175 --- /dev/null +++ b/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml @@ -0,0 +1,5 @@ +--- +title: Fix get a single pages domain when project path contains a period +merge_request: 17206 +author: Travis Miller +type: fixed diff --git a/changelogs/unreleased/40755-snippets-author-n-1.yml b/changelogs/unreleased/40755-snippets-author-n-1.yml deleted file mode 100644 index 6e09c8a54ec..00000000000 --- a/changelogs/unreleased/40755-snippets-author-n-1.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix N+1 query problem for snippets dashboard. -merge_request: 16944 -author: -type: performance diff --git a/changelogs/unreleased/40793-fix-mr-title-for-jira.yml b/changelogs/unreleased/40793-fix-mr-title-for-jira.yml deleted file mode 100644 index 69461510a87..00000000000 --- a/changelogs/unreleased/40793-fix-mr-title-for-jira.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Prevent JIRA issue identifier from being humanized. -merge_request: 16491 -author: Andrew McCallum -type: fixed diff --git a/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml b/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml deleted file mode 100644 index c57caf31d10..00000000000 --- a/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Last push widget will show banner for new pushes to previously merged branch -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml b/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml deleted file mode 100644 index a08f75f9fb9..00000000000 --- a/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds sorting to deployments API -merge_request: !16396 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml b/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml deleted file mode 100644 index 9c48831855c..00000000000 --- a/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve wording about additional costs for Ingress on custom clusters -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml b/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml deleted file mode 100644 index 5e706740962..00000000000 --- a/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Shows signin tab after new user email confirmation -merge_request: 16174 -author: Jacopo Beschi @jacopo-beschi -type: fixed diff --git a/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml b/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml deleted file mode 100644 index 76d3c6eda24..00000000000 --- a/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allows html text in commits atom feed -merge_request: 16603 -author: Jacopo Beschi @jacopo-beschi -type: fixed diff --git a/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml b/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml deleted file mode 100644 index 61d6bf8fd36..00000000000 --- a/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Handle all Psych YAML parser exceptions (fixes #41209)' -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/41247-timestamp.yml b/changelogs/unreleased/41247-timestamp.yml deleted file mode 100644 index 65f1a7485ad..00000000000 --- a/changelogs/unreleased/41247-timestamp.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: For issues display time of last edit of title or description instead of time - of any attribute change -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml b/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml new file mode 100644 index 00000000000..27eee7d943b --- /dev/null +++ b/changelogs/unreleased/41461-project-members-slow-due-to-sql.yml @@ -0,0 +1,5 @@ +--- +title: Improve query performance of MembersFinder. +merge_request: 17190 +author: +type: performance diff --git a/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml b/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml deleted file mode 100644 index bb5c1fdf082..00000000000 --- a/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enables Project Milestone Deletion via the API -merge_request: 16478 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/41532-email-reason.yml b/changelogs/unreleased/41532-email-reason.yml deleted file mode 100644 index 83c28769217..00000000000 --- a/changelogs/unreleased/41532-email-reason.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Initial work to add notification reason to emails -merge_request: 16160 -author: Mario de la Ossa -type: added diff --git a/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml b/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml deleted file mode 100644 index 7e42dc20ae8..00000000000 --- a/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix double query execution on groups page -merge_request: 16314 -author: -type: performance diff --git a/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml b/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml deleted file mode 100644 index e50f6046b17..00000000000 --- a/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make project README containers wider on fixed layout -merge_request: 16181 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/41613-fix-redundant-modal.yml b/changelogs/unreleased/41613-fix-redundant-modal.yml deleted file mode 100644 index 9e157b3065a..00000000000 --- a/changelogs/unreleased/41613-fix-redundant-modal.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make modal dialog common for Groups tree app -merge_request: 16311 -author: -type: fixed diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml deleted file mode 100644 index 48893862071..00000000000 --- a/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Only highlight search results under the highlighting size limit -merge_request: 16462 -author: -type: performance diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml deleted file mode 100644 index 3a6fa425c9c..00000000000 --- a/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix file search results when they match file contents with a number between - two colons -merge_request: 16462 -author: -type: fixed diff --git a/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml b/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml deleted file mode 100644 index 6b0d443e097..00000000000 --- a/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add blue dot feature highlight to make GKE Clusters more visible to users -merge_request: 16379 -author: -type: added diff --git a/changelogs/unreleased/41673-blank-query-members-api.yml b/changelogs/unreleased/41673-blank-query-members-api.yml deleted file mode 100644 index 677c5e250c8..00000000000 --- a/changelogs/unreleased/41673-blank-query-members-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix error on empty query for Members API -merge_request: 16235 -author: -type: fixed diff --git a/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml b/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml deleted file mode 100644 index 51285e5476f..00000000000 --- a/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make rich blob viewer wider for PC -merge_request: 16262 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/41731-predicate-memoization.yml b/changelogs/unreleased/41731-predicate-memoization.yml deleted file mode 100644 index 110f78063f4..00000000000 --- a/changelogs/unreleased/41731-predicate-memoization.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Properly memoize some predicate methods -merge_request: 16329 -author: -type: performance diff --git a/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml b/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml deleted file mode 100644 index 03060c357fe..00000000000 --- a/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove unused CSS selectors for Cycle Analytics -merge_request: 16270 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml b/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml deleted file mode 100644 index 593d3741a09..00000000000 --- a/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Substitute deprecated ui_charcoal with new default ui_indigo -merge_request: 16271 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml b/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml deleted file mode 100644 index 2a3d00f8e5f..00000000000 --- a/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add reason to keep postgresql 9.2 for CI -merge_request: 16277 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/41763-search-api.yml b/changelogs/unreleased/41763-search-api.yml deleted file mode 100644 index 0a760a66510..00000000000 --- a/changelogs/unreleased/41763-search-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add search support into the API -merge_request: 16878 -author: -type: added diff --git a/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml b/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml deleted file mode 100644 index f64fd66ef79..00000000000 --- a/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reduce the number of Prometheus metrics -merge_request: 16443 -author: -type: performance diff --git a/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml b/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml deleted file mode 100644 index f23a6452b0d..00000000000 --- a/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds spacing between edit and delete tag btn in tag list -merge_request: 16757 -author: Jacopo Beschi @jacopo-beschi -type: fixed diff --git a/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml b/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml deleted file mode 100644 index 146ae12afbd..00000000000 --- a/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Speed up loading merged merge requests when they contained a lot of commits - before merging -merge_request: 16320 -author: -type: performance diff --git a/changelogs/unreleased/41814-text-decoration-skip.yml b/changelogs/unreleased/41814-text-decoration-skip.yml deleted file mode 100644 index 3e39d26be93..00000000000 --- a/changelogs/unreleased/41814-text-decoration-skip.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve readability of underlined links for dyslexic users -merge_request: -author: -type: other diff --git a/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml new file mode 100644 index 00000000000..29ab7cc7cab --- /dev/null +++ b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml @@ -0,0 +1,5 @@ +--- +title: API endpoint for importing a project export +merge_request: 17025 +author: +type: added diff --git a/changelogs/unreleased/41949-move.yml b/changelogs/unreleased/41949-move.yml new file mode 100644 index 00000000000..40ccac63a28 --- /dev/null +++ b/changelogs/unreleased/41949-move.yml @@ -0,0 +1,5 @@ +--- +title: Remember assignee when moving an issue +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml b/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml deleted file mode 100644 index 32a6f87d98e..00000000000 --- a/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix Ctrl+Enter keyboard shortcut saving comment/note edit -merge_request: 16415 -author: -type: fixed diff --git a/changelogs/unreleased/42047-pg-10-support.yml b/changelogs/unreleased/42047-pg-10-support.yml deleted file mode 100644 index f98e59329c3..00000000000 --- a/changelogs/unreleased/42047-pg-10-support.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Support PostgreSQL 10 -merge_request: 16471 -author: -type: added diff --git a/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml b/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml deleted file mode 100644 index 5cb5dc3ccd8..00000000000 --- a/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Link Auto DevOps settings to Clusters page -merge_request: 16641 -author: -type: changed diff --git a/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml b/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml deleted file mode 100644 index 2b043761856..00000000000 --- a/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update marked from 0.3.6 to 0.3.12 -merge_request: 16480 -author: Takuya Noguchi -type: security diff --git a/changelogs/unreleased/42154-fix-artifact-size-calc.yml b/changelogs/unreleased/42154-fix-artifact-size-calc.yml deleted file mode 100644 index 3d6911abf09..00000000000 --- a/changelogs/unreleased/42154-fix-artifact-size-calc.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix a bug calculating artifact size for project statistics -merge_request: 16539 -author: -type: fixed diff --git a/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml b/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml deleted file mode 100644 index ac8e4b034b5..00000000000 --- a/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix duplicate item in protected branch/tag dropdown -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml b/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml deleted file mode 100644 index 64340ab08cd..00000000000 --- a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix 500 error when loading a merge request with an invalid comment -merge_request: 16795 -author: -type: fixed diff --git a/changelogs/unreleased/42206-permit-password-for-git-param.yml b/changelogs/unreleased/42206-permit-password-for-git-param.yml deleted file mode 100644 index 563dd528ad5..00000000000 --- a/changelogs/unreleased/42206-permit-password-for-git-param.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/42220-add-pending-empty-state.yml b/changelogs/unreleased/42220-add-pending-empty-state.yml deleted file mode 100644 index ad39578f2d9..00000000000 --- a/changelogs/unreleased/42220-add-pending-empty-state.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds empty state illustration for pending job -merge_request: -author: -type: other diff --git a/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml b/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml deleted file mode 100644 index fbc589ea53d..00000000000 --- a/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix protected branches API to accept name parameter with dot -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml deleted file mode 100644 index 25e0e774c48..00000000000 --- a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Set timezone for karma to UTC -merge_request: 16602 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml deleted file mode 100644 index bd7e0d3a1b0..00000000000 --- a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disable MR check out button when source branch is deleted -merge_request: 16631 -author: Jacopo Beschi @jacopo-beschi -type: fixed diff --git a/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml b/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml deleted file mode 100644 index d7a8b6e6f81..00000000000 --- a/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix export removal for hashed-storage projects within a renamed or deleted - namespace -merge_request: 16658 -author: -type: fixed diff --git a/changelogs/unreleased/42274-group-request-membership-long-too.yml b/changelogs/unreleased/42274-group-request-membership-long-too.yml new file mode 100644 index 00000000000..03efedba638 --- /dev/null +++ b/changelogs/unreleased/42274-group-request-membership-long-too.yml @@ -0,0 +1,5 @@ +--- +title: Fix long list of recipients on group request membership email +merge_request: 17121 +author: Jacopo Beschi @jacopo-beschi +type: fixed diff --git a/changelogs/unreleased/42285-not-found-status-icon.yml b/changelogs/unreleased/42285-not-found-status-icon.yml deleted file mode 100644 index ea7ff9d6ae7..00000000000 --- a/changelogs/unreleased/42285-not-found-status-icon.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace verified badge icons and uniform colors -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/42462-edit-note.yml b/changelogs/unreleased/42462-edit-note.yml deleted file mode 100644 index 8df98f3ecef..00000000000 --- a/changelogs/unreleased/42462-edit-note.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix cnacel edit note button reverting changes -merge_request: 42462 -author: -type: fixed diff --git a/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml b/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml deleted file mode 100644 index 6053883bac4..00000000000 --- a/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable RuboCop Style/RegexpLiteral -merge_request: 16752 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml b/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml new file mode 100644 index 00000000000..a3dc1917001 --- /dev/null +++ b/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml @@ -0,0 +1,5 @@ +--- +title: Allow to call PUT /projects/:id API with only ci_config_path specified +merge_request: 17105 +author: Laszlo Karpati +type: fixed diff --git a/changelogs/unreleased/42547-upload-store-mount-point.yml b/changelogs/unreleased/42547-upload-store-mount-point.yml deleted file mode 100644 index 35ae022984e..00000000000 --- a/changelogs/unreleased/42547-upload-store-mount-point.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Added uploader metadata to the uploads. -merge_request: 16779 -author: -type: added diff --git a/changelogs/unreleased/42584-fix-margins-in-tag-list.yml b/changelogs/unreleased/42584-fix-margins-in-tag-list.yml deleted file mode 100644 index 38b3dd85fd8..00000000000 --- a/changelogs/unreleased/42584-fix-margins-in-tag-list.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixes different margins between buttons in tag list -merge_request: 16927 -author: Jacopo Beschi @jacopo-beschi -type: fixed diff --git a/changelogs/unreleased/42591-update-nokogiri.yml b/changelogs/unreleased/42591-update-nokogiri.yml deleted file mode 100644 index 5f9587d2d92..00000000000 --- a/changelogs/unreleased/42591-update-nokogiri.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update nokogiri to 1.8.2 -merge_request: 16807 -author: -type: security diff --git a/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml b/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml deleted file mode 100644 index 24fcc38ee0e..00000000000 --- a/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add sorting options for /users API (admin only) -merge_request: 16945 -author: -type: added diff --git a/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml b/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml deleted file mode 100644 index 0ef28e2ee01..00000000000 --- a/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rename button to enable CI/CD configuration to "Set up CI/CD" -merge_request: 16870 -author: -type: changed diff --git a/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml b/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml deleted file mode 100644 index aeadf8ffc4a..00000000000 --- a/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add a link to documentation on how to get external ip in the Kubernetes cluster details page -merge_request: 16937 -author: -type: added diff --git a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml b/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml deleted file mode 100644 index c46cc86c99b..00000000000 --- a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix GitLab import leaving group_id on ProjectLabel -merge_request: 16877 -author: -type: fixed diff --git a/changelogs/unreleased/42730-close-rugged-repository.yml b/changelogs/unreleased/42730-close-rugged-repository.yml deleted file mode 100644 index a632f5030a5..00000000000 --- a/changelogs/unreleased/42730-close-rugged-repository.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Close low level rugged repository in project cache worker -merge_request: 16930 -author: Bastian Blank -type: fixed diff --git a/changelogs/unreleased/42877-snippets-dashboard-slow.yml b/changelogs/unreleased/42877-snippets-dashboard-slow.yml new file mode 100644 index 00000000000..839b44ad272 --- /dev/null +++ b/changelogs/unreleased/42877-snippets-dashboard-slow.yml @@ -0,0 +1,5 @@ +--- +title: Improve query performance for snippets dashboard. +merge_request: 17088 +author: +type: performance diff --git a/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml b/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml new file mode 100644 index 00000000000..c1e9614b676 --- /dev/null +++ b/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of pipeline page by reducing DB queries +merge_request: 17168 +author: +type: performance diff --git a/changelogs/unreleased/4826-github-import-wiki-fix-1.yml b/changelogs/unreleased/4826-github-import-wiki-fix-1.yml new file mode 100644 index 00000000000..69145cb6daf --- /dev/null +++ b/changelogs/unreleased/4826-github-import-wiki-fix-1.yml @@ -0,0 +1,5 @@ +--- +title: "[GitHub Import] Create an empty wiki if wiki import failed" +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/add-confirmation-input-for-modals.yml b/changelogs/unreleased/add-confirmation-input-for-modals.yml deleted file mode 100644 index ff1027bc55a..00000000000 --- a/changelogs/unreleased/add-confirmation-input-for-modals.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add confirmation-input component -merge_request: 16816 -author: -type: other diff --git a/changelogs/unreleased/asciidoc_inter_document_cross_references.yml b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml new file mode 100644 index 00000000000..34b26753312 --- /dev/null +++ b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml @@ -0,0 +1,5 @@ +--- +title: Asciidoc now support inter-document cross references between files in repository +merge_request: 17125 +author: Turo Soisenniemi +type: changed diff --git a/changelogs/unreleased/bump-workhorse.yml b/changelogs/unreleased/bump-workhorse.yml deleted file mode 100644 index 37ee402dac7..00000000000 --- a/changelogs/unreleased/bump-workhorse.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Upgrade GitLab Workhorse to v3.6.0 -merge_request: -author: -type: other diff --git a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml b/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml deleted file mode 100644 index 378f0ef7ce9..00000000000 --- a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix forking projects when no restricted visibility levels are defined applicationwide -merge_request: 16881 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-fix-concurrent-fork-network-migrations.yml b/changelogs/unreleased/bvl-fix-concurrent-fork-network-migrations.yml deleted file mode 100644 index b2a77f75e55..00000000000 --- a/changelogs/unreleased/bvl-fix-concurrent-fork-network-migrations.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Avoid running `PopulateForkNetworksRange`-migration multiple times -merge_request: 16988 -author: -type: fixed diff --git a/changelogs/unreleased/contribution_calendar_label_cut_off.yml b/changelogs/unreleased/contribution_calendar_label_cut_off.yml deleted file mode 100644 index 0b4a746bab8..00000000000 --- a/changelogs/unreleased/contribution_calendar_label_cut_off.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Contribution calendar label was cut off -merge_request: -author: Branka Martinovic -type: fixed diff --git a/changelogs/unreleased/cs-fix-commercial-content-check.yml b/changelogs/unreleased/cs-fix-commercial-content-check.yml deleted file mode 100644 index fec80e3ecd2..00000000000 --- a/changelogs/unreleased/cs-fix-commercial-content-check.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix version information not showing on help page if commercial content display - was disabled. -merge_request: 16743 -author: -type: fixed diff --git a/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml b/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml deleted file mode 100644 index 5b850c92d17..00000000000 --- a/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add rake task to check integrity of uploaded files -merge_request: -author: -type: added diff --git a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml deleted file mode 100644 index 544c34fe31d..00000000000 --- a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Default to HTTPS for all Gravatar URLs -merge_request: 16666 -author: -type: fixed diff --git a/changelogs/unreleased/disable-throwOnError-in-katex.yml b/changelogs/unreleased/disable-throwOnError-in-katex.yml deleted file mode 100644 index 0cd17bb29fe..00000000000 --- a/changelogs/unreleased/disable-throwOnError-in-katex.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disable throwOnError in KaTeX to reveal user where is the problem -merge_request: 16684 -author: Jakub Jirutka -type: other diff --git a/changelogs/unreleased/display-mr-in-commit-page.yml b/changelogs/unreleased/display-mr-in-commit-page.yml deleted file mode 100644 index a9224c00b66..00000000000 --- a/changelogs/unreleased/display-mr-in-commit-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add link on commit page to merge request that introduced that commit -merge_request: 13713 -author: Hiroyuki Sato -type: added diff --git a/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml b/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml new file mode 100644 index 00000000000..4dab7d0ffca --- /dev/null +++ b/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml @@ -0,0 +1,5 @@ +--- +title: Don't cache a nil repository root ref to prevent caching issues +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml deleted file mode 100644 index f59021c0ec9..00000000000 --- a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Execute system hooks after-commit when executing project hooks -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/dm-route-path-validation.yml b/changelogs/unreleased/dm-route-path-validation.yml deleted file mode 100644 index df3ed1de1b9..00000000000 --- a/changelogs/unreleased/dm-route-path-validation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Validate user, group and project paths consistently, and only once -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml b/changelogs/unreleased/dm-user-namespace-route-path-validation.yml deleted file mode 100644 index 36615e5b976..00000000000 --- a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Validate user namespace before saving so that errors persist on model -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/expired-ci-artifacts.yml b/changelogs/unreleased/expired-ci-artifacts.yml deleted file mode 100644 index 2fcbdb02f84..00000000000 --- a/changelogs/unreleased/expired-ci-artifacts.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Change SQL for expired artifacts to use new ci_job_artifacts.expire_at -merge_request: 16578 -author: -type: performance diff --git a/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml b/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml deleted file mode 100644 index 3fce53bc941..00000000000 --- a/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add section headers to plus button dropdown -merge_request: 16394 -author: George Tsiolis -type: added diff --git a/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml b/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml deleted file mode 100644 index 8f3459a7381..00000000000 --- a/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve empty project overview -merge_request: 16617 -author: George Tsiolis -type: added diff --git a/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml b/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml new file mode 100644 index 00000000000..ca877d32b05 --- /dev/null +++ b/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml @@ -0,0 +1,5 @@ +--- +title: "#28481: Display time tracking totals on milestone page" +merge_request: 16753 +author: Riccardo Padovani +type: added diff --git a/changelogs/unreleased/feature-39591-visibility-level.yml b/changelogs/unreleased/feature-39591-visibility-level.yml deleted file mode 100644 index 4bbc9bdbb2e..00000000000 --- a/changelogs/unreleased/feature-39591-visibility-level.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Open visibility level help in a new tab -merge_request: -author: Jussi Räsänen -type: fixed diff --git a/changelogs/unreleased/feature-merge-request-system-hook.yml b/changelogs/unreleased/feature-merge-request-system-hook.yml deleted file mode 100644 index cfc4c4235d6..00000000000 --- a/changelogs/unreleased/feature-merge-request-system-hook.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: System hooks for Merge Requests -merge_request: 14387 -author: Alexis Reigel -type: added diff --git a/changelogs/unreleased/feature-sm-artifacts-trace.yml b/changelogs/unreleased/feature-sm-artifacts-trace.yml deleted file mode 100644 index 7654ce58aeb..00000000000 --- a/changelogs/unreleased/feature-sm-artifacts-trace.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Save traces as artifacts -merge_request: 16702 -author: -type: changed diff --git a/changelogs/unreleased/file-content-large-screen-padding.yml b/changelogs/unreleased/file-content-large-screen-padding.yml deleted file mode 100644 index 5691cd09b1f..00000000000 --- a/changelogs/unreleased/file-content-large-screen-padding.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Double padding for file-content wiki class on larger screens -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml b/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml deleted file mode 100644 index d8e97b7ad04..00000000000 --- a/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add horizontal scroll to wiki tables -merge_request: 16527 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml b/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml deleted file mode 100644 index b79ec2944fd..00000000000 --- a/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Change button group width on mobile -merge_request: 16726 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml b/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml deleted file mode 100644 index 2e0f59f81e9..00000000000 --- a/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adjust layout width for fixed layout -merge_request: 16337 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/fix-change-event-body-label-font-size.yml b/changelogs/unreleased/fix-change-event-body-label-font-size.yml new file mode 100644 index 00000000000..3192a7bff92 --- /dev/null +++ b/changelogs/unreleased/fix-change-event-body-label-font-size.yml @@ -0,0 +1,5 @@ +--- +title: Apply new default and inline label design +merge_request: 16956 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml b/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml deleted file mode 100644 index 2f6a07bb234..00000000000 --- a/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix dashboard projects nav links height -merge_request: 16204 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml b/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml deleted file mode 100644 index 31b4734bc79..00000000000 --- a/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix tooltip displayed for running manual actions -merge_request: 16489 -author: -type: fixed diff --git a/changelogs/unreleased/fix-improve-issue-note-dropdown.yml b/changelogs/unreleased/fix-improve-issue-note-dropdown.yml deleted file mode 100644 index aaf4811c64a..00000000000 --- a/changelogs/unreleased/fix-improve-issue-note-dropdown.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve issue note dropdown and mr button -merge_request: 16758 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/fix-install-docs.yml b/changelogs/unreleased/fix-install-docs.yml deleted file mode 100644 index c2c0dd1364b..00000000000 --- a/changelogs/unreleased/fix-install-docs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update minimum git version to 2.9.5 -merge_request: 16683 -author: -type: other diff --git a/changelogs/unreleased/fix-new-project-path-input-overlapping.yml b/changelogs/unreleased/fix-new-project-path-input-overlapping.yml new file mode 100644 index 00000000000..fb33ce9437a --- /dev/null +++ b/changelogs/unreleased/fix-new-project-path-input-overlapping.yml @@ -0,0 +1,5 @@ +--- +title: Fix new project path input overlapping +merge_request: 16755 +author: George Tsiolis +type: fixed diff --git a/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml b/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml deleted file mode 100644 index 883eecabe04..00000000000 --- a/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Override group sidebar links -merge_request: 16942 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/fix-squash-with-renamed-files.yml b/changelogs/unreleased/fix-squash-with-renamed-files.yml new file mode 100644 index 00000000000..f7cd3a84367 --- /dev/null +++ b/changelogs/unreleased/fix-squash-with-renamed-files.yml @@ -0,0 +1,5 @@ +--- +title: Fix squashing when a file is renamed +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml b/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml deleted file mode 100644 index 5424c15a8ae..00000000000 --- a/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix validation of environment scope of variables -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix_gitlab-ce-41891.yml b/changelogs/unreleased/fix_gitlab-ce-41891.yml deleted file mode 100644 index 56bdc1a7c32..00000000000 --- a/changelogs/unreleased/fix_gitlab-ce-41891.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Fix custom header logo design nitpick: Remove unneeded margin on empty logo text' -merge_request: 16383 -author: Markus Doits -type: fixed diff --git a/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml b/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml deleted file mode 100644 index 77142528be2..00000000000 --- a/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Added ldap config setting to lower case the username -merge_request: 16791 -author: -type: added diff --git a/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml b/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml deleted file mode 100644 index 5b5310dcfef..00000000000 --- a/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow moving wiki pages from the UI -merge_request: 16313 -author: -type: fixed diff --git a/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml b/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml deleted file mode 100644 index 1e35f2b537d..00000000000 --- a/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fixed error 500 when removing an identity with synced attributes and visiting - the profile page -merge_request: 17054 -author: -type: fixed diff --git a/changelogs/unreleased/fl-mr-widget-refactor.yml b/changelogs/unreleased/fl-mr-widget-refactor.yml deleted file mode 100644 index d59cca68409..00000000000 --- a/changelogs/unreleased/fl-mr-widget-refactor.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Refactors mr widget components into vue files and adds i18n -merge_request: -author: -type: other diff --git a/changelogs/unreleased/fl-refresh-btn.yml b/changelogs/unreleased/fl-refresh-btn.yml new file mode 100644 index 00000000000..640fdda9ce7 --- /dev/null +++ b/changelogs/unreleased/fl-refresh-btn.yml @@ -0,0 +1,5 @@ +--- +title: Show loading button inline in refresh button in MR widget +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/flipper-caching.yml b/changelogs/unreleased/flipper-caching.yml new file mode 100644 index 00000000000..6db27fd579e --- /dev/null +++ b/changelogs/unreleased/flipper-caching.yml @@ -0,0 +1,5 @@ +--- +title: Increase feature flag cache TTL to one hour +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/gitaly-git-http-ssh.yml b/changelogs/unreleased/gitaly-git-http-ssh.yml deleted file mode 100644 index 98812e92e2a..00000000000 --- a/changelogs/unreleased/gitaly-git-http-ssh.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH - pull -merge_request: 16586 -author: -type: other diff --git a/changelogs/unreleased/gitaly-repo-exists.yml b/changelogs/unreleased/gitaly-repo-exists.yml deleted file mode 100644 index a9eb42a2038..00000000000 --- a/changelogs/unreleased/gitaly-repo-exists.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make Gitaly RepositoryExists opt-out -merge_request: 16680 -author: -type: other diff --git a/changelogs/unreleased/internationalize-charts-page.yml b/changelogs/unreleased/internationalize-charts-page.yml deleted file mode 100644 index 481b83fb059..00000000000 --- a/changelogs/unreleased/internationalize-charts-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Internationalize charts page -merge_request: 16687 -author: selrahman -type: changed diff --git a/changelogs/unreleased/internationalize-graph-page.yml b/changelogs/unreleased/internationalize-graph-page.yml deleted file mode 100644 index 904dbd606d7..00000000000 --- a/changelogs/unreleased/internationalize-graph-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Internationalize graph page selrahman -merge_request: 16688 -author: Shah El-Rahman -type: changed diff --git a/changelogs/unreleased/issue-42689-new-file-template.yml b/changelogs/unreleased/issue-42689-new-file-template.yml deleted file mode 100644 index d6b77b87605..00000000000 --- a/changelogs/unreleased/issue-42689-new-file-template.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Trigger change event on filename input when file template is applied -merge_request: 16911 -author: Sebastian Klingler -type: fixed diff --git a/changelogs/unreleased/issue_41460.yml b/changelogs/unreleased/issue_41460.yml deleted file mode 100644 index 24d3eae6bf8..00000000000 --- a/changelogs/unreleased/issue_41460.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix error on changes tab when merge request cannot be created -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/issues-closed-at-steal.yml b/changelogs/unreleased/issues-closed-at-steal.yml deleted file mode 100644 index a5f0898995f..00000000000 --- a/changelogs/unreleased/issues-closed-at-steal.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Finish any remaining jobs for issues.closed_at -merge_request: -author: -type: other diff --git a/changelogs/unreleased/jej-upload-file-tracks-lfs.yml b/changelogs/unreleased/jej-upload-file-tracks-lfs.yml deleted file mode 100644 index a7cf6b6ba2c..00000000000 --- a/changelogs/unreleased/jej-upload-file-tracks-lfs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: File Upload UI can create LFS pointers based on .gitattributes -merge_request: 16412 -author: -type: fixed diff --git a/changelogs/unreleased/jivl-update-katex.yml b/changelogs/unreleased/jivl-update-katex.yml deleted file mode 100644 index 99b5fe49620..00000000000 --- a/changelogs/unreleased/jivl-update-katex.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Updated the katex library -merge_request: 15864 -author: -type: other diff --git a/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml b/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml new file mode 100644 index 00000000000..690536a533b --- /dev/null +++ b/changelogs/unreleased/kp-fix-stacked-bar-progress-value-clipping.yml @@ -0,0 +1,5 @@ +--- +title: Fix single digit value clipping for stacked progress bar +merge_request: 17217 +author: +type: fixed diff --git a/changelogs/unreleased/merge-request-target-branch-perf.yml b/changelogs/unreleased/merge-request-target-branch-perf.yml deleted file mode 100644 index 37e326bfde3..00000000000 --- a/changelogs/unreleased/merge-request-target-branch-perf.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve performance of target branch dropdown -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml b/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml deleted file mode 100644 index fddfba94192..00000000000 --- a/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Resolve PrepareUntrackedUploads PostgreSQL syntax error -merge_request: 17019 -author: -type: fixed diff --git a/changelogs/unreleased/move-board-list-vue-component.yml b/changelogs/unreleased/move-board-list-vue-component.yml deleted file mode 100644 index 9c566b43cc2..00000000000 --- a/changelogs/unreleased/move-board-list-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move BoardList vue component to vue file -merge_request: 16888 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml deleted file mode 100644 index 1cffb213f23..00000000000 --- a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Close and do not reload MR diffs when source branch is deleted -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml b/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml deleted file mode 100644 index b2c1cd9710a..00000000000 --- a/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Bypass commits title markdown on notes -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml b/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml deleted file mode 100644 index 03940555162..00000000000 --- a/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove duplicate calls of MergeRequest#can_be_reverted? -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml b/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml deleted file mode 100644 index 62931218861..00000000000 --- a/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Stop checking if discussions are in a mergeable state if the MR isn't -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/osw-system-notes-for-commits-regression.yml b/changelogs/unreleased/osw-system-notes-for-commits-regression.yml deleted file mode 100644 index 6d0943c7716..00000000000 --- a/changelogs/unreleased/osw-system-notes-for-commits-regression.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reload MRs memoization after diffs creation -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml deleted file mode 100644 index 3854985e576..00000000000 --- a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Return more consistent values for merge_status on MR APIs -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml b/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml deleted file mode 100644 index b2bb173912a..00000000000 --- a/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Implement multi server support and use kube proxy to connect to Prometheus - servers inside K8S cluster -merge_request: 16182 -author: -type: added diff --git a/changelogs/unreleased/persistent-callouts.yml b/changelogs/unreleased/persistent-callouts.yml deleted file mode 100644 index ca949a3b96c..00000000000 --- a/changelogs/unreleased/persistent-callouts.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add backend for persistently dismissably callouts -merge_request: -author: -type: added diff --git a/changelogs/unreleased/query-counts.yml b/changelogs/unreleased/query-counts.yml deleted file mode 100644 index e01ff8a4ad8..00000000000 --- a/changelogs/unreleased/query-counts.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Track and act upon the number of executed queries -merge_request: -author: -type: added diff --git a/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml b/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml deleted file mode 100644 index d43675e175d..00000000000 --- a/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Hide variable values on pipeline schedule edit page -merge_request: 16729 -author: -type: changed diff --git a/changelogs/unreleased/remove-unnecessary-validate-project.yml b/changelogs/unreleased/remove-unnecessary-validate-project.yml new file mode 100644 index 00000000000..ebc8da03dd8 --- /dev/null +++ b/changelogs/unreleased/remove-unnecessary-validate-project.yml @@ -0,0 +1,5 @@ +--- +title: 'Remove unecessary validate: true from belongs_to :project' +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/remove_ldap_person_validation.yml b/changelogs/unreleased/remove_ldap_person_validation.yml deleted file mode 100644 index da7f0a52886..00000000000 --- a/changelogs/unreleased/remove_ldap_person_validation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: LDAP Person no longer throws exception on invalid entry -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-add-gitaly-health-check.yml b/changelogs/unreleased/sh-add-gitaly-health-check.yml deleted file mode 100644 index 32c4c5362b4..00000000000 --- a/changelogs/unreleased/sh-add-gitaly-health-check.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add a gRPC health check to ensure Gitaly is up -merge_request: -author: -type: added diff --git a/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml b/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml deleted file mode 100644 index c62fad927d0..00000000000 --- a/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix bug where award emojis would be lost when moving issues between projects -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-events-collection.yml b/changelogs/unreleased/sh-fix-events-collection.yml deleted file mode 100644 index 50af39d9caf..00000000000 --- a/changelogs/unreleased/sh-fix-events-collection.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix not all events being shown in group dashboard -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml b/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml new file mode 100644 index 00000000000..5b4bbe0dc7a --- /dev/null +++ b/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml @@ -0,0 +1,5 @@ +--- +title: Fix Error 500 when viewing a commit with a GPG signature in Geo +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml b/changelogs/unreleased/sh-fix-jira-trailing-slash.yml deleted file mode 100644 index 786f6cd3727..00000000000 --- a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix JIRA not working when a trailing slash is included -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-project-members-api-perf.yml b/changelogs/unreleased/sh-fix-project-members-api-perf.yml deleted file mode 100644 index c3fff933547..00000000000 --- a/changelogs/unreleased/sh-fix-project-members-api-perf.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Remove N+1 queries with /projects/:project_id/{access_requests,members} API - endpoints -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/sh-guard-read-only-user-updates.yml b/changelogs/unreleased/sh-guard-read-only-user-updates.yml new file mode 100644 index 00000000000..b8dbd840ed9 --- /dev/null +++ b/changelogs/unreleased/sh-guard-read-only-user-updates.yml @@ -0,0 +1,5 @@ +--- +title: Don't attempt to update user tracked fields if database is in read-only +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-log-when-user-blocked.yml b/changelogs/unreleased/sh-log-when-user-blocked.yml deleted file mode 100644 index 9abf2017514..00000000000 --- a/changelogs/unreleased/sh-log-when-user-blocked.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Log and send a system hook if a blocked user attempts to login -merge_request: -author: -type: added diff --git a/changelogs/unreleased/sh-remove-shared-runners-and-more.yml b/changelogs/unreleased/sh-remove-shared-runners-and-more.yml deleted file mode 100644 index cc079617883..00000000000 --- a/changelogs/unreleased/sh-remove-shared-runners-and-more.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove erroneous text in shared runners page that suggested more runners available -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-store-user-in-api-logs.yml b/changelogs/unreleased/sh-store-user-in-api-logs.yml deleted file mode 100644 index d904dcaf6d3..00000000000 --- a/changelogs/unreleased/sh-store-user-in-api-logs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Save user ID and username in Grape API log (api_json.log) -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/style-include-branch-in-mobile-view.yml b/changelogs/unreleased/style-include-branch-in-mobile-view.yml deleted file mode 100644 index 5c8ef86992d..00000000000 --- a/changelogs/unreleased/style-include-branch-in-mobile-view.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Include branch in mobile view for pipelines -merge_request: 16910 -author: George Tsiolis -type: other diff --git a/changelogs/unreleased/tc-info-version-check.yml b/changelogs/unreleased/tc-info-version-check.yml new file mode 100644 index 00000000000..9f20d03b864 --- /dev/null +++ b/changelogs/unreleased/tc-info-version-check.yml @@ -0,0 +1,5 @@ +--- +title: Add more info about data collected when version check is enabled +merge_request: 17257 +author: +type: changed diff --git a/changelogs/unreleased/update-node-docs.yml b/changelogs/unreleased/update-node-docs.yml deleted file mode 100644 index a1d9d12f0ca..00000000000 --- a/changelogs/unreleased/update-node-docs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: fix documentation about node version -merge_request: 16720 -author: Tobias Gurtzick -type: other diff --git a/changelogs/unreleased/ux-guide-deprecation.yml b/changelogs/unreleased/ux-guide-deprecation.yml deleted file mode 100644 index 16477f59abf..00000000000 --- a/changelogs/unreleased/ux-guide-deprecation.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Add note within ux documentation that further changes should be made within - the design.gitlab project -merge_request: -author: -type: deprecated diff --git a/changelogs/unreleased/winh-delete-milestone-modal.yml b/changelogs/unreleased/winh-delete-milestone-modal.yml deleted file mode 100644 index 6517fbd5f63..00000000000 --- a/changelogs/unreleased/winh-delete-milestone-modal.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add modal for deleting a milestone -merge_request: 16229 -author: -type: other diff --git a/changelogs/unreleased/winh-kubernetes-clusters.yml b/changelogs/unreleased/winh-kubernetes-clusters.yml deleted file mode 100644 index 387a719848d..00000000000 --- a/changelogs/unreleased/winh-kubernetes-clusters.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace "cluster" with "Kubernetes cluster" -merge_request: 16778 -author: -type: changed diff --git a/changelogs/unreleased/winh-new-branch-dropdown-style.yml b/changelogs/unreleased/winh-new-branch-dropdown-style.yml deleted file mode 100644 index 007e9e9f453..00000000000 --- a/changelogs/unreleased/winh-new-branch-dropdown-style.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Cleanup new branch/merge request form in issues -merge_request: 16854 -author: -type: fixed diff --git a/changelogs/unreleased/winh-search-page-filters.yml b/changelogs/unreleased/winh-search-page-filters.yml deleted file mode 100644 index 90c5cd8d818..00000000000 --- a/changelogs/unreleased/winh-search-page-filters.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Filter groups and projects dropdowns of search page on backend -merge_request: 16336 -author: -type: fixed diff --git a/changelogs/unreleased/winh-style-modals.yml b/changelogs/unreleased/winh-style-modals.yml deleted file mode 100644 index b7d0293960d..00000000000 --- a/changelogs/unreleased/winh-style-modals.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adjust modal style to new design -merge_request: 16310 -author: -type: other diff --git a/changelogs/unreleased/zj-branch-contains-git-message.yml b/changelogs/unreleased/zj-branch-contains-git-message.yml new file mode 100644 index 00000000000..ce034e7ec87 --- /dev/null +++ b/changelogs/unreleased/zj-branch-contains-git-message.yml @@ -0,0 +1,5 @@ +--- +title: Allow branch names to be named the same as the sha it points to +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/zj-gitaly-server-info.yml b/changelogs/unreleased/zj-gitaly-server-info.yml deleted file mode 100644 index cf6295f2bbc..00000000000 --- a/changelogs/unreleased/zj-gitaly-server-info.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Gitaly Servers admin dashboard -merge_request: -author: -type: added diff --git a/changelogs/unreleased/zj-protobuf.yml b/changelogs/unreleased/zj-protobuf.yml deleted file mode 100644 index 830c2e82da9..00000000000 --- a/changelogs/unreleased/zj-protobuf.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Downgrade google-protobuf gem -merge_request: 16941 -author: -type: other diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 778cca4297f..6616b85129e 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -467,8 +467,8 @@ - - :license - pikaday - MIT - - :who: - :why: + - :who: + :why: :versions: [] :when: 2017-10-17 17:46:12.367554000 Z - - :license @@ -516,3 +516,21 @@ :why: zlib license + Development Lib + https://github.com/uNetworking/uWebSockets/blob/master/LICENSE :versions: [] :when: 2018-01-17 23:46:12.367554000 Z +- - :approve + - atob + - :who: Mike Greiling + :why: https://github.com/node-browser-compat/atob/blob/master/LICENSE + :versions: [] + :when: 2018-02-20 19:42:08.409887000 Z +- - :approve + - cyclist + - :who: Mike Greiling + :why: https://github.com/mafintosh/cyclist/blob/master/LICENSE + :versions: [] + :when: 2018-02-20 21:37:43.774978000 Z +- - :approve + - bitsyntax + - :who: Mike Greiling + :why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT + :versions: [] + :when: 2018-02-20 22:20:25.958123000 Z diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 28e05bfc18d..17a8801f7bc 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -262,7 +262,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled']. Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? -Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? +Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10 diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index cc9167d29b9..c60ad535fd5 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -8,7 +8,7 @@ Flipper.configure do |config| cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new( adapter, Rails.cache, - expires_in: 10.seconds) + expires_in: 1.hour) Flipper.new(cached_adapter) end diff --git a/config/karma.config.js b/config/karma.config.js index a101d35704e..3d95e1622b2 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -24,7 +24,7 @@ module.exports = function(config) { var karmaConfig = { basePath: ROOT_PATH, - browsers: ['ChromeHeadlessCustom'], + browsers: ['ChromeHeadlessCustom'], customLaunchers: { ChromeHeadlessCustom: { base: 'ChromeHeadless', diff --git a/config/webpack.config.js b/config/webpack.config.js index d6cb6a8892f..06737b34aba 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -27,11 +27,11 @@ var pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'ap // filter out entries currently imported dynamically in dispatcher.js var dispatcher = fs.readFileSync(path.join(ROOT_PATH, 'app/assets/javascripts/dispatcher.js')).toString(); -var dispatcherChunks = dispatcher.match(/(?!import\('.\/)pages\/[^']+/g); +var dispatcherChunks = dispatcher.match(/(?!import\(')\.\/pages\/[^']+/g); pageEntries.forEach(( path ) => { let chunkPath = path.replace(/\/index\.js$/, ''); - if (!dispatcherChunks.includes(chunkPath)) { + if (!dispatcherChunks.includes('./' + chunkPath)) { let chunkName = chunkPath.replace(/\//g, '.'); autoEntries[chunkName] = './' + path; } @@ -48,7 +48,6 @@ var config = { }, context: path.join(ROOT_PATH, 'app/assets/javascripts'), entry: { - account: './profile/account/index.js', balsamiq_viewer: './blob/balsamiq_viewer.js', blob: './blob_edit/blob_bundle.js', boards: './boards/boards_bundle.js', @@ -57,35 +56,27 @@ var config = { cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', commit_pipelines: './commit/pipelines/pipelines_bundle.js', deploy_keys: './deploy_keys/index.js', - docs: './docs/docs_bundle.js', diff_notes: './diff_notes/diff_notes_bundle.js', environments: './environments/environments_bundle.js', environments_folder: './environments/folder/environments_folder_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', - graphs: './graphs/graphs_bundle.js', - graphs_charts: './graphs/graphs_charts.js', - graphs_show: './graphs/graphs_show.js', help: './help/help.js', - how_to_merge: './how_to_merge.js', issue_show: './issue_show/index.js', - job_details: './jobs/job_details_bundle.js', locale: './locale/index.js', main: './main.js', merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js', monitoring: './monitoring/monitoring_bundle.js', network: './network/network_bundle.js', notebook_viewer: './blob/notebook_viewer.js', - notes: './notes/index.js', pdf_viewer: './blob/pdf_viewer.js', pipelines: './pipelines/pipelines_bundle.js', pipelines_details: './pipelines/pipeline_details_bundle.js', profile: './profile/profile_bundle.js', project_import_gl: './projects/project_import_gitlab_project.js', - prometheus_metrics: './prometheus_metrics', protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', - ide: './ide/index.js', + ide: './ide/index.js', sidebar: './sidebar/sidebar_bundle.js', snippet: './snippet/snippet_bundle.js', sketch_viewer: './blob/sketch_viewer.js', @@ -260,11 +251,9 @@ var config = { 'filtered_search', 'groups', 'issue_show', - 'job_details', 'merge_conflicts', 'monitoring', 'notebook_viewer', - 'notes', 'pdf_viewer', 'pipelines', 'pipelines_details', @@ -280,20 +269,6 @@ var config = { }, }), - // create cacheable common library bundle for all d3 chunks - new webpack.optimize.CommonsChunkPlugin({ - name: 'common_d3', - chunks: [ - 'graphs', - 'graphs_show', - 'monitoring', - 'users', - ], - minChunks: function (module, count) { - return module.resource && /d3-/.test(module.resource); - }, - }), - // create cacheable common library bundles new webpack.optimize.CommonsChunkPlugin({ names: ['main', 'common', 'webpack_runtime'], diff --git a/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb b/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb new file mode 100644 index 00000000000..cedf2510dda --- /dev/null +++ b/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb @@ -0,0 +1,21 @@ +class AddPartialIndexToProjectsForIndexOnlyScans < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX_NAME = 'index_projects_on_id_partial_for_visibility' + + disable_ddl_transaction! + + # Adds a partial index to leverage index-only scans when looking up project ids + def up + unless index_exists?(:projects, :id, name: INDEX_NAME) + add_concurrent_index :projects, :id, name: INDEX_NAME, unique: true, where: 'visibility_level IN (10,20)' + end + end + + def down + if index_exists?(:projects, :id, name: INDEX_NAME) + remove_concurrent_index_by_name :projects, INDEX_NAME + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 1f41b97041e..409d1ac7644 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: 20180212102028) do +ActiveRecord::Schema.define(version: 20180213131630) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -1468,6 +1468,7 @@ ActiveRecord::Schema.define(version: 20180212102028) do add_index "projects", ["created_at"], name: "index_projects_on_created_at", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} + add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md index 9b9b8ca89e5..aa5e9513290 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md @@ -1,9 +1,12 @@ -# How to configure LDAP with GitLab CE +--- +author: Chris Wilson +author_gitlab: MrChrisW +level: intermediary +article_type: admin guide +date: 2017-05-03 +--- -> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** admin guide || -> **Level:** intermediary || -> **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) || -> **Publication date:** 2017-05-03 +# How to configure LDAP with GitLab CE ## Introduction diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md index d6333ee62b4..b6320aba83e 100644 --- a/doc/administration/monitoring/index.md +++ b/doc/administration/monitoring/index.md @@ -5,5 +5,5 @@ Explore our features to monitor your GitLab instance: - [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance. - [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products. - [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics. -- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint. +- [Monitoring uptime](../../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint. - [IP whitelists](ip_whitelist.md): Configure GitLab for monitoring endpoints that provide health check information when probed. diff --git a/doc/api/README.md b/doc/api/README.md index 88710eae4fe..b193ef4ab7f 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -43,6 +43,7 @@ following locations: - [Pipeline Schedules](pipeline_schedules.md) - [Projects](projects.md) including setting Webhooks - [Project Access Requests](access_requests.md) +- [Project import/export](project_import_export.md) - [Project Members](members.md) - [Project Snippets](project_snippets.md) - [Protected Branches](protected_branches.md) diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md new file mode 100644 index 00000000000..e442442c750 --- /dev/null +++ b/doc/api/project_import_export.md @@ -0,0 +1,74 @@ +# Project import API + +[Introduced][ce-41899] in GitLab 10.6 + +[See also the project import/export documentation](../user/project/settings/import_export.md) + +## Import a file + +```http +POST /projects/import +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ---------------------------------------- | +| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the current user's namespace | +| `file` | string | yes | The file to be uploaded | +| `path` | string | yes | Name and path for new project | + +To upload a file from your filesystem, use the `--form` argument. This causes +cURL to post data using the header `Content-Type: multipart/form-data`. +The `file=` parameter must point to a file on your filesystem and be preceded +by `@`. For example: + +```console +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import +``` + +```json +{ + "id": 1, + "description": null, + "name": "api-project", + "name_with_namespace": "Administrator / api-project", + "path": "api-project", + "path_with_namespace": "root/api-project", + "created_at": "2018-02-13T09:05:58.023Z", + "import_status": "scheduled" +} +``` + +## Import status + +Get the status of an import. + +```http +GET /projects/:id/import +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ---------------------------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | + +```console +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/import +``` + +Status can be one of `none`, `scheduled`, `failed`, `started`, or `finished`. + +If the status is `failed`, it will include the import error message under `import_error`. + +```json +{ + "id": 1, + "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "import_status": "started" +} +``` + +[ce-41899]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41899 diff --git a/doc/api/projects.md b/doc/api/projects.md index 05d2f2af00b..9e649efea9c 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1330,6 +1330,10 @@ POST /projects/:id/housekeeping Read more in the [Branches](branches.md) documentation. +## Project Import/Export + +Read more in the [Project import/export](project_import_export.md) documentation. + ## Project members Read more in the [Project members](members.md) documentation. diff --git a/doc/ci/README.md b/doc/ci/README.md index eabeb4510db..532ae52a184 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -70,6 +70,8 @@ learn how to leverage its potential even more. - [Use SSH keys in your build environment](ssh_keys/README.md) - [Trigger pipelines through the GitLab API](triggers/README.md) - [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md) +- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or + more Kubernetes clusters to your project ## GitLab CI/CD for Docker diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index 9f6b0c54990..ffebe1618d3 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -23,7 +23,7 @@ There's also a collection of repositories with [example projects](https://gitlab - **Scala**: [Test a Scala application](test-scala-application.md) - **Clojure**: [Test a Clojure application](test-clojure-application.md) - **Elixir**: - - [Test a Phoenix application](test-phoenix-application.md) + - [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md) - [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) - **iOS and macOS**: - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/) diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md index 8e91cd05d8a..d931c9a77f4 100644 --- a/doc/ci/examples/artifactory_and_gitlab/index.md +++ b/doc/ci/examples/artifactory_and_gitlab/index.md @@ -1,14 +1,14 @@ --- redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html' +author: Fabio Busatto +author_gitlab: bikebilly +level: intermediary +article_type: tutorial +date: 2017-08-15 --- # How to deploy Maven projects to Artifactory with GitLab CI/CD -> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) || -> **Publication date:** 2017-08-15 - ## Introduction In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md index 7bd0514d406..42dc6ef36ba 100644 --- a/doc/ci/examples/browser_performance.md +++ b/doc/ci/examples/browser_performance.md @@ -13,16 +13,20 @@ Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `performan services: - docker:dind script: + - mkdir gitlab-exporter + - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js - mkdir sitespeed-results - - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io --outputFolder sitespeed-results https://my.website.com + - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results https://my.website.com + - mv sitespeed-results/data/performance.json performance.json artifacts: paths: + - performance.json - sitespeed-results/ ``` -This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The full HTML Sitespeed.io report will be saved as an artifact, and if you have Pages enabled it can be viewed directly in your browser. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/). +This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The GitLab plugin for Sitespeed.io is downloaded in order to export key metrics to JSON. The full HTML Sitespeed.io report will also be saved as an artifact, and if you have Pages enabled it can be viewed directly in your browser. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/). -For [GitLab Premium](https://about.gitlab.com/products/) users, a performance score can be automatically +For [GitLab Premium](https://about.gitlab.com/products/) users, key metrics are automatically extracted and shown right in the merge request widget. Learn more about [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html). ## Performance testing on Review Apps @@ -42,9 +46,15 @@ A simple `performance` job would look like: - docker:dind script: - export CI_ENVIRONMENT_URL=$(cat environment_url.txt) + - mkdir gitlab-exporter + - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js - mkdir sitespeed-results - - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io --outputFolder sitespeed-results $CI_ENVIRONMENT_URL + - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL" + - mv sitespeed-results/data/performance.json performance.json artifacts: paths: + - performance.json - sitespeed-results/ ``` + +A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml).
\ No newline at end of file diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md index e1aff6fdf36..b62874ef029 100644 --- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md +++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md @@ -1,14 +1,14 @@ --- redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html' +author: Mehran Rasulian +author_gitlab: mehranrasulian +level: intermediary +article_type: tutorial +date: 2017-08-31 --- # Test and deploy Laravel applications with GitLab CI/CD and Envoy -> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Mehran Rasulian](https://gitlab.com/mehranrasulian) || -> **Publication date:** 2017-08-31 - ## Introduction GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want. diff --git a/doc/ci/examples/test-phoenix-application.md b/doc/ci/examples/test-phoenix-application.md index 7e49721daf1..52db5740c34 100644 --- a/doc/ci/examples/test-phoenix-application.md +++ b/doc/ci/examples/test-phoenix-application.md @@ -1,55 +1,5 @@ -# Test a Phoenix application with GitLab CI/CD +--- +redirect_to: '../../ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md' +--- -This example demonstrates the integration of Gitlab CI with Phoenix, Elixir and -Postgres. - -## Add `.gitlab-ci.yml` to project - -The following `.gitlab-ci.yml` should be added in the root of your -repository to trigger CI: - -```yaml -image: elixir:1.3 - -services: - - postgres:9.6 - -variables: - MIX_ENV: "test" - -before_script: - # Setup phoenix dependencies - - apt-get update - - apt-get install -y postgresql-client - - mix local.hex --force - - mix deps.get --only test - - mix ecto.reset - -test: - script: - - mix test -``` - -The variables will set the Mix environment to "test". The -`before_script` will install `psql`, some Phoenix dependencies, and will also -run your migrations. - -Finally, the test `script` will run your tests. - -## Update the Config Settings - -In `config/test.exs`, update the database hostname: - -```elixir -config :my_app, MyApp.Repo, - hostname: if(System.get_env("CI"), do: "postgres", else: "localhost"), -``` - -## Add the Migrations Folder - -If you do not have any migrations yet, you will need to create an empty -`.gitkeep` file in `priv/repo/migrations`. - -## Sources - -- https://medium.com/@nahtnam/using-phoenix-on-gitlab-ci-5a51eec81142 +The content of this page was incorporated in [this document](../../ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md). diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png Binary files differnew file mode 100644 index 00000000000..0f94ac60fee --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png Binary files differnew file mode 100644 index 00000000000..94828a20f51 --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png Binary files differnew file mode 100644 index 00000000000..68503b392ed --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png Binary files differnew file mode 100644 index 00000000000..d73140ccdd9 --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png Binary files differnew file mode 100644 index 00000000000..38bfde0a3dd --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png Binary files differnew file mode 100644 index 00000000000..bfe85c6a10b --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md new file mode 100644 index 00000000000..7f6519fd38e --- /dev/null +++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md @@ -0,0 +1,423 @@ +--- +author: Alexandre S Hostert +author_gitlab: Hostert +level: beginner +article_type: tutorial +date: 2018-02-20 +--- + +# Testing a Phoenix application with GitLab CI/CD + +[Phoenix][phoenix-site] is a web development framework written in [Elixir][elixir-site], which is a +functional language designed for productivity and maintainability that runs on the +[Erlang VM][erlang-site]. Erlang VM is really really fast and can handle very large numbers of +simultaneous users. + +That's why we're hearing so much about Phoenix today. + +In this tutorial, we'll teach you how to set up GitLab CI/CD to build and test a Phoenix +application. + +_We assume that you know how to create a Phoenix app, run tests locally, and how to work with Git +and GitLab UI._ + +## Introduction + +### What is Phoenix? + +[Phoenix][phoenix-site] is a web development framework written in [Elixir][elixir-site] very useful + to build fast, reliable, and high-performance applications, as it uses [Erlang VM][erlang-site]. + +Many components and concepts are similar to Ruby on Rails or Python's Django. High developer +productivity and high application performance are only a few advantages on learning how to use it. +Working on the MVC pattern, it's was designed to be modular and flexible. Easy to mantain a growing +app is a plus. + +Phoenix can run in any OS where Erlang is supported: + +- Ubuntu +- CentOS +- Mac OS X +- Debian +- Windows +- Fedora +- Raspbian + +Check the [Phoenix learning guide][phoenix-learning-guide] for more information. + +### What is Elixir? + +[Elixir][elixir-site] is a dynamic, functional language created to use all the maturity of Erlang +(30 years old!) in these days, in an easy way. It has similarities with Ruby, specially on sintax, +so Ruby developers are quite excited with the rapid growing of Elixir. A full-stack Ruby developer +can learn how to use Elixir and Phoenix in just a few weeks! + +In Elixir we have a command called `mix`, which is a helper to create projects, testing, run +migrations and [much more][elixir-mix]. We'll use it later on in this tutorial. + +Check the [Elixir documentation][elixir-docs] for more information. + +## Requirements + +To follow this tutorial, you'll need to have installed: + +- Elixir [installation instructions][elixir-install] +- Phoenix Framework [installation instructions][phoenix-install] +- PostgreSQL (if you need to use MySQL server, check [Phoenix instructions][phoenix-mysql]) + +### Create a new Phoenix project + +Open your terminal and go to the directory you wish to create your project. +You don't need to create an empty directory for the project's files, because the `mix` command will +do it for us. + +When we call `mix` command, we'll pass two arguments: + +- The task we want it to run: `phoenix.new` +- And the parameter `phoenix.new` requires, which is the name of the new project. In this case, +we're calling it `hello_gitlab_ci`, but you're free to set your own name: + +```bash +mix phoenix.new hello_gitlab_ci +``` + +When asked, answer `Y` to fetch and install dependencies. + +If everything went fine, you'll get an output like this: + +![`mix phoenix.new`](img/mix-phoenix-new.png) + +Now, our project is located inside the directory with the same name we pass to `mix` command, for +example, `~/GitLab/hello_gitlab_ci`. +If we take a look at the directory, we'll see the Phoenix files and the dependencies needed to run. + +### Initialize the PostgreSQL database + +By default, Phoenix requires a PostgreSQL database to store whatever we need to store in our app. In +this case, we'll only create an empty database. + +First, we need to navigate to our recently created project's directory, and then execute again +`mix`. This time, `mix` will receive the parameter `ecto.create`, which is the task to create our +new database. [Ecto][ecto] is the database wrapper for Elixir. + +When we do run `mix` the first time after creating our project, it will compile our files to +bytecode, which will be interpreted by Erlang VM. In the next times, it will only compile our +changes. + +Run the commands below to create our empty database: + +```bash +cd hello_gitlab_ci +mix ecto.create +``` + +We expect to see this output at the end of the command: + +```bash +Generated hello_gitlab_ci app +The database for HelloGitlabCi.Repo has been created +``` + +> **Note:** +Phoenix assumes that our PostgreSQL database will have a `postgres` user account with the correct +permissions and a password of `postgres`. If it's not your case, check +[Ecto's instructions][ecto-repo]. + +### Start Phoenix server + +Now, it's time to see if everything we did until now went well. We'll call `mix` again, this time +with `phoenix.server` parameter, which will start Phoenix's HTTP Server. + +```bash +mix phoenix.server +``` + +This will be the output to this command: + +```bash +[info] Running HelloGitlabCi.Endpoint with Cowboy using http://localhost:4000 +23 May 11:44:35 - info: compiling +23 May 11:44:37 - info: compiled 6 files into 2 files, copied 3 in 9.8 sec +``` + +Now, we have our app running locally. We can preview it directly on our browser. Let's open +[`localhost:4000`](http://localhost:4000) to see our Phoenix Framework welcome page. If the link do +not work, open [`127.0.0.1:4000`](http://127.0.0.1:4000) instead and later, configure your OS to +point `localhost` to `127.0.0.1`. + +![`mix phoenix.server`](img/mix-phoenix-server.png) + +Great, now we have a local Phoenix Server running our app. + +Locally, our application is running in an `iex` session. [iex][iex] stands for Interactive Elixir. +In this interactive mode, we can type any Elixir expression and get its result. To exit `iex`, we +need to press `Ctrl+C` twice. So, when we need to stop the Phoenix server, we have to hit `Ctrl+C` +twice. + +## Introducing GitLab CI/CD + +With GitLab, we can manage our development workflow, improve our productivity, track issues, +perform code review, and much more from a single platform. With GitLab CI/CD, we can be much more +productive, because every time we, or our co-workers push any code, GitLab CI/CD will build and +test the changes, telling us in realtime if anything goes wrong. + +Certainly, when our application starts to grow, we'll need more developers working on the same +project and this process of building and testing can easely become a mess without proper management. +That's also why GitLab CI/CD is so important to our application. Every time someone pushes its code to +GitLab, we'll quickly know if their changes broke something or not. We don't need to stop everything +we're doing to test manually and locally every change our team does. + +Let's see this in practice. + +## Adjusting Phoenix configuration + +Now, we need to adjust our Phoenix configuration before configuring GitLab CI/CD. +There is a directory (`config`) in your Phoenix project that contains a configuration file for every +environment it can run. Since we will work with a single environment, we'll edit just the test +configuration file (`test.exs`). + +But, why do we need to adjust our configuration? Well, GitLab CI/CD builds and tests our code in one +isolated virtual machine, called [Runner][runner-site], using Docker technology. In this Runner, +GitLab CI/CD has access to everything our Phoenix application need to run, exactly as we have in our +`localhost`, but we have to tell GitLab CI/CD where to create and find this database using system +variables. This way, GitLab CI/CD will create our test database inside the Runner, just like we do +when running our Phoenix in our `localhost`. + +- Open `hello_gitlab_ci/config/test.exs` on your favorite code editor +- Go to **Configure your database** session and edit the block to include `System.get_env`: + + ```elixir + # Configure your database + config :hello_gitlab_ci, HelloGitlabCi.Repo, + adapter: Ecto.Adapters.Postgres, + username: System.get_env("POSTGRES_USER") || "postgres", + password: System.get_env("POSTGRES_PASSWORD") || "postgres", + database: System.get_env("POSTGRES_DB") || "hello_gitlab_ci_test", + hostname: System.get_env("POSTGRES_HOST") || "localhost", + pool: Ecto.Adapters.SQL.Sandbox + ``` + + We'll need these system variables later on. + +- Create an empty file named `.gitkeep` into `hello_gitlab_ci/priv/repo/migrations` + + As our project is still fresh, we don't have any data on our database, so, the `migrations` +directory will be empty. + Without `.gitkeep`, git will not upload this empty directory and we'll got an error when running our +test on GitLab. + + > **Note:** + If we add a folder via the GitLab UI, GitLab itself will add the `.gitkeep` to that new dir. + +Now, let's run a local test and see if everything we did didn't break anything. + +## Testing + +Earlier, when we created our project, we ran `mix phoenix.new`. +This task created everything a Phoenix application needed, including some unit tests into +`hello_gitlab_ci/test` directory. + +Let's run a new task with `mix` to run those tests for us. This time, the parameter expected is +`test`. We can add `--trace` parameter for debugging purposes. + +In your terminal, navigate to the directory `hello_gitlab_ci` and run: + +```bash +mix test +``` + +Our expected result is this: + +```bash +.... + +Finished in 0.7 seconds +4 tests, 0 failures + +Randomized with seed 610000 +``` + +Our test was successfull. It's time to push our files to GitLab. + +## Configuring CI/CD Pipeline + +The first step is to create a new file called `.gitlab-ci.yml` in `hello_gitlab_ci` directory of our +project. + +- The fastest and easiest way to do this, is to click on **Set up CI** on project's main page: + + ![Set up CI](img/setup-ci.png) + +- On next screen, we can select a template ready to go. Click on **Apply a GitLab CI/CD Yaml +template** and select **Elixir**: + + ![Select template](img/select-template.png) + + This template file tells GitLab CI/CD about what we wish to do every time a new commit is made. + However, we have to adapt it to run a Phoenix app. + +- The first line tells GitLab what Docker image will be used. + + Remember when we learn about Runners, the isolated virtual machine where GitLab CI/CD build and test + our application? This virtual machine must have all dependencies to run our application. This is + where a Docker image is needed. The correct image will provide the entire system for us. + + As a suggestion, you can use [trenpixster's elixir image][docker-image], which already has all + dependencies for Phoenix installed, such as Elixir, Erlang, NodeJS and PostgreSQL: + + ```yml + image: trenpixster/elixir:latest + ``` + +- At `services` session, we'll only use `postgres`, so we'll delete `mysql` and `redis` lines: + + ```yml + services: + - postgres:latest + ``` + +- Now, we'll create a new entry called `variables`, before `before_script` session: + + ```yml + variables: + POSTGRES_DB: hello_gitlab_ci_test + POSTGRES_HOST: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: "postgres" + MIX_ENV: "test" + ``` + + Here, we are setting up the values for GitLab CI/CD authenticate into PostgreSQL, as we did on + `config/test.exs` earlier. + +- In `before_script` session, we'll add some commands to prepare everything to the test: + + ```yml + before_script: + - apt-get update && apt-get -y install postgresql-client + - mix local.hex --force + - mix deps.get --only test + - mix ecto.create + - mix ecto.migrate + ``` + + It's important to install `postgresql-client` to let GitLab CI/CD access PostgreSQL and create our + database with the login information provided earlier. More important is to respect the identation, + to avoid syntax errors when running the build. + +- And finally, we'll let `mix` session intact. + +Let's take a look at the completed file after the editions: + +```yml +image: trenpixster/elixir:latest + +services: + - postgres:latest + +variables: + POSTGRES_DB: test_test + POSTGRES_HOST: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: "postgres" + MIX_ENV: "test" + +before_script: + - apt-get update && apt-get -y install postgresql-client + - mix deps.get + - mix ecto.create + - mix ecto.migrate + +mix: + script: + - mix test +``` + +For safety, we can check if we get any syntax errors before submiting this file to GitLab. Copy the +contents of `.gitlab-ci.yml` and paste it on [GitLab CI/CD Lint tool][ci-lint]. Please note that +this link will only work for logged in users. + +## Watching the build + +I don't know about you, but I love to watch that black screen being filled with compilation output. +With this, I can feel the happiness of something I made working correctly. On `localhost` it's easy +to watch our build, but on GitLab, is it possible? Yes! + +Let's go to **Pipelines** and see GitLab doing the job. Just click on **Pipelines** to find the +actual running build job. + +![Pipelines](img/pipelines.png) + +Click on build's ID to watch the entire process. If everything went as expected, we can wait for the +**Build succeeded** at the end of the process! :) + +``` +$ mix test +.... + +Finished in 0.3 seconds +4 tests, 0 failures + +Randomized with seed 206909 +Build succeeded +``` + +If we take a look at the project's main page on the GitLab UI, we can see the status of the last +build made by GitLab CI/CD. + +Time to show the world our green build badge! Navigate to your project's **Settings > CI/CD** and +expand **General pipelines settings**. Scroll down to **Pipeline status** and copy the markdown code +for your badge. Paste it on the top of your `README.md` file, to let people outside of our project +see if our latest code is running without errors. + +When we finish this edition, GitLab will start another build and show a **build running** badge. It +is expected, after all we just configured GitLab CI/CD to do this for every push! But you may think +"Why run build and tests for simple things like editing README.md?" and it is a good question. +For changes that don't affect your application, you can add the keyword [`[ci skip]`][skipping-jobs] +to commit message and the build related to that commit will be skipped. + +In the end, we finally got our pretty green build succeeded badge! By outputting the result on the +README file, it shows to whoever lands on your project's page that your code is up-to-date and +working properly. + +## Conclusion + +When we have a growing application with many developers working on it, or when we have an open +source project being watched and contributed by the community, it is really important to have our +code permanently working. GitLab CI/CD is a time saving powerfull tool to help us mantain our code +organized and working. + +As we could see in this post, GitLab CI/CD is really really easy to configure and use. We have [many +other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams will be huge! + +## References + +- [GitLab CI/CD introductory guide][ci-guide] +- [GitLab CI/CD full Documentation][ci-docs] +- [GitLab Runners documentation][gitlab-runners] +- [Using Docker images documentation][using-docker] +- [Example project: Hello GitLab CI/CD on GitLab][hello-gitlab] + +[phoenix-site]: http://phoenixframework.org/ "Phoenix Framework" +[phoenix-learning-guide]: https://hexdocs.pm/phoenix/learning.html "Phoenix Learning Guide" +[phoenix-install]: http://www.phoenixframework.org/docs/installation "Phoenix Installation" +[phoenix-mysql]: http://www.phoenixframework.org/docs/using-mysql "Phoenix with MySQL" +[elixir-site]: http://elixir-lang.org/ "Elixir" +[elixir-mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix" +[elixir-docs]: http://elixir-lang.org/getting-started/introduction.html "Elixir Documentation" +[erlang-site]: http://erlang.org "Erlang" +[elixir-install]: https://elixir-lang.org/install.html "Elixir Installation" +[ecto]: http://hexdocs.pm/ecto "Ecto" +[ecto-repo]: https://hexdocs.pm/ecto/Ecto.html#module-repositories "Ecto Repositories" +[mix-ecto]: https://hexdocs.pm/ecto/Mix.Tasks.Ecto.Create.html "mix and Ecto" +[iex]: http://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode" +[ci-lint]: https://gitlab.com/ci/lint "CI Lint Tool" +[ci-reasons]: https://about.gitlab.com/2015/02/03/7-reasons-why-you-should-be-using-ci/ "7 Reasons Why You Should Be Using CI" +[ci-guide]: https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/ "Getting Started With GitLab And GitLab CI/CD" +[ci-docs]: ../../README.md "GitLab CI/CD Documentation" +[skipping-jobs]: ../../yaml/README.md#skipping-jobs "Skipping Jobs" +[gitlab-runners]: ../../runners/README.md "GitLab Runners Documentation" +[runner-site]: ../../runners/README.md#runners "Runners" +[docker-image]: https://hub.docker.com/r/trenpixster/elixir/ "Elixir Docker Image" +[using-docker]: ../../docker/using_docker_images.md "Using Docker Images" +[hello-gitlab]: https://gitlab.com/Hostert/hello_gitlab_ci "Hello GitLab CI/CD" diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 4a650303d45..80ab63468f2 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -276,7 +276,7 @@ To enable per-branch caching: ```yaml cache: - key: "$CI_COMMIT_REF_NAME" + key: "$CI_COMMIT_REF_SLUG" untracked: true ``` @@ -284,7 +284,7 @@ To enable per-job and per-branch caching: ```yaml cache: - key: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME" + key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" untracked: true ``` @@ -292,7 +292,7 @@ To enable per-branch and per-stage caching: ```yaml cache: - key: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME" + key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG" untracked: true ``` @@ -301,7 +301,7 @@ If you use **Windows Batch** to run your shell scripts you need to replace ```yaml cache: - key: "%CI_JOB_STAGE%-%CI_COMMIT_REF_NAME%" + key: "%CI_JOB_STAGE%-%CI_COMMIT_REF_SLUG%" untracked: true ``` @@ -310,7 +310,7 @@ If you use **Windows PowerShell** to run your shell scripts you need to replace ```yaml cache: - key: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_NAME" + key: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_SLUG" untracked: true ``` diff --git a/doc/development/changelog.md b/doc/development/changelog.md index c1f783ce877..1962392a9eb 100644 --- a/doc/development/changelog.md +++ b/doc/development/changelog.md @@ -125,7 +125,7 @@ author: type: ``` If you're working on the GitLab EE repository, the entry will be added to -`changelogs/unreleased-ee/` instead. +`ee/changelogs/unreleased/` instead. ### Arguments @@ -279,8 +279,8 @@ After much discussion we settled on the current solution of one file per entry, and then compiling the entries into the overall `CHANGELOG.md` file during the [release process]. -[boring solution]: https://about.gitlab.com/handbook/#boring-solutions -[release managers]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/release-manager.md +[boring solution]: https://about.gitlab.com/handbook/values/#boring-solutions +[release managers]: https://gitlab.com/gitlab-org/release/docs/blob/master/quickstart/release-manager.md [started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826 [release process]: https://gitlab.com/gitlab-org/release-tools diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index 795e1e83105..ece9a9bc0fe 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -37,12 +37,31 @@ are very appreciative of the work done by translators and proofreaders! > sure that you have a history of contributing translations to the GitLab > project. -1. Once your translations have been accepted, - [open a merge request](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/new) - to request Proofreader permissions and add yourself to the list above. +1. Contribute translations to GitLab. See instructions for + [translating GitLab](translation.md). + + Translating GitLab is a community effort that requires team work and + attention to detail. Proofreaders play an important role helping new + contributors, and ensuring the consistency and quality of translations. + Your conduct and contributions as a translator should reflect this before + requesting to be a proofreader. + +1. Request proofreader permissions by opening a merge request to add yourself + to the list of proofreaders. + + Open the [proofreader.md source file][proofreader-src] and click **Edit**. + + Add your language in alphabetical order, and add yourself to the list + including: + + - name + - link to your GitLab profile + - link to your CrowdIn profile In the merge request description, please include links to any projects you have previously translated. 1. Your request to become a proofreader will be considered on the merits of your previous translations. + +[proofreader-src]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/i18n/proofreader.md diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md index 2a1d744668b..403c9d08752 100644 --- a/doc/development/writing_documentation.md +++ b/doc/development/writing_documentation.md @@ -240,7 +240,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 > A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them. -- Live example: "GitLab Pages from A to Z - [Part 1](../user/project/pages/getting_started_part_one.md) to [Part 4](../user/project/pages/getting_started_part_four.md)" +- Live example: "[Static sites and GitLab Pages domains (Part 1)](../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../user/project/pages/getting_started_part_four.md)" A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B. It does not only describes steps 2 and 3, but also shows you how to accomplish them. @@ -254,18 +254,25 @@ through the process of how to use it systematically. #### Special format -Every **Technical Article** contains, in the very beginning, a blockquote with the following information: +Every **Technical Article** contains a frontmatter at the beginning of the doc +with the following information: -- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial) -- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced) -- A reference to the **author's name** and **GitLab.com handle** -- A reference of the **publication date** +- **Type of article** (user guide, admin guide, technical overview, tutorial) +- **Knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced) +- **Author's name** and **GitLab.com handle** +- **Publication date** (ISO format YYYY-MM-DD) -```md -> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Name Surname](https://gitlab.com/username) || -> **Publication date:** AAAA-MM-DD +For example: + + +```yaml +--- +author: John Doe +author_gitlab: johnDoe +level: beginner +article_type: user guide +date: 2017-02-01 +--- ``` #### Technical Articles - Writing Method diff --git a/doc/install/google_cloud_platform/img/boot_disk.png b/doc/install/google_cloud_platform/img/boot_disk.png Binary files differnew file mode 100644 index 00000000000..37b2d9eaae7 --- /dev/null +++ b/doc/install/google_cloud_platform/img/boot_disk.png diff --git a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png b/doc/install/google_cloud_platform/img/change_admin_passwd_email.png Binary files differdeleted file mode 100644 index 1ffe14f60ff..00000000000 --- a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png b/doc/install/google_cloud_platform/img/chrome_not_secure_page.png Binary files differdeleted file mode 100644 index e732066908f..00000000000 --- a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/first_signin.png b/doc/install/google_cloud_platform/img/first_signin.png Binary files differnew file mode 100644 index 00000000000..6eb3392d674 --- /dev/null +++ b/doc/install/google_cloud_platform/img/first_signin.png diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png b/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png Binary files differdeleted file mode 100644 index 2a1859da6e3..00000000000 --- a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png b/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png Binary files differdeleted file mode 100644 index 1c4c870dbc9..00000000000 --- a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png Binary files differindex 6398d247ba0..d6390c4dd4f 100644 --- a/doc/install/google_cloud_platform/img/gcp_landing.png +++ b/doc/install/google_cloud_platform/img/gcp_landing.png diff --git a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png b/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png Binary files differdeleted file mode 100644 index f492888ea4a..00000000000 --- a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png b/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png Binary files differdeleted file mode 100644 index b38af3966e2..00000000000 --- a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png b/doc/install/google_cloud_platform/img/gitlab_deployed_page.png Binary files differdeleted file mode 100644 index fef9ae45f32..00000000000 --- a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png b/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png Binary files differdeleted file mode 100644 index 381c0fe48a5..00000000000 --- a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/gitlab_launch_button.png b/doc/install/google_cloud_platform/img/gitlab_launch_button.png Binary files differdeleted file mode 100644 index 50f66f66118..00000000000 --- a/doc/install/google_cloud_platform/img/gitlab_launch_button.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/launch_vm.png b/doc/install/google_cloud_platform/img/launch_vm.png Binary files differnew file mode 100644 index 00000000000..3fd13f232bb --- /dev/null +++ b/doc/install/google_cloud_platform/img/launch_vm.png diff --git a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png b/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png Binary files differdeleted file mode 100644 index 00060841619..00000000000 --- a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/ssh_terminal.png b/doc/install/google_cloud_platform/img/ssh_terminal.png Binary files differnew file mode 100644 index 00000000000..6a1a418d8e9 --- /dev/null +++ b/doc/install/google_cloud_platform/img/ssh_terminal.png diff --git a/doc/install/google_cloud_platform/img/ssh_via_button.png b/doc/install/google_cloud_platform/img/ssh_via_button.png Binary files differdeleted file mode 100644 index 26106f159ad..00000000000 --- a/doc/install/google_cloud_platform/img/ssh_via_button.png +++ /dev/null diff --git a/doc/install/google_cloud_platform/img/vm_created.png b/doc/install/google_cloud_platform/img/vm_created.png Binary files differnew file mode 100644 index 00000000000..fb467f40838 --- /dev/null +++ b/doc/install/google_cloud_platform/img/vm_created.png diff --git a/doc/install/google_cloud_platform/img/vm_details.png b/doc/install/google_cloud_platform/img/vm_details.png Binary files differnew file mode 100644 index 00000000000..2d230416a4b --- /dev/null +++ b/doc/install/google_cloud_platform/img/vm_details.png diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md index c6b767fff02..3389f0260f9 100644 --- a/doc/install/google_cloud_platform/index.md +++ b/doc/install/google_cloud_platform/index.md @@ -2,12 +2,7 @@ ![GCP landing page](img/gcp_landing.png) -The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through -the [Google Cloud Launcher][launcher] program. - -GitLab's official Google Launcher apps: -1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public) -2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public) +Gettung started with GitLab on a [Google Cloud Platform (GCP)][gcp] instance is quick and easy. ## Prerequisites @@ -17,84 +12,52 @@ There are only two prerequisites in order to install GitLab on GCP: 1. You need to sign up for the GCP program. If this is your first time, Google gives you [$300 credit for free][freetrial] to consume over a 60-day period. -Once you have performed those two steps, you can visit the -[GCP launcher console][console] which has a list of all the things you can -deploy on GCP. - -![GCP launcher console](img/gcp_launcher_console_home_page.png) - -The next step is to find and install GitLab. +Once you have performed those two steps, you can [create a VM](#creating-the-vm). -## Configuring and deploying the VM +## Creating the VM To deploy GitLab on GCP you need to follow five simple steps: -1. Go to https://cloud.google.com/launcher and login with your Google credentials -1. Search for GitLab from GitLab Inc. (not the same as Bitnami) and click on - the tile. +1. Go to https://console.cloud.google.com/compute/instances and login with your Google credentials. - ![Search for GitLab](img/gcp_search_for_gitlab.png) +1. Click on **Create** -1. In the next page, you can see an overview of the GitLab VM as well as some - estimated costs. Click the **Launch on Compute Engine** button to choose the - hardware and network settings. + ![Search for GitLab](img/launch_vm.png) - ![Launch on Compute Engine](img/gcp_gitlab_overview.png) +1. On the next page, you can select the type of VM as well as the + estimated costs. Provide the name of the instance, desired datacenter, and machine type. Note that GitLab recommends at least 2 vCPU's and 4GB of RAM. -1. In the settings page you can choose things like the datacenter where your GitLab - server will be hosted, the number of CPUs and amount of RAM, the disk size - and type, etc. Read GitLab's [requirements documentation][req] for more - details on what to choose depending on your needs. + ![Launch on Compute Engine](img/vm_details.png) - ![Deploy settings](img/new_gitlab_deployment_settings.png) +1. Click **Change** under Boot disk to select the size, type, and desired operating system. GitLab supports a [variety of linux operating systems][req], including Ubuntu and Debian. Click **Select** when finished. -1. As a last step, hit **Deploy** when ready. The process will finish in a few - seconds. + ![Deploy in progress](img/boot_disk.png) - ![Deploy in progress](img/gcp_gitlab_being_deployed.png) +1. As a last step allow HTTP and HTTPS traffic, then click **Create**. The process will finish in a few seconds. +## Installing GitLab -## Visiting GitLab for the first time +After a few seconds, the instance will be created and available to log in. The next step is to install GitLab onto the instance. -After a few seconds, GitLab will be successfully deployed and you should be -able to see the IP address that Google assigned to the VM, as well as the -credentials to the GitLab admin account. +![Deploy settings](img/vm_created.png) -![Deploy settings](img/gitlab_deployed_page.png) +1. Make a note of the IP address of the instance, as you will need that in a later step. +1. Click on the SSH button to connect to the instance. +1. A new window will appear, with you logged into the instance. -1. Click on the IP under **Site address** to visit GitLab. -1. Accept the self-signed certificate that Google automatically deployed in - order to securely reach GitLab's login page. -1. Use the username and password that are present in the Google console page - to login into GitLab and click **Sign in**. + ![GitLab first sign in](img/ssh_terminal.png) - ![GitLab first sign in](img/gitlab_first_sign_in.png) +1. Next, follow the instructions for installing GitLab for the operating system you choose, at https://about.gitlab.com/installation/. You can use the IP address from the step above, as the hostname. -Congratulations! GitLab is now installed and you can access it via your browser, -but we're not done yet. There are some steps you need to take in order to have -a fully functional GitLab installation. +1. Congratulations! GitLab is now installed and you can access it via your browser. To finish installation, open the URL in your browser and provide the initial administrator password. The username for this account is `root`. + + ![GitLab first sign in](img/first_signin.png) ## Next steps These are the most important next steps to take after you installed GitLab for the first time. -### Changing the admin password and email - -Google assigned a random password for the GitLab admin account and you should -change it ASAP: - -1. Visit the GitLab admin page through the link in the Google console under - **Admin URL**. -1. Find the Administrator user under the **Users** page and hit **Edit**. -1. Change the email address to a real one and enter a new password. - - ![Change GitLab admin password](img/change_admin_passwd_email.png) - -1. Hit **Save changes** for the changes to take effect. -1. After changing the password, you will be signed out from GitLab. Use the - new credentials to login again. - ### Assigning a static IP By default, Google assigns an ephemeral IP to your instance. It is strongly @@ -112,7 +75,7 @@ here's how you configure GitLab to be aware of the change: 1. SSH into the VM. You can easily use the **SSH** button in the Google console and a new window will pop up. - ![SSH button](img/ssh_via_button.png) + ![SSH button](img/vm_created.png) In the future you might want to set up [connecting with an SSH key][ssh] instead. @@ -161,7 +124,6 @@ Kerberos, etc. Here are some documents you might be interested in reading: - [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html) - [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html) -[console]: https://console.cloud.google.com/launcher "GCP launcher console" [freetrial]: https://console.cloud.google.com/freetrial "GCP free trial" [ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses" [gcp]: https://cloud.google.com/ "Google Cloud Platform" diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md index 448cbe1077d..1f46ee4c1ea 100644 --- a/doc/install/openshift_and_gitlab/index.md +++ b/doc/install/openshift_and_gitlab/index.md @@ -1,9 +1,12 @@ -# Getting started with OpenShift Origin 3 and GitLab +--- +author: Achilleas Pipinellis +author_gitlab: axil +level: intermediary +article_type: tutorial +date: 2016-06-28 +--- -> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) || -> **Publication date:** 2016-06-28 +# Getting started with OpenShift Origin 3 and GitLab ## Introduction diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 4324b4ca0b8..b2c9177e6eb 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -192,3 +192,6 @@ use the CI features. We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11). Each time a new browser version is released, we begin supporting that version and stop supporting the third most recent version. + +Note: We do not support running GitLab with JavaScript disabled in the browser and have no plans of supporting that +in the future because we have features such as Issue Boards which require JavaScript extensively. diff --git a/doc/topics/git/how_to_install_git/index.md b/doc/topics/git/how_to_install_git/index.md index 7fb578e9ea8..6c909a1ba86 100644 --- a/doc/topics/git/how_to_install_git/index.md +++ b/doc/topics/git/how_to_install_git/index.md @@ -1,9 +1,12 @@ -# Installing Git +--- +author: Sean Packham +author_gitlab: SeanPackham +level: beginner +article_type: user guide +date: 2017-05-15 +--- -> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** user guide || -> **Level:** beginner || -> **Author:** [Sean Packham](https://gitlab.com/SeanPackham) || -> **Publication date:** 2017-05-15 +# Installing Git To begin contributing to GitLab projects you will need to install the Git client on your computer. diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md index 6a2f7b30dd3..4cb8f083fb5 100644 --- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md +++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md @@ -1,9 +1,12 @@ -# Numerous undo possibilities in Git +--- +author: Crt Mori +author_gitlab: Letme +level: intermediary +article_type: tutorial +date: 2017-05-15 +--- -> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial || -> **Level:** intermediary || -> **Author:** [Crt Mori](https://gitlab.com/Letme) || -> **Publication date:** 2017-08-17 +# Numerous undo possibilities in Git ## Introduction diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md index d874688cc29..81b135a5b37 100644 --- a/doc/user/admin_area/settings/usage_statistics.md +++ b/doc/user/admin_area/settings/usage_statistics.md @@ -10,8 +10,9 @@ under **Admin area > Settings > Usage statistics**. GitLab can inform you when an update is available and the importance of it. -No information other than the GitLab version and the instance's hostname (through the HTTP referer) -are collected. +GitLab Inc. collects version statistics, but no information other than +the GitLab version and the instance's hostname (through the HTTP +referer) is collected. In the **Overview** tab you can see if your GitLab version is up to date. There are three cases: 1) you are up to date (green), 2) there is an update available diff --git a/doc/user/markdown.md b/doc/user/markdown.md index b590dfa0d40..ea7b1c9a0ed 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -226,7 +226,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. - Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup: + Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup: Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you: @@ -236,7 +236,7 @@ You can use it to point out a :bug: or warn about :speak_no_evil: patches. And i If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes. -Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup: +Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup: ### Special GitLab References @@ -750,7 +750,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) spaces. @@ -763,7 +763,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa This line is also a separate paragraph, but... This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*. -This line is also a separate paragraph, and... +This line is also a separate paragraph, and... This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*) spaces. diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 50a8e0d5ec5..bbe25c2d911 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -5,20 +5,23 @@ Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes cluster in a few steps. -With a cluster associated to your project, you can use Review Apps, deploy your -applications, run your pipelines, and much more, in an easy way. +## Overview + +With a Kubernetes cluster associated to your project, you can use +[Review Apps](../../../ci/review_apps/index.md), deploy your applications, run +your pipelines, and much more, in an easy way. There are two options when adding a new cluster to your project; either associate your account with Google Kubernetes Engine (GKE) so that you can [create new clusters](#adding-and-creating-a-new-gke-cluster-via-gitlab) from within GitLab, or provide the credentials to an [existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster). -## Prerequisites +## Adding and creating a new GKE cluster via GitLab -In order to be able to manage your Kubernetes cluster through GitLab, the -following prerequisites must be met. +NOTE: **Note:** +You need Master [permissions] and above to access the Kubernetes page. -**For a cluster hosted on GKE:** +Before proceeding, make sure the following requirements are met: - The [Google authentication integration](../../../integration/google.md) must be enabled in GitLab at the instance level. If that's not the case, ask your @@ -28,30 +31,16 @@ following prerequisites must be met. account](https://cloud.google.com/billing/docs/how-to/manage-billing-account) must be set up and that you have to have permissions to access it. - You must have Master [permissions] in order to be able to access the - **Cluster** page. + **Kubernetes** page. - You must have [Cloud Billing API](https://cloud.google.com/billing/) enabled - You must have [Resource Manager API](https://cloud.google.com/resource-manager/) -**For an existing Kubernetes cluster:** - -- Since the cluster is already created, there are no prerequisites. - ---- - -If all of the above requirements are met, you can proceed to add a new Kubernetes -cluster. - -## Adding and creating a new GKE cluster via GitLab - -NOTE: **Note:** -You need Master [permissions] and above to access the Clusters page. - -Before proceeding, make sure all [prerequisites](#prerequisites) are met. -To add a new cluster hosted on GKE to your project: +If all of the above requirements are met, you can proceed to create and add a +new Kubernetes cluster that will be hosted on GKE to your project: -1. Navigate to your project's **CI/CD > Clusters** page. -1. Click on **Add cluster**. +1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Click on **Add Kubernetes cluster**. 1. Click on **Create with GKE**. 1. Connect your Google account if you haven't done already by clicking the **Sign in with Google** button. @@ -66,7 +55,7 @@ To add a new cluster hosted on GKE to your project: - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types) of the Virtual Machine instance that the cluster will be based on. - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster. -1. Finally, click the **Create cluster** button. +1. Finally, click the **Create Kubernetes cluster** button. After a few moments, your cluster should be created. If something goes wrong, you will be notified. @@ -77,14 +66,14 @@ enable the Cluster integration. ## Adding an existing Kubernetes cluster NOTE: **Note:** -You need Master [permissions] and above to access the Clusters page. +You need Master [permissions] and above to access the Kubernetes page. To add an existing Kubernetes cluster to your project: -1. Navigate to your project's **CI/CD > Clusters** page. -1. Click on **Add cluster**. -1. Click on **Add an existing cluster** and fill in the details: - - **Cluster name** (required) - The name you wish to give the cluster. +1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Click on **Add Kuberntes cluster**. +1. Click on **Add an existing Kubernetes cluster** and fill in the details: + - **Kubernetes cluster name** (required) - The name you wish to give the cluster. - **Environment scope** (required)- The [associated environment](#setting-the-environment-scope) to this cluster. - **API URL** (required) - @@ -112,15 +101,13 @@ To add an existing Kubernetes cluster to your project: - If you or someone created a secret specifically for the project, usually with limited permissions, the secret's namespace and project namespace may be the same. -1. Finally, click the **Create cluster** button. - -The Kubernetes service takes the following parameters: +1. Finally, click the **Create Kuberntes cluster** button. After a few moments, your cluster should be created. If something goes wrong, you will be notified. You can now proceed to install some pre-defined applications and then -enable the Cluster integration. +enable the Kubernetes cluster integration. ## Installing applications @@ -139,7 +126,7 @@ added directly to your configured cluster. Those applications are needed for NOTE: **Note:** You need a load balancer installed in your cluster in order to obtain the external IP address with the following procedure. It can be deployed using the -**Ingress** application described in the previous section. +[**Ingress** application](#installing-appplications). In order to publish your web application, you first need to find the external IP address associated to your load balancer. @@ -153,7 +140,8 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**. If the cluster is not on GKE, follow the specific instructions for your Kubernetes provider to configure `kubectl` with the right credentials. -If you installed the Ingress using the **Applications** section, run the following command: +If you installed the Ingress [via the **Applications**](#installing-applications), +run the following command: ```bash kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' @@ -171,9 +159,14 @@ your deployed applications. ## Setting the environment scope -When adding more than one clusters, you need to differentiate them with an -environment scope. The environment scope associates clusters and -[environments](../../../ci/environments.md) in an 1:1 relationship similar to how the +NOTE: **Note:** +This is only available for [GitLab Premium][ee] where you can add more than +one Kubernetes cluster. + +When adding more than one Kubernetes clusters to your project, you need to +differentiate them with an environment scope. The environment scope associates +clusters and [environments](../../../ci/environments.md) in an 1:1 relationship +similar to how the [environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-secret-variables) work. @@ -183,7 +176,7 @@ cluster in a project, and a validation error will occur if otherwise. --- -For example, let's say the following clusters exist in a project: +For example, let's say the following Kubernetes clusters exist in a project: | Cluster | Environment scope | | ---------- | ------------------- | @@ -231,8 +224,7 @@ With GitLab Premium, you can associate more than one Kubernetes clusters to your project. That way you can have different clusters for different environments, like dev, staging, production, etc. -To add another cluster, follow the same steps as described in [adding a -Kubernetes cluster](#adding-a-kubernetes-cluster) and make sure to +Simply add another cluster, like you did the first time, and make sure to [set an environment scope](#setting-the-environment-scope) that will differentiate the new cluster with the rest. @@ -240,45 +232,42 @@ differentiate the new cluster with the rest. The Kubernetes cluster integration exposes the following [deployment variables](../../../ci/variables/README.md#deployment-variables) in the -GitLab CI/CD build environment: - -- `KUBE_URL` - Equal to the API URL. -- `KUBE_TOKEN` - The Kubernetes token. -- `KUBE_NAMESPACE` - The Kubernetes namespace is auto-generated if not specified. - The default value is `<project_name>-<project_id>`. You can overwrite it to - use different one if needed, otherwise the `KUBE_NAMESPACE` variable will - receive the default value. -- `KUBE_CA_PEM_FILE` - Only present if a custom CA bundle was specified. Path - to a file containing PEM data. -- `KUBE_CA_PEM` (deprecated) - Only if a custom CA bundle was specified. Raw PEM data. -- `KUBECONFIG` - Path to a file containing `kubeconfig` for this deployment. - CA bundle would be embedded if specified. - -## Enabling or disabling the Cluster integration +GitLab CI/CD build environment. + +| Variable | Description | +| -------- | ----------- | +| `KUBE_URL` | Equal to the API URL. | +| `KUBE_TOKEN` | The Kubernetes token. | +| `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. | +| `KUBE_CA_PEM_FILE` | Only present if a custom CA bundle was specified. Path to a file containing PEM data. | +| `KUBE_CA_PEM` | (**deprecated**) Only if a custom CA bundle was specified. Raw PEM data. | +| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. | + +## Enabling or disabling the Kubernetes cluster integration After you have successfully added your cluster information, you can enable the -Cluster integration: +Kubernetes cluster integration: 1. Click the "Enabled/Disabled" switch 1. Hit **Save** for the changes to take effect You can now start using your Kubernetes cluster for your deployments. -To disable the Cluster integration, follow the same procedure. +To disable the Kubernetes cluster integration, follow the same procedure. -## Removing the Cluster integration +## Removing the Kubernetes cluster integration NOTE: **Note:** -You need Master [permissions] and above to remove a cluster integration. +You need Master [permissions] and above to remove a Kubernetes cluster integration. NOTE: **Note:** When you remove a cluster, you only remove its relation to GitLab, not the cluster itself. To remove the cluster, you can do so by visiting the GKE dashboard or using `kubectl`. -To remove the Cluster integration from your project, simply click on the +To remove the Kubernetes cluster integration from your project, simply click on the **Remove integration** button. You will then be able to follow the procedure -and [add a cluster](#adding-a-cluster) again. +and add a Kubernetes cluster again. ## What you can get with the Kubernetes integration diff --git a/doc/user/project/integrations/custom_issue_tracker.md b/doc/user/project/integrations/custom_issue_tracker.md index 757522c2ae3..731291ebe84 100644 --- a/doc/user/project/integrations/custom_issue_tracker.md +++ b/doc/user/project/integrations/custom_issue_tracker.md @@ -13,6 +13,8 @@ in the table below. | `issues_url` | The URL to the issue in the issue tracker project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. For example, `https://customissuetracker.com/project-name/:id`. | | `new_issue_url` | Currently unused. Will be changed in a future release. | +Once you have configured and enabled Custom Issue Tracker Service you'll see a link on the GitLab project pages that takes you to that custom issue tracker. + ## Referencing issues diff --git a/doc/user/project/integrations/img/prometheus_dashboard.png b/doc/user/project/integrations/img/prometheus_dashboard.png Binary files differnew file mode 100644 index 00000000000..bd19f1b44cc --- /dev/null +++ b/doc/user/project/integrations/img/prometheus_dashboard.png diff --git a/doc/user/project/integrations/img/prometheus_deploy.png b/doc/user/project/integrations/img/prometheus_deploy.png Binary files differnew file mode 100644 index 00000000000..d39081bcc7b --- /dev/null +++ b/doc/user/project/integrations/img/prometheus_deploy.png diff --git a/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png b/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png Binary files differdeleted file mode 100644 index e30cba211e6..00000000000 --- a/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png +++ /dev/null diff --git a/doc/user/project/integrations/img/prometheus_gcp_node_name.png b/doc/user/project/integrations/img/prometheus_gcp_node_name.png Binary files differdeleted file mode 100644 index ea289431454..00000000000 --- a/doc/user/project/integrations/img/prometheus_gcp_node_name.png +++ /dev/null diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 5fefb3b69c4..249463fb86e 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -2,119 +2,69 @@ > [Introduced][ce-8935] in GitLab 9.0. -GitLab offers powerful integration with [Prometheus] for monitoring your apps. -Metrics are retrieved from the configured Prometheus server, and then displayed +GitLab offers powerful integration with [Prometheus] for monitoring key metrics your apps, directly within GitLab. +Metrics for each environment are retrieved from Prometheus, and then displayed within the GitLab interface. -Each project can be configured with its own specific Prometheus server, see the -[configuration](#configuration) section for more details. If you have a single -Prometheus server which monitors all of your infrastructure, you can pre-fill -the settings page with a default template. To configure the template, see the -[Services templates](services_templates.md) document. +![Environment Dashboard](img/prometheus_dashboard.png) -## Requirements +There are two ways to setup Prometheus integration, depending on where your apps are running: +* For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes) +* For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus). -Integration with Prometheus requires the following: - -1. GitLab 9.0 or higher -1. Prometheus must be configured to collect one of the [supported metrics](prometheus_library/metrics.md) -1. Each metric must be have a label to indicate the environment -1. GitLab must have network connectivity to the Prometheus server - -## Getting started with Prometheus monitoring +## Managed Prometheus on Kubernetes +> **Note**: [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28916) in GitLab 10.5 -Depending on your deployment and where you have located your GitLab server, there are a few options to get started with Prometheus monitoring. +GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cluster](../clusters/index.md), making monitoring of your apps easy. -* If both GitLab and your applications are installed in the same Kubernetes cluster, you can leverage the [bundled Prometheus server within GitLab](#configuring-omnibus-gitlab-prometheus-to-monitor-kubernetes). -* If your applications are deployed on Kubernetes, but GitLab is not in the same cluster, then you can [configure a Prometheus server in your Kubernetes cluster](#configuring-your-own-prometheus-server-within-kubernetes). -* If your applications are not running in Kubernetes, [get started with Prometheus](#getting-started-with-prometheus-outside-of-kubernetes). - -### Getting started with Prometheus outside of Kubernetes - -Installing and configuring Prometheus to monitor applications is fairly straight forward. - -1. [Install Prometheus](https://prometheus.io/docs/introduction/install/) -1. Set up one of the [supported monitoring targets](prometheus_library/metrics.md) -1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config) +### Requirements -### Configuring Omnibus GitLab Prometheus to monitor Kubernetes deployments +* A [connected Kubernetes cluster](../clusters/index.md) +* Helm Tiller [installed by GitLab](../clusters/index.md#installing-applications) -With Omnibus GitLab running inside of Kubernetes, you can leverage the bundled -version of Prometheus to collect the supported metrics. Once enabled, Prometheus will automatically begin monitoring Kubernetes Nodes and any [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>). +### Getting started -1. Read how to configure the bundled Prometheus server in the - [Administration guide][gitlab-prometheus-k8s-monitor]. -1. Now that Prometheus is configured, proceed on - [configuring the Prometheus project service in GitLab](#configuration-in-gitlab). +Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click. -### Configuring your own Prometheus server within Kubernetes +1. Go to the `CI/CD > Kubernetes` page, to view your connected clusters +1. Select the cluster you would like to deploy Prometheus to +1. Click the **Install** button to deploy Prometheus to the cluster -Setting up and configuring Prometheus within Kubernetes is quick and painless. -The Prometheus project provides an [official Docker image][prometheus-docker-image] -which we can use as a starting point. +![Managed Prometheus Deploy](img/prometheus_deploy.png) -To get started quickly, we have provided a [sample YML file][prometheus-yml] -that can be used as a template. This file will create a `prometheus` **Namespace**, -**Service**, **Deployment**, and **ConfigMap** in Kubernetes. You can upload -this file to the Kubernetes dashboard using **+ Create** at the top right. +### About managed Prometheus deployments -![Deploy Prometheus](img/prometheus_yaml_deploy.png) +Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/). -Or use `kubectl`: +The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Ckubernetes_sd_config%3E) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/): +* `prometheus.io/scrape` to `true` to enable monitoring of the resource. +* `prometheus.io/port` to define the port of the metrics endpoint. +* `prometheus.io/path` to define the path of the metrics endpoint. Defaults to `/metrics`. -```bash -kubectl apply -f path/to/prometheus.yml -``` +CPU and Memory consumption is monitored, but requires [naming conventions](prometheus_library/kubernetes.html#specifying-the-environment) in order to determine the environment. If you are using [Auto DevOps](../../../topics/autodevops/), this is handled automatically. -Once deployed, you should see the Prometheus service, deployment, and -pod start within the `prometheus` namespace. The server will begin to collect -metrics from each Kubernetes Node in the cluster, based on the configuration -provided in the template. It will also attempt to collect metrics from any Kubernetes Pods that have been [annotated for Prometheus](https://prometheus.io/docs/operating/configuration/#pod). +The [NGINX Ingress](../clusters/index.md#installing-applications) that is deployed by GitLab to clusters, is automatically annotated for monitoring providing key response metrics: latency, throughput, and error rates. -Since GitLab is not running within Kubernetes, the template provides external -network access via a `NodePort` running on `30090`. This method allows access -to be controlled using provider firewall rules, like within Google Compute Engine. +## Manual configuration of Prometheus -Since a `NodePort` does not automatically have firewall rules created for it, -one will need to be created manually to allow access. In GCP/GKE, you will want -to confirm the Node that the Prometheus pod is running on. This can be done -either by looking at the Pod in the Kubernetes dashboard, or by running: +### Requirements -```bash -kubectl describe pods -n prometheus -``` - -Next on GKE, we need to get the `tag` of the Node or VM Instance, so we can -create an accurate firewall rule. The easiest way to do this is to go into the -Google Cloud Platform Compute console and select the VM instance that matches -the name of the Node gathered from the step above. In this case, the node tag -needed is `gke-prometheus-demo-5d5ada10-node`. Also make a note of the -**External IP**, which will be the IP address the Prometheus server is reachable -on. - -![GCP Node Detail](img/prometheus_gcp_node_name.png) - -Armed with the proper Node tag, the firewall rule can now be created -specifically for this node. To create the firewall rule, open the Google Cloud -Platform Networking console, and select **Firewall Rules**. - -Create a new rule: +Integration with Prometheus requires the following: -- Specify the source IP range to match your desired access list, which should - include your GitLab server. A sample of GitLab.com's IP address range is - available [in this issue][gitlab.com-ip-range], but note that GitLab.com's IPs - are subject to change without prior notification. -- Allowed protocol and port should be `tcp:30090`. -- The target tags should match the Node tag identified earlier in this step. +1. GitLab 9.0 or higher +1. Prometheus must be configured to collect one of the [supported metrics](prometheus_library/metrics.md) +1. Each metric must be have a label to indicate the environment +1. GitLab must have network connectivity to the Prometheus server -![GCP Firewall Rule](img/prometheus_gcp_firewall_rule.png) +### Getting started ---- +Installing and configuring Prometheus to monitor applications is fairly straight forward. -Now that Prometheus is configured, proceed to -[configure the Prometheus project service in GitLab](##configuration-in-gitlab). +1. [Install Prometheus](https://prometheus.io/docs/introduction/install/) +1. Set up one of the [supported monitoring targets](prometheus_library/metrics.md) +1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config) -## Configuration in GitLab +### Configuration in GitLab The actual configuration of Prometheus integration within GitLab is very simple. All you will need is the DNS or IP address of the Prometheus server you'd like diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md index a6673fa2a00..02adc562028 100644 --- a/doc/user/project/integrations/prometheus_library/kubernetes.md +++ b/doc/user/project/integrations/prometheus_library/kubernetes.md @@ -24,9 +24,10 @@ Prometheus server up and running. You have two options here: - If you have an Omnibus based GitLab installation within your Kubernetes cluster, you can leverage the bundled Prometheus server to [monitor Kubernetes](../../../../administration/monitoring/prometheus/index.md#configuring-prometheus-to-monitor-kubernetes). - To configure your own Prometheus server, you can follow the [Prometheus documentation](https://prometheus.io/docs/introduction/overview/) or [our guide](../../../../administration/monitoring/prometheus/index.md#configuring-your-own-prometheus-server-within-kubernetes). -## Specifying the Environment label +## Specifying the Environment -In order to isolate and only display relevant metrics for a given environment -however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](metrics.md#identifying-environments). +In order to isolate and only display relevant CPU and Memory metrics for a given environment, GitLab needs a method to detect which containers it is running. Because these metrics are tracked at the container level, traditional Kubernetes labels are not available. + +Instead, the [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) name should begin with [CI_ENVIRONMENT_SLUG](../../../../ci/variables/README.md#predefined-variables-environment-variables). It can be followed by a `-` and additional content if desired. For example, a deployment name of `review-homepage-5620p5` would match the `review/homepage` environment. If you are using [GitLab Auto-Deploy](../../../../ci/autodeploy/index.md) and one of the two [provided Kubernetes monitoring solutions](../prometheus.md#getting-started-with-prometheus-monitoring), the `environment` label will be automatically added. diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md index e6f13d0630b..49b34c82ae6 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -2,11 +2,11 @@ > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13438) in GitLab 9.5 -GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) of the ingress. +GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) and above of the ingress. ## Requirements -The [Prometheus service](../prometheus/index.md) must be enabled. +[Prometheus integration](../prometheus/index.md) must be active. ## Metrics supported @@ -18,24 +18,34 @@ The [Prometheus service](../prometheus/index.md) must be enabled. ## Configuring NGINX ingress monitoring -If you have deployed with the [gitlab-omnibus](https://docs.gitlab.com/ee/install/kubernetes/gitlab_omnibus.md) Helm chart, and your application is running in the same cluster, no further action is required. The ingress metrics will be automatically enabled and annotated for Prometheus monitoring. Simply ensure Prometheus monitoring is [enabled for your project](../prometheus.md), which is on by default. +If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), it will [automatically be monitored](#about-managed-nginx-ingress-deployments) by Prometheus. -For other deployments, there is some configuration required depending on your installation: -* NGINX Ingress should be version 0.9.0 or above +For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation: +* NGINX Ingress should be version 0.9.0 or above, with metrics enabled * NGINX Ingress should be annotated for Prometheus monitoring * Prometheus should be configured to monitor annotated pods -### Setting up NGINX Ingress for Prometheus monitoring +### About managed NGINX Ingress deployments + +NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's IP](https://docs.gitlab.com/ce/user/project/clusters/index.html#getting-the-external-ip-address). + +NGINX is configured for Prometheus monitoring, by setting: +* `enable-vts-status: "true"`, to export Prometheus metrics +* `prometheus.io/scrape: "true"`, to enable automatic discovery +* `prometheus.io/port: "10254"`, to specify the metrics port + +When used in conjunction with the GitLab deployed Prometheus service, response metrics will be automatically collected. + +### Manually setting up NGINX Ingress for Prometheus monitoring Version 0.9.0 and above of [NGINX ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx) have built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint will start running on port 10254. -With metric data now available, Prometheus needs to be configured to collect it. The easiest way to do this is to leverage Prometheus' [built-in Kubernetes service discovery](https://prometheus.io/docs/operating/configuration/#kubernetes_sd_config), which automatically detects a variety of Kubernetes components and makes them available for monitoring. Since NGINX ingress metrics are exposed per pod, a scrape job for Kubernetes pods is required. A sample pod scraping configuration [is available](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml#L248). This configuration will detect pods and enable collection of metrics **only if** they have been specifically annotated for monitoring. +Next, the ingress needs to be annotated for Prometheus monitoring. Two new annotations need to be added: -Depending on how NGINX ingress was deployed, typically a DaemonSet or Deployment, edit the corresponding YML spec. Two new annotations need to be added: * `prometheus.io/scrape: "true"` * `prometheus.io/port: "10254"` -Prometheus should now be collecting NGINX ingress metrics. To validate view the Prometheus Targets, available under `Status > Targets` on the Prometheus dashboard. New entries for NGINX should be listed in the kubernetes pod monitoring job, `kubernetes-pods`. +Managing these settings depends on how NGINX ingress has been deployed. If you have deployed via the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress), metrics can be enabled with `controller.stats.enabled` along with the required annotations. Alternatively it is possible edit the NGINX ingress YML directly in the [Kubernetes dashboard](https://github.com/kubernetes/dashboard). ## Specifying the Environment label diff --git a/doc/user/project/integrations/samples/prometheus.yml b/doc/user/project/integrations/samples/prometheus.yml deleted file mode 100644 index 3a4735d282f..00000000000 --- a/doc/user/project/integrations/samples/prometheus.yml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: prometheus ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: prometheus - namespace: prometheus -data: - prometheus.yml: |- - scrape_configs: - - job_name: 'kubernetes-nodes' - scheme: https - tls_config: - ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - insecure_skip_verify: true - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - kubernetes_sd_configs: - - role: node - metric_relabel_configs: - - source_labels: [pod_name] - target_label: environment - regex: (.+)-.+-.+ - replacement: $1 - - job_name: kubernetes-pods - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - insecure_skip_verify: true - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - kubernetes_sd_configs: - - role: pod - api_server: https://kubernetes.default.svc:443 - tls_config: - ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token" - relabel_configs: - - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_scrape - action: keep - regex: 'true' - - source_labels: - - __meta_kubernetes_pod_annotation_prometheus_io_path - action: replace - target_label: __metrics_path__ - regex: "(.+)" - - source_labels: - - __address__ - - __meta_kubernetes_pod_annotation_prometheus_io_port - action: replace - regex: "([^:]+)(?::[0-9]+)?;([0-9]+)" - replacement: "$1:$2" - target_label: __address__ - - action: labelmap - regex: __meta_kubernetes_pod_label_(.+) - - source_labels: - - __meta_kubernetes_namespace - action: replace - target_label: kubernetes_namespace - - source_labels: - - __meta_kubernetes_pod_name - action: replace - target_label: kubernetes_pod_name ---- -apiVersion: v1 -kind: Service -metadata: - name: prometheus - namespace: prometheus -spec: - selector: - app: prometheus - ports: - - name: prometheus - protocol: TCP - port: 9090 - nodePort: 30090 - type: NodePort ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: prometheus - namespace: prometheus -spec: - replicas: 1 - template: - metadata: - labels: - app: prometheus - spec: - containers: - - name: prometheus - image: prom/prometheus:latest - args: - - '--config.file=/prometheus-data/prometheus.yml' - ports: - - name: prometheus - containerPort: 9090 - volumeMounts: - - name: data-volume - mountPath: /prometheus-data - volumes: - - name: data-volume - configMap: - name: prometheus diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md index 49f7baf9652..dabffaec5fa 100644 --- a/doc/user/project/labels.md +++ b/doc/user/project/labels.md @@ -15,7 +15,7 @@ In GitLab, you can create project and group labels: ## Creating labels >**Note:** -A permission level of `Developer` or higher is required in order to create labels. +A permission level of `Developer` or higher is required to create labels. ### New project label @@ -27,7 +27,7 @@ If a project has no labels, you can generate a default set of project labels fro ![Labels generate default](img/labels_generate_default.png) -GitLab will add the following default labels to the project: +GitLab will add the following default labels to the project: ![Labels default](img/labels_default.png) @@ -48,9 +48,9 @@ From the sidebar of an issue or a merge request, you can create a create a new * ## Editing labels NOTE: **Note:** -A permission level of `Developer` or higher is required in order to edit labels. +A permission level of `Developer` or higher is required to edit labels. -You can update a label by navigating to **Issues > Labels** in the project ot group and clicking the pencil icon. +You can update a label by navigating to **Issues > Labels** in the project or group and clicking the pencil icon. You can delete a label by clicking the trash icon. diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md index bd0cb437924..e4ee2f7cdfa 100644 --- a/doc/user/project/pages/getting_started_part_four.md +++ b/doc/user/project/pages/getting_started_part_four.md @@ -1,16 +1,13 @@ -# GitLab Pages from A to Z: Part 4 +--- +last_updated: 2018-02-16 +author: Marcia Ramos +author_gitlab: marcia +level: intermediate +article_type: user guide +date: 2017-02-22 +--- -> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || -> **Level**: intermediate || -> **Author**: [Marcia Ramos](https://gitlab.com/marcia) || -> **Publication date:** 2017/02/22 - -- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md) -- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) -- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md) -- **Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages** - -## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages +# Creating and Tweaking GitLab CI/CD for GitLab Pages [GitLab CI](https://about.gitlab.com/gitlab-ci/) serves numerous purposes, to build, test, and deploy your app @@ -19,10 +16,13 @@ from GitLab through methods. You will need it to build your website with GitLab Pages, and deploy it to the Pages server. +To implement GitLab CI/CD, the first thing we need is a configuration +file called `.gitlab-ci.yml` placed at your website's root directory. + What this file actually does is telling the [GitLab Runner](https://docs.gitlab.com/runner/) to run scripts as you would do from the command line. The Runner acts as your -terminal. GitLab CI tells the Runner which commands to run. +terminal. GitLab CI/CD tells the Runner which commands to run. Both are built-in in GitLab, and you don't need to set up anything for them to work. @@ -34,7 +34,7 @@ need to understand just a few things to be able to write our own with its own syntax. You can always check your CI syntax with the [GitLab CI Lint Tool](https://gitlab.com/ci/lint). -**Practical Example:** +## Practical example Let's consider you have a [Jekyll](https://jekyllrb.com/) site. To build it locally, you would open your terminal, and run `jekyll build`. @@ -384,7 +384,3 @@ in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) to deploy this website you're looking at, docs.gitlab.com. - On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/). - -||| -|:--|--:| -|[**← Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**](getting_started_part_three.md)|| diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md index 1e19f422d94..290dfa5af84 100644 --- a/doc/user/project/pages/getting_started_part_one.md +++ b/doc/user/project/pages/getting_started_part_one.md @@ -1,31 +1,28 @@ -# GitLab Pages from A to Z: Part 1 - -> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || -> **Level**: beginner || -> **Author**: [Marcia Ramos](https://gitlab.com/marcia) || -> **Publication date:** 2017/02/22 - -- **Part 1: Static sites and GitLab Pages domains** -- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) -- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md) -- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md) - -## GitLab Pages from A to Z - -This is a comprehensive guide, made for those who want to +--- +last_updated: 2018-02-16 +author: Marcia Ramos +author_gitlab: marcia +level: beginner +article_type: user guide +date: 2017-02-22 +--- + +# Static sites and GitLab Pages domains + +This document is the beginning of a comprehensive guide, made for those who want to publish a website with GitLab Pages but aren't familiar with the entire process involved. -This [first part](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of +This [first document](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of static sites, and go over how the default Pages domains work. -The [second part](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy +The [second document](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy a website from a forked project or create a new one from scratch. -The [third part](getting_started_part_three.md) will show you how to set up a custom domain or subdomain +The [third document](getting_started_part_three.md) will show you how to set up a custom domain or subdomain to your site already deployed. -The [fourth part](getting_started_part_four.md) will show you how to create and tweak GitLab CI for +The [fourth document](getting_started_part_four.md) will show you how to create and tweak GitLab CI for GitLab Pages. To **enable** GitLab Pages for GitLab CE (Community Edition) @@ -113,6 +110,4 @@ You can only create the highest level group website. - On your GitLab instance, replace `gitlab.io` above with your Pages server domain. Ask your sysadmin for this information. -||| -|:--|--:| -||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)| +_Read on about [Projects for GitLab Pages and URL structure](getting_started_part_two.md)._ diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md index a153610c712..b6cf68a02a2 100644 --- a/doc/user/project/pages/getting_started_part_three.md +++ b/doc/user/project/pages/getting_started_part_three.md @@ -1,27 +1,19 @@ --- -last_updated: 2017-09-28 +last_updated: 2018-02-16 +author: Marcia Ramos +author_gitlab: marcia +level: beginner +article_type: user guide +date: 2017-02-22 --- -# GitLab Pages from A to Z: Part 3 +# GitLab Pages custom domains and SSL/TLS Certificates -> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles)**: user guide || -> **Level**: beginner || -> **Author**: [Marcia Ramos](https://gitlab.com/marcia) || -> **Publication date:** 2017-02-22 || -> **Last updated**: 2017-09-28 - -- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md) -- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) -- **Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates** -- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md) - -## Setting Up Custom Domains - DNS Records and SSL/TLS Certificates - -As described in the previous part of this series, setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages. +Setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages. These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`. -### Adding your custom domain to GitLab Pages +## Adding your custom domain to GitLab Pages To use one or more custom domain with your Pages site, there are two things you should consider first, which we'll cover in this guide: @@ -36,7 +28,7 @@ Let's start from the beginning with [DNS records](#dns-records). If you already know how they work and want to skip the introduction to DNS, you may be interested in skipping it until the [TL;DR](#tl-dr) section below. -### DNS Records +## DNS Records A Domain Name System (DNS) web service routes visitors to websites by translating domain names (such as `www.example.com`) into the @@ -72,7 +64,7 @@ for the most popular hosting services: If your hosting service is not listed above, you can just try to search the web for "how to add dns record on <my hosting service>". -#### DNS A record +### DNS A record In case you want to point a root domain (`example.com`) to your GitLab Pages site, deployed to `namespace.gitlab.io`, you need to @@ -87,7 +79,7 @@ running on your instance). ![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png) -#### DNS CNAME record +### DNS CNAME record In case you want to point a subdomain (`hello-world.example.com`) to your GitLab Pages site initially deployed to `namespace.gitlab.io`, @@ -103,7 +95,7 @@ without any `/project-name`. ![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png) -#### TL;DR +### TL;DR | From | DNS Record | To | | ---- | ---------- | -- | @@ -119,7 +111,7 @@ domain. E.g., **do not** point your `subdomain.domain.com` to `namespace.gitlab.io.` or `namespace.gitlab.io/`. > - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`. -### Add your custom domain to GitLab Pages settings +## Add your custom domain to GitLab Pages settings Once you've set the DNS record, you'll need navigate to your project's **Setting > Pages** and click **+ New domain** to add your custom domain to @@ -142,7 +134,7 @@ to your domain will respond with a 404. Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding custom domains to GitLab Pages sites. -### SSL/TLS Certificates +## SSL/TLS Certificates Every GitLab Pages project on GitLab.com will be available under HTTPS for the default Pages domain (`*.gitlab.io`). Once you set @@ -158,7 +150,7 @@ highly recommendable. Let's start with an introduction to the importance of HTTPS. Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-your-project). -#### Why should I care about HTTPS? +### Why should I care about HTTPS? This might be your first question. If our sites are hosted by GitLab Pages, they are static, hence we are not dealing with server-side scripts @@ -179,7 +171,7 @@ authentications and validations. How about taking Josh's advice and protecting our sites too? We will be well supported, and we'll contribute to a safer internet. -#### Organizations supporting HTTPS +### Organizations supporting HTTPS There is a huge movement in favor of securing all the web. W3C fully [supports the cause](https://w3ctag.github.io/web-https/) and explains very well @@ -189,7 +181,7 @@ and would no longer accept unsecured connections. Recently, Mozilla published a [communication](https://blog.mozilla.org/security/2016/03/29/march-2016-ca-communication/) reiterating the importance of HTTPS. -### Issuing Certificates +## Issuing Certificates GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by [Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) @@ -218,7 +210,7 @@ Their certs are valid up to 15 years. Read through the tutorial on Regardless the CA you choose, the steps to add your certificate to your Pages project are the same. -#### What do you need +### What do you need 1. A PEM certificate 1. An intermediate certificate @@ -228,7 +220,7 @@ your Pages project are the same. These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**. -#### What's what? +### What's what? - A PEM certificate is the certificate generated by the CA, which needs to be added to the field **Certificate (PEM)**. @@ -241,7 +233,7 @@ are one of these cases. - A public key is an encrypted key which validates your PEM against your domain. -#### Now what? +### Now what? Now that you hopefully understand why you need all of this, it's simple: @@ -258,6 +250,4 @@ just jumping a line between them. regular text editors. Always use code editors (such as Sublime Text, Atom, Dreamweaver, Brackets, etc). -||| -|:--|--:| -|[**← Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)|[**Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_four.md)| +_Read on about [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md)_ diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md index 4a724dd5c1b..2274cac8ace 100644 --- a/doc/user/project/pages/getting_started_part_two.md +++ b/doc/user/project/pages/getting_started_part_two.md @@ -1,27 +1,23 @@ -# GitLab Pages from A to Z: Part 2 +--- +last_updated: 2018-02-16 +author: Marcia Ramos +author_gitlab: marcia +level: beginner +article_type: user guide +date: 2017-02-22 +--- -> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || -> **Level**: beginner || -> **Author**: [Marcia Ramos](https://gitlab.com/marcia) || -> **Publication date:** 2017/02/22 - -- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md) -- **Part 2: Quick start guide - Setting up GitLab Pages** -- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md) -- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md) - -## Setting up GitLab Pages - -For a complete step-by-step tutorial, please read the -blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain -what do you need and why do you need them. +# Projects for GitLab Pages and URL structure ## What you need to get started +To get started with GitLab Pages, you need: + 1. A project 1. A configuration file (`.gitlab-ci.yml`) to deploy your site 1. A specific `job` called `pages` in the configuration file that will make GitLab aware that you are deploying a GitLab Pages website +1. A `public` directory with the content of the website Optional Features: @@ -51,35 +47,26 @@ containing the most popular SSGs templates. Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've created for the steps below. -1. Choose your SSG template -1. Fork a project from the [Pages group](https://gitlab.com/pages) -1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project** +1. [Fork a sample project](../../../gitlab-basics/fork-project.md) from the [Pages group](https://gitlab.com/pages) +1. Trigger a build (push a change to any file) +1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your project's **Settings** > **Pages** +1. Optionally, remove the fork relationship by navigating to your project's **Settings** > expanding **Advanced settings** and scrolling down to **Remove fork relashionship**: ![remove fork relashionship](img/remove_fork_relashionship.png) -1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **Pipelines** -1. Trigger a build (push a change to any file) -1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your **Project**'s **Settings** > **Pages** - To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to: -- Rename it to `namespace.gitlab.io`: navigate to **Project**'s **Settings** > **Edit Project** > **Rename repository** +- Rename it to `namespace.gitlab.io`: navigate to project's **Settings** > expand **Advanced settings** > and scroll down to **Rename repository** - Adjust your SSG's [base URL](#urls-and-baseurls) to from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likelly, it will be in the SSG's config file. > **Notes:** > ->1. Why do I need to remove the fork relationship? +> Why do I need to remove the fork relationship? > -> Unless you want to contribute to the original project, +> Unless you want to contribute to the original project, you won't need it connected to the upstream. A [fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork) is useful for submitting merge requests to the upstream. -> -> 2. Why do I need to enable Shared Runners? -> -> Shared Runners will run the script set by your GitLab CI/CD -configuration file. They're enabled by default to new projects, -but not to forks. ### Create a project from scratch @@ -108,7 +95,7 @@ where you'll find its default URL. > - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but, if you don't find yours among the templates, you'll need to configure your own `.gitlab-ci.yml`. Do do that, please -read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among +read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among the [example projects](https://gitlab.com/pages). If you set up a new one, please [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md) @@ -121,7 +108,7 @@ you can run `git init` in your local website directory, add the remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`, then add, commit, and push. -### URLs and Baseurls +## URLs and Baseurls Every Static Site Generator (SSG) default configuration expects to find your website under a (sub)domain (`example.com`), not @@ -149,11 +136,7 @@ example we've just mentioned, you'd have to change Jekyll's `_config.yml` to: baseurl: "" ``` -### Custom Domains +## Custom Domains -GitLab Pages supports custom domains and subdomains, served under HTTPS or HTTPS. +GitLab Pages supports custom domains and subdomains, served under HTTP or HTTPS. Please check the [next part](getting_started_part_three.md) of this series for an overview. - -||| -|:--|--:| -|[**← Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Setting Up Custom Domains - DNS Records and SSL/TLS Certificates →**](getting_started_part_three.md)| diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index df245710940..a65aa758198 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -37,10 +37,10 @@ to secure them. Read the following tutorials to know more about: -- [Static websites and GitLab Pages domains](getting_started_part_one.md) -- [Forking projects and creating new ones from scratch, URLs and baseurls](getting_started_part_two.md) -- [Custom domains and subdomains, DNS records, SSL/TLS certificates](getting_started_part_three.md) -- [How to create your own `.gitlab-ci.yml` for your site](getting_started_part_four.md) +- [Static websites and GitLab Pages domains](getting_started_part_one.md): Understand what is a static website, and how GitLab Pages default domains work +- [Projects for GitLab Pages and URL structure](getting_started_part_two.md): Forking projects and creating new ones from scratch, understanding URLs structure and baseurls +- [GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md): How to add custom domains and subdomains to your website, configure DNS records, and SSL/TLS certificates +- [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md): Understand how to create your own `.gitlab-ci.yml` for your site - [Technical aspects, custom 404 pages, limitations](introduction.md) - [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) (outdated) diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index b8f865679a2..dedf102fc37 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -22,6 +22,7 @@ > in the import side is required to map the users, based on email or username. > Otherwise, a supplementary comment is left to mention the original author and > the MRs, notes or issues will be owned by the importer. +> - Control project Import/Export with the [API](../../../api/project_import_export.md). Existing projects running on any GitLab instance or GitLab.com can be exported with all their related data and be moved into a new GitLab instance. diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 8fff3d591fe..377eee69c11 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -34,7 +34,7 @@ Documentation for GitLab instance administrators is under [LFS administration do credentials store is recommended * Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see [troubleshooting](#troubleshooting)) - + >**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication still goes over HTTP, but now the SSH client passes the correct credentials to the Git LFS client, so no action is required by the user. @@ -85,6 +85,8 @@ git lfs fetch master ## File Locking +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35856) in GitLab 10.5. + The first thing to do before using File Locking is to tell Git LFS which kind of files are lockable. The following command will store PNG files in LFS and flag them as lockable: diff --git a/features/project/network_graph.feature b/features/project/network_graph.feature deleted file mode 100644 index 93c884e23c5..00000000000 --- a/features/project/network_graph.feature +++ /dev/null @@ -1,46 +0,0 @@ -Feature: Project Network Graph - Background: - Given I sign in as a user - And I own project "Shop" - And I visit project "Shop" network page - - @javascript - Scenario: I should see project network - Then page should have network graph - And page should select "master" in select box - And page should have "master" on graph - - @javascript - Scenario: I should see project network with 'test' branch - When I visit project network page on branch 'test' - Then page should have 'test' on graph - - @javascript - Scenario: I should switch "branch" and "tag" - When I switch ref to "feature" - Then page should select "feature" in select box - And page should have "feature" on graph - When I switch ref to "v1.0.0" - Then page should select "v1.0.0" in select box - And page should have "v1.0.0" on graph - - @javascript - Scenario: I should looking for a commit by SHA - When I looking for a commit by SHA of "v1.0.0" - Then page should have network graph - And page should select "master" in select box - And page should have "v1.0.0" on graph - - @javascript - Scenario: I should filter selected tag - When I switch ref to "v1.0.0" - Then page should have "v1.0.0" in title - Then page should have content not containing "v1.0.0" - When click "Show only selected branch" checkbox - Then page should only have content from "v1.0.0" - When click "Show only selected branch" checkbox - Then page should have content not containing "v1.0.0" - - Scenario: I should fail to look for a commit - When I look for a commit by ";" - Then I should see non-existent git revision error message diff --git a/features/project/pages.feature b/features/project/pages.feature deleted file mode 100644 index 56e47287b5c..00000000000 --- a/features/project/pages.feature +++ /dev/null @@ -1,87 +0,0 @@ -Feature: Project Pages - Background: - Given I sign in as a user - And I own a project - - Scenario: I cannot navigate to Pages settings if pages enabled - Given pages are disabled - And I visit my project's settings page - Then I should not see the "Pages" tab - - Scenario: I can navigate to Pages settings if pages enabled - Given pages are enabled - And I visit my project's settings page - Then I should see the "Pages" tab - - Scenario: I can see the pages usage if not deployed - Given pages are enabled - When I visit the Project Pages - Then I should see the usage of GitLab Pages - - Scenario: I can access the pages if deployed - Given pages are enabled - And pages are deployed - When I visit the Project Pages - Then I should be able to access the Pages - - Scenario: I should message that domains support is disabled - Given pages are enabled - And pages are deployed - And support for external domains is disabled - When I visit the Project Pages - Then I should see that support for domains is disabled - - Scenario: I should see a new domain button - Given pages are enabled - And pages are exposed on external HTTP address - When I visit the Project Pages - And I should be able to add a New Domain - - Scenario: I should be able to add a new domain - Given pages are enabled - And pages are exposed on external HTTP address - When I visit add a new Pages Domain - And I fill the domain - And I click on "Create New Domain" - Then I should see a new domain added - - Scenario: I should be able to add a new domain for project in group namespace - Given I own a project in some group namespace - And pages are enabled - And pages are exposed on external HTTP address - When I visit add a new Pages Domain - And I fill the domain - And I click on "Create New Domain" - Then I should see a new domain added - - Scenario: I should be denied to add the same domain twice - Given pages are enabled - And pages are exposed on external HTTP address - And pages domain is added - When I visit add a new Pages Domain - And I fill the domain - And I click on "Create New Domain" - Then I should see error message that domain already exists - - Scenario: I should message that certificates support is disabled when trying to add a new domain - Given pages are enabled - And pages are exposed on external HTTP address - And pages domain is added - When I visit add a new Pages Domain - Then I should see that support for certificates is disabled - - Scenario: I should be able to add a new domain with certificate - Given pages are enabled - And pages are exposed on external HTTPS address - When I visit add a new Pages Domain - And I fill the domain - And I fill the certificate and key - And I click on "Create New Domain" - Then I should see a new domain added - - Scenario: I can remove the pages if deployed - Given pages are enabled - And pages are deployed - When I visit the Project Pages - And I click Remove Pages - Then The Pages should get removed diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb deleted file mode 100644 index ba98d861e7b..00000000000 --- a/features/steps/project/network_graph.rb +++ /dev/null @@ -1,116 +0,0 @@ -class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - step 'page should have network graph' do - expect(page).to have_selector ".network-graph" - end - - When 'I visit project "Shop" network page' do - # Stub Graph max_size to speed up test (10 commits vs. 650) - Network::Graph.stub(max_count: 10) - - @project = Project.find_by(name: "Shop") - visit project_network_path(@project, "master") - end - - step "I visit project network page on branch 'test'" do - visit project_network_path(@project, "'test'") - end - - step 'page should select "master" in select box' do - expect(page).to have_selector '.dropdown-menu-toggle', text: "master" - end - - step 'page should select "v1.0.0" in select box' do - expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0" - end - - step 'page should have "master" on graph' do - page.within '.network-graph' do - expect(page).to have_content 'master' - end - end - - step "page should have 'test' on graph" do - page.within '.network-graph' do - expect(page).to have_content "'test'" - end - end - - When 'I switch ref to "feature"' do - first('.js-project-refs-dropdown').click - - page.within '.project-refs-form' do - click_link 'feature' - end - end - - When 'I switch ref to "v1.0.0"' do - first('.js-project-refs-dropdown').click - - page.within '.project-refs-form' do - click_link 'v1.0.0' - end - end - - When 'click "Show only selected branch" checkbox' do - find('#filter_ref').click - end - - step 'page should have content not containing "v1.0.0"' do - page.within '.network-graph' do - expect(page).to have_content 'Change some files' - end - end - - step 'page should have "v1.0.0" in title' do - expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false - end - - step 'page should only have content from "v1.0.0"' do - page.within '.network-graph' do - expect(page).not_to have_content 'Change some files' - end - end - - step 'page should select "feature" in select box' do - expect(page).to have_selector '.dropdown-menu-toggle', text: "feature" - end - - step 'page should select "v1.0.0" in select box' do - expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0" - end - - step 'page should have "feature" on graph' do - page.within '.network-graph' do - expect(page).to have_content 'feature' - end - end - - When 'I looking for a commit by SHA of "v1.0.0"' do - page.within ".network-form" do - fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' - find('button').click - end - sleep 2 - end - - step 'page should have "v1.0.0" on graph' do - page.within '.network-graph' do - expect(page).to have_content 'v1.0.0' - end - end - - When 'I look for a commit by ";"' do - page.within ".network-form" do - fill_in 'extended_sha1', with: ';' - find('button').click - end - end - - step 'I should see non-existent git revision error message' do - expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist." - end -end diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb deleted file mode 100644 index f03630e5a91..00000000000 --- a/features/steps/project/pages.rb +++ /dev/null @@ -1,152 +0,0 @@ -class Spinach::Features::ProjectPages < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - step 'pages are enabled' do - allow(Gitlab.config.pages).to receive(:enabled).and_return(true) - allow(Gitlab.config.pages).to receive(:host).and_return('example.com') - allow(Gitlab.config.pages).to receive(:port).and_return(80) - allow(Gitlab.config.pages).to receive(:https).and_return(false) - end - - step 'pages are disabled' do - allow(Gitlab.config.pages).to receive(:enabled).and_return(false) - end - - step 'I visit the Project Pages' do - visit project_pages_path(@project) - end - - step 'I should see the usage of GitLab Pages' do - expect(page).to have_content('Configure pages') - end - - step 'I should see the "Pages" tab' do - page.within '.nav-sidebar' do - expect(page).to have_link('Pages') - end - end - - step 'I should not see the "Pages" tab' do - page.within '.nav-sidebar' do - expect(page).not_to have_link('Pages') - end - end - - step 'pages are deployed' do - pipeline = @project.pipelines.create(ref: 'HEAD', - sha: @project.commit('HEAD').sha, - source: :push, - protected: false) - - build = build(:ci_build, - project: @project, - pipeline: pipeline, - ref: 'HEAD', - legacy_artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'), - legacy_artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') - ) - - result = ::Projects::UpdatePagesService.new(@project, build).execute - expect(result[:status]).to eq(:success) - end - - step 'I should be able to access the Pages' do - expect(page).to have_content('Access pages') - end - - step 'I should see that support for domains is disabled' do - expect(page).to have_content('Support for domains and certificates is disabled') - end - - step 'support for external domains is disabled' do - allow(Gitlab.config.pages).to receive(:external_http).and_return(nil) - allow(Gitlab.config.pages).to receive(:external_https).and_return(nil) - end - - step 'pages are exposed on external HTTP address' do - allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80']) - allow(Gitlab.config.pages).to receive(:external_https).and_return(nil) - end - - step 'pages are exposed on external HTTPS address' do - allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80']) - allow(Gitlab.config.pages).to receive(:external_https).and_return(['1.1.1.1:443']) - end - - step 'I should be able to add a New Domain' do - expect(page).to have_content('New Domain') - end - - step 'I visit add a new Pages Domain' do - visit new_project_pages_domain_path(@project) - end - - step 'I fill the domain' do - fill_in 'Domain', with: 'my.test.domain.com' - end - - step 'I click on "Create New Domain"' do - click_button 'Create New Domain' - end - - step 'I should see a new domain added' do - expect(page).to have_content('Domains (1)') - expect(page).to have_content('my.test.domain.com') - end - - step 'pages domain is added' do - @project.pages_domains.create!(domain: 'my.test.domain.com') - end - - step 'I should see error message that domain already exists' do - expect(page).to have_content('Domain has already been taken') - end - - step 'I should see that support for certificates is disabled' do - expect(page).to have_content('Support for custom certificates is disabled') - end - - step 'I fill the certificate and key' do - fill_in 'Certificate (PEM)', with: '-----BEGIN CERTIFICATE----- -MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0 -LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ -MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa -SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT -nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w -DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD -VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh -IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ -joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese -5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg -YHi2yesCrOvVXt+lgPTd ------END CERTIFICATE-----' - - fill_in 'Key (PEM)', with: '-----BEGIN PRIVATE KEY----- -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN -SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t -PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB -kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd -j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/ -uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR -5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O -AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K -EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh -Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C -m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH -EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx -63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi -nNp/xedE1YxutQ== ------END PRIVATE KEY-----' - end - - step 'I click Remove Pages' do - click_link 'Remove pages' - end - - step 'The Pages should get removed' do - expect(@project.pages_deployed?).to be_falsey - end -end diff --git a/lib/api/api.rb b/lib/api/api.rb index e953f3d2eca..754549f72f0 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -138,6 +138,7 @@ module API mount ::API::PagesDomains mount ::API::Pipelines mount ::API::PipelineSchedules + mount ::API::ProjectImport mount ::API::ProjectHooks mount ::API::Projects mount ::API::ProjectMilestones diff --git a/lib/api/commits.rb b/lib/api/commits.rb index d83c43ee49b..3d6e78d2d80 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -97,13 +97,16 @@ module API end params do requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + use :pagination end get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do commit = user_project.commit(params[:sha]) not_found! 'Commit' unless commit - present commit.raw_diffs.to_a, with: Entities::Diff + raw_diffs = ::Kaminari.paginate_array(commit.raw_diffs.to_a) + + present paginate(raw_diffs), with: Entities::Diff end desc "Get a commit's comments" do diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 03abc1b95c5..45c737c6c29 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -91,6 +91,13 @@ module API expose :created_at end + class ProjectImportStatus < ProjectIdentity + expose :import_status + + # TODO: Use `expose_nil` once we upgrade the grape-entity gem + expose :import_error, if: lambda { |status, _ops| status.import_error } + end + class BasicProjectDetails < ProjectIdentity include ::API::ProjectsRelationBuilder diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb index d7b613a717e..ba33993d852 100644 --- a/lib/api/pages_domains.rb +++ b/lib/api/pages_domains.rb @@ -2,6 +2,8 @@ module API class PagesDomains < Grape::API include PaginationParams + PAGES_DOMAINS_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(domain: API::NO_SLASH_URL_PART_REGEX) + before do authenticate! end @@ -48,7 +50,7 @@ module API params do requires :id, type: String, desc: 'The ID of a project' end - resource :projects, requirements: { id: %r{[^/]+} } do + resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do before do require_pages_enabled! end @@ -71,7 +73,7 @@ module API params do requires :domain, type: String, desc: 'The domain' end - get ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do + get ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do authorize! :read_pages, user_project present pages_domain, with: Entities::PagesDomain @@ -105,7 +107,7 @@ module API optional :certificate, allow_blank: false, types: [File, String], desc: 'The certificate' optional :key, allow_blank: false, types: [File, String], desc: 'The key' end - put ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do + put ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do authorize! :update_pages, user_project pages_domain_params = declared(params, include_parent_namespaces: false) @@ -126,7 +128,7 @@ module API params do requires :domain, type: String, desc: 'The domain' end - delete ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do + delete ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do authorize! :update_pages, user_project status 204 diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb new file mode 100644 index 00000000000..a509c1f32c1 --- /dev/null +++ b/lib/api/project_import.rb @@ -0,0 +1,69 @@ +module API + class ProjectImport < Grape::API + include PaginationParams + + helpers do + def import_params + declared_params(include_missing: false) + end + + def file_is_valid? + import_params[:file] && import_params[:file]['tempfile'].respond_to?(:read) + end + + def validate_file! + render_api_error!('The file is invalid', 400) unless file_is_valid? + end + end + + before do + forbidden! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') + end + + resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do + params do + requires :path, type: String, desc: 'The new project path and name' + requires :file, type: File, desc: 'The project export file to be imported' + optional :namespace, type: String, desc: "The ID or name of the namespace that the project will be imported into. Defaults to the current user's namespace." + end + desc 'Create a new project import' do + detail 'This feature was introduced in GitLab 10.6.' + success Entities::ProjectImportStatus + end + post 'import' do + validate_file! + + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42437') + + namespace = if import_params[:namespace] + find_namespace!(import_params[:namespace]) + else + current_user.namespace + end + + project_params = { + path: import_params[:path], + namespace_id: namespace.id, + file: import_params[:file]['tempfile'] + } + + project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute + + render_api_error!(project.errors.full_messages&.first, 400) unless project.saved? + + present project, with: Entities::ProjectImportStatus + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + desc 'Get a project export status' do + detail 'This feature was introduced in GitLab 10.6.' + success Entities::ProjectImportStatus + end + get ':id/import' do + present user_project, with: Entities::ProjectImportStatus + end + end + end +end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index e90892a90f7..b552b0e0c5d 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -271,6 +271,7 @@ module API [ :jobs_enabled, :resolve_outdated_diff_discussions, + :ci_config_path, :container_registry_enabled, :default_branch, :description, diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index ee7f4be6b9f..62c41801d75 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -8,7 +8,8 @@ module Gitlab module Asciidoc DEFAULT_ADOC_ATTRS = [ 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', - 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font' + 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font', + 'outfilesuffix=.adoc' ].freeze # Public: Converts the provided Asciidoc markup into HTML. diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb index ee55fabd6f0..9232f20a063 100644 --- a/lib/gitlab/background_migration/populate_untracked_uploads.rb +++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb @@ -5,157 +5,10 @@ module Gitlab # This class processes a batch of rows in `untracked_files_for_uploads` by # adding each file to the `uploads` table if it does not exist. class PopulateUntrackedUploads # rubocop:disable Metrics/ClassLength - # This class is responsible for producing the attributes necessary to - # track an uploaded file in the `uploads` table. - class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength - self.table_name = 'untracked_files_for_uploads' - - # Ends with /:random_hex/:filename - FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z} - FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/ - - # These regex patterns are tested against a relative path, relative to - # the upload directory. - # For convenience, if there exists a capture group in the pattern, then - # it indicates the model_id. - PATH_PATTERNS = [ - { - pattern: %r{\A-/system/appearance/logo/(\d+)/}, - uploader: 'AttachmentUploader', - model_type: 'Appearance' - }, - { - pattern: %r{\A-/system/appearance/header_logo/(\d+)/}, - uploader: 'AttachmentUploader', - model_type: 'Appearance' - }, - { - pattern: %r{\A-/system/note/attachment/(\d+)/}, - uploader: 'AttachmentUploader', - model_type: 'Note' - }, - { - pattern: %r{\A-/system/user/avatar/(\d+)/}, - uploader: 'AvatarUploader', - model_type: 'User' - }, - { - pattern: %r{\A-/system/group/avatar/(\d+)/}, - uploader: 'AvatarUploader', - model_type: 'Namespace' - }, - { - pattern: %r{\A-/system/project/avatar/(\d+)/}, - uploader: 'AvatarUploader', - model_type: 'Project' - }, - { - pattern: FILE_UPLOADER_PATH, - uploader: 'FileUploader', - model_type: 'Project' - } - ].freeze - - def to_h - @upload_hash ||= { - path: upload_path, - uploader: uploader, - model_type: model_type, - model_id: model_id, - size: file_size, - checksum: checksum - } - end - - def upload_path - # UntrackedFile#path is absolute, but Upload#path depends on uploader - @upload_path ||= - if uploader == 'FileUploader' - # Path relative to project directory in uploads - matchd = path_relative_to_upload_dir.match(FILE_UPLOADER_PATH) - matchd[0].sub(%r{\A/}, '') # remove leading slash - else - path - end - end - - def uploader - matching_pattern_map[:uploader] - end - - def model_type - matching_pattern_map[:model_type] - end - - def model_id - return @model_id if defined?(@model_id) - - pattern = matching_pattern_map[:pattern] - matchd = path_relative_to_upload_dir.match(pattern) - - # If something is captured (matchd[1] is not nil), it is a model_id - # Only the FileUploader pattern will not match an ID - @model_id = matchd[1] ? matchd[1].to_i : file_uploader_model_id - end - - def file_size - File.size(absolute_path) - end - - def checksum - Digest::SHA256.file(absolute_path).hexdigest - end - - private - - def matching_pattern_map - @matching_pattern_map ||= PATH_PATTERNS.find do |path_pattern_map| - path_relative_to_upload_dir.match(path_pattern_map[:pattern]) - end - - unless @matching_pattern_map - raise "Unknown upload path pattern \"#{path}\"" - end - - @matching_pattern_map - end - - def file_uploader_model_id - matchd = path_relative_to_upload_dir.match(FULL_PATH_CAPTURE) - not_found_msg = <<~MSG - Could not capture project full_path from a FileUploader path: - "#{path_relative_to_upload_dir}" - MSG - raise not_found_msg unless matchd - - full_path = matchd[1] - project = Project.find_by_full_path(full_path) - return nil unless project - - project.id - end - - # Not including a leading slash - def path_relative_to_upload_dir - upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength - base = %r{\A#{Regexp.escape(upload_dir)}/} - @path_relative_to_upload_dir ||= path.sub(base, '') - end - - def absolute_path - File.join(Gitlab.config.uploads.storage_path, path) - end - end - - # This class is used to query the `uploads` table. - class Upload < ActiveRecord::Base - self.table_name = 'uploads' - end - def perform(start_id, end_id) return unless migrate? - files = UntrackedFile.where(id: start_id..end_id) + files = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.where(id: start_id..end_id) processed_files = insert_uploads_if_needed(files) processed_files.delete_all @@ -165,7 +18,8 @@ module Gitlab private def migrate? - UntrackedFile.table_exists? && Upload.table_exists? + Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.table_exists? && + Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Upload.table_exists? end def insert_uploads_if_needed(files) @@ -197,7 +51,7 @@ module Gitlab def filter_existing_uploads(files) paths = files.map(&:upload_path) - existing_paths = Upload.where(path: paths).pluck(:path).to_set + existing_paths = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Upload.where(path: paths).pluck(:path).to_set files.reject do |file| existing_paths.include?(file.upload_path) @@ -229,7 +83,7 @@ module Gitlab end ids.each do |model_type, model_ids| - model_class = Object.const_get(model_type) + model_class = "Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::#{model_type}".constantize found_ids = model_class.where(id: model_ids.uniq).pluck(:id) deleted_ids = ids[model_type] - found_ids ids[model_type] = deleted_ids @@ -249,8 +103,8 @@ module Gitlab end def drop_temp_table_if_finished - if UntrackedFile.all.empty? && !Rails.env.test? # Dropping a table intermittently breaks test cleanup - UntrackedFile.connection.drop_table(:untracked_files_for_uploads, + if Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.all.empty? && !Rails.env.test? # Dropping a table intermittently breaks test cleanup + Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.connection.drop_table(:untracked_files_for_uploads, if_exists: true) end end diff --git a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb new file mode 100644 index 00000000000..a2c5acbde71 --- /dev/null +++ b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb @@ -0,0 +1,201 @@ +# frozen_string_literal: true +module Gitlab + module BackgroundMigration + module PopulateUntrackedUploadsDependencies + # This class is responsible for producing the attributes necessary to + # track an uploaded file in the `uploads` table. + class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength + self.table_name = 'untracked_files_for_uploads' + + # Ends with /:random_hex/:filename + FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z} + FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/ + + # These regex patterns are tested against a relative path, relative to + # the upload directory. + # For convenience, if there exists a capture group in the pattern, then + # it indicates the model_id. + PATH_PATTERNS = [ + { + pattern: %r{\A-/system/appearance/logo/(\d+)/}, + uploader: 'AttachmentUploader', + model_type: 'Appearance' + }, + { + pattern: %r{\A-/system/appearance/header_logo/(\d+)/}, + uploader: 'AttachmentUploader', + model_type: 'Appearance' + }, + { + pattern: %r{\A-/system/note/attachment/(\d+)/}, + uploader: 'AttachmentUploader', + model_type: 'Note' + }, + { + pattern: %r{\A-/system/user/avatar/(\d+)/}, + uploader: 'AvatarUploader', + model_type: 'User' + }, + { + pattern: %r{\A-/system/group/avatar/(\d+)/}, + uploader: 'AvatarUploader', + model_type: 'Namespace' + }, + { + pattern: %r{\A-/system/project/avatar/(\d+)/}, + uploader: 'AvatarUploader', + model_type: 'Project' + }, + { + pattern: FILE_UPLOADER_PATH, + uploader: 'FileUploader', + model_type: 'Project' + } + ].freeze + + def to_h + @upload_hash ||= { + path: upload_path, + uploader: uploader, + model_type: model_type, + model_id: model_id, + size: file_size, + checksum: checksum + } + end + + def upload_path + # UntrackedFile#path is absolute, but Upload#path depends on uploader + @upload_path ||= + if uploader == 'FileUploader' + # Path relative to project directory in uploads + matchd = path_relative_to_upload_dir.match(FILE_UPLOADER_PATH) + matchd[0].sub(%r{\A/}, '') # remove leading slash + else + path + end + end + + def uploader + matching_pattern_map[:uploader] + end + + def model_type + matching_pattern_map[:model_type] + end + + def model_id + return @model_id if defined?(@model_id) + + pattern = matching_pattern_map[:pattern] + matchd = path_relative_to_upload_dir.match(pattern) + + # If something is captured (matchd[1] is not nil), it is a model_id + # Only the FileUploader pattern will not match an ID + @model_id = matchd[1] ? matchd[1].to_i : file_uploader_model_id + end + + def file_size + File.size(absolute_path) + end + + def checksum + Digest::SHA256.file(absolute_path).hexdigest + end + + private + + def matching_pattern_map + @matching_pattern_map ||= PATH_PATTERNS.find do |path_pattern_map| + path_relative_to_upload_dir.match(path_pattern_map[:pattern]) + end + + unless @matching_pattern_map + raise "Unknown upload path pattern \"#{path}\"" + end + + @matching_pattern_map + end + + def file_uploader_model_id + matchd = path_relative_to_upload_dir.match(FULL_PATH_CAPTURE) + not_found_msg = <<~MSG + Could not capture project full_path from a FileUploader path: + "#{path_relative_to_upload_dir}" + MSG + raise not_found_msg unless matchd + + full_path = matchd[1] + project = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Project.find_by_full_path(full_path) + return nil unless project + + project.id + end + + # Not including a leading slash + def path_relative_to_upload_dir + upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength + base = %r{\A#{Regexp.escape(upload_dir)}/} + @path_relative_to_upload_dir ||= path.sub(base, '') + end + + def absolute_path + File.join(Gitlab.config.uploads.storage_path, path) + end + end + + # Avoid using application code + class Upload < ActiveRecord::Base + self.table_name = 'uploads' + end + + # Avoid using application code + class Appearance < ActiveRecord::Base + self.table_name = 'appearances' + end + + # Avoid using application code + class Namespace < ActiveRecord::Base + self.table_name = 'namespaces' + end + + # Avoid using application code + class Note < ActiveRecord::Base + self.table_name = 'notes' + end + + # Avoid using application code + class User < ActiveRecord::Base + self.table_name = 'users' + end + + # Since project Markdown upload paths don't contain the project ID, we have to find the + # project by its full_path. Due to MySQL/PostgreSQL differences, and historical reasons, + # the logic is somewhat complex, so I've mostly copied it in here. + class Project < ActiveRecord::Base + self.table_name = 'projects' + + def self.find_by_full_path(path) + binary = Gitlab::Database.mysql? ? 'BINARY' : '' + order_sql = "(CASE WHEN #{binary} routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)" + where_full_path_in(path).reorder(order_sql).take + end + + def self.where_full_path_in(path) + cast_lower = Gitlab::Database.postgresql? + + path = connection.quote(path) + + where = + if cast_lower + "(LOWER(routes.path) = LOWER(#{path}))" + else + "(routes.path = #{path})" + end + + joins("INNER JOIN routes ON routes.source_id = projects.id AND routes.source_type = 'Project'").where(where) + end + end + end + end +end diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb index 521680b8708..3ce5f807989 100644 --- a/lib/gitlab/checks/change_access.rb +++ b/lib/gitlab/checks/change_access.rb @@ -121,6 +121,7 @@ module Gitlab def commits_check return if deletion? || newrev.nil? + return unless should_run_commit_validations? # n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593 ::Gitlab::GitalyClient.allow_n_plus_1_calls do @@ -139,6 +140,10 @@ module Gitlab private + def should_run_commit_validations? + commit_check.validate_lfs_file_locks? + end + def updated_from_web? protocol == 'web' end @@ -176,7 +181,7 @@ module Gitlab end def commits - project.repository.new_commits(newrev) + @commits ||= project.repository.new_commits(newrev) end end end diff --git a/lib/gitlab/checks/commit_check.rb b/lib/gitlab/checks/commit_check.rb index ae0cd142378..43a52b493bb 100644 --- a/lib/gitlab/checks/commit_check.rb +++ b/lib/gitlab/checks/commit_check.rb @@ -35,14 +35,14 @@ module Gitlab end end - private - def validate_lfs_file_locks? strong_memoize(:validate_lfs_file_locks) do project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev end end + private + def lfs_file_locks_validation lambda do |paths| lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index d95561fe1b2..ae27a138b7c 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -508,7 +508,7 @@ module Gitlab @committed_date = Time.at(commit.committer.date.seconds).utc @committer_name = commit.committer.name.dup @committer_email = commit.committer.email.dup - @parent_ids = commit.parent_ids + @parent_ids = Array(commit.parent_ids) end def serialize_keys diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 5f014e43c6f..e3cbf017e55 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1234,7 +1234,13 @@ module Gitlab end def squash_in_progress?(squash_id) - fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)) + gitaly_migrate(:squash_in_progress) do |is_enabled| + if is_enabled + gitaly_repository_client.squash_in_progress?(squash_id) + else + fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id)) + end + end end def push_remote_branches(remote_name, branch_names, forced: true) @@ -1349,7 +1355,7 @@ module Gitlab if is_enabled gitaly_ref_client.branch_names_contains_sha(sha) else - refs_contains_sha(:branch, sha) + refs_contains_sha('refs/heads/', sha) end end end @@ -1359,7 +1365,7 @@ module Gitlab if is_enabled gitaly_ref_client.tag_names_contains_sha(sha) else - refs_contains_sha(:tag, sha) + refs_contains_sha('refs/tags/', sha) end end end @@ -1458,19 +1464,25 @@ module Gitlab end end - def refs_contains_sha(ref_type, sha) - args = %W(#{ref_type} --contains #{sha}) - names = run_git(args).first + def refs_contains_sha(refs_prefix, sha) + refs_prefix << "/" unless refs_prefix.ends_with?('/') + + # By forcing the output to %(refname) each line wiht a ref will start with + # the ref prefix. All other lines can be discarded. + args = %W(for-each-ref --contains=#{sha} --format=%(refname) #{refs_prefix}) + names, code = run_git(args) - return [] unless names.respond_to?(:split) + return [] unless code.zero? - names = names.split("\n").map(&:strip) + refs = [] + left_slice_count = refs_prefix.length + names.lines.each do |line| + next unless line.start_with?(refs_prefix) - names.each do |name| - name.slice! '* ' + refs << line.rstrip[left_slice_count..-1] end - names + refs end def rugged_write_config(full_path:) @@ -2188,13 +2200,14 @@ module Gitlab ) diff_range = "#{start_sha}...#{end_sha}" diff_files = run_git!( - %W(diff --name-only --diff-filter=a --binary #{diff_range}) + %W(diff --name-only --diff-filter=ar --binary #{diff_range}) ).chomp with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do # Apply diff of the `diff_range` to the worktree diff = run_git!(%W(diff --binary #{diff_range})) run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin| + stdin.binmode stdin.write(diff) end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 9ec3858b493..bbdb593d4e2 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -198,7 +198,7 @@ module Gitlab end def check_repository_existence! - unless project.repository.exists? + unless repository.exists? raise UnauthorizedError, ERROR_MESSAGES[:no_repo] end end @@ -327,5 +327,9 @@ module Gitlab def push_to_read_only_message ERROR_MESSAGES[:cannot_push_to_read_only] end + + def repository + project.repository + end end end diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb index 84d6e1490c3..a5b3902ebf4 100644 --- a/lib/gitlab/git_access_wiki.rb +++ b/lib/gitlab/git_access_wiki.rb @@ -28,5 +28,11 @@ module Gitlab def push_to_read_only_message ERROR_MESSAGES[:read_only] end + + private + + def repository + project.wiki.repository + end end end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 60706b4f0d8..603457d0664 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -134,6 +134,23 @@ module Gitlab response.in_progress end + def squash_in_progress?(squash_id) + request = Gitaly::IsSquashInProgressRequest.new( + repository: @gitaly_repo, + squash_id: squash_id.to_s + ) + + response = GitalyClient.call( + @storage, + :repository_service, + :is_squash_in_progress, + request, + timeout: GitalyClient.default_timeout + ) + + response.in_progress + end + def fetch_source_branch(source_repository, source_branch, local_ref) request = Gitaly::FetchSourceBranchRequest.new( repository: @gitaly_repo, diff --git a/lib/gitlab/github_import/importer/repository_importer.rb b/lib/gitlab/github_import/importer/repository_importer.rb index 7dd68a0d1cd..ab0b751fe24 100644 --- a/lib/gitlab/github_import/importer/repository_importer.rb +++ b/lib/gitlab/github_import/importer/repository_importer.rb @@ -63,6 +63,7 @@ module Gitlab true rescue Gitlab::Shell::Error => e if e.message !~ /repository not exported/ + project.create_wiki fail_import("Failed to import the wiki: #{e.message}") else true diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb index 672b5579dfd..90dd569aaf8 100644 --- a/lib/gitlab/gpg/commit.rb +++ b/lib/gitlab/gpg/commit.rb @@ -60,7 +60,9 @@ module Gitlab def create_cached_signature! using_keychain do |gpg_key| - GpgSignature.create!(attributes(gpg_key)) + signature = GpgSignature.new(attributes(gpg_key)) + signature.save! unless Gitlab::Database.read_only? + signature end end diff --git a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb index 5980a4ded2b..db8bdde74b2 100644 --- a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb +++ b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb @@ -23,7 +23,7 @@ module Gitlab end def stop_working - server.shutdown + server.shutdown if server @server = nil end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 45b9e14ba55..f3e48083c19 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -73,7 +73,7 @@ module Gitlab # event_name - The name of the event (e.g. "git_push"). # tags - A set of tags to attach to the event. def add_event(event_name, tags = {}) - self.class.transaction_metric(event_name, :counter, prefix: 'event_', tags: tags).increment(tags.merge(labels)) + self.class.transaction_metric(event_name, :counter, prefix: 'event_', use_feature_flag: true, tags: tags).increment(tags.merge(labels)) @metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event) end @@ -150,11 +150,12 @@ module Gitlab with_feature :prometheus_metrics_transaction_allocated_memory end - def self.transaction_metric(name, type, prefix: nil, tags: {}) + def self.transaction_metric(name, type, prefix: nil, use_feature_flag: false, tags: {}) metric_name = "gitlab_transaction_#{prefix}#{name}_total".to_sym fetch_metric(type, metric_name) do docstring "Transaction #{prefix}#{name} #{type}" base_labels tags.merge(BASE_LABELS) + with_feature "prometheus_transaction_#{prefix}#{name}_total".to_sym if use_feature_flag if type == :gauge multiprocess_mode :livesum diff --git a/lib/gitlab/query_limiting/active_support_subscriber.rb b/lib/gitlab/query_limiting/active_support_subscriber.rb index 66049c94ec6..4c83581c4b1 100644 --- a/lib/gitlab/query_limiting/active_support_subscriber.rb +++ b/lib/gitlab/query_limiting/active_support_subscriber.rb @@ -3,8 +3,10 @@ module Gitlab class ActiveSupportSubscriber < ActiveSupport::Subscriber attach_to :active_record - def sql(*) - Transaction.current&.increment + def sql(event) + unless event.payload[:name] == 'CACHE' + Transaction.current&.increment + end end end end diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb index e90a90508a2..07d7c91cb5d 100644 --- a/lib/gitlab/setup_helper.rb +++ b/lib/gitlab/setup_helper.rb @@ -37,7 +37,7 @@ module Gitlab config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path } config[:bin_dir] = Gitlab.config.gitaly.client_path - TOML.dump(config) + TomlRB.dump(config) end # rubocop:disable Rails/Output diff --git a/lib/gitlab/ssh_public_key.rb b/lib/gitlab/ssh_public_key.rb index 89ca1298120..6f63ea91ae8 100644 --- a/lib/gitlab/ssh_public_key.rb +++ b/lib/gitlab/ssh_public_key.rb @@ -21,6 +21,22 @@ module Gitlab technology(name)&.supported_sizes end + def self.sanitize(key_content) + ssh_type, *parts = key_content.strip.split + + return key_content if parts.empty? + + parts.each_with_object("#{ssh_type} ").with_index do |(part, content), index| + content << part + + if Gitlab::SSHPublicKey.new(content).valid? + break [content, parts[index + 1]].compact.join(' ') # Add the comment part if present + elsif parts.size == index + 1 # return original content if we've reached the last element + break key_content + end + end + end + attr_reader :key_text, :key # Unqualified MD5 fingerprint for compatibility @@ -37,23 +53,23 @@ module Gitlab end def valid? - key.present? + SSHKey.valid_ssh_public_key?(key_text) end def type - technology.name if valid? + technology.name if key.present? end def bits - return unless valid? + return if key.blank? case type when :rsa - key.n.num_bits + key.n&.num_bits when :dsa - key.p.num_bits + key.p&.num_bits when :ecdsa - key.group.order.num_bits + key.group.order&.num_bits when :ed25519 256 else diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index 107ff1d8aeb..e9ca6404fe8 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -2,7 +2,7 @@ namespace :gitlab do namespace :gitaly do desc "GitLab | Install or upgrade gitaly" task :install, [:dir, :repo] => :gitlab_environment do |t, args| - require 'toml' + require 'toml-rb' warn_user_is_not_gitlab @@ -38,7 +38,7 @@ namespace :gitlab do desc "GitLab | Print storage configuration in TOML format" task storage_config: :environment do - require 'toml' + require 'toml-rb' puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}" puts "# This is in TOML format suitable for use in Gitaly's config.toml file." diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake index 3ab406eff2c..fe5032cae18 100644 --- a/lib/tasks/lint.rake +++ b/lib/tasks/lint.rake @@ -16,5 +16,54 @@ unless Rails.env.production? task :javascript do Rake::Task['eslint'].invoke end + + desc "GitLab | lint | Run several lint checks" + task :all do + status = 0 + + %w[ + config_lint + haml_lint + scss_lint + flay + gettext:lint + lint:static_verification + ].each do |task| + pid = Process.fork do + rd, wr = IO.pipe + stdout = $stdout.dup + stderr = $stderr.dup + $stdout.reopen(wr) + $stderr.reopen(wr) + + begin + begin + Rake::Task[task].invoke + rescue RuntimeError # The haml_lint tasks raise a RuntimeError + exit(1) + end + rescue SystemExit => ex + msg = "*** Rake task #{task} failed with the following error(s):" + raise ex + ensure + $stdout.reopen(stdout) + $stderr.reopen(stderr) + wr.close + + if msg + warn "\n#{msg}\n\n" + IO.copy_stream(rd, $stderr) + else + IO.copy_stream(rd, $stdout) + end + end + end + + Process.waitpid(pid) + status += $?.exitstatus + end + + exit(status) + end end end diff --git a/package.json b/package.json index 4eccd51a3a6..043af80a3be 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.0", - "babel-eslint": "^8.0.2", "babel-loader": "^7.1.2", "babel-plugin-transform-define": "^1.3.0", "babel-preset-latest": "^6.24.1", @@ -24,13 +23,14 @@ "blackst0ne-mermaid": "^7.1.0-fixed", "bootstrap-sass": "^3.3.6", "brace-expansion": "^1.1.8", + "chart.js": "1.0.2", "classlist-polyfill": "^1.2.0", "clipboard": "^1.7.1", - "compression-webpack-plugin": "^1.0.0", - "copy-webpack-plugin": "^4.0.1", + "compression-webpack-plugin": "^1.1.7", + "copy-webpack-plugin": "^4.4.1", "core-js": "^2.4.1", "cropper": "^2.3.0", - "css-loader": "^0.28.0", + "css-loader": "^0.28.9", "d3-array": "^1.2.1", "d3-axis": "^1.0.8", "d3-brush": "^1.0.4", @@ -44,13 +44,13 @@ "document-register-element": "1.3.0", "dropzone": "^4.2.0", "emoji-unicode-version": "^0.2.1", - "exports-loader": "^0.6.4", - "file-loader": "^0.11.1", + "exports-loader": "^0.7.0", + "file-loader": "^1.1.8", "fuzzaldrin-plus": "^0.5.0", "glob": "^7.1.2", - "imports-loader": "^0.7.1", + "imports-loader": "^0.8.0", "jed": "^1.1.1", - "jquery": "^2.2.4", + "jquery": "^3.2.1", "jquery-ujs": "1.2.2", "jquery.waitforimages": "^2.2.0", "js-cookie": "^2.1.3", @@ -66,33 +66,34 @@ "raphael": "^2.2.7", "raven-js": "^3.22.1", "raw-loader": "^0.5.1", - "react-dev-utils": "^0.5.2", + "react-dev-utils": "^5.0.0", "sanitize-html": "^1.16.1", "select2": "3.5.2-browserify", "sql.js": "^0.4.0", - "style-loader": "^0.19.1", + "style-loader": "^0.20.2", "svg4everybody": "2.1.9", "three": "^0.84.0", "three-orbit-controls": "^82.1.0", "three-stl-loader": "^1.0.4", "timeago.js": "^3.0.2", "underscore": "^1.8.3", - "url-loader": "^0.5.8", + "url-loader": "^0.6.2", "visibilityjs": "^1.2.4", "vue": "^2.5.13", - "vue-loader": "^13.7.0", + "vue-loader": "^14.1.1", "vue-resource": "^1.3.5", "vue-router": "^3.0.1", "vue-template-compiler": "^2.5.13", "vuex": "^3.0.1", - "webpack": "^3.5.5", - "webpack-bundle-analyzer": "^2.8.2", + "webpack": "^3.11.0", + "webpack-bundle-analyzer": "^2.10.0", "webpack-stats-plugin": "^0.1.5", "worker-loader": "^1.1.0" }, "devDependencies": { "@gitlab-org/gitlab-svgs": "^1.8.0", "axios-mock-adapter": "^1.10.0", + "babel-eslint": "^8.0.2", "babel-plugin-istanbul": "^4.1.5", "eslint": "^3.18.0", "eslint-config-airbnb-base": "^10.0.1", @@ -108,13 +109,13 @@ "jasmine-jquery": "^2.1.1", "karma": "^2.0.0", "karma-chrome-launcher": "^2.2.0", - "karma-coverage-istanbul-reporter": "^1.3.3", + "karma-coverage-istanbul-reporter": "^1.4.1", "karma-jasmine": "^1.1.1", "karma-mocha-reporter": "^2.2.5", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "2.0.7", - "nodemon": "^1.11.0", + "nodemon": "^1.15.1", "prettier": "1.9.2", - "webpack-dev-server": "^2.6.1" + "webpack-dev-server": "^2.11.1" } } diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb index af0fa8af2df..c734d739b4a 100644 --- a/qa/qa/factory/resource/secret_variable.rb +++ b/qa/qa/factory/resource/secret_variable.rb @@ -4,18 +4,6 @@ module QA class SecretVariable < Factory::Base attr_accessor :key, :value - product :key do - Page::Project::Settings::CICD.act do - expand_secret_variables(&:variable_key) - end - end - - product :value do - Page::Project::Settings::CICD.act do - expand_secret_variables(&:variable_value) - end - end - dependency Factory::Resource::Project, as: :project do |project| project.name = 'project-with-secret-variables' project.description = 'project for adding secret variable test' diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 7924479e2a1..a313d46205d 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -98,6 +98,10 @@ module QA views.map(&:errors).flatten end + def self.elements + views.map(&:elements).flatten + end + class DSL attr_reader :views diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb index fea4acb389a..c95c79f137d 100644 --- a/qa/qa/page/project/settings/secret_variables.rb +++ b/qa/qa/page/project/settings/secret_variables.rb @@ -6,39 +6,37 @@ module QA include Common view 'app/views/ci/variables/_variable_row.html.haml' do + element :variable_row, '.ci-variable-row-body' element :variable_key, '.js-ci-variable-input-key' element :variable_value, '.js-ci-variable-input-value' + element :key_placeholder, 'Input variable key' + element :value_placeholder, 'Input variable value' end view 'app/views/ci/variables/_index.html.haml' do element :save_variables, '.js-secret-variables-save-button' + element :reveal_values, '.js-secret-value-reveal-button' end def fill_variable_key(key) - page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do - page.find('.js-ci-variable-input-key').set(key) - end + fill_in('Input variable key', with: key, match: :first) end def fill_variable_value(value) - page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do - page.find('.js-ci-variable-input-value').set(value) - end + fill_in('Input variable value', with: value, match: :first) end def save_variables - click_button('Save variables') + find('.js-secret-variables-save-button').click end - def variable_key - page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do - page.find('.js-ci-variable-input-key').value - end + def reveal_variables + find('.js-secret-value-reveal-button').click end - def variable_value - page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do - page.find('.js-ci-variable-input-value').value + def variable_value(key) + within('.ci-variable-row-body', text: key) do + find('.js-ci-variable-input-value').value end end end diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb index 36422a92afc..d1bf7849bd0 100644 --- a/qa/qa/specs/features/project/add_secret_variable_spec.rb +++ b/qa/qa/specs/features/project/add_secret_variable_spec.rb @@ -4,16 +4,21 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - variable_key = 'VARIABLE_KEY' - variable_value = 'variable value' - - variable = Factory::Resource::SecretVariable.fabricate! do |resource| - resource.key = variable_key - resource.value = variable_value + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.key = 'VARIABLE_KEY' + resource.value = 'some secret variable' end - expect(variable.key).to eq(variable_key) - expect(variable.value).to eq(variable_value) + Page::Project::Settings::CICD.perform do |settings| + settings.expand_secret_variables do |page| + expect(page).to have_field(with: 'VARIABLE_KEY') + expect(page).not_to have_field(with: 'some secret variable') + + page.reveal_variables + + expect(page).to have_field(with: 'some secret variable') + end + end end end end diff --git a/qa/spec/page/base_spec.rb b/qa/spec/page/base_spec.rb index 287adf35c46..52daa9697ee 100644 --- a/qa/spec/page/base_spec.rb +++ b/qa/spec/page/base_spec.rb @@ -14,7 +14,7 @@ describe QA::Page::Base do end view 'path/to/some/_partial.html.haml' do - element :something, 'string pattern' + element :another_element, 'string pattern' end end end @@ -25,11 +25,10 @@ describe QA::Page::Base do end it 'populates views objects with data about elements' do - subject.views.first.elements.tap do |elements| - expect(elements.size).to eq 2 - expect(elements).to all(be_an_instance_of QA::Page::Element) - expect(elements.map(&:name)).to eq [:something, :something_else] - end + expect(subject.elements.size).to eq 3 + expect(subject.elements).to all(be_an_instance_of QA::Page::Element) + expect(subject.elements.map(&:name)) + .to eq [:something, :something_else, :another_element] end end diff --git a/scripts/codequality b/scripts/codequality new file mode 100755 index 00000000000..2f3ccef7d2d --- /dev/null +++ b/scripts/codequality @@ -0,0 +1,19 @@ +#!/bin/sh + +set -eo pipefail + +code_path=$(pwd) + +# docker run --tty will merge stderr and stdout, we don't need this on CI or +# it will break codequality json file +[ "$CI" != "" ] || docker_tty="--tty" + +# The codebase and instructions for the following image can be found at https://gitlab.com/gitlab-org/codeclimate-rubocop/wikis/home +docker pull dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-rubocop-0-52-1 > /dev/null +docker tag dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-rubocop-0-52-1 codeclimate/codeclimate-rubocop:gitlab-codeclimate-rubocop-0-52-1 > /dev/null + +exec docker run --rm $docker_tty --env CODECLIMATE_CODE="$code_path" \ + --volume "$code_path":/code \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + --volume /tmp/cc:/tmp/cc \ + "codeclimate/codeclimate:${CODECLIMATE_VERSION:-0.71.1}" "$@" diff --git a/scripts/static-analysis b/scripts/static-analysis index bdb88f3cb57..0e67eabfec1 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -7,7 +7,7 @@ require_relative '../lib/gitlab/popen/runner' def emit_warnings(static_analysis) static_analysis.warned_results.each do |result| puts - puts "**** #{result.cmd.join(' ')} had the following warnings:" + puts "**** #{result.cmd.join(' ')} had the following warning(s):" puts puts result.stderr puts @@ -17,7 +17,7 @@ end def emit_errors(static_analysis) static_analysis.failed_results.each do |result| puts - puts "**** #{result.cmd.join(' ')} failed with the following error:" + puts "**** #{result.cmd.join(' ')} failed with the following error(s):" puts puts result.stdout puts result.stderr @@ -26,15 +26,10 @@ def emit_errors(static_analysis) end tasks = [ - %w[bundle exec rake config_lint], - %w[bundle exec rake flay], - %w[bundle exec rake haml_lint], - %w[bundle exec rake scss_lint], + %w[bin/rake lint:all], %w[bundle exec license_finder], %w[yarn run eslint], %w[bundle exec rubocop --parallel], - %w[bundle exec rake gettext:lint], - %w[bundle exec rake lint:static_verification], %w[scripts/lint-conflicts.sh], %w[scripts/lint-rugged] ] diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb index d7825364ed5..c1f42bbb9d7 100644 --- a/spec/controllers/concerns/issuable_collections_spec.rb +++ b/spec/controllers/concerns/issuable_collections_spec.rb @@ -8,6 +8,10 @@ describe IssuableCollections do def self.helper_method(name); end include IssuableCollections + + def finder_type + IssuesFinder + end end controller = klass.new diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb index f0c43f3d6f5..3f0c60f32b7 100644 --- a/spec/factories/keys.rb +++ b/spec/factories/keys.rb @@ -5,6 +5,10 @@ FactoryBot.define do title key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com' } + factory :key_without_comment do + key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate } + end + factory :deploy_key, class: 'DeployKey' factory :personal_key do @@ -18,38 +22,104 @@ FactoryBot.define do factory :rsa_key_2048 do key do <<~KEY.delete("\n") - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9 - 6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5 - /jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7 - M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC - rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0 - 5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC98dbu7gxcbmAvwMqz/6AALhSr1jiX + G0UC8FQMvoDt+ciB+uSJhg7KlxinKjYJnPGfhX+q2K+mmCGAmI/D6q7rFxE+bn09O+75 + qgkTHi+suDVE6KG7L3n0alGd/qSevfomR77Snh6fQPdG6sEAZz3kehcpfVnq5/IuLFq9 + FBrgmu52Jd4XZLQZKkDq6zYOJ69FUkGf93LZIV/OOaS+f+qkOGPCUkdKl7oEcgpVNY9S + RjBCduXnvi2CyQnnJVkBguGL5VlXwFXH+17Whs7oFWmdiG+4jzBRLIMz4EuIW09b8Su5 + PW6+bBuXOifHA8KG5TMmjs5LYdCMPFnhTyDyO3a1 dummy@gitlab.com KEY end factory :rsa_deploy_key_2048, class: 'DeployKey' end + factory :rsa_key_4096 do + key do + <<~KEY.delete("\n") + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGSD77lLtjmzewiBs6nu2R5nu6oNkrA + kH/0co1fHHosKfRr+sWkSTKXOVcL7bhRu+tniGBmB5pn+i1qX7BXtrcnv//bCXWIp+me0 + 27L4RJa5/Ep077iiTJlzTpcV664xNUXC8mzBr601HR/Z2TzX5DWJvnyqqFkN7qHTYo/+I + oKECnKqNzI5SQrAxgi6sbWA5DFQ/nwcqsUSBo5gCCJ/0QPrR19yVV5lJA19EY2LawOb1S + JNOFo4mQupSlBZwvERZJ7IqhBTPtQIfrqqz5VJbI13jK3ViZTugIZqydWAhosUyejP3Sd + Cj1KMexrvV95tjUtmhVFlph4tKThQO0p9pXKZNCzYsbQTye6O6Hk2rojOJLyFWqNBVKtI + 8Ymfu7OQWppRnuUFuhuuS515H1s888bZFMPsC74mPyo0Y7Q9wAoTnQ9Hw6b0J6OfY3PIR + VphaCmxh6b7dgSPFdD7TA6j0xk6PCTOIEzBKuc85B3GQc8Nt4sTv6fW8lGeuYWqepW74i + geC4qB6U3/3+p3nPdq/bTM1txrhnQsl1r4dv6TLZ51EtHp6sXayp0qd0pRaiavebXFC0i + aETLraQpye4FWbBL/8xTjQ/0VPrYVuUCDvDSMIIS3/9g7Kp7ERUDC9jUqOVonm4pTXL9i + ItiUBlK7Mob9C4fQIRFnVR00DCmkmVgw== dummy@gitlab.com + KEY + end + end + + factory :rsa_key_5120 do + key do + <<~KEY.delete("\n") + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACgQDxnZP0TucLH3zcrvt75DPNq+xKqOmJk + CEzTytKq4S5MDH0nlx+xOZ9WykhwDHXU0iZBJF7yRdLkZweYDJVKnBzr4t7QP5Sw2/ZdL + elvUMWGJjuz28x8Z+8NZ+IxL/exDz7itrhCsLupQhGO1obiIwf8xVzzPoxrQ9dxaN4x96 + 5N+QdQcld8O6xfpSE0p5Y3sRn3kp57aHWoNa/bUGZy0OHLr/ig0uc6EKyWsTmEESOgDyV + 94wOyHR0KNGEENyxQt4BwAbEBn3Y41HKqD358KKh+XjbECebrrBFigdDL/eYFIUlstJ07 + SK/HtYjZbiUZCPs8bJA+SBaLK0pGGqguM2LXRoMeMUZFwKKKS2LpRqjKGj3Qt7qMnp1Sk + VhiMnxNqL4nJnDOOVo07xDIPKqIBYO67/cp4Icv3IjKxy6K3EIpLr+iRCxcllpDogxolz + FC+pEDVpmEvcrGEv1ON6HcCdk/6Q8Iekr8rYDHpKCU5FF2uBHkqq7yNJ1/+NFC4dgyOo0 + xCVL4D3DvDKNxFYkrzW4ICt0f5XcMnU10yS/OFXz8JwA3jvuLvMRe5JdFiIjb/l86+TgY + yvK8Y8N/UWgSgyjXUCv8nxdvpsxdz5h7HBF8E2DIxCVMC23655e5rp5eJW9EU9X5YFZc3 + u6uWJ1f1aO+1ViTtqkPrqxovNDD+gVel8Ny6MJ4MvmDKY+eM8beNMSSf1n1Oyh/SvCffh + ZpUqrXdTr9qwZEOaC75T74AJ7KBl9VvO3vPLZuJrt38R2OZG/4SlNEUA6bb5TWQLtdor/ + qpPN5jAskkAUzOh5L/M+dmq2jNn03U9xwORCYPZj+fFM9bL99/0knsV0ypZDZyWH dummy@gitlab.com + KEY + end + end + + factory :rsa_key_8192 do + key do + <<~KEY.delete("\n") + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQC5jMyGtgMOVX4t2GuXkbirJA0Edr+ql + OH9grnRBPHPo0Npt6XE6ZN3J3hDULTQo03wmekGw42dxdNSgk+F0GjsUBrMLbqrk485MM + e0cUbP4lRXNu4ao87wPVM5fAsD4E3FQiZcI6Df011ZGIL7hGTHt6eafTfr9cJheRyYSu6 + g06rlnFWbbtSh9oQ7Y6sfDLBcsC9ECcXwe3mwViuQXPIVomZ02EdnBbAhbGHDtA+ZbSvT + fraxOMjkxkVvvdjLxXEykpwVuZf8eZ+R/Js8jQ5RKvTZMbfxJNsGEqHD32s43ml4VF549 + Qz2GJDXF7Cld/n3CT6wvw0mMPM0LnykL2v0CMr44bjIA3KsNEs5MhkcBO8sv5hGfcPhrp + m9WwI6gd9vdZVcxarVI+iQS947owvdn4VbEZXynCDqEEv3Zh+FA5p23mf2p7DkG/swiK/ + IPrjr1wmsiWmwIUsENzJNyJtibKuRsBawC4ZdL797tFilSoTzSpriegSL13joPXz3eOHC + Vu4ATHMo3QyLfIFbxrf9PQ79nyOpHoX2YeFXvei3xFkGMundkOqeI+pnJKDyqbiLV7UVl + clua11QWNQZf1ZUd0n1wZ1g89de+wl3oJSRbSA5ZpveZEPstcMC/JhogY4JBYsvCT1yHO + oNWHo90NZQsUCjNnR+/FVaACtpt2zcPTjjbXvxwCDlT3gXTmTBp/kEZq6u8p+BOlqFgxc + P/sdAR8jWTin3Iw/YAcbqNgRHdjMUzJBrPQ5NcK6xFcmkOEQahdJDZs98xozCHkD4Urx6 + +auTr/uqRYobKoNUNiYqN1n7/dfZjQJJVkHtKd06JTFx+7/SqyfrTKS+/EIf2Hypdy9r9 + IFR+SWAOi11N/wflS/ZbH95Qt3STifXRecmHzyYGkMOZ+mg3Hi2YU0yn7k+P1jy627xud + pT9Ak3HWT5ji8tMyn9udL7m80dYpUiEAxoYZdbSSNCDaKP4ViABnGIeZreIujabI8IdtE + IjFQTaF2d5HTYjp28/qf576CFP5L7AGydypipYqZUmsYnay5YVjdm89He3TMD71SwspJl + POC4RnM0HS87OE+U0+mVaIe8YYbcjTekpVU9mkqsE/GQ34Egw79VMNNgWq5avOzpT8msC + lTJxgfJ1agGgigTvGxUM0FB07+sIdJxxNymAGpLKZ1op8xaJI3o8D86jWgI22za1zxUB5 + il9U7+KOzaWo9mp3bmhvZWGDwzTXEZhUJYMRby7o6UxSHlA6fKE63JSDD2yhXk4CjsQRN + C7Ph9cYSB+Wa3i9Am4rRlJgrF79okmEOMpj1idliHkpIsy/k2CN9Lf2EIHOD4NMuLrSUH + 4qJsPUq19ZbGIMdImD3vMS5b dummy@gitlab.com + KEY + end + end + factory :dsa_key_2048 do key do <<~KEY.delete("\n") - ssh-dss AAAAB3NzaC1kc3MAAAEBAO/3/NPLA/zSFkMOCaTtGo+uos1flfQ5f038Uk+G - Y9AeLGzX+Srhw59GdVXmOQLYBrOt5HdGwqYcmLnE2VurUGmhtfeO5H+3p5pGJbkS0Gxp - YH1HRO9lWsncF3Hh1w4lYsDjkclDiSTdfTuN8F4Kb3DXNnVSCieeonp+B25F/CXagyTQ - /pvNmHFeYgGCVdnBtFdi+xfxaZ8NKdPrGggzokbKHElDZQ4Xo5EpdcyLajgM7nB2r2Rz - OrmeaevKi5lV68ehRa9Yyrb7vxvwiwBwOgqR/mnN7Gnaq1jUdmJY+ct04Qwx37f5jvhv - 5gA4U40SGMoiHM8RFIN7Ksz0jsyX73MAAAAVALRWOfjfzHpK7KLz4iqDvvTUAevJAAAB - AEa9NZ+6y9iQ5erGsdfLTXFrhSefTG0NhghoO/5IFkSGfd8V7kzTvCHaFrcfpEA5kP8t - poeOG0TASB6tgGOxm1Bq4Wncry5RORBPJlAVpDGRcvZ931ddH7IgltEInS6za2uH6F/1 - M1QfKePSLr6xJ1ZLYfP0Og5KTp1x6yMQvfwV0a+XdA+EPgaJWLWp/pWwKWa0oLUgjsIH - MYzuOGh5c708uZrmkzqvgtW2NgXhcIroRgynT3IfI2lP2rqqb3uuuE/qH5UCUFO+Dc3H - nAFNeQDT/M25AERdPYBAY5a+iPjIgO+jT7BfmfByT+AZTqZySrCyc7nNZL3YgGLK0l6A - 1GgAAAEBAN9FpFOdIXE+YEZhKl1vPmbcn+b1y5zOl6N4x1B7Q8pD/pLMziWROIS8uLzb - aZ0sMIWezHIkxuo1iROMeT+jtCubn7ragaN6AX7nMpxYUH9+mYZZs/fyElt6wCviVhTI - zM+u7VdQsnZttOOlQfogHdL+SpeAft0DsfJjlcgQnsLlHQKv6aPqCPYUST2nE7RyW/Ex - PrMxLtOWt0/j8RYHbwwqvyeZqBz3ESBgrS9c5tBdBfauwYUV/E7gPLOU3OZFw9ue7o+z - wzoTZqW6Xouy5wtWvSLQSLT5XwOslmQz8QMBxD0AQyDfEFGsBCWzmbTgKv9uqrBjubsS - Taja+Cf9kMo== dummy@gitlab.com + ssh-dss AAAAB3NzaC1kc3MAAAEBALEB3sM2kPy6LKLiyL+UlDx2vzuKrzSD2nsW2Kb7 + 0ivIqDNJu5CbqIQSkjdMzJiocs33ESFqXid6ezOtVdDwXHJQRxKGalW1kBbFAPjtMxlD + bf559+7qN2zfCfcQsgTmNAZ7O+wltqJmyLv5i4QqNwPDvyeBvJ4C+770DzlcQtpkflKJ + X+O7i8Ylq34h6UTCTnjry+dFVm1xz97LPf7XuzXGZcAG/eGUNQgxQ2bferKnrpYOXx6c + ocSRj9W54nrRFMWuDeOspWp4MoYK0FRMfDQYPksUayGUnm1KQTGuDbB0ahRNCOm8b3tf + P9Z+vjANAkqenzDuXCpz2PU/Oj6/N/UAAAAhAPOLyut12Mjcp3eUXLe1xSoI5IRXSLso + W9no93dcFNprAAABAQCLhpqKY+PNcwbhhPruL+f+uROghHzDwRNX+e231F4wHHeDDomf + WyLVFj31XrHdDXZnS9tTTj5D2XWLovSSxYb3H7earTctmktL0lQ3HapujzvOkn+VM0pG + s6B3j54+AM3mg50KZdYWxxv+v/lb6oEcsCjfKNyRIx/5pqX6XI3dxl9MMIxrfVWpkNX+ + FI68v1LVV61DC9PkNyEHU0v9YBOfrTiS21TIlVIZcSFhuDjg52MekfZAnoKaP7YFJNF3 + fdCrXaU3hYQrwB9XdskBUppwxKGhf7O6SWEZhAEfPA9kgxaWHoJvsDz8aca576UNe7BP + mjzo/SLUX+P4uvcaffd+AAABAEqzpmwjzTxB+DV8C+0LnmKf3L/UlQWyGdmhd65rnbkH + GgRMAAkoh4GBOEHL5bznNRmO7X/H6g2fR7SEabxfbvb903KI4nbfFF+3QtnwyIbTBAcH + 0893D3bi5rsaJcz+c6lBob2En2nThRciefXUk2oPzCQuDyFIyHLJikqRQVcalHCdQ00c + /H/JkiJedHNqaeU4TeMk8SM53Brjplj/iiJq+ujc5MlEgACdCwWp0BviFACEoYyFaa3R + kc7Xdm9vFpclm9fzgUfPloASA0SkO945in3mIqMfODTb4yRvbjk8If9483fEPgQkczpd + ptBz1VAKg8AmRcz1GmBIxs+Stn0= dummy@gitlab.com KEY end end diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb index b02d2d4261c..cc12a1005ba 100644 --- a/spec/features/milestone_spec.rb +++ b/spec/features/milestone_spec.rb @@ -66,15 +66,16 @@ feature 'Milestone' do end end - feature 'Open a milestone' do + feature 'Open a milestone', :js do scenario 'shows total issue time spent correctly when no time has been logged' do milestone = create(:milestone, project: project, title: 8.7) visit project_milestone_path(project, milestone) - page.within('.block.time_spent') do - expect(page).to have_content 'No time spent' - expect(page).to have_content 'None' + wait_for_requests + + page.within('.time-tracking-no-tracking-pane') do + expect(page).to have_content 'No estimate or time spent' end end @@ -89,8 +90,10 @@ feature 'Milestone' do visit project_milestone_path(project, milestone) - page.within('.block.time_spent') do - expect(page).to have_content '3h' + wait_for_requests + + page.within('.time-tracking-spend-only-pane') do + expect(page).to have_content 'Spent: 3h' end end end diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb index 4cf48098401..134c8b8bc39 100644 --- a/spec/features/projects/members/share_with_group_spec.rb +++ b/spec/features/projects/members/share_with_group_spec.rb @@ -149,6 +149,11 @@ feature 'Project > Members > Share with Group', :js do create(:group).add_owner(master) visit project_settings_members_path(project) + + click_link 'Share with group' + + find('.ajax-groups-select.select2-container') + execute_script 'GROUP_SELECT_PER_PAGE = 1;' open_select2 '#link_group_id' end diff --git a/spec/features/projects/network_graph_spec.rb b/spec/features/projects/network_graph_spec.rb new file mode 100644 index 00000000000..9f9a7787093 --- /dev/null +++ b/spec/features/projects/network_graph_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe 'Project Network Graph', :js do + let(:user) { create :user } + let(:project) { create :project, :repository, namespace: user.namespace } + + before do + sign_in(user) + + # Stub Graph max_size to speed up test (10 commits vs. 650) + allow(Network::Graph).to receive(:max_count).and_return(10) + end + + context 'when branch is master' do + def switch_ref_to(ref_name) + first('.js-project-refs-dropdown').click + + page.within '.project-refs-form' do + click_link ref_name + end + end + + def click_show_only_selected_branch_checkbox + find('#filter_ref').click + end + + before do + visit project_network_path(project, 'master') + end + + it 'renders project network' do + expect(page).to have_selector ".network-graph" + expect(page).to have_selector '.dropdown-menu-toggle', text: "master" + page.within '.network-graph' do + expect(page).to have_content 'master' + end + end + + it 'switches ref to branch' do + switch_ref_to('feature') + + expect(page).to have_selector '.dropdown-menu-toggle', text: 'feature' + page.within '.network-graph' do + expect(page).to have_content 'feature' + end + end + + it 'switches ref to tag' do + switch_ref_to('v1.0.0') + + expect(page).to have_selector '.dropdown-menu-toggle', text: 'v1.0.0' + page.within '.network-graph' do + expect(page).to have_content 'v1.0.0' + end + end + + it 'renders by commit sha of "v1.0.0"' do + page.within ".network-form" do + fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' + find('button').click + end + + expect(page).to have_selector ".network-graph" + expect(page).to have_selector '.dropdown-menu-toggle', text: "master" + page.within '.network-graph' do + expect(page).to have_content 'v1.0.0' + end + end + + it 'filters select tag' do + switch_ref_to('v1.0.0') + + expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false + page.within '.network-graph' do + expect(page).to have_content 'Change some files' + end + + click_show_only_selected_branch_checkbox + + page.within '.network-graph' do + expect(page).not_to have_content 'Change some files' + end + + click_show_only_selected_branch_checkbox + + page.within '.network-graph' do + expect(page).to have_content 'Change some files' + end + end + + it 'renders error message when sha commit not exists' do + page.within ".network-form" do + fill_in 'extended_sha1', with: ';' + find('button').click + end + + expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist." + end + end + + it 'renders project network with test branch' do + visit project_network_path(project, "'test'") + + page.within '.network-graph' do + expect(page).to have_content "'test'" + end + end +end diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index 2e334caa98f..3f1ef0b2a47 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -17,6 +17,7 @@ feature 'Pages' do scenario 'does not see anything to destroy' do visit project_pages_path(project) + expect(page).to have_content('Configure pages') expect(page).not_to have_link('Remove pages') expect(page).not_to have_text('Only the project owner can remove pages') end @@ -32,14 +33,163 @@ feature 'Pages' do allow_any_instance_of(Project).to receive(:pages_deployed?) { true } end - scenario 'sees "Remove pages" link' do + scenario 'renders Access pages' do visit project_pages_path(project) - expect(page).to have_link('Remove pages') + expect(page).to have_content('Access pages') + end + + context 'when support for external domains is disabled' do + before do + allow(Gitlab.config.pages).to receive(:external_http).and_return(nil) + allow(Gitlab.config.pages).to receive(:external_https).and_return(nil) + end + + it 'renders message that support is disabled' do + visit project_pages_path(project) + + expect(page).to have_content('Support for domains and certificates is disabled') + end + end + + context 'when pages are exposed on external HTTP address' do + shared_examples 'adds new domain' do + it 'adds new domain' do + visit new_project_pages_domain_path(project) + + fill_in 'Domain', with: 'my.test.domain.com' + click_button 'Create New Domain' + + expect(page).to have_content('Domains (1)') + expect(page).to have_content('my.test.domain.com') + end + end + + before do + allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80']) + allow(Gitlab.config.pages).to receive(:external_https).and_return(nil) + end + + it 'allows to add new domain' do + visit project_pages_path(project) + + expect(page).to have_content('New Domain') + end + + it_behaves_like 'adds new domain' + + context 'when project in group namespace' do + it_behaves_like 'adds new domain' do + let(:group) { create :group } + let(:project) { create :project, namespace: group } + end + end + + context 'when pages domain is added' do + before do + project.pages_domains.create!(domain: 'my.test.domain.com') + + visit new_project_pages_domain_path(project) + end + + it 'renders certificates is disabled' do + expect(page).to have_content('Support for custom certificates is disabled') + end + + it 'does not adds new domain and renders error message' do + fill_in 'Domain', with: 'my.test.domain.com' + click_button 'Create New Domain' + + expect(page).to have_content('Domain has already been taken') + end + end + end + + context 'when pages are exposed on external HTTPS address' do + let(:certificate_pem) do + <<~PEM + -----BEGIN CERTIFICATE----- + MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0 + LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ + MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw + gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa + SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT + nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w + DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD + VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh + IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ + joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese + 5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg + YHi2yesCrOvVXt+lgPTd + -----END CERTIFICATE----- + PEM + end + + let(:certificate_key) do + <<~KEY + -----BEGIN PRIVATE KEY----- + MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN + SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t + PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB + kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd + j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/ + uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR + 5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O + AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K + EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh + Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C + m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH + EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx + 63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi + nNp/xedE1YxutQ== + -----END PRIVATE KEY----- + KEY + end + + before do + allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80']) + allow(Gitlab.config.pages).to receive(:external_https).and_return(['1.1.1.1:443']) + end + + it 'adds new domain with certificate' do + visit new_project_pages_domain_path(project) + + fill_in 'Domain', with: 'my.test.domain.com' + fill_in 'Certificate (PEM)', with: certificate_pem + fill_in 'Key (PEM)', with: certificate_key + click_button 'Create New Domain' + + expect(page).to have_content('Domains (1)') + expect(page).to have_content('my.test.domain.com') + end end end it_behaves_like 'no pages deployed' + + describe 'project settings page' do + it 'renders "Pages" tab' do + visit edit_project_path(project) + + page.within '.nav-sidebar' do + expect(page).to have_link('Pages') + end + end + + context 'when pages are disabled' do + before do + allow(Gitlab.config.pages).to receive(:enabled).and_return(false) + end + + it 'does not render "Pages" tab' do + visit edit_project_path(project) + + page.within '.nav-sidebar' do + expect(page).not_to have_link('Pages') + end + end + end + end end context 'when the user is not the owner' do @@ -57,4 +207,54 @@ feature 'Pages' do it_behaves_like 'no pages deployed' end + + describe 'Remove page' do + context 'when user is the owner' do + let(:project) { create :project, :repository } + + before do + project.namespace.update(owner: user) + end + + context 'when pages are deployed' do + let(:pipeline) do + commit_sha = project.commit('HEAD').sha + + project.pipelines.create( + ref: 'HEAD', + sha: commit_sha, + source: :push, + protected: false + ) + end + + let(:ci_build) do + build( + :ci_build, + project: project, + pipeline: pipeline, + ref: 'HEAD', + legacy_artifacts_file: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip')), + legacy_artifacts_metadata: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip.meta')) + ) + end + + before do + result = Projects::UpdatePagesService.new(project, ci_build).execute + expect(result[:status]).to eq(:success) + expect(project).to be_pages_deployed + end + + it 'removes the pages' do + visit project_pages_path(project) + + expect(page).to have_link('Remove pages') + + click_link 'Remove pages' + + expect(project.pages_deployed?).to be_falsey + end + end + end + end end diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js index c93b7cc6cac..95c2c122403 100644 --- a/spec/javascripts/ajax_loading_spinner_spec.js +++ b/spec/javascripts/ajax_loading_spinner_spec.js @@ -1,5 +1,3 @@ -import 'jquery'; -import 'jquery-ujs'; import AjaxLoadingSpinner from '~/ajax_loading_spinner'; describe('Ajax Loading Spinner', () => { diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 1c1b3f3ced3..8e4bbb90ccb 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -138,7 +138,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); $thumbsUpEmoji.attr('data-title', 'sam'); awardsHandler.userAuthored($thumbsUpEmoji); - return expect($thumbsUpEmoji.data("original-title")).toBe("You cannot vote on your own issue, MR and note"); + return expect($thumbsUpEmoji.data("originalTitle")).toBe("You cannot vote on your own issue, MR and note"); }); it('should restore tooltip back to initial vote list', function() { var $thumbsUpEmoji, $votesBlock; @@ -149,7 +149,7 @@ import '~/lib/utils/common_utils'; awardsHandler.userAuthored($thumbsUpEmoji); jasmine.clock().tick(2801); jasmine.clock().uninstall(); - return expect($thumbsUpEmoji.data("original-title")).toBe("sam"); + return expect($thumbsUpEmoji.data("originalTitle")).toBe("sam"); }); }); describe('::getAwardUrl', function() { @@ -194,7 +194,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("original-title")).toBe('You, sam, jerry, max, and andy'); + return expect($thumbsUpEmoji.data("originalTitle")).toBe('You, sam, jerry, max, and andy'); }); return it('handles the special case where "You" is not cleanly comma seperated', function() { var $thumbsUpEmoji, $votesBlock, awardUrl; @@ -204,7 +204,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.attr('data-title', 'sam'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("original-title")).toBe('You and sam'); + return expect($thumbsUpEmoji.data("originalTitle")).toBe('You and sam'); }); }); describe('::removeYouToUserList', function() { @@ -217,7 +217,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.addClass('active'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("original-title")).toBe('sam, jerry, max, and andy'); + return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam, jerry, max, and andy'); }); return it('handles the special case where "You" is not cleanly comma seperated', function() { var $thumbsUpEmoji, $votesBlock, awardUrl; @@ -228,7 +228,7 @@ import '~/lib/utils/common_utils'; $thumbsUpEmoji.addClass('active'); awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data("original-title")).toBe('sam'); + return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam'); }); }); describe('::searchEmojis', () => { diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js index 8287c58ac5a..e500bbe750f 100644 --- a/spec/javascripts/behaviors/requires_input_spec.js +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -15,7 +15,7 @@ describe('requiresInput', () => { }); it('enables submit when no field is required', () => { - $('*[required=required]').removeAttr('required'); + $('*[required=required]').prop('required', false); $('.js-requires-input').requiresInput(); expect(submitButton).not.toBeDisabled(); }); diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js index 6e1b0429ab7..f3f80cb3771 100644 --- a/spec/javascripts/feature_highlight/feature_highlight_spec.js +++ b/spec/javascripts/feature_highlight/feature_highlight_spec.js @@ -1,11 +1,13 @@ import * as featureHighlightHelper from '~/feature_highlight/feature_highlight_helper'; import * as featureHighlight from '~/feature_highlight/feature_highlight'; +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; describe('feature highlight', () => { beforeEach(() => { setFixtures(` <div> - <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled> + <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" data-dismiss-endpoint="/test" disabled> Trigger </div> </div> @@ -19,13 +21,21 @@ describe('feature highlight', () => { }); describe('setupFeatureHighlightPopover', () => { + let mock; const selector = '.js-feature-highlight[data-highlight=test]'; + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet('/test').reply(200); spyOn(window, 'addEventListener'); spyOn(window, 'removeEventListener'); featureHighlight.setupFeatureHighlightPopover('test', 0); }); + afterEach(() => { + mock.restore(); + }); + it('setup popover content', () => { const $popoverContent = $('.feature-highlight-popover-content'); const outerHTML = $popoverContent.prop('outerHTML'); @@ -51,15 +61,6 @@ describe('feature highlight', () => { }, 0); }); - it('setup inserted.bs.popover', () => { - $(selector).trigger('mouseenter'); - const popoverId = $(selector).attr('aria-describedby'); - const spyEvent = spyOnEvent(`#${popoverId} .dismiss-feature-highlight`, 'click'); - - $(`#${popoverId} .dismiss-feature-highlight`).click(); - expect(spyEvent).toHaveBeenTriggered(); - }); - it('setup show.bs.popover', () => { $(selector).trigger('show.bs.popover'); expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function)); @@ -75,9 +76,19 @@ describe('feature highlight', () => { }); it('displays popover', () => { - expect($(selector).attr('aria-describedby')).toBeFalsy(); + expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeFalsy(); $(selector).trigger('mouseenter'); - expect($(selector).attr('aria-describedby')).toBeTruthy(); + expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeTruthy(); + }); + + it('toggles when clicked', () => { + $(selector).trigger('mouseenter'); + const popoverId = $(selector).attr('aria-describedby'); + const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call'); + + $(`#${popoverId} .dismiss-feature-highlight`).click(); + + expect(toggleSpy).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js index f1e6119253e..c37a964975d 100644 --- a/spec/javascripts/filtered_search/dropdown_user_spec.js +++ b/spec/javascripts/filtered_search/dropdown_user_spec.js @@ -1,8 +1,6 @@ -import '~/filtered_search/dropdown_utils'; -import '~/filtered_search/filtered_search_tokenizer'; -import '~/filtered_search/filtered_search_dropdown'; -import '~/filtered_search/dropdown_user'; - +import DropdownUtils from '~/filtered_search/dropdown_utils'; +import DropdownUser from '~/filtered_search/dropdown_user'; +import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer'; import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; describe('Dropdown User', () => { @@ -10,18 +8,18 @@ describe('Dropdown User', () => { let dropdownUser; beforeEach(() => { - spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); - spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); - spyOn(gl.DropdownUser.prototype, 'getGroupId').and.callFake(() => {}); - spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'getGroupId').and.callFake(() => {}); + spyOn(DropdownUtils, 'getSearchInput').and.callFake(() => {}); - dropdownUser = new gl.DropdownUser({ + dropdownUser = new DropdownUser({ tokenKeys: FilteredSearchTokenKeys, }); }); it('should not return the double quote found in value', () => { - spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({ + spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({ lastToken: '"johnny appleseed', }); @@ -29,7 +27,7 @@ describe('Dropdown User', () => { }); it('should not return the single quote found in value', () => { - spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({ + spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({ lastToken: '\'larry boy', }); @@ -39,22 +37,22 @@ describe('Dropdown User', () => { describe('config AjaxFilter\'s endpoint', () => { beforeEach(() => { - spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); - spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); - spyOn(gl.DropdownUser.prototype, 'getGroupId').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); + spyOn(DropdownUser.prototype, 'getGroupId').and.callFake(() => {}); }); it('should return endpoint', () => { window.gon = { relative_url_root: '', }; - const dropdown = new gl.DropdownUser(); + const dropdown = new DropdownUser(); expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json'); }); it('should return endpoint when relative_url_root is undefined', () => { - const dropdown = new gl.DropdownUser(); + const dropdown = new DropdownUser(); expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json'); }); @@ -63,7 +61,7 @@ describe('Dropdown User', () => { window.gon = { relative_url_root: '/gitlab_directory', }; - const dropdown = new gl.DropdownUser(); + const dropdown = new DropdownUser(); expect(dropdown.config.AjaxFilter.endpoint).toBe('/gitlab_directory/autocomplete/users.json'); }); @@ -84,7 +82,7 @@ describe('Dropdown User', () => { loadFixtures(fixtureTemplate); authorFilterDropdownElement = document.querySelector('#js-dropdown-author'); const dummyInput = document.createElement('div'); - dropdown = new gl.DropdownUser({ + dropdown = new DropdownUser({ dropdown: authorFilterDropdownElement, input: dummyInput, }); diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js index d6e1af105f1..3d6dec19eca 100644 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js @@ -1,6 +1,5 @@ -import '~/filtered_search/dropdown_utils'; -import '~/filtered_search/filtered_search_tokenizer'; -import '~/filtered_search/filtered_search_dropdown_manager'; +import DropdownUtils from '~/filtered_search/dropdown_utils'; +import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; @@ -10,25 +9,25 @@ describe('Dropdown Utils', () => { describe('getEscapedText', () => { it('should return same word when it has no space', () => { - const escaped = gl.DropdownUtils.getEscapedText('textWithoutSpace'); + const escaped = DropdownUtils.getEscapedText('textWithoutSpace'); expect(escaped).toBe('textWithoutSpace'); }); it('should escape with double quotes', () => { - let escaped = gl.DropdownUtils.getEscapedText('text with space'); + let escaped = DropdownUtils.getEscapedText('text with space'); expect(escaped).toBe('"text with space"'); - escaped = gl.DropdownUtils.getEscapedText('won\'t fix'); + escaped = DropdownUtils.getEscapedText('won\'t fix'); expect(escaped).toBe('"won\'t fix"'); }); it('should escape with single quotes', () => { - const escaped = gl.DropdownUtils.getEscapedText('won"t fix'); + const escaped = DropdownUtils.getEscapedText('won"t fix'); expect(escaped).toBe('\'won"t fix\''); }); it('should escape with single quotes by default', () => { - const escaped = gl.DropdownUtils.getEscapedText('won"t\' fix'); + const escaped = DropdownUtils.getEscapedText('won"t\' fix'); expect(escaped).toBe('\'won"t\' fix\''); }); }); @@ -50,14 +49,14 @@ describe('Dropdown Utils', () => { it('should filter without symbol', () => { input.value = 'roo'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item); + const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with symbol', () => { input.value = '@roo'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item); + const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); expect(updatedItem.droplab_hidden).toBe(false); }); @@ -69,56 +68,56 @@ describe('Dropdown Utils', () => { it('should filter with double quote', () => { input.value = '"'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with double quote and symbol', () => { input.value = '~"'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with double quote and multiple words', () => { input.value = '"community con'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with double quote, symbol and multiple words', () => { input.value = '~"community con'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote', () => { input.value = '\''; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote and symbol', () => { input.value = '~\''; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote and multiple words', () => { input.value = '\'community con'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote, symbol and multiple words', () => { input.value = '~\'community con'; - const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); expect(updatedItem.droplab_hidden).toBe(false); }); }); @@ -150,26 +149,26 @@ describe('Dropdown Utils', () => { it('should filter', () => { input.value = 'l'; - let updatedItem = gl.DropdownUtils.filterHint(config(), { + let updatedItem = DropdownUtils.filterHint(config(), { hint: 'label', }); expect(updatedItem.droplab_hidden).toBe(false); input.value = 'o'; - updatedItem = gl.DropdownUtils.filterHint(config(), { + updatedItem = DropdownUtils.filterHint(config(), { hint: 'label', }); expect(updatedItem.droplab_hidden).toBe(true); }); it('should return droplab_hidden false when item has no hint', () => { - const updatedItem = gl.DropdownUtils.filterHint(config(), {}, ''); + const updatedItem = DropdownUtils.filterHint(config(), {}, ''); expect(updatedItem.droplab_hidden).toBe(false); }); it('should allow multiple if item.type is array', () => { input.value = 'label:~first la'; - const updatedItem = gl.DropdownUtils.filterHint(config(), { + const updatedItem = DropdownUtils.filterHint(config(), { hint: 'label', type: 'array', }); @@ -178,12 +177,12 @@ describe('Dropdown Utils', () => { it('should prevent multiple if item.type is not array', () => { input.value = 'milestone:~first mile'; - let updatedItem = gl.DropdownUtils.filterHint(config(), { + let updatedItem = DropdownUtils.filterHint(config(), { hint: 'milestone', }); expect(updatedItem.droplab_hidden).toBe(true); - updatedItem = gl.DropdownUtils.filterHint(config(), { + updatedItem = DropdownUtils.filterHint(config(), { hint: 'milestone', type: 'string', }); @@ -205,7 +204,7 @@ describe('Dropdown Utils', () => { color: '#000000', }; - const updated = gl.DropdownUtils.mergeDuplicateLabels(dataMap, newLabel); + const updated = DropdownUtils.mergeDuplicateLabels(dataMap, newLabel); expect(updated[newLabel.title]).toEqual(newLabel); }); @@ -215,36 +214,36 @@ describe('Dropdown Utils', () => { color: '#000000', }; - const updated = gl.DropdownUtils.mergeDuplicateLabels(dataMap, duplicate); + const updated = DropdownUtils.mergeDuplicateLabels(dataMap, duplicate); expect(updated.label.multipleColors).toEqual([dataMap.label.color, duplicate.color]); }); }); describe('duplicateLabelColor', () => { it('should linear-gradient 2 colors', () => { - const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']); + const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']); expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)'); }); it('should linear-gradient 3 colors', () => { - const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']); + const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']); expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)'); }); it('should linear-gradient 4 colors', () => { - const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']); + const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']); expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)'); }); it('should not linear-gradient more than 4 colors', () => { - const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']); + const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']); expect(gradient.indexOf('#EEEEEE') === -1).toEqual(true); }); }); describe('duplicateLabelPreprocessing', () => { it('should set preprocessed to true', () => { - const results = gl.DropdownUtils.duplicateLabelPreprocessing([]); + const results = DropdownUtils.duplicateLabelPreprocessing([]); expect(results.preprocessed).toEqual(true); }); @@ -256,7 +255,7 @@ describe('Dropdown Utils', () => { title: 'label2', color: '#000000', }]; - const results = gl.DropdownUtils.duplicateLabelPreprocessing(data); + const results = DropdownUtils.duplicateLabelPreprocessing(data); expect(results.length).toEqual(2); expect(results[0]).toEqual(data[0]); @@ -271,14 +270,14 @@ describe('Dropdown Utils', () => { title: 'label', color: '#000000', }]; - const results = gl.DropdownUtils.duplicateLabelPreprocessing(data); + const results = DropdownUtils.duplicateLabelPreprocessing(data); it('should merge duplicate labels', () => { expect(results.length).toEqual(1); }); it('should convert multiple colored labels into linear-gradient', () => { - expect(results[0].color).toEqual(gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000'])); + expect(results[0].color).toEqual(DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000'])); }); it('should set multiple colored label text color to black', () => { @@ -289,7 +288,7 @@ describe('Dropdown Utils', () => { describe('setDataValueIfSelected', () => { beforeEach(() => { - spyOn(gl.FilteredSearchDropdownManager, 'addWordToInput') + spyOn(FilteredSearchDropdownManager, 'addWordToInput') .and.callFake(() => {}); }); @@ -298,8 +297,8 @@ describe('Dropdown Utils', () => { getAttribute: () => 'value', }; - gl.DropdownUtils.setDataValueIfSelected(null, selected); - expect(gl.FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1); + DropdownUtils.setDataValueIfSelected(null, selected); + expect(FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1); }); it('returns true when dataValue exists', () => { @@ -307,7 +306,7 @@ describe('Dropdown Utils', () => { getAttribute: () => 'value', }; - const result = gl.DropdownUtils.setDataValueIfSelected(null, selected); + const result = DropdownUtils.setDataValueIfSelected(null, selected); expect(result).toBe(true); }); @@ -316,7 +315,7 @@ describe('Dropdown Utils', () => { getAttribute: () => null, }; - const result = gl.DropdownUtils.setDataValueIfSelected(null, selected); + const result = DropdownUtils.setDataValueIfSelected(null, selected); expect(result).toBe(false); }); }); @@ -326,7 +325,7 @@ describe('Dropdown Utils', () => { const value = 'label:none '; it('should return selectionStart when cursor is at the trailing space', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 11, value, }); @@ -336,7 +335,7 @@ describe('Dropdown Utils', () => { }); it('should return input when cursor is at the start of input', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 0, value, }); @@ -346,7 +345,7 @@ describe('Dropdown Utils', () => { }); it('should return input when cursor is at the middle of input', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 7, value, }); @@ -356,7 +355,7 @@ describe('Dropdown Utils', () => { }); it('should return input when cursor is at the end of input', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 10, value, }); @@ -370,7 +369,7 @@ describe('Dropdown Utils', () => { const value = 'label:~"Community Contribution"'; it('should return input when cursor is after the first word', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 17, value, }); @@ -380,7 +379,7 @@ describe('Dropdown Utils', () => { }); it('should return input when cursor is before the second word', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 18, value, }); @@ -394,7 +393,7 @@ describe('Dropdown Utils', () => { const value = 'label:~"Community Contribution'; it('should return entire input when cursor is at the start of input', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 0, value, }); @@ -404,7 +403,7 @@ describe('Dropdown Utils', () => { }); it('should return entire input when cursor is at the end of input', () => { - const { left, right } = gl.DropdownUtils.getInputSelectionPosition({ + const { left, right } = DropdownUtils.getInputSelectionPosition({ selectionStart: 30, value, }); @@ -434,7 +433,7 @@ describe('Dropdown Utils', () => { const valueContainer = authorToken.querySelector('.value-container'); valueContainer.dataset.originalValue = originalValue; - const searchQuery = gl.DropdownUtils.getSearchQuery(); + const searchQuery = DropdownUtils.getSearchQuery(); expect(searchQuery).toBe(' search term author:original dance'); }); diff --git a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js index 5c7e9115aac..71c14582329 100644 --- a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js @@ -1,6 +1,4 @@ -import '~/filtered_search/filtered_search_visual_tokens'; -import '~/filtered_search/filtered_search_tokenizer'; -import '~/filtered_search/filtered_search_dropdown_manager'; +import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; describe('Filtered Search Dropdown Manager', () => { beforeEach(() => { @@ -28,7 +26,7 @@ describe('Filtered Search Dropdown Manager', () => { describe('input has no existing value', () => { it('should add just tokenName', () => { - gl.FilteredSearchDropdownManager.addWordToInput('milestone'); + FilteredSearchDropdownManager.addWordToInput('milestone'); const token = document.querySelector('.tokens-container .js-visual-token'); @@ -38,7 +36,7 @@ describe('Filtered Search Dropdown Manager', () => { }); it('should add tokenName and tokenValue', () => { - gl.FilteredSearchDropdownManager.addWordToInput('label'); + FilteredSearchDropdownManager.addWordToInput('label'); let token = document.querySelector('.tokens-container .js-visual-token'); @@ -46,9 +44,9 @@ describe('Filtered Search Dropdown Manager', () => { expect(token.querySelector('.name').innerText).toBe('label'); expect(getInputValue()).toBe(''); - gl.FilteredSearchDropdownManager.addWordToInput('label', 'none'); + FilteredSearchDropdownManager.addWordToInput('label', 'none'); // We have to get that reference again - // Because gl.FilteredSearchDropdownManager deletes the previous token + // Because FilteredSearchDropdownManager deletes the previous token token = document.querySelector('.tokens-container .js-visual-token'); expect(token.classList.contains('filtered-search-token')).toEqual(true); @@ -61,7 +59,7 @@ describe('Filtered Search Dropdown Manager', () => { describe('input has existing value', () => { it('should be able to just add tokenName', () => { setInputValue('a'); - gl.FilteredSearchDropdownManager.addWordToInput('author'); + FilteredSearchDropdownManager.addWordToInput('author'); const token = document.querySelector('.tokens-container .js-visual-token'); @@ -71,10 +69,10 @@ describe('Filtered Search Dropdown Manager', () => { }); it('should replace tokenValue', () => { - gl.FilteredSearchDropdownManager.addWordToInput('author'); + FilteredSearchDropdownManager.addWordToInput('author'); setInputValue('roo'); - gl.FilteredSearchDropdownManager.addWordToInput(null, '@root'); + FilteredSearchDropdownManager.addWordToInput(null, '@root'); const token = document.querySelector('.tokens-container .js-visual-token'); @@ -85,10 +83,10 @@ describe('Filtered Search Dropdown Manager', () => { }); it('should add tokenValues containing spaces', () => { - gl.FilteredSearchDropdownManager.addWordToInput('label'); + FilteredSearchDropdownManager.addWordToInput('label'); setInputValue('"test '); - gl.FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\''); + FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\''); const token = document.querySelector('.tokens-container .js-visual-token'); diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js index 0ed9a587dc1..95d02974bdc 100644 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js @@ -5,9 +5,10 @@ import RecentSearchesServiceError from '~/filtered_search/services/recent_search import RecentSearchesRoot from '~/filtered_search/recent_searches_root'; import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; import '~/lib/utils/common_utils'; -import '~/filtered_search/filtered_search_tokenizer'; -import '~/filtered_search/filtered_search_dropdown_manager'; -import '~/filtered_search/filtered_search_manager'; +import DropdownUtils from '~/filtered_search/dropdown_utils'; +import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; +import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; +import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; describe('Filtered Search Manager', () => { @@ -49,21 +50,21 @@ describe('Filtered Search Manager', () => { </div> `); - spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {}); + spyOn(FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {}); }); const initializeManager = () => { /* eslint-disable jasmine/no-unsafe-spy */ - spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {}); - spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {}); - spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {}); + spyOn(FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {}); + spyOn(FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {}); + spyOn(FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {}); spyOn(gl.utils, 'getParameterByName').and.returnValue(null); - spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'unselectTokens').and.callThrough(); /* eslint-enable jasmine/no-unsafe-spy */ input = document.querySelector('.filtered-search'); tokensContainer = document.querySelector('.tokens-container'); - manager = new gl.FilteredSearchManager({ page }); + manager = new FilteredSearchManager({ page }); manager.setup(); }; @@ -81,7 +82,7 @@ describe('Filtered Search Manager', () => { }); it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => { - manager = new gl.FilteredSearchManager({ page }); + manager = new FilteredSearchManager({ page }); expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({ @@ -93,7 +94,7 @@ describe('Filtered Search Manager', () => { describe('setup', () => { beforeEach(() => { - manager = new gl.FilteredSearchManager({ page }); + manager = new FilteredSearchManager({ page }); }); it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => { @@ -108,7 +109,7 @@ describe('Filtered Search Manager', () => { describe('searchState', () => { beforeEach(() => { - spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {}); + spyOn(FilteredSearchManager.prototype, 'search').and.callFake(() => {}); initializeManager(); }); @@ -134,7 +135,7 @@ describe('Filtered Search Manager', () => { }; manager.searchState(e); - expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled(); + expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled(); }); it('should call search when there is state', () => { @@ -149,7 +150,7 @@ describe('Filtered Search Manager', () => { }; manager.searchState(e); - expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened'); + expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened'); }); }); @@ -251,44 +252,44 @@ describe('Filtered Search Manager', () => { }); it('removes last token', () => { - spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); dispatchBackspaceEvent(input, 'keyup'); dispatchBackspaceEvent(input, 'keyup'); - expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled(); }); it('sets the input', () => { - spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); dispatchDeleteEvent(input, 'keyup'); dispatchDeleteEvent(input, 'keyup'); - expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled(); expect(input.value).toEqual('~bug'); }); }); it('does not remove token or change input when there is existing input', () => { - spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); input.value = 'text'; dispatchDeleteEvent(input, 'keyup'); - expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); expect(input.value).toEqual('text'); }); it('does not remove previous token on single backspace press', () => { - spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); - spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough(); input.value = 't'; dispatchDeleteEvent(input, 'keyup'); - expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); - expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled(); expect(input.value).toEqual('t'); }); }); @@ -309,7 +310,7 @@ describe('Filtered Search Manager', () => { describe('unselected token', () => { beforeEach(() => { - spyOn(gl.FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough(); + spyOn(FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough(); tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none'), @@ -380,16 +381,16 @@ describe('Filtered Search Manager', () => { describe('removeSelectedToken', () => { beforeEach(() => { - spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough(); - spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough(); - spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough(); + spyOn(FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough(); + spyOn(FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough(); + spyOn(FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough(); initializeManager(); }); it('calls FilteredSearchVisualTokens.removeSelectedToken', () => { manager.removeSelectedToken(); - expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled(); + expect(FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled(); }); it('calls handleInputPlaceholder', () => { @@ -421,12 +422,12 @@ describe('Filtered Search Manager', () => { manager.filteredSearchInput.value = inputValue; manager.filteredSearchInput.dispatchEvent(new Event('input')); - expect(gl.DropdownUtils.getSearchQuery()).toEqual(inputValue); + expect(DropdownUtils.getSearchQuery()).toEqual(inputValue); manager.clearSearchButton.click(); expect(manager.filteredSearchInput.value).toEqual(''); - expect(gl.DropdownUtils.getSearchQuery()).toEqual(''); + expect(DropdownUtils.getSearchQuery()).toEqual(''); }); }); diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js index bf8b66f1110..465f5f79931 100644 --- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js @@ -1,19 +1,19 @@ import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; -import '~/filtered_search/filtered_search_tokenizer'; +import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer'; describe('Filtered Search Tokenizer', () => { const allowedKeys = FilteredSearchTokenKeys.getKeys(); describe('processTokens', () => { it('returns for input containing only search value', () => { - const results = gl.FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys); + const results = FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys); expect(results.searchToken).toBe('searchTerm'); expect(results.tokens.length).toBe(0); expect(results.lastToken).toBe(results.searchToken); }); it('returns for input containing only tokens', () => { - const results = gl.FilteredSearchTokenizer + const results = FilteredSearchTokenizer .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys); expect(results.searchToken).toBe(''); expect(results.tokens.length).toBe(4); @@ -37,7 +37,7 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input starting with search value and ending with tokens', () => { - const results = gl.FilteredSearchTokenizer + const results = FilteredSearchTokenizer .processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys); expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.tokens.length).toBe(1); @@ -48,7 +48,7 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input starting with tokens and ending with search value', () => { - const results = gl.FilteredSearchTokenizer + const results = FilteredSearchTokenizer .processTokens('assignee:@user searchTerm', allowedKeys); expect(results.searchToken).toBe('searchTerm'); @@ -60,7 +60,7 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input containing search value wrapped between tokens', () => { - const results = gl.FilteredSearchTokenizer + const results = FilteredSearchTokenizer .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys); expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); @@ -81,7 +81,7 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input containing search value in between tokens', () => { - const results = gl.FilteredSearchTokenizer + const results = FilteredSearchTokenizer .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys); expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.tokens.length).toBe(3); @@ -101,14 +101,14 @@ describe('Filtered Search Tokenizer', () => { }); it('returns search value for invalid tokens', () => { - const results = gl.FilteredSearchTokenizer.processTokens('fake:token', allowedKeys); + const results = FilteredSearchTokenizer.processTokens('fake:token', allowedKeys); expect(results.lastToken).toBe('fake:token'); expect(results.searchToken).toBe('fake:token'); expect(results.tokens.length).toEqual(0); }); it('returns search value and token for mix of valid and invalid tokens', () => { - const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys); + const results = FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys); expect(results.tokens.length).toEqual(1); expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].value).toBe('real'); @@ -118,13 +118,13 @@ describe('Filtered Search Tokenizer', () => { }); it('returns search value for invalid symbols', () => { - const results = gl.FilteredSearchTokenizer.processTokens('std::includes', allowedKeys); + const results = FilteredSearchTokenizer.processTokens('std::includes', allowedKeys); expect(results.lastToken).toBe('std::includes'); expect(results.searchToken).toBe('std::includes'); }); it('removes duplicated values', () => { - const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys); + const results = FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys); expect(results.tokens.length).toBe(1); expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].value).toBe('foo'); 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 0684c3498a2..f1da5f81c0f 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -2,11 +2,12 @@ import _ from 'underscore'; import AjaxCache from '~/lib/utils/ajax_cache'; import UsersCache from '~/lib/utils/users_cache'; -import '~/filtered_search/filtered_search_visual_tokens'; +import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; +import DropdownUtils from '~/filtered_search//dropdown_utils'; import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; describe('Filtered Search Visual Tokens', () => { - const subject = gl.FilteredSearchVisualTokens; + const subject = FilteredSearchVisualTokens; const findElements = (tokenElement) => { const tokenNameElement = tokenElement.querySelector('.name'); @@ -860,25 +861,25 @@ describe('Filtered Search Visual Tokens', () => { it('does not preprocess more than once', () => { let labels = []; - spyOn(gl.DropdownUtils, 'duplicateLabelPreprocessing').and.callFake(() => []); + spyOn(DropdownUtils, 'duplicateLabelPreprocessing').and.callFake(() => []); - labels = gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); - gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); + labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); + FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); - expect(gl.DropdownUtils.duplicateLabelPreprocessing.calls.count()).toEqual(1); + expect(DropdownUtils.duplicateLabelPreprocessing.calls.count()).toEqual(1); }); describe('not preprocessed before', () => { it('returns preprocessed labels', () => { let labels = []; expect(labels.preprocessed).not.toEqual(true); - labels = gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); + labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); expect(labels.preprocessed).toEqual(true); }); it('overrides AjaxCache with preprocessed results', () => { spyOn(AjaxCache, 'override').and.callFake(() => {}); - gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, []); + FilteredSearchVisualTokens.preprocessLabel(endpoint, []); expect(AjaxCache.override.calls.count()).toEqual(1); }); }); @@ -926,7 +927,7 @@ describe('Filtered Search Visual Tokens', () => { }; const findLabel = tokenValue => labelData.find( - label => tokenValue === `~${gl.DropdownUtils.getEscapedText(label.title)}`, + label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`, ); it('updates the color of a label token', (done) => { diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js index b13d1bf8dff..67b854f61c0 100644 --- a/spec/javascripts/gl_dropdown_spec.js +++ b/spec/javascripts/gl_dropdown_spec.js @@ -64,8 +64,8 @@ describe('glDropdown', function describeDropdown() { }); afterEach(() => { - $('body').unbind('keydown'); - this.dropdownContainerElement.unbind('keyup'); + $('body').off('keydown'); + this.dropdownContainerElement.off('keyup'); }); it('should open on click', () => { diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js index 6599839a526..d8a8c8cc260 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js @@ -1,7 +1,7 @@ /* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */ import { scaleLinear, scaleTime } from 'd3-scale'; import { timeParse } from 'd3-time-format'; -import { ContributorsGraph, ContributorsMasterGraph } from '~/graphs/stat_graph_contributors_graph'; +import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; const d3 = { scaleLinear, scaleTime, timeParse }; diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js index 962423462e7..e03114c1cc5 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js @@ -1,5 +1,5 @@ -import ContributorsStatGraph from '~/graphs/stat_graph_contributors'; -import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph'; +import ContributorsStatGraph from '~/pages/projects/graphs/show/stat_graph_contributors'; +import { ContributorsGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; import { setLanguage } from '../helpers/locale_helper'; diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js index 9b47ab62181..22a9afe1a9d 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js @@ -1,6 +1,6 @@ /* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */ -import ContributorsStatGraphUtil from '~/graphs/stat_graph_contributors_util'; +import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util'; describe("ContributorsStatGraphUtil", function () { describe("#parse_log", function () { diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js index 5d0ee91d977..0d16b23302f 100644 --- a/spec/javascripts/merge_request_notes_spec.js +++ b/spec/javascripts/merge_request_notes_spec.js @@ -23,7 +23,7 @@ describe('Merge request notes', () => { gl.utils.disableButtonIfEmptyField = _.noop; window.project_uploads_path = 'http://test.host/uploads'; $('body').attr('data-page', 'projects:merge_requests:show'); - window.gon.current_user_id = $('.note:last').data('author-id'); + window.gon.current_user_id = $('.note:last').data('authorId'); return new Notes('', []); }); @@ -76,7 +76,7 @@ describe('Merge request notes', () => { </form>`; setFixtures(diffsResponse.html + noteFormHtml); $('body').attr('data-page', 'projects:merge_requests:show'); - window.gon.current_user_id = $('.note:last').data('author-id'); + window.gon.current_user_id = $('.note:last').data('authorId'); return new Notes('', []); }); diff --git a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js index d2386077aa6..349549b9e1f 100644 --- a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js +++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js @@ -22,19 +22,19 @@ describe('Abuse Reports', () => { it('should truncate long messages', () => { const $longMessage = findMessage('LONG MESSAGE'); - expect($longMessage.data('original-message')).toEqual(jasmine.anything()); + expect($longMessage.data('originalMessage')).toEqual(jasmine.anything()); assertMaxLength($longMessage); }); it('should not truncate short messages', () => { const $shortMessage = findMessage('SHORT MESSAGE'); - expect($shortMessage.data('original-message')).not.toEqual(jasmine.anything()); + expect($shortMessage.data('originalMessage')).not.toEqual(jasmine.anything()); }); it('should allow clicking a truncated message to expand and collapse the full message', () => { const $longMessage = findMessage('LONG MESSAGE'); $longMessage.click(); - expect($longMessage.data('original-message').length).toEqual($longMessage.text().length); + expect($longMessage.data('originalMessage').length).toEqual($longMessage.text().length); $longMessage.click(); assertMaxLength($longMessage); }); diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js index 8ce33d410a7..e0ea3649646 100644 --- a/spec/javascripts/pipelines/async_button_spec.js +++ b/spec/javascripts/pipelines/async_button_spec.js @@ -15,7 +15,8 @@ describe('Pipelines Async Button', () => { title: 'Foo', icon: 'repeat', cssClass: 'bar', - id: 123, + pipelineId: 123, + type: 'explode', }, }).$mount(); }); @@ -39,8 +40,9 @@ describe('Pipelines Async Button', () => { describe('With confirm dialog', () => { it('should call the service when confimation is positive', () => { - eventHub.$on('actionConfirmationModal', (data) => { - expect(data.id).toEqual(123); + eventHub.$on('openConfirmationModal', (data) => { + expect(data.pipelineId).toEqual(123); + expect(data.type).toEqual('explode'); }); component = new AsyncButtonComponent({ @@ -49,7 +51,8 @@ describe('Pipelines Async Button', () => { title: 'Foo', icon: 'fa fa-foo', cssClass: 'bar', - id: 123, + pipelineId: 123, + type: 'explode', }, }).$mount(); diff --git a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js index bc6413a159f..e58a8018ed5 100644 --- a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js +++ b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import Vue from 'vue'; -import PipelineMediator from '~/pipelines/pipeline_details_mediatior'; +import PipelineMediator from '~/pipelines/pipeline_details_mediator'; describe('PipelineMdediator', () => { let mediator; diff --git a/spec/javascripts/pipelines/pipeline_store_spec.js b/spec/javascripts/pipelines/pipeline_store_spec.js index 85d13445b01..ab2287cc344 100644 --- a/spec/javascripts/pipelines/pipeline_store_spec.js +++ b/spec/javascripts/pipelines/pipeline_store_spec.js @@ -8,7 +8,6 @@ describe('Pipeline Store', () => { }); it('should set defaults', () => { - expect(store.state).toEqual({ pipeline: {} }); expect(store.state.pipeline).toEqual({}); }); diff --git a/spec/javascripts/projects/project_import_gitlab_project_spec.js b/spec/javascripts/projects/project_import_gitlab_project_spec.js index 2f1aae109e3..126f73103e0 100644 --- a/spec/javascripts/projects/project_import_gitlab_project_spec.js +++ b/spec/javascripts/projects/project_import_gitlab_project_spec.js @@ -10,7 +10,7 @@ describe('Import Gitlab project', () => { <input class="js-path-name" /> `); - projectImportGitlab.bindEvents(); + projectImportGitlab(); }); afterEach(() => { diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js index c314ca8ab72..8731ce35d81 100644 --- a/spec/javascripts/projects/project_new_spec.js +++ b/spec/javascripts/projects/project_new_spec.js @@ -27,7 +27,7 @@ describe('New Project', () => { }); it('does not change project path for disabled $projectImportUrl', () => { - $projectImportUrl.attr('disabled', true); + $projectImportUrl.prop('disabled', true); projectNew.deriveProjectPathFromUrl($projectImportUrl); @@ -36,7 +36,7 @@ describe('New Project', () => { describe('for enabled $projectImportUrl', () => { beforeEach(() => { - $projectImportUrl.attr('disabled', false); + $projectImportUrl.prop('disabled', false); }); it('does not change project path if it is set by user', () => { diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js index 97f762d07a7..0da5d91e376 100644 --- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js +++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js @@ -78,7 +78,7 @@ describe('SidebarMoveIssue', () => { this.sidebarMoveIssue.onConfirmClicked(); expect(this.mediator.moveIssue).toHaveBeenCalled(); - expect(this.$confirmButton.attr('disabled')).toBe('disabled'); + expect(this.$confirmButton.prop('disabled')).toBeTruthy(); expect(this.$confirmButton.hasClass('is-loading')).toBe(true); }); @@ -93,7 +93,7 @@ describe('SidebarMoveIssue', () => { // Wait for the move issue request to fail setTimeout(() => { expect(window.Flash).toHaveBeenCalled(); - expect(this.$confirmButton.attr('disabled')).toBe(undefined); + expect(this.$confirmButton.prop('disabled')).toBeFalsy(); expect(this.$confirmButton.hasClass('is-loading')).toBe(false); done(); }); @@ -120,7 +120,7 @@ describe('SidebarMoveIssue', () => { this.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click'); expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(0); - expect(this.$confirmButton.attr('disabled')).toBe('disabled'); + expect(this.$confirmButton.prop('disabled')).toBeTruthy(); done(); }, 0); }); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 9b2a5379855..323b8a9572d 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -1,6 +1,6 @@ /* eslint-disable jasmine/no-global-setup */ import $ from 'jquery'; -import 'jasmine-jquery'; +import 'vendor/jasmine-jquery'; import '~/commons'; import Vue from 'vue'; @@ -144,6 +144,9 @@ if (process.env.BABEL_ENV === 'coverage') { describe('Uncovered files', function () { const sourceFiles = require.context('~', true, /\.js$/); + + $.holdReady(true); + sourceFiles.keys().forEach(function (path) { // ignore if there is a matching spec file if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) { diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index f668f78c2b8..2a0e19ae796 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -95,6 +95,14 @@ module Gitlab expect(render(input, context)).to include('<p><code data-math-style="inline" class="code math js-render-math">2+2</code> is 4</p>') end end + + context 'outfilesuffix' do + it 'defaults to adoc' do + output = render("Inter-document reference <<README.adoc#>>", context) + + expect(output).to include("a href=\"README.adoc\"") + end + end end def render(*args) diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb new file mode 100644 index 00000000000..c76adcbe2f5 --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb @@ -0,0 +1,262 @@ +require 'spec_helper' + +# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work. +describe Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile, :migration, schema: 20180208183958 do + include MigrationsHelpers::TrackUntrackedUploadsHelpers + + let!(:appearances) { table(:appearances) } + let!(:namespaces) { table(:namespaces) } + let!(:projects) { table(:projects) } + let!(:routes) { table(:routes) } + let!(:uploads) { table(:uploads) } + + before(:all) do + ensure_temporary_tracking_table_exists + end + + describe '#upload_path' do + def assert_upload_path(file_path, expected_upload_path) + untracked_file = create_untracked_file(file_path) + + expect(untracked_file.upload_path).to eq(expected_upload_path) + end + + context 'for an appearance logo file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg') + end + end + + context 'for an appearance header_logo file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg') + end + end + + context 'for a pre-Markdown Note attachment file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf') + end + end + + context 'for a user avatar file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg') + end + end + + context 'for a group avatar file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg') + end + end + + context 'for a project avatar file path' do + it 'returns the file path relative to the CarrierWave root' do + assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg') + end + end + + context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do + it 'returns the file path relative to the project directory in uploads' do + project = create_project + random_hex = SecureRandom.hex + + assert_upload_path("/#{get_full_path(project)}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg") + end + end + end + + describe '#uploader' do + def assert_uploader(file_path, expected_uploader) + untracked_file = create_untracked_file(file_path) + + expect(untracked_file.uploader).to eq(expected_uploader) + end + + context 'for an appearance logo file path' do + it 'returns AttachmentUploader as a string' do + assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader') + end + end + + context 'for an appearance header_logo file path' do + it 'returns AttachmentUploader as a string' do + assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader') + end + end + + context 'for a pre-Markdown Note attachment file path' do + it 'returns AttachmentUploader as a string' do + assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader') + end + end + + context 'for a user avatar file path' do + it 'returns AvatarUploader as a string' do + assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader') + end + end + + context 'for a group avatar file path' do + it 'returns AvatarUploader as a string' do + assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader') + end + end + + context 'for a project avatar file path' do + it 'returns AvatarUploader as a string' do + assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader') + end + end + + context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do + it 'returns FileUploader as a string' do + project = create_project + + assert_uploader("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader') + end + end + end + + describe '#model_type' do + def assert_model_type(file_path, expected_model_type) + untracked_file = create_untracked_file(file_path) + + expect(untracked_file.model_type).to eq(expected_model_type) + end + + context 'for an appearance logo file path' do + it 'returns Appearance as a string' do + assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance') + end + end + + context 'for an appearance header_logo file path' do + it 'returns Appearance as a string' do + assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance') + end + end + + context 'for a pre-Markdown Note attachment file path' do + it 'returns Note as a string' do + assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note') + end + end + + context 'for a user avatar file path' do + it 'returns User as a string' do + assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User') + end + end + + context 'for a group avatar file path' do + it 'returns Namespace as a string' do + assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace') + end + end + + context 'for a project avatar file path' do + it 'returns Project as a string' do + assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project') + end + end + + context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do + it 'returns Project as a string' do + project = create_project + + assert_model_type("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'Project') + end + end + end + + describe '#model_id' do + def assert_model_id(file_path, expected_model_id) + untracked_file = create_untracked_file(file_path) + + expect(untracked_file.model_id).to eq(expected_model_id) + end + + context 'for an appearance logo file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1) + end + end + + context 'for an appearance header_logo file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1) + end + end + + context 'for a pre-Markdown Note attachment file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234) + end + end + + context 'for a user avatar file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234) + end + end + + context 'for a group avatar file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234) + end + end + + context 'for a project avatar file path' do + it 'returns the ID as a string' do + assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234) + end + end + + context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do + it 'returns the ID as a string' do + project = create_project + + assert_model_id("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", project.id) + end + end + end + + describe '#file_size' do + context 'for an appearance logo file path' do + let(:appearance) { create_or_update_appearance(logo: true) } + let(:untracked_file) { described_class.create!(path: get_uploads(appearance, 'Appearance').first.path) } + + it 'returns the file size' do + expect(untracked_file.file_size).to eq(1062) + end + end + + context 'for a project avatar file path' do + let(:project) { create_project(avatar: true) } + let(:untracked_file) { described_class.create!(path: get_uploads(project, 'Project').first.path) } + + it 'returns the file size' do + expect(untracked_file.file_size).to eq(1062) + end + end + + context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do + let(:project) { create_project } + let(:untracked_file) { create_untracked_file("/#{get_full_path(project)}/#{get_uploads(project, 'Project').first.path}") } + + before do + add_markdown_attachment(project) + end + + it 'returns the file size' do + expect(untracked_file.file_size).to eq(1062) + end + end + end + + def create_untracked_file(path_relative_to_upload_dir) + described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}") + end +end diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb index c8fa252439a..0d2074eed22 100644 --- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb @@ -1,17 +1,19 @@ require 'spec_helper' -# This migration is using UploadService, which sets uploads.secret that is only -# added to the DB schema in 20180129193323. Since the test isn't isolated, we -# just use the latest schema when testing this migration. -# Ideally, the test should not use factories nor UploadService, and rely on the -# `table` helper instead. -describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migration, schema: 20180129193323 do - include TrackUntrackedUploadsHelpers +# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work. +describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do + include MigrationsHelpers::TrackUntrackedUploadsHelpers subject { described_class.new } - let!(:untracked_files_for_uploads) { described_class::UntrackedFile } - let!(:uploads) { described_class::Upload } + let!(:appearances) { table(:appearances) } + let!(:namespaces) { table(:namespaces) } + let!(:notes) { table(:notes) } + let!(:projects) { table(:projects) } + let!(:routes) { table(:routes) } + let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) } + let!(:uploads) { table(:uploads) } + let!(:users) { table(:users) } before do ensure_temporary_tracking_table_exists @@ -19,30 +21,30 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra end context 'with untracked files and tracked files in untracked_files_for_uploads' do - let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) } - let!(:user1) { create(:user, :with_avatar) } - let!(:user2) { create(:user, :with_avatar) } - let!(:project1) { create(:project, :legacy_storage, :with_avatar) } - let!(:project2) { create(:project, :legacy_storage, :with_avatar) } + let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) } + let!(:user1) { create_user(avatar: true) } + let!(:user2) { create_user(avatar: true) } + let!(:project1) { create_project(avatar: true) } + let!(:project2) { create_project(avatar: true) } before do - UploadService.new(project1, uploaded_file, FileUploader).execute # Markdown upload - UploadService.new(project2, uploaded_file, FileUploader).execute # Markdown upload + add_markdown_attachment(project1) + add_markdown_attachment(project2) # File records created by PrepareUntrackedUploads - untracked_files_for_uploads.create!(path: appearance.uploads.first.path) - untracked_files_for_uploads.create!(path: appearance.uploads.last.path) - untracked_files_for_uploads.create!(path: user1.uploads.first.path) - untracked_files_for_uploads.create!(path: user2.uploads.first.path) - untracked_files_for_uploads.create!(path: project1.uploads.first.path) - untracked_files_for_uploads.create!(path: project2.uploads.first.path) - untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project1.full_path}/#{project1.uploads.last.path}") - untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/#{project2.uploads.last.path}") + untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').first.path) + untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').last.path) + untracked_files_for_uploads.create!(path: get_uploads(user1, 'User').first.path) + untracked_files_for_uploads.create!(path: get_uploads(user2, 'User').first.path) + untracked_files_for_uploads.create!(path: get_uploads(project1, 'Project').first.path) + untracked_files_for_uploads.create!(path: get_uploads(project2, 'Project').first.path) + untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project1).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project1, 'Project').last.path}") + untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project2).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project2, 'Project').last.path}") # Untrack 4 files - user2.uploads.delete_all - project2.uploads.delete_all # 2 files: avatar and a Markdown upload - appearance.uploads.where("path like '%header_logo%'").delete_all + get_uploads(user2, 'User').delete_all + get_uploads(project2, 'Project').delete_all # 2 files: avatar and a Markdown upload + get_uploads(appearance, 'Appearance').where("path like '%header_logo%'").delete_all end it 'adds untracked files to the uploads table' do @@ -50,9 +52,9 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra subject.perform(1, untracked_files_for_uploads.reorder(:id).last.id) end.to change { uploads.count }.from(4).to(8) - expect(user2.uploads.count).to eq(1) - expect(project2.uploads.count).to eq(2) - expect(appearance.uploads.count).to eq(2) + expect(get_uploads(user2, 'User').count).to eq(1) + expect(get_uploads(project2, 'Project').count).to eq(2) + expect(get_uploads(appearance, 'Appearance').count).to eq(2) end it 'deletes rows after processing them' do @@ -66,9 +68,9 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra it 'does not create duplicate uploads of already tracked files' do subject.perform(1, untracked_files_for_uploads.last.id) - expect(user1.uploads.count).to eq(1) - expect(project1.uploads.count).to eq(2) - expect(appearance.uploads.count).to eq(2) + expect(get_uploads(user1, 'User').count).to eq(1) + expect(get_uploads(project1, 'Project').count).to eq(2) + expect(get_uploads(appearance, 'Appearance').count).to eq(2) end it 'uses the start and end batch ids [only 1st half]' do @@ -80,11 +82,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra subject.perform(start_id, end_id) end.to change { uploads.count }.from(4).to(6) - expect(user1.uploads.count).to eq(1) - expect(user2.uploads.count).to eq(1) - expect(appearance.uploads.count).to eq(2) - expect(project1.uploads.count).to eq(2) - expect(project2.uploads.count).to eq(0) + expect(get_uploads(user1, 'User').count).to eq(1) + expect(get_uploads(user2, 'User').count).to eq(1) + expect(get_uploads(appearance, 'Appearance').count).to eq(2) + expect(get_uploads(project1, 'Project').count).to eq(2) + expect(get_uploads(project2, 'Project').count).to eq(0) # Only 4 have been either confirmed or added to uploads expect(untracked_files_for_uploads.count).to eq(4) @@ -99,11 +101,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra subject.perform(start_id, end_id) end.to change { uploads.count }.from(4).to(6) - expect(user1.uploads.count).to eq(1) - expect(user2.uploads.count).to eq(0) - expect(appearance.uploads.count).to eq(1) - expect(project1.uploads.count).to eq(2) - expect(project2.uploads.count).to eq(2) + expect(get_uploads(user1, 'User').count).to eq(1) + expect(get_uploads(user2, 'User').count).to eq(0) + expect(get_uploads(appearance, 'Appearance').count).to eq(1) + expect(get_uploads(project1, 'Project').count).to eq(2) + expect(get_uploads(project2, 'Project').count).to eq(2) # Only 4 have been either confirmed or added to uploads expect(untracked_files_for_uploads.count).to eq(4) @@ -122,7 +124,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra end it 'does not block a whole batch because of one bad path' do - untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a") + untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a") expect(untracked_files_for_uploads.count).to eq(9) expect(uploads.count).to eq(4) @@ -133,7 +135,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra end it 'an unparseable path is shown in error output' do - bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a" + bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a" untracked_files_for_uploads.create!(path: bad_path) expect(Rails.logger).to receive(:error).with(/Error parsing path "#{bad_path}":/) @@ -152,363 +154,100 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra describe 'upload outcomes for each path pattern' do shared_examples_for 'non_markdown_file' do - let!(:expected_upload_attrs) { model.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') } + let!(:expected_upload_attrs) { model_uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') } let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) } before do - model.uploads.delete_all + model_uploads.delete_all end it 'creates an Upload record' do expect do subject.perform(1, untracked_files_for_uploads.last.id) - end.to change { model.reload.uploads.count }.from(0).to(1) + end.to change { model_uploads.count }.from(0).to(1) - expect(model.uploads.first.attributes).to include(expected_upload_attrs) + expect(model_uploads.first.attributes).to include(expected_upload_attrs) end end context 'for an appearance logo file path' do - let(:model) { create_or_update_appearance(logo: uploaded_file) } + let(:model) { create_or_update_appearance(logo: true) } + let(:model_uploads) { get_uploads(model, 'Appearance') } it_behaves_like 'non_markdown_file' end context 'for an appearance header_logo file path' do - let(:model) { create_or_update_appearance(header_logo: uploaded_file) } + let(:model) { create_or_update_appearance(header_logo: true) } + let(:model_uploads) { get_uploads(model, 'Appearance') } it_behaves_like 'non_markdown_file' end context 'for a pre-Markdown Note attachment file path' do - let(:model) { create(:note, :with_attachment) } - let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') } + let(:model) { create_note(attachment: true) } + let!(:expected_upload_attrs) { get_uploads(model, 'Note').first.attributes.slice('path', 'uploader', 'size', 'checksum') } let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) } before do - Upload.where(model_type: 'Note', model_id: model.id).delete_all + get_uploads(model, 'Note').delete_all end # Can't use the shared example because Note doesn't have an `uploads` association it 'creates an Upload record' do expect do subject.perform(1, untracked_files_for_uploads.last.id) - end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1) + end.to change { get_uploads(model, 'Note').count }.from(0).to(1) - expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs) + expect(get_uploads(model, 'Note').first.attributes).to include(expected_upload_attrs) end end context 'for a user avatar file path' do - let(:model) { create(:user, :with_avatar) } + let(:model) { create_user(avatar: true) } + let(:model_uploads) { get_uploads(model, 'User') } it_behaves_like 'non_markdown_file' end context 'for a group avatar file path' do - let(:model) { create(:group, :with_avatar) } + let(:model) { create_group(avatar: true) } + let(:model_uploads) { get_uploads(model, 'Namespace') } it_behaves_like 'non_markdown_file' end context 'for a project avatar file path' do - let(:model) { create(:project, :legacy_storage, :with_avatar) } + let(:model) { create_project(avatar: true) } + let(:model_uploads) { get_uploads(model, 'Project') } it_behaves_like 'non_markdown_file' end context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - let(:model) { create(:project, :legacy_storage) } + let(:model) { create_project } before do # Upload the file - UploadService.new(model, uploaded_file, FileUploader).execute + add_markdown_attachment(model) # Create the untracked_files_for_uploads record - untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{model.full_path}/#{model.uploads.first.path}") + untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(model)}/#{get_uploads(model, 'Project').first.path}") # Save the expected upload attributes - @expected_upload_attrs = model.reload.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') + @expected_upload_attrs = get_uploads(model, 'Project').first.attributes.slice('path', 'uploader', 'size', 'checksum') # Untrack the file - model.reload.uploads.delete_all + get_uploads(model, 'Project').delete_all end it 'creates an Upload record' do expect do subject.perform(1, untracked_files_for_uploads.last.id) - end.to change { model.reload.uploads.count }.from(0).to(1) + end.to change { get_uploads(model, 'Project').count }.from(0).to(1) - expect(model.uploads.first.attributes).to include(@expected_upload_attrs) + expect(get_uploads(model, 'Project').first.attributes).to include(@expected_upload_attrs) end end end end - -describe Gitlab::BackgroundMigration::PopulateUntrackedUploads::UntrackedFile do - include TrackUntrackedUploadsHelpers - - let(:upload_class) { Gitlab::BackgroundMigration::PopulateUntrackedUploads::Upload } - - before(:all) do - ensure_temporary_tracking_table_exists - end - - describe '#upload_path' do - def assert_upload_path(file_path, expected_upload_path) - untracked_file = create_untracked_file(file_path) - - expect(untracked_file.upload_path).to eq(expected_upload_path) - end - - context 'for an appearance logo file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg') - end - end - - context 'for an appearance header_logo file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg') - end - end - - context 'for a pre-Markdown Note attachment file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf') - end - end - - context 'for a user avatar file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg') - end - end - - context 'for a group avatar file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg') - end - end - - context 'for a project avatar file path' do - it 'returns the file path relative to the CarrierWave root' do - assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg') - end - end - - context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - it 'returns the file path relative to the project directory in uploads' do - project = create(:project, :legacy_storage) - random_hex = SecureRandom.hex - - assert_upload_path("/#{project.full_path}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg") - end - end - end - - describe '#uploader' do - def assert_uploader(file_path, expected_uploader) - untracked_file = create_untracked_file(file_path) - - expect(untracked_file.uploader).to eq(expected_uploader) - end - - context 'for an appearance logo file path' do - it 'returns AttachmentUploader as a string' do - assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader') - end - end - - context 'for an appearance header_logo file path' do - it 'returns AttachmentUploader as a string' do - assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader') - end - end - - context 'for a pre-Markdown Note attachment file path' do - it 'returns AttachmentUploader as a string' do - assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader') - end - end - - context 'for a user avatar file path' do - it 'returns AvatarUploader as a string' do - assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader') - end - end - - context 'for a group avatar file path' do - it 'returns AvatarUploader as a string' do - assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader') - end - end - - context 'for a project avatar file path' do - it 'returns AvatarUploader as a string' do - assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader') - end - end - - context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - it 'returns FileUploader as a string' do - project = create(:project, :legacy_storage) - - assert_uploader("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader') - end - end - end - - describe '#model_type' do - def assert_model_type(file_path, expected_model_type) - untracked_file = create_untracked_file(file_path) - - expect(untracked_file.model_type).to eq(expected_model_type) - end - - context 'for an appearance logo file path' do - it 'returns Appearance as a string' do - assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance') - end - end - - context 'for an appearance header_logo file path' do - it 'returns Appearance as a string' do - assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance') - end - end - - context 'for a pre-Markdown Note attachment file path' do - it 'returns Note as a string' do - assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note') - end - end - - context 'for a user avatar file path' do - it 'returns User as a string' do - assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User') - end - end - - context 'for a group avatar file path' do - it 'returns Namespace as a string' do - assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace') - end - end - - context 'for a project avatar file path' do - it 'returns Project as a string' do - assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project') - end - end - - context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - it 'returns Project as a string' do - project = create(:project, :legacy_storage) - - assert_model_type("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'Project') - end - end - end - - describe '#model_id' do - def assert_model_id(file_path, expected_model_id) - untracked_file = create_untracked_file(file_path) - - expect(untracked_file.model_id).to eq(expected_model_id) - end - - context 'for an appearance logo file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1) - end - end - - context 'for an appearance header_logo file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1) - end - end - - context 'for a pre-Markdown Note attachment file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234) - end - end - - context 'for a user avatar file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234) - end - end - - context 'for a group avatar file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234) - end - end - - context 'for a project avatar file path' do - it 'returns the ID as a string' do - assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234) - end - end - - context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - it 'returns the ID as a string' do - project = create(:project, :legacy_storage) - - assert_model_id("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", project.id) - end - end - end - - describe '#file_size' do - context 'for an appearance logo file path' do - let(:appearance) { create_or_update_appearance(logo: uploaded_file) } - let(:untracked_file) { described_class.create!(path: appearance.uploads.first.path) } - - it 'returns the file size' do - expect(untracked_file.file_size).to eq(35255) - end - - it 'returns the same thing that CarrierWave would return' do - expect(untracked_file.file_size).to eq(appearance.logo.size) - end - end - - context 'for a project avatar file path' do - let(:project) { create(:project, :legacy_storage, avatar: uploaded_file) } - let(:untracked_file) { described_class.create!(path: project.uploads.first.path) } - - it 'returns the file size' do - expect(untracked_file.file_size).to eq(35255) - end - - it 'returns the same thing that CarrierWave would return' do - expect(untracked_file.file_size).to eq(project.avatar.size) - end - end - - context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do - let(:project) { create(:project, :legacy_storage) } - let(:untracked_file) { create_untracked_file("/#{project.full_path}/#{project.uploads.first.path}") } - - before do - UploadService.new(project, uploaded_file, FileUploader).execute - end - - it 'returns the file size' do - expect(untracked_file.file_size).to eq(35255) - end - - it 'returns the same thing that CarrierWave would return' do - expect(untracked_file.file_size).to eq(project.uploads.first.size) - end - end - end - - def create_untracked_file(path_relative_to_upload_dir) - described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}") - end -end diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb index ca77e64ae40..35750d89c35 100644 --- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb +++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb @@ -1,10 +1,16 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migration, schema: 20180129193323 do - include TrackUntrackedUploadsHelpers - include MigrationsHelpers - - let!(:untracked_files_for_uploads) { described_class::UntrackedFile } +# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work. +describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do + include MigrationsHelpers::TrackUntrackedUploadsHelpers + + let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) } + let!(:appearances) { table(:appearances) } + let!(:namespaces) { table(:namespaces) } + let!(:projects) { table(:projects) } + let!(:routes) { table(:routes) } + let!(:uploads) { table(:uploads) } + let!(:users) { table(:users) } around do |example| # Especially important so the follow-up migration does not get run @@ -15,19 +21,17 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migrat shared_examples 'prepares the untracked_files_for_uploads table' do context 'when files were uploaded before and after hashed storage was enabled' do - let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) } - let!(:user) { create(:user, :with_avatar) } - let!(:project1) { create(:project, :with_avatar, :legacy_storage) } - let(:project2) { create(:project) } # instantiate after enabling hashed_storage + let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) } + let!(:user) { create_user(avatar: true) } + let!(:project1) { create_project(avatar: true) } + let(:project2) { create_project } # instantiate after enabling hashed_storage before do # Markdown upload before enabling hashed_storage - UploadService.new(project1, uploaded_file, FileUploader).execute - - stub_application_setting(hashed_storage_enabled: true) + add_markdown_attachment(project1) # Markdown upload after enabling hashed_storage - UploadService.new(project2, uploaded_file, FileUploader).execute + add_markdown_attachment(project2, hashed_storage: true) end it 'has a path field long enough for really long paths' do @@ -61,7 +65,7 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migrat it 'does not add hashed files to the untracked_files_for_uploads table' do described_class.new.perform - hashed_file_path = project2.uploads.where(uploader: 'FileUploader').first.path + hashed_file_path = get_uploads(project2, 'Project').where(uploader: 'FileUploader').first.path expect(untracked_files_for_uploads.where("path like '%#{hashed_file_path}%'").exists?).to be_falsey end diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index 475b5c5cfb2..b49ddbfc780 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -190,7 +190,7 @@ describe Gitlab::Checks::ChangeAccess do context 'with LFS not enabled' do it 'skips the validation' do - expect_any_instance_of(described_class).not_to receive(:lfs_file_locks_validation) + expect_any_instance_of(Gitlab::Checks::CommitCheck).not_to receive(:validate) subject.exec end @@ -207,7 +207,7 @@ describe Gitlab::Checks::ChangeAccess do end end - context 'when change is sent by the author od the lock' do + context 'when change is sent by the author of the lock' do let(:user) { owner } it "doesn't raise any error" do diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 28c679af12a..8d4862932b2 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -365,6 +365,20 @@ describe Gitlab::ClosingIssueExtractor do .to match_array([issue, other_issue, third_issue]) end + it 'allows oxford commas (comma before and) when referencing multiple issues' do + message = "Closes #{reference}, #{reference2}, and #{reference3}" + + expect(subject.closed_by_message(message)) + .to match_array([issue, other_issue, third_issue]) + end + + it 'allows spaces before commas when referencing multiple issues' do + message = "Closes #{reference} , #{reference2} , and #{reference3}" + + expect(subject.closed_by_message(message)) + .to match_array([issue, other_issue, third_issue]) + end + it 'fetches issues in multi-line message' do message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}" diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 59e9e1cc94c..a6341cd509b 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -276,6 +276,7 @@ describe Gitlab::Git::Blob, seed_helper: true do expect(blobs.count).to eq(1) expect(blobs).to all( be_a(Gitlab::Git::Blob) ) + expect(blobs).to be_an(Array) end it 'accepts blob IDs as a lazy enumerator' do diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 85e6efd7ca2..0b20a6349a2 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -102,6 +102,10 @@ describe Gitlab::Git::Commit, seed_helper: true do expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_valid_commit end + it "returns an array of parent ids" do + expect(described_class.find(repository, SeedRepo::Commit::ID).parent_ids).to be_an(Array) + end + it "should return valid commit for tag" do expect(described_class.find(repository, 'v1.0.0').id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index edcf8889c27..d601a383a98 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require "spec_helper" describe Gitlab::Git::Repository, seed_helper: true do @@ -599,6 +600,33 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#branch_names_contains_sha' do + shared_examples 'returning the right branches' do + let(:head_id) { repository.rugged.head.target.oid } + let(:new_branch) { head_id } + + before do + repository.create_branch(new_branch, 'master') + end + + after do + repository.delete_branch(new_branch) + end + + it 'displays that branch' do + expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch) + end + end + + context 'when Gitaly is enabled' do + it_behaves_like 'returning the right branches' + end + + context 'when Gitaly is disabled', :disable_gitaly do + it_behaves_like 'returning the right branches' + end + end + describe "#refs_hash" do subject { repository.refs_hash } @@ -1589,7 +1617,7 @@ describe Gitlab::Git::Repository, seed_helper: true do expected_languages = [ { value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" }, { value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" }, - { value: 7.9, label: "HTML", color: "#e44b23", highlight: "#e44b23" }, + { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" }, { value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" } ] @@ -2203,7 +2231,7 @@ describe Gitlab::Git::Repository, seed_helper: true do context 'sparse checkout', :skip_gitaly_mock do let(:expected_files) { %w(files files/js files/js/application.js) } - before do + it 'checks out only the files in the diff' do allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args| m.call(*args) do worktree_path = args[0] @@ -2215,11 +2243,45 @@ describe Gitlab::Git::Repository, seed_helper: true do expect(Dir[files_pattern]).to eq(expected) end end - end - it 'checkouts only the files in the diff' do subject 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 } + + 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) + end + + it 'does not include the renamed file in the sparse checkout' do + allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args| + m.call(*args) do + worktree_path = args[0] + files_pattern = File.join(worktree_path, '**', '*') + + expect(Dir[files_pattern]).not_to include('CHANGELOG') + expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG') + end + end + + subject + end + end + end + + context 'with an ASCII-8BIT diff', :skip_gitaly_mock do + let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" } + + it 'applies a ASCII-8BIT diff' do + allow(repository).to receive(:run_git!).and_call_original + allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT')) + + expect(subject).to match(/\h{40}/) + end end end end diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 186b2d9279d..215f1ecc9c5 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::GitAccessWiki do let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) } - let(:project) { create(:project, :repository) } + let(:project) { create(:project, :wiki_repo) } let(:user) { create(:user) } let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] } let(:redirected_path) { nil } @@ -48,6 +48,18 @@ describe Gitlab::GitAccessWiki do it 'give access to download wiki code' do expect { subject }.not_to raise_error end + + context 'when the wiki repository does not exist' do + it 'returns not found' do + wiki_repo = project.wiki.repository + FileUtils.rm_rf(wiki_repo.path) + + # Sanity check for rm_rf + expect(wiki_repo.exists?).to eq(false) + + expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'A repository for this project does not exist yet.') + end + end end context 'when wiki feature is disabled' do diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index cbc7ce1c1b0..c50e73cecfc 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -84,4 +84,30 @@ describe Gitlab::GitalyClient::RepositoryService do expect(client.has_local_branches?).to be(true) end end + + describe '#rebase_in_progress?' do + let(:rebase_id) { 1 } + + it 'sends a repository_rebase_in_progress message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:is_rebase_in_progress) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(in_progress: true)) + + client.rebase_in_progress?(rebase_id) + end + end + + describe '#squash_in_progress?' do + let(:squash_id) { 1 } + + it 'sends a repository_squash_in_progress message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:is_squash_in_progress) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(in_progress: true)) + + client.squash_in_progress?(squash_id) + end + end end diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb index 46a57e08963..5bedfc79dd3 100644 --- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb @@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do import_source: 'foo/bar', repository_storage_path: 'foo', disk_path: 'foo', - repository: repository + repository: repository, + create_wiki: true ) end @@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do expect(importer.import_wiki_repository).to eq(true) end - it 'marks the import as failed if an error was raised' do + it 'marks the import as failed and creates an empty repo if an error was raised' do expect(importer.gitlab_shell) .to receive(:import_repository) .and_raise(Gitlab::Shell::Error) @@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do .to receive(:fail_import) .and_return(false) + expect(project) + .to receive(:create_wiki) + expect(importer.import_wiki_repository).to eq(false) end end diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb index e3bf2801406..67c62458f0f 100644 --- a/spec/lib/gitlab/gpg/commit_spec.rb +++ b/spec/lib/gitlab/gpg/commit_spec.rb @@ -49,7 +49,9 @@ describe Gitlab::Gpg::Commit do end it 'returns a valid signature' do - expect(described_class.new(commit).signature).to have_attributes( + signature = described_class.new(commit).signature + + expect(signature).to have_attributes( commit_sha: commit_sha, project: project, gpg_key: gpg_key, @@ -58,9 +60,31 @@ describe Gitlab::Gpg::Commit do gpg_key_user_email: GpgHelpers::User1.emails.first, verification_status: 'verified' ) + expect(signature.persisted?).to be_truthy end it_behaves_like 'returns the cached signature on second call' + + context 'read-only mode' do + before do + allow(Gitlab::Database).to receive(:read_only?).and_return(true) + end + + it 'does not create a cached signature' do + signature = described_class.new(commit).signature + + expect(signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + verification_status: 'verified' + ) + expect(signature.persisted?).to be_falsey + end + end end context 'commit signed with a subkey' do diff --git a/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb b/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb index b49bc5c328c..f8faeffb935 100644 --- a/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb +++ b/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb @@ -1,19 +1,34 @@ require 'spec_helper' describe Gitlab::QueryLimiting::ActiveSupportSubscriber do + let(:transaction) { instance_double(Gitlab::QueryLimiting::Transaction, increment: true) } + + before do + allow(Gitlab::QueryLimiting::Transaction) + .to receive(:current) + .and_return(transaction) + end + describe '#sql' do it 'increments the number of executed SQL queries' do - transaction = double(:transaction) - - allow(Gitlab::QueryLimiting::Transaction) - .to receive(:current) - .and_return(transaction) + User.count expect(transaction) - .to receive(:increment) - .at_least(:once) + .to have_received(:increment) + .once + end - User.count + context 'when the query is actually a rails cache hit' do + it 'does not increment the number of executed SQL queries' do + ActiveRecord::Base.connection.cache do + User.count + User.count + end + + expect(transaction) + .to have_received(:increment) + .once + end end end end diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb index 93d538141ce..a6ea07e8b6d 100644 --- a/spec/lib/gitlab/ssh_public_key_spec.rb +++ b/spec/lib/gitlab/ssh_public_key_spec.rb @@ -37,11 +37,60 @@ describe Gitlab::SSHPublicKey, lib: true do end end + describe '.sanitize(key_content)' do + let(:content) { build(:key).key } + + context 'when key has blank space characters' do + it 'removes the extra blank space characters' do + unsanitized = content.insert(100, "\n") + .insert(40, "\r\n") + .insert(30, ' ') + + sanitized = described_class.sanitize(unsanitized) + _, body = sanitized.split + + expect(sanitized).not_to eq(unsanitized) + expect(body).not_to match(/\s/) + end + end + + context "when key doesn't have blank space characters" do + it "doesn't modify the content" do + sanitized = described_class.sanitize(content) + + expect(sanitized).to eq(content) + end + end + + context "when key is invalid" do + it 'returns the original content' do + unsanitized = "ssh-foo any content==" + sanitized = described_class.sanitize(unsanitized) + + expect(sanitized).to eq(unsanitized) + end + end + end + describe '#valid?' do subject { public_key } context 'with a valid SSH key' do - it { is_expected.to be_valid } + where(:factory) do + %i(rsa_key_2048 + rsa_key_4096 + rsa_key_5120 + rsa_key_8192 + dsa_key_2048 + ecdsa_key_256 + ed25519_key_256) + end + + with_them do + let(:key) { attributes_for(factory)[:key] } + + it { is_expected.to be_valid } + end end context 'with an invalid SSH key' do @@ -82,6 +131,9 @@ describe Gitlab::SSHPublicKey, lib: true do where(:factory, :bits) do [ [:rsa_key_2048, 2048], + [:rsa_key_4096, 4096], + [:rsa_key_5120, 5120], + [:rsa_key_8192, 8192], [:dsa_key_2048, 2048], [:ecdsa_key_256, 256], [:ed25519_key_256, 256] @@ -106,8 +158,11 @@ describe Gitlab::SSHPublicKey, lib: true do where(:factory, :fingerprint) do [ - [:rsa_key_2048, '2e:ca:dc:e0:37:29:ed:fc:f0:1d:bf:66:d4:cd:51:b1'], - [:dsa_key_2048, 'bc:c1:a4:be:7e:8c:84:56:b3:58:93:53:c6:80:78:8c'], + [:rsa_key_2048, '58:a8:9d:cd:1f:70:f8:5a:d9:e4:24:8e:da:89:e4:fc'], + [:rsa_key_4096, 'df:73:db:29:3c:a5:32:cf:09:17:7e:8e:9d:de:d7:f7'], + [:rsa_key_5120, 'fe:fa:3a:4d:7d:51:ec:bf:c7:64:0c:96:d0:17:8a:d0'], + [:rsa_key_8192, 'fb:53:7f:e9:2f:f7:17:aa:c8:32:52:06:8e:05:e2:82'], + [:dsa_key_2048, 'c8:85:1e:df:44:0f:20:00:3c:66:57:2b:21:10:5a:27'], [:ecdsa_key_256, '67:a3:a9:7d:b8:e1:15:d4:80:40:21:34:bb:ed:97:38'], [:ed25519_key_256, 'e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73'] ] diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 59eda025108..bcbb9287199 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -463,59 +463,30 @@ describe Notify do end describe 'project access requested' do - context 'for a project in a user namespace' do - let(:project) do - create(:project, :public, :access_requestable) do |project| - project.add_master(project.owner, current_user: project.owner) - end - end - - let(:project_member) do - project.request_access(user) - project.requesters.find_by(user_id: user.id) - end - subject { described_class.member_access_requested_email('project', project_member.id) } - - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like "a user cannot unsubscribe through footer link" - - it 'contains all the useful information' do - to_emails = subject.header[:to].addrs - expect(to_emails.size).to eq(1) - expect(to_emails[0].address).to eq(project.members.owners_and_masters.first.user.notification_email) - - is_expected.to have_subject "Request to join the #{project.name_with_namespace} project" - is_expected.to have_html_escaped_body_text project.name_with_namespace - is_expected.to have_body_text project_project_members_url(project) - is_expected.to have_body_text project_member.human_access + let(:project) do + create(:project, :public, :access_requestable) do |project| + project.add_master(project.owner) end end - context 'for a project in a group' do - let(:group_owner) { create(:user) } - let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } } - let(:project) { create(:project, :public, :access_requestable, namespace: group) } - let(:project_member) do - project.request_access(user) - project.requesters.find_by(user_id: user.id) - end - subject { described_class.member_access_requested_email('project', project_member.id) } + let(:project_member) do + project.request_access(user) + project.requesters.find_by(user_id: user.id) + end + subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) } - it_behaves_like 'an email sent from GitLab' - it_behaves_like 'it should not have Gmail Actions links' - it_behaves_like "a user cannot unsubscribe through footer link" + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like "a user cannot unsubscribe through footer link" - it 'contains all the useful information' do - to_emails = subject.header[:to].addrs - expect(to_emails.size).to eq(1) - expect(to_emails[0].address).to eq(group.members.owners_and_masters.first.user.notification_email) + it 'contains all the useful information' do + to_emails = subject.header[:to].addrs.map(&:address) + expect(to_emails).to eq([recipient.notification_email]) - is_expected.to have_subject "Request to join the #{project.name_with_namespace} project" - is_expected.to have_html_escaped_body_text project.name_with_namespace - is_expected.to have_body_text project_project_members_url(project) - is_expected.to have_body_text project_member.human_access - end + is_expected.to have_subject "Request to join the #{project.name_with_namespace} project" + is_expected.to have_html_escaped_body_text project.name_with_namespace + is_expected.to have_body_text project_project_members_url(project) + is_expected.to have_body_text project_member.human_access end end @@ -959,13 +930,16 @@ describe Notify do group.request_access(user) group.requesters.find_by(user_id: user.id) end - subject { described_class.member_access_requested_email('group', group_member.id) } + subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it 'contains all the useful information' do + to_emails = subject.header[:to].addrs.map(&:address) + expect(to_emails).to eq([recipient.notification_email]) + is_expected.to have_subject "Request to join the #{group.name} group" is_expected.to have_html_escaped_body_text group.name is_expected.to have_body_text group_group_members_url(group) diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb index fe4d5b8a279..2fccfb3f12c 100644 --- a/spec/migrations/track_untracked_uploads_spec.rb +++ b/spec/migrations/track_untracked_uploads_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_uploads') describe TrackUntrackedUploads, :migration, :sidekiq do - include TrackUntrackedUploadsHelpers + include MigrationsHelpers::TrackUntrackedUploadsHelpers it 'correctly schedules the follow-up background migration' do Sidekiq::Testing.fake! do diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 7398fd25aa8..06d26ef89f1 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -12,6 +12,9 @@ describe Key, :mailer do it { is_expected.to validate_presence_of(:key) } it { is_expected.to validate_length_of(:key).is_at_most(5000) } it { is_expected.to allow_value(attributes_for(:rsa_key_2048)[:key]).for(:key) } + it { is_expected.to allow_value(attributes_for(:rsa_key_4096)[:key]).for(:key) } + it { is_expected.to allow_value(attributes_for(:rsa_key_5120)[:key]).for(:key) } + it { is_expected.to allow_value(attributes_for(:rsa_key_8192)[:key]).for(:key) } it { is_expected.to allow_value(attributes_for(:dsa_key_2048)[:key]).for(:key) } it { is_expected.to allow_value(attributes_for(:ecdsa_key_256)[:key]).for(:key) } it { is_expected.to allow_value(attributes_for(:ed25519_key_256)[:key]).for(:key) } @@ -72,16 +75,35 @@ describe Key, :mailer do expect(build(:key)).to be_valid end - it 'accepts a key with newline charecters after stripping them' do - key = build(:key) - key.key = key.key.insert(100, "\n") - key.key = key.key.insert(40, "\r\n") - expect(key).to be_valid - end - it 'rejects the unfingerprintable key (not a key)' do expect(build(:key, key: 'ssh-rsa an-invalid-key==')).not_to be_valid end + + where(:factory, :chars, :expected_sections) do + [ + [:key, ["\n", "\r\n"], 3], + [:key, [' ', ' '], 3], + [:key_without_comment, [' ', ' '], 2] + ] + end + + with_them do + let!(:key) { create(factory) } + let!(:original_fingerprint) { key.fingerprint } + + it 'accepts a key with blank space characters after stripping them' do + modified_key = key.key.insert(100, chars.first).insert(40, chars.last) + _, content = modified_key.split + + key.update!(key: modified_key) + + expect(key).to be_valid + expect(key.key.split.size).to eq(expected_sections) + + expect(content).not_to match(/\s/) + expect(original_fingerprint).to eq(key.fingerprint) + end + end end context 'validate it meets key restrictions' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 1815696a8a0..3531de244bd 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -496,6 +496,14 @@ describe User do user2.update_tracked_fields!(request) end.to change { user2.reload.current_sign_in_at } end + + it 'does not write if the DB is in read-only mode' do + expect(Gitlab::Database).to receive(:read_only?).and_return(true) + + expect do + user.update_tracked_fields!(request) + end.not_to change { user.reload.current_sign_in_at } + end end shared_context 'user keys' do diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 31959d28fee..ad3eec88952 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -698,6 +698,7 @@ describe API::Commits do get api(route, current_user) expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers expect(json_response.size).to be >= 1 expect(json_response.first.keys).to include 'diff' end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index c7df6251d74..827f4c04324 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::Internal do let(:user) { create(:user) } let(:key) { create(:key, user: user) } - let(:project) { create(:project, :repository) } + let(:project) { create(:project, :repository, :wiki_repo) } let(:secret_token) { Gitlab::Shell.secret_token } let(:gl_repository) { "project-#{project.id}" } let(:reference_counter) { double('ReferenceCounter') } diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 13db40d21a5..e6d7b9fde02 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Issues, :mailer do +describe API::Issues do set(:user) { create(:user) } set(:project) do create(:project, :public, creator_id: user.id, namespace: user.namespace) @@ -932,18 +932,6 @@ describe API::Issues, :mailer do expect(json_response['error']).to eq('confidential is invalid') end - it "sends notifications for subscribers of newly added labels" do - label = project.labels.first - label.toggle_subscription(user2, project) - - perform_enqueued_jobs do - post api("/projects/#{project.id}/issues", user), - title: 'new issue', labels: label.title - end - - should_email(user2) - end - it "returns a 400 bad request if title not given" do post api("/projects/#{project.id}/issues", user), labels: 'label, label2' expect(response).to have_gitlab_http_status(400) @@ -1246,18 +1234,6 @@ describe API::Issues, :mailer do expect(json_response['labels']).to eq([label.title]) end - it "sends notifications for subscribers of newly added labels when issue is updated" do - label = create(:label, title: 'foo', color: '#FFAABB', project: project) - label.toggle_subscription(user2, project) - - perform_enqueued_jobs do - put api("/projects/#{project.id}/issues/#{issue.iid}", user), - title: 'updated title', labels: label.title - end - - should_email(user2) - end - it 'removes all labels' do put api("/projects/#{project.id}/issues/#{issue.iid}", user), labels: '' @@ -1380,7 +1356,7 @@ describe API::Issues, :mailer do end describe '/projects/:id/issues/:issue_iid/move' do - let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) } + let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) } let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) } it 'moves an issue' do diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb index 5d01dc37f0e..025165622b7 100644 --- a/spec/requests/api/pages_domains_spec.rb +++ b/spec/requests/api/pages_domains_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe API::PagesDomains do - set(:project) { create(:project) } + set(:project) { create(:project, path: 'my.project') } set(:user) { create(:user) } set(:admin) { create(:admin) } @@ -16,6 +16,7 @@ describe API::PagesDomains do let(:route) { "/projects/#{project.id}/pages/domains" } let(:route_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain.domain}" } + let(:route_domain_path) { "/projects/#{project.path_with_namespace.gsub('/', '%2F')}/pages/domains/#{pages_domain.domain}" } let(:route_secure_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_secure.domain}" } let(:route_expired_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_expired.domain}" } let(:route_vacant_domain) { "/projects/#{project.id}/pages/domains/www.vacant-domain.test" } @@ -144,6 +145,16 @@ describe API::PagesDomains do expect(json_response['certificate']).to be_nil end + it 'returns pages domain with project path' do + get api(route_domain_path, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(json_response['domain']).to eq(pages_domain.domain) + expect(json_response['url']).to eq(pages_domain.url) + expect(json_response['certificate']).to be_nil + end + it 'returns pages domain with a certificate' do get api(route_secure_domain, user) diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb new file mode 100644 index 00000000000..987f6e26971 --- /dev/null +++ b/spec/requests/api/project_import_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe API::ProjectImport do + let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } + let(:user) { create(:user) } + let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:namespace) { create(:group) } + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + + namespace.add_owner(user) + end + + after do + FileUtils.rm_rf(export_path, secure: true) + end + + describe 'POST /projects/import' do + it 'schedules an import using a namespace' do + stub_import(namespace) + + post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id + + expect(response).to have_gitlab_http_status(201) + end + + it 'schedules an import using the namespace path' do + stub_import(namespace) + + post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path + + expect(response).to have_gitlab_http_status(201) + end + + it 'schedules an import at the user namespace level' do + stub_import(user.namespace) + + post api('/projects/import', user), path: 'test-import2', file: fixture_file_upload(file) + + expect(response).to have_gitlab_http_status(201) + end + + it 'schedules an import at the user namespace level' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + expect(::Projects::CreateService).not_to receive(:new) + + post api('/projects/import', user), namespace: 'nonexistent', path: 'test-import2', file: fixture_file_upload(file) + + expect(response).to have_gitlab_http_status(404) + expect(json_response['message']).to eq('404 Namespace Not Found') + end + + it 'does not schedule an import if the user has no permission to the namespace' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + post(api('/projects/import', create(:user)), + path: 'test-import3', + file: fixture_file_upload(file), + namespace: namespace.full_path) + + expect(response).to have_gitlab_http_status(404) + expect(json_response['message']).to eq('404 Namespace Not Found') + end + + it 'does not schedule an import if the user uploads no valid file' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + post api('/projects/import', user), path: 'test-import3', file: './random/test' + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to eq('file is invalid') + end + + def stub_import(namespace) + expect_any_instance_of(Project).to receive(:import_schedule) + expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original + end + end + + describe 'GET /projects/:id/import' do + it 'returns the import status' do + project = create(:project, import_status: 'started') + project.add_master(user) + + get api("/projects/#{project.id}/import", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to include('import_status' => 'started') + end + + it 'returns the import status and the error if failed' do + project = create(:project, import_status: 'failed', import_error: 'error') + project.add_master(user) + + get api("/projects/#{project.id}/import", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to include('import_status' => 'failed', + 'import_error' => 'error') + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 00dd8897e6a..cee93f6ed14 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -7,7 +7,7 @@ describe API::Projects do let(:user3) { create(:user) } let(:admin) { create(:admin) } let(:project) { create(:project, namespace: user.namespace) } - let(:project2) { create(:project, path: 'project2', namespace: user.namespace) } + let(:project2) { create(:project, namespace: user.namespace) } let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') } let(:project_member) { create(:project_member, :developer, user: user3, project: project) } let(:user4) { create(:user) } @@ -315,7 +315,7 @@ describe API::Projects do context 'and with all query parameters' do let!(:project5) { create(:project, :public, path: 'gitlab5', namespace: create(:namespace)) } - let!(:project6) { create(:project, :public, path: 'project6', namespace: user.namespace) } + let!(:project6) { create(:project, :public, namespace: user.namespace) } let!(:project7) { create(:project, :public, path: 'gitlab7', namespace: user.namespace) } let!(:project8) { create(:project, path: 'gitlab8', namespace: user.namespace) } let!(:project9) { create(:project, :public, path: 'gitlab9') } diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb index 0dd6d673625..0e745c82395 100644 --- a/spec/requests/api/v3/issues_spec.rb +++ b/spec/requests/api/v3/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::V3::Issues, :mailer do +describe API::V3::Issues do set(:user) { create(:user) } set(:user2) { create(:user) } set(:non_member) { create(:user) } @@ -780,18 +780,6 @@ describe API::V3::Issues, :mailer do expect(json_response['error']).to eq('confidential is invalid') end - it "sends notifications for subscribers of newly added labels" do - label = project.labels.first - label.toggle_subscription(user2, project) - - perform_enqueued_jobs do - post v3_api("/projects/#{project.id}/issues", user), - title: 'new issue', labels: label.title - end - - should_email(user2) - end - it "returns a 400 bad request if title not given" do post v3_api("/projects/#{project.id}/issues", user), labels: 'label, label2' @@ -1045,18 +1033,6 @@ describe API::V3::Issues, :mailer do expect(json_response['labels']).to eq([label.title]) end - it "sends notifications for subscribers of newly added labels when issue is updated" do - label = create(:label, title: 'foo', color: '#FFAABB', project: project) - label.toggle_subscription(user2, project) - - perform_enqueued_jobs do - put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), - title: 'updated title', labels: label.title - end - - should_email(user2) - end - it 'removes all labels' do put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), labels: '' @@ -1191,7 +1167,7 @@ describe API::V3::Issues, :mailer do end describe '/projects/:id/issues/:issue_id/move' do - let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) } + let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) } let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) } it 'moves an issue' do diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index bf36d3e245a..4c25bd935c6 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -6,7 +6,7 @@ describe API::V3::Projects do let(:user3) { create(:user) } let(:admin) { create(:admin) } let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) } + let(:project2) { create(:project, creator_id: user.id, namespace: user.namespace) } let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') } let(:project_member) { create(:project_member, :developer, user: user3, project: project) } let(:user4) { create(:user) } diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 942e5b2bb1b..c6fdda203ad 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -150,7 +150,7 @@ describe 'Git HTTP requests' do let(:path) { "/#{wiki.repository.full_path}.git" } context "when the project is public" do - let(:project) { create(:project, :repository, :public, :wiki_enabled) } + let(:project) { create(:project, :wiki_repo, :public, :wiki_enabled) } it_behaves_like 'pushes require Basic HTTP Authentication' @@ -177,7 +177,7 @@ describe 'Git HTTP requests' do end context 'but the repo is disabled' do - let(:project) { create(:project, :repository, :public, :repository_disabled, :wiki_enabled) } + let(:project) { create(:project, :wiki_repo, :public, :repository_disabled, :wiki_enabled) } it_behaves_like 'pulls are allowed' it_behaves_like 'pushes are allowed' @@ -198,7 +198,7 @@ describe 'Git HTTP requests' do end context "when the project is private" do - let(:project) { create(:project, :repository, :private, :wiki_enabled) } + let(:project) { create(:project, :wiki_repo, :private, :wiki_enabled) } it_behaves_like 'pulls require Basic HTTP Authentication' it_behaves_like 'pushes require Basic HTTP Authentication' @@ -210,7 +210,7 @@ describe 'Git HTTP requests' do end context 'but the repo is disabled' do - let(:project) { create(:project, :repository, :private, :repository_disabled, :wiki_enabled) } + let(:project) { create(:project, :wiki_repo, :private, :repository_disabled, :wiki_enabled) } it 'allows clones' do download(path, user: user.username, password: user.password) do |response| diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 322c91065e7..c148a98569b 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -232,6 +232,28 @@ describe Issues::MoveService do end end + context 'issue with assignee' do + let(:assignee) { create(:user) } + + before do + old_issue.assignees = [assignee] + end + + it 'preserves assignee with access to the new issue' do + new_project.add_reporter(assignee) + + new_issue = move_service.execute(old_issue, new_project) + + expect(new_issue.assignees).to eq([assignee]) + end + + it 'ignores assignee without access to the new issue' do + new_issue = move_service.execute(old_issue, new_project) + + expect(new_issue.assignees).to be_empty + end + end + context 'notes with references' do before do create(:merge_request, source_project: old_project) diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 35eb84e5e88..836ffb7cea0 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1307,6 +1307,33 @@ describe NotificationService, :mailer do end describe 'GroupMember' do + let(:added_user) { create(:user) } + + describe '#new_access_request' do + let(:master) { create(:user) } + let(:owner) { create(:user) } + let(:developer) { create(:user) } + let!(:group) do + create(:group, :public, :access_requestable) do |group| + group.add_owner(owner) + group.add_master(master) + group.add_developer(developer) + end + end + + before do + reset_delivered_emails! + end + + it 'sends notification to group owners_and_masters' do + group.request_access(added_user) + + should_email(owner) + should_email(master) + should_not_email(developer) + end + end + describe '#decline_group_invite' do let(:creator) { create(:user) } let(:group) { create(:group) } @@ -1328,18 +1355,9 @@ describe NotificationService, :mailer do describe '#new_group_member' do let(:group) { create(:group) } - let(:added_user) { create(:user) } - - def create_member! - GroupMember.create( - group: group, - user: added_user, - access_level: Gitlab::Access::GUEST - ) - end it 'sends a notification' do - create_member! + group.add_guest(added_user) should_only_email(added_user) end @@ -1349,7 +1367,7 @@ describe NotificationService, :mailer do end it 'does not send a notification' do - create_member! + group.add_guest(added_user) should_not_email_anyone end end @@ -1357,8 +1375,42 @@ describe NotificationService, :mailer do end describe 'ProjectMember' do + let(:project) { create(:project) } + set(:added_user) { create(:user) } + + describe '#new_access_request' do + context 'for a project in a user namespace' do + let(:project) do + create(:project, :public, :access_requestable) do |project| + project.add_master(project.owner) + end + end + + it 'sends notification to project owners_and_masters' do + project.request_access(added_user) + + should_only_email(project.owner) + end + end + + context 'for a project in a group' do + let(:group_owner) { create(:user) } + let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } } + let!(:project) { create(:project, :public, :access_requestable, namespace: group) } + + before do + reset_delivered_emails! + end + + it 'sends notification to group owners_and_masters' do + project.request_access(added_user) + + should_only_email(group_owner) + end + end + end + describe '#decline_group_invite' do - let(:project) { create(:project) } let(:member) { create(:user) } before do @@ -1375,19 +1427,12 @@ describe NotificationService, :mailer do end describe '#new_project_member' do - let(:project) { create(:project) } - let(:added_user) { create(:user) } - - def create_member! - create(:project_member, user: added_user, project: project) - end - it do create_member! should_only_email(added_user) end - describe 'when notifications are disabled' do + context 'when notifications are disabled' do before do create_global_setting_for(added_user, :disabled) end @@ -1398,6 +1443,10 @@ describe NotificationService, :mailer do end end end + + def create_member! + create(:project_member, user: added_user, project: project) + end end context 'guest user in private project' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 85de0a14631..5600c9c6ad5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -154,6 +154,22 @@ RSpec.configure do |config| Sidekiq.redis(&:flushall) end + # The :each scope runs "inside" the example, so this hook ensures the DB is in the + # correct state before any examples' before hooks are called. This prevents a + # problem where `ScheduleIssuesClosedAtTypeChange` (or any migration that depends + # on background migrations being run inline during test setup) can be broken by + # altering Sidekiq behavior in an unrelated spec like so: + # + # around do |example| + # Sidekiq::Testing.fake! do + # example.run + # end + # end + config.before(:context, :migration) do + schema_migrate_down! + end + + # Each example may call `migrate!`, so we must ensure we are migrated down every time config.before(:each, :migration) do schema_migrate_down! end diff --git a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb new file mode 100644 index 00000000000..016bcfa9b1b --- /dev/null +++ b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb @@ -0,0 +1,128 @@ +module MigrationsHelpers + module TrackUntrackedUploadsHelpers + PUBLIC_DIR = File.join(Rails.root, 'tmp', 'tests', 'public') + UPLOADS_DIR = File.join(PUBLIC_DIR, 'uploads') + SYSTEM_DIR = File.join(UPLOADS_DIR, '-', 'system') + UPLOAD_FILENAME = 'image.png'.freeze + FIXTURE_FILE_PATH = File.join(Rails.root, 'spec', 'fixtures', 'dk.png') + FIXTURE_CHECKSUM = 'b804383982bb89b00e828e3f44c038cc991d3d1768009fc39ba8e2c081b9fb75'.freeze + + def create_or_update_appearance(logo: false, header_logo: false) + appearance = appearances.first_or_create(title: 'foo', description: 'bar', logo: (UPLOAD_FILENAME if logo), header_logo: (UPLOAD_FILENAME if header_logo)) + + add_upload(appearance, 'Appearance', 'logo', 'AttachmentUploader') if logo + add_upload(appearance, 'Appearance', 'header_logo', 'AttachmentUploader') if header_logo + + appearance + end + + def create_group(avatar: false) + index = unique_index(:group) + group = namespaces.create(name: "group#{index}", path: "group#{index}", avatar: (UPLOAD_FILENAME if avatar)) + + add_upload(group, 'Group', 'avatar', 'AvatarUploader') if avatar + + group + end + + def create_note(attachment: false) + note = notes.create(attachment: (UPLOAD_FILENAME if attachment)) + + add_upload(note, 'Note', 'attachment', 'AttachmentUploader') if attachment + + note + end + + def create_project(avatar: false) + group = create_group + project = projects.create(namespace_id: group.id, path: "project#{unique_index(:project)}", avatar: (UPLOAD_FILENAME if avatar)) + routes.create(path: "#{group.path}/#{project.path}", source_id: project.id, source_type: 'Project') # so Project.find_by_full_path works + + add_upload(project, 'Project', 'avatar', 'AvatarUploader') if avatar + + project + end + + def create_user(avatar: false) + user = users.create(email: "foo#{unique_index(:user)}@bar.com", avatar: (UPLOAD_FILENAME if avatar), projects_limit: 100) + + add_upload(user, 'User', 'avatar', 'AvatarUploader') if avatar + + user + end + + def unique_index(name = :unnamed) + @unique_index ||= {} + @unique_index[name] ||= 0 + @unique_index[name] += 1 + end + + def add_upload(model, model_type, attachment_type, uploader) + file_path = upload_file_path(model, model_type, attachment_type) + path_relative_to_public = file_path.sub("#{PUBLIC_DIR}/", '') + create_file(file_path) + + uploads.create!( + size: 1062, + path: path_relative_to_public, + model_id: model.id, + model_type: model_type == 'Group' ? 'Namespace' : model_type, + uploader: uploader, + checksum: FIXTURE_CHECKSUM + ) + end + + def add_markdown_attachment(project, hashed_storage: false) + project_dir = hashed_storage ? hashed_project_uploads_dir(project) : legacy_project_uploads_dir(project) + attachment_dir = File.join(project_dir, SecureRandom.hex) + attachment_file_path = File.join(attachment_dir, UPLOAD_FILENAME) + project_attachment_path_relative_to_project = attachment_file_path.sub("#{project_dir}/", '') + create_file(attachment_file_path) + + uploads.create!( + size: 1062, + path: project_attachment_path_relative_to_project, + model_id: project.id, + model_type: 'Project', + uploader: 'FileUploader', + checksum: FIXTURE_CHECKSUM + ) + end + + def legacy_project_uploads_dir(project) + namespace = namespaces.find_by(id: project.namespace_id) + File.join(UPLOADS_DIR, namespace.path, project.path) + end + + def hashed_project_uploads_dir(project) + File.join(UPLOADS_DIR, '@hashed', 'aa', 'aaaaaaaaaaaa') + end + + def upload_file_path(model, model_type, attachment_type) + dir = File.join(upload_dir(model_type.downcase, attachment_type.to_s), model.id.to_s) + File.join(dir, UPLOAD_FILENAME) + end + + def upload_dir(model_type, attachment_type) + File.join(SYSTEM_DIR, model_type, attachment_type) + end + + def create_file(path) + File.delete(path) if File.exist?(path) + FileUtils.mkdir_p(File.dirname(path)) + FileUtils.cp(FIXTURE_FILE_PATH, path) + end + + def get_uploads(model, model_type) + uploads.where(model_type: model_type, model_id: model.id) + end + + def get_full_path(project) + routes.find_by(source_id: project.id, source_type: 'Project').path + end + + def ensure_temporary_tracking_table_exists + Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists) + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index c275522159c..01321989f01 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -1,5 +1,5 @@ require 'rspec/mocks' -require 'toml' +require 'toml-rb' module TestEnv extend self diff --git a/spec/support/track_untracked_uploads_helpers.rb b/spec/support/track_untracked_uploads_helpers.rb deleted file mode 100644 index a8b3ed1f41c..00000000000 --- a/spec/support/track_untracked_uploads_helpers.rb +++ /dev/null @@ -1,16 +0,0 @@ -module TrackUntrackedUploadsHelpers - def uploaded_file - fixture_path = Rails.root.join('spec/fixtures/rails_sample.jpg') - fixture_file_upload(fixture_path) - end - - def ensure_temporary_tracking_table_exists - Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists) - end - - def create_or_update_appearance(attrs) - a = Appearance.first_or_initialize(title: 'foo', description: 'bar') - a.update!(attrs) - a - end -end diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index b37d6ac831f..1f4053ff9ad 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -132,7 +132,7 @@ describe 'gitlab:gitaly namespace rake task' do expect { run_rake_task('gitlab:gitaly:storage_config')} .to output(expected_output).to_stdout - parsed_output = TOML.parse(expected_output) + parsed_output = TomlRB.parse(expected_output) config.each do |name, params| expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params['path'] }) end diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb index 24f8ca67594..76ef57b6b1e 100644 --- a/spec/workers/process_commit_worker_spec.rb +++ b/spec/workers/process_commit_worker_spec.rb @@ -20,6 +20,32 @@ describe ProcessCommitWorker do worker.perform(project.id, -1, commit.to_hash) end + context 'when commit is a merge request merge commit' do + let(:merge_request) do + create(:merge_request, + description: "Closes #{issue.to_reference}", + source_branch: 'feature-merged', + target_branch: 'master', + source_project: project) + end + + let(:commit) do + project.repository.create_branch('feature-merged', 'feature') + + sha = project.repository.merge(user, + merge_request.diff_head_sha, + merge_request, + "Closes #{issue.to_reference}") + project.repository.commit(sha) + end + + it 'it does not close any issues from the commit message' do + expect(worker).not_to receive(:close_issues) + + worker.perform(project.id, user.id, commit.to_hash) + end + end + it 'processes the commit message' do expect(worker).to receive(:process_commit_message).and_call_original @@ -48,11 +74,9 @@ describe ProcessCommitWorker do describe '#process_commit_message' do context 'when pushing to the default branch' do it 'closes issues that should be closed per the commit message' do - allow(commit).to receive(:safe_message) - .and_return("Closes #{issue.to_reference}") + allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}") - expect(worker).to receive(:close_issues) - .with(project, user, user, commit, [issue]) + expect(worker).to receive(:close_issues).with(project, user, user, commit, [issue]) worker.process_commit_message(project, commit, user, user, true) end @@ -60,8 +84,7 @@ describe ProcessCommitWorker do context 'when pushing to a non-default branch' do it 'does not close any issues' do - allow(commit).to receive(:safe_message) - .and_return("Closes #{issue.to_reference}") + allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}") expect(worker).not_to receive(:close_issues) @@ -102,8 +125,7 @@ describe ProcessCommitWorker do describe '#update_issue_metrics' do it 'updates any existing issue metrics' do - allow(commit).to receive(:safe_message) - .and_return("Closes #{issue.to_reference}") + allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}") worker.update_issue_metrics(commit, user) @@ -113,10 +135,10 @@ describe ProcessCommitWorker do end it "doesn't execute any queries with false conditions" do - allow(commit).to receive(:safe_message) - .and_return("Lorem Ipsum") + allow(commit).to receive(:safe_message).and_return("Lorem Ipsum") - expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/) + expect { worker.update_issue_metrics(commit, user) } + .not_to make_queries_matching(/WHERE (?:1=0|0=1)/) end end @@ -128,8 +150,9 @@ describe ProcessCommitWorker do end it 'parses date strings into Time instances' do - commit = worker - .build_commit(project, id: '123', authored_date: Time.now.to_s) + commit = worker.build_commit(project, + id: '123', + authored_date: Time.now.to_s) expect(commit.authored_date).to be_an_instance_of(Time) end diff --git a/vendor/assets/javascripts/Chart.js b/vendor/assets/javascripts/Chart.js deleted file mode 100644 index c264262ba73..00000000000 --- a/vendor/assets/javascripts/Chart.js +++ /dev/null @@ -1,3477 +0,0 @@ -/*! - * Chart.js - * http://chartjs.org/ - * Version: 1.0.2 - * - * Copyright 2015 Nick Downie - * Released under the MIT license - * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md - */ - - -(function(){ - - "use strict"; - - //Declare root variable - window in the browser, global on the server - var root = this, - previous = root.Chart; - - //Occupy the global variable of Chart, and create a simple base class - var Chart = function(context){ - var chart = this; - this.canvas = context.canvas; - - this.ctx = context; - - //Variables global to the chart - var computeDimension = function(element,dimension) - { - if (element['offset'+dimension]) - { - return element['offset'+dimension]; - } - else - { - return document.defaultView.getComputedStyle(element).getPropertyValue(dimension); - } - } - - var width = this.width = computeDimension(context.canvas,'Width'); - var height = this.height = computeDimension(context.canvas,'Height'); - - // Firefox requires this to work correctly - context.canvas.width = width; - context.canvas.height = height; - - var width = this.width = context.canvas.width; - var height = this.height = context.canvas.height; - this.aspectRatio = this.width / this.height; - //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. - helpers.retinaScale(this); - - return this; - }; - //Globally expose the defaults to allow for user updating/changing - Chart.defaults = { - global: { - // Boolean - Whether to animate the chart - animation: true, - - // Number - Number of animation steps - animationSteps: 60, - - // String - Animation easing effect - animationEasing: "easeOutQuart", - - // Boolean - If we should show the scale at all - showScale: true, - - // Boolean - If we want to override with a hard coded scale - scaleOverride: false, - - // ** Required if scaleOverride is true ** - // Number - The number of steps in a hard coded scale - scaleSteps: null, - // Number - The value jump in the hard coded scale - scaleStepWidth: null, - // Number - The scale starting value - scaleStartValue: null, - - // String - Colour of the scale line - scaleLineColor: "rgba(0,0,0,.1)", - - // Number - Pixel width of the scale line - scaleLineWidth: 1, - - // Boolean - Whether to show labels on the scale - scaleShowLabels: true, - - // Interpolated JS string - can access value - scaleLabel: "<%=value%>", - - // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there - scaleIntegersOnly: true, - - // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero: false, - - // String - Scale label font declaration for the scale label - scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Scale label font size in pixels - scaleFontSize: 12, - - // String - Scale label font weight style - scaleFontStyle: "normal", - - // String - Scale label font colour - scaleFontColor: "#666", - - // Boolean - whether or not the chart should be responsive and resize when the browser does. - responsive: false, - - // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container - maintainAspectRatio: true, - - // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove - showTooltips: true, - - // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function - customTooltips: false, - - // Array - Array of string names to attach tooltip events - tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], - - // String - Tooltip background colour - tooltipFillColor: "rgba(0,0,0,0.8)", - - // String - Tooltip label font declaration for the scale label - tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip label font size in pixels - tooltipFontSize: 14, - - // String - Tooltip font weight style - tooltipFontStyle: "normal", - - // String - Tooltip label font colour - tooltipFontColor: "#fff", - - // String - Tooltip title font declaration for the scale label - tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - - // Number - Tooltip title font size in pixels - tooltipTitleFontSize: 14, - - // String - Tooltip title font weight style - tooltipTitleFontStyle: "bold", - - // String - Tooltip title font colour - tooltipTitleFontColor: "#fff", - - // Number - pixel width of padding around tooltip text - tooltipYPadding: 6, - - // Number - pixel width of padding around tooltip text - tooltipXPadding: 6, - - // Number - Size of the caret on the tooltip - tooltipCaretSize: 8, - - // Number - Pixel radius of the tooltip border - tooltipCornerRadius: 6, - - // Number - Pixel offset from point x to tooltip edge - tooltipXOffset: 10, - - // String - Template string for single tooltips - tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>", - - // String - Template string for single tooltips - multiTooltipTemplate: "<%= value %>", - - // String - Colour behind the legend colour block - multiTooltipKeyBackground: '#fff', - - // Function - Will fire on animation progression. - onAnimationProgress: function(){}, - - // Function - Will fire on animation completion. - onAnimationComplete: function(){} - - } - }; - - //Create a dictionary of chart types, to allow for extension of existing types - Chart.types = {}; - - //Global Chart helpers object for utility methods and classes - var helpers = Chart.helpers = {}; - - //-- Basic js utility methods - var each = helpers.each = function(loopable,callback,self){ - var additionalArgs = Array.prototype.slice.call(arguments, 3); - // Check to see if null or undefined firstly. - if (loopable){ - if (loopable.length === +loopable.length){ - var i; - for (i=0; i<loopable.length; i++){ - callback.apply(self,[loopable[i], i].concat(additionalArgs)); - } - } - else{ - for (var item in loopable){ - callback.apply(self,[loopable[item],item].concat(additionalArgs)); - } - } - } - }, - clone = helpers.clone = function(obj){ - var objClone = {}; - each(obj,function(value,key){ - if (obj.hasOwnProperty(key)) objClone[key] = value; - }); - return objClone; - }, - extend = helpers.extend = function(base){ - each(Array.prototype.slice.call(arguments,1), function(extensionObject) { - each(extensionObject,function(value,key){ - if (extensionObject.hasOwnProperty(key)) base[key] = value; - }); - }); - return base; - }, - merge = helpers.merge = function(base,master){ - //Merge properties in left object over to a shallow clone of object right. - var args = Array.prototype.slice.call(arguments,0); - args.unshift({}); - return extend.apply(null, args); - }, - indexOf = helpers.indexOf = function(arrayToSearch, item){ - if (Array.prototype.indexOf) { - return arrayToSearch.indexOf(item); - } - else{ - for (var i = 0; i < arrayToSearch.length; i++) { - if (arrayToSearch[i] === item) return i; - } - return -1; - } - }, - where = helpers.where = function(collection, filterCallback){ - var filtered = []; - - helpers.each(collection, function(item){ - if (filterCallback(item)){ - filtered.push(item); - } - }); - - return filtered; - }, - findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){ - // Default to start of the array - if (!startIndex){ - startIndex = -1; - } - for (var i = startIndex + 1; i < arrayToSearch.length; i++) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)){ - return currentItem; - } - } - }, - findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){ - // Default to end of the array - if (!startIndex){ - startIndex = arrayToSearch.length; - } - for (var i = startIndex - 1; i >= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)){ - return currentItem; - } - } - }, - inherits = helpers.inherits = function(extensions){ - //Basic javascript inheritance based on the model created in Backbone.js - var parent = this; - var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); }; - - var Surrogate = function(){ this.constructor = ChartElement;}; - Surrogate.prototype = parent.prototype; - ChartElement.prototype = new Surrogate(); - - ChartElement.extend = inherits; - - if (extensions) extend(ChartElement.prototype, extensions); - - ChartElement.__super__ = parent.prototype; - - return ChartElement; - }, - noop = helpers.noop = function(){}, - uid = helpers.uid = (function(){ - var id=0; - return function(){ - return "chart-" + id++; - }; - })(), - warn = helpers.warn = function(str){ - //Method for warning of errors - if (window.console && typeof window.console.warn == "function") console.warn(str); - }, - amd = helpers.amd = (typeof define == 'function' && define.amd), - //-- Math methods - isNumber = helpers.isNumber = function(n){ - return !isNaN(parseFloat(n)) && isFinite(n); - }, - max = helpers.max = function(array){ - return Math.max.apply( Math, array ); - }, - min = helpers.min = function(array){ - return Math.min.apply( Math, array ); - }, - cap = helpers.cap = function(valueToCap,maxValue,minValue){ - if(isNumber(maxValue)) { - if( valueToCap > maxValue ) { - return maxValue; - } - } - else if(isNumber(minValue)){ - if ( valueToCap < minValue ){ - return minValue; - } - } - return valueToCap; - }, - getDecimalPlaces = helpers.getDecimalPlaces = function(num){ - if (num%1!==0 && isNumber(num)){ - return num.toString().split(".")[1].length; - } - else { - return 0; - } - }, - toRadians = helpers.radians = function(degrees){ - return degrees * (Math.PI/180); - }, - // Gets the angle from vertical upright to the point about a centre. - getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){ - var distanceFromXCenter = anglePoint.x - centrePoint.x, - distanceFromYCenter = anglePoint.y - centrePoint.y, - radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - - var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter); - - //If the segment is in the top left quadrant, we need to add another rotation to the angle - if (distanceFromXCenter < 0 && distanceFromYCenter < 0){ - angle += Math.PI*2; - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }, - aliasPixel = helpers.aliasPixel = function(pixelWidth){ - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }, - splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){ - //Props to Rob Spencer at scaled innovation for his post on splining between points - //http://scaledinnovation.com/analytics/splines/aboutSplines.html - var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)), - d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)), - fa=t*d01/(d01+d12),// scaling factor for triangle Ta - fb=t*d12/(d01+d12); - return { - inner : { - x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y) - }, - outer : { - x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x), - y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y) - } - }; - }, - calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){ - return Math.floor(Math.log(val) / Math.LN10); - }, - calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){ - - //Set a minimum step of two - a point at the top of the graph, and a point at the base - var minSteps = 2, - maxSteps = Math.floor(drawingSize/(textSize * 1.5)), - skipFitting = (minSteps >= maxSteps); - - var maxValue = max(valuesArray), - minValue = min(valuesArray); - - // We need some degree of seperation here to calculate the scales if all the values are the same - // Adding/minusing 0.5 will give us a range of 1. - if (maxValue === minValue){ - maxValue += 0.5; - // So we don't end up with a graph with a negative start value if we've said always start from zero - if (minValue >= 0.5 && !startFromZero){ - minValue -= 0.5; - } - else{ - // Make up a whole number above the values - maxValue += 0.5; - } - } - - var valueRange = Math.abs(maxValue - minValue), - rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange), - graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude), - graphRange = graphMax - graphMin, - stepValue = Math.pow(10, rangeOrderOfMagnitude), - numberOfSteps = Math.round(graphRange / stepValue); - - //If we have more space on the graph we'll use it to give more definition to the data - while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) { - if(numberOfSteps > maxSteps){ - stepValue *=2; - numberOfSteps = Math.round(graphRange/stepValue); - // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps. - if (numberOfSteps % 1 !== 0){ - skipFitting = true; - } - } - //We can fit in double the amount of scale points on the scale - else{ - //If user has declared ints only, and the step value isn't a decimal - if (integersOnly && rangeOrderOfMagnitude >= 0){ - //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float - if(stepValue/2 % 1 === 0){ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - //If it would make it a float break out of the loop - else{ - break; - } - } - //If the scale doesn't have to be an int, make the scale more granular anyway. - else{ - stepValue /=2; - numberOfSteps = Math.round(graphRange/stepValue); - } - - } - } - - if (skipFitting){ - numberOfSteps = minSteps; - stepValue = graphRange / numberOfSteps; - } - - return { - steps : numberOfSteps, - stepValue : stepValue, - min : graphMin, - max : graphMin + (numberOfSteps * stepValue) - }; - - }, - /* jshint ignore:start */ - // Blows up jshint errors based on the new Function constructor - //Templating methods - //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ - template = helpers.template = function(templateString, valuesObject){ - - // If templateString is function rather than string-template - call the function for valuesObject - - if(templateString instanceof Function){ - return templateString(valuesObject); - } - - var cache = {}; - function tmpl(str, data){ - // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. - var fn = !/\W/.test(str) ? - cache[str] = cache[str] : - - // Generate a reusable function that will serve as a template - // generator (and which will be cached). - new Function("obj", - "var p=[],print=function(){p.push.apply(p,arguments);};" + - - // Introduce the data as local variables using with(){} - "with(obj){p.push('" + - - // Convert the template into pure JavaScript - str - .replace(/[\r\t\n]/g, " ") - .split("<%").join("\t") - .replace(/((^|%>)[^\t]*)'/g, "$1\r") - .replace(/\t=(.*?)%>/g, "',$1,'") - .split("\t").join("');") - .split("%>").join("p.push('") - .split("\r").join("\\'") + - "');}return p.join('');" - ); - - // Provide some basic currying to the user - return data ? fn( data ) : fn; - } - return tmpl(templateString,valuesObject); - }, - /* jshint ignore:end */ - generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){ - var labelsArray = new Array(numberOfSteps); - if (labelTemplateString){ - each(labelsArray,function(val,index){ - labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))}); - }); - } - return labelsArray; - }, - //--Animation methods - //Easing functions adapted from Robert Penner's easing equations - //http://www.robertpenner.com/easing/ - easingEffects = helpers.easingEffects = { - linear: function (t) { - return t; - }, - easeInQuad: function (t) { - return t * t; - }, - easeOutQuad: function (t) { - return -1 * t * (t - 2); - }, - easeInOutQuad: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t; - return -1 / 2 * ((--t) * (t - 2) - 1); - }, - easeInCubic: function (t) { - return t * t * t; - }, - easeOutCubic: function (t) { - return 1 * ((t = t / 1 - 1) * t * t + 1); - }, - easeInOutCubic: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t; - return 1 / 2 * ((t -= 2) * t * t + 2); - }, - easeInQuart: function (t) { - return t * t * t * t; - }, - easeOutQuart: function (t) { - return -1 * ((t = t / 1 - 1) * t * t * t - 1); - }, - easeInOutQuart: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t; - return -1 / 2 * ((t -= 2) * t * t * t - 2); - }, - easeInQuint: function (t) { - return 1 * (t /= 1) * t * t * t * t; - }, - easeOutQuint: function (t) { - return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); - }, - easeInOutQuint: function (t) { - if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t; - return 1 / 2 * ((t -= 2) * t * t * t * t + 2); - }, - easeInSine: function (t) { - return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; - }, - easeOutSine: function (t) { - return 1 * Math.sin(t / 1 * (Math.PI / 2)); - }, - easeInOutSine: function (t) { - return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); - }, - easeInExpo: function (t) { - return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); - }, - easeOutExpo: function (t) { - return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); - }, - easeInOutExpo: function (t) { - if (t === 0) return 0; - if (t === 1) return 1; - if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1)); - return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); - }, - easeInCirc: function (t) { - if (t >= 1) return t; - return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); - }, - easeOutCirc: function (t) { - return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); - }, - easeInOutCirc: function (t) { - if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1); - return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - easeInElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - }, - easeOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1) == 1) return 1; - if (!p) p = 1 * 0.3; - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; - }, - easeInOutElastic: function (t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) return 0; - if ((t /= 1 / 2) == 2) return 1; - if (!p) p = 1 * (0.3 * 1.5); - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else s = p / (2 * Math.PI) * Math.asin(1 / a); - if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function (t) { - var s = 1.70158; - return 1 * (t /= 1) * t * ((s + 1) * t - s); - }, - easeOutBack: function (t) { - var s = 1.70158; - return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); - }, - easeInOutBack: function (t) { - var s = 1.70158; - if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); - return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - easeInBounce: function (t) { - return 1 - easingEffects.easeOutBounce(1 - t); - }, - easeOutBounce: function (t) { - if ((t /= 1) < (1 / 2.75)) { - return 1 * (7.5625 * t * t); - } else if (t < (2 / 2.75)) { - return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); - } else if (t < (2.5 / 2.75)) { - return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); - } else { - return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); - } - }, - easeInOutBounce: function (t) { - if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5; - return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; - } - }, - //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - requestAnimFrame = helpers.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - })(), - cancelAnimFrame = helpers.cancelAnimFrame = (function(){ - return window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(callback) { - return window.clearTimeout(callback, 1000 / 60); - }; - })(), - animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){ - - var currentStep = 0, - easingFunction = easingEffects[easingString] || easingEffects.linear; - - var animationFrame = function(){ - currentStep++; - var stepDecimal = currentStep/totalSteps; - var easeDecimal = easingFunction(stepDecimal); - - callback.call(chartInstance,easeDecimal,stepDecimal, currentStep); - onProgress.call(chartInstance,easeDecimal,stepDecimal); - if (currentStep < totalSteps){ - chartInstance.animationFrame = requestAnimFrame(animationFrame); - } else{ - onComplete.apply(chartInstance); - } - }; - requestAnimFrame(animationFrame); - }, - //-- DOM methods - getRelativePosition = helpers.getRelativePosition = function(evt){ - var mouseX, mouseY; - var e = evt.originalEvent || evt, - canvas = evt.currentTarget || evt.srcElement, - boundingRect = canvas.getBoundingClientRect(); - - if (e.touches){ - mouseX = e.touches[0].clientX - boundingRect.left; - mouseY = e.touches[0].clientY - boundingRect.top; - - } - else{ - mouseX = e.clientX - boundingRect.left; - mouseY = e.clientY - boundingRect.top; - } - - return { - x : mouseX, - y : mouseY - }; - - }, - addEvent = helpers.addEvent = function(node,eventType,method){ - if (node.addEventListener){ - node.addEventListener(eventType,method); - } else if (node.attachEvent){ - node.attachEvent("on"+eventType, method); - } else { - node["on"+eventType] = method; - } - }, - removeEvent = helpers.removeEvent = function(node, eventType, handler){ - if (node.removeEventListener){ - node.removeEventListener(eventType, handler, false); - } else if (node.detachEvent){ - node.detachEvent("on"+eventType,handler); - } else{ - node["on" + eventType] = noop; - } - }, - bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){ - // Create the events object if it's not already present - if (!chartInstance.events) chartInstance.events = {}; - - each(arrayOfEvents,function(eventName){ - chartInstance.events[eventName] = function(){ - handler.apply(chartInstance, arguments); - }; - addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]); - }); - }, - unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) { - each(arrayOfEvents, function(handler,eventName){ - removeEvent(chartInstance.chart.canvas, eventName, handler); - }); - }, - getMaximumWidth = helpers.getMaximumWidth = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientWidth; - }, - getMaximumHeight = helpers.getMaximumHeight = function(domNode){ - var container = domNode.parentNode; - // TODO = check cross browser stuff with this. - return container.clientHeight; - }, - getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support - retinaScale = helpers.retinaScale = function(chart){ - var ctx = chart.ctx, - width = chart.canvas.width, - height = chart.canvas.height; - - if (window.devicePixelRatio) { - ctx.canvas.style.width = width + "px"; - ctx.canvas.style.height = height + "px"; - ctx.canvas.height = height * window.devicePixelRatio; - ctx.canvas.width = width * window.devicePixelRatio; - ctx.scale(window.devicePixelRatio, window.devicePixelRatio); - } - }, - //-- Canvas methods - clear = helpers.clear = function(chart){ - chart.ctx.clearRect(0,0,chart.width,chart.height); - }, - fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){ - return fontStyle + " " + pixelSize+"px " + fontFamily; - }, - longestText = helpers.longestText = function(ctx,font,arrayOfStrings){ - ctx.font = font; - var longest = 0; - each(arrayOfStrings,function(string){ - var textWidth = ctx.measureText(string).width; - longest = (textWidth > longest) ? textWidth : longest; - }); - return longest; - }, - drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){ - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - }; - - - //Store a reference to each instance - allowing us to globally resize chart instances on window resize. - //Destroy method on the chart will remove the instance of the chart from this reference. - Chart.instances = {}; - - Chart.Type = function(data,options,chart){ - this.options = options; - this.chart = chart; - this.id = uid(); - //Add the chart instance to the global namespace - Chart.instances[this.id] = this; - - // Initialize is always called when a chart type is created - // By default it is a no op, but it should be extended - if (options.responsive){ - this.resize(); - } - this.initialize.call(this,data); - }; - - //Core methods that'll be a part of every chart type - extend(Chart.Type.prototype,{ - initialize : function(){return this;}, - clear : function(){ - clear(this.chart); - return this; - }, - stop : function(){ - // Stops any current animation loop occuring - cancelAnimFrame(this.animationFrame); - return this; - }, - resize : function(callback){ - this.stop(); - var canvas = this.chart.canvas, - newWidth = getMaximumWidth(this.chart.canvas), - newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas); - - canvas.width = this.chart.width = newWidth; - canvas.height = this.chart.height = newHeight; - - retinaScale(this.chart); - - if (typeof callback === "function"){ - callback.apply(this, Array.prototype.slice.call(arguments, 1)); - } - return this; - }, - reflow : noop, - render : function(reflow){ - if (reflow){ - this.reflow(); - } - if (this.options.animation && !reflow){ - helpers.animationLoop( - this.draw, - this.options.animationSteps, - this.options.animationEasing, - this.options.onAnimationProgress, - this.options.onAnimationComplete, - this - ); - } - else{ - this.draw(); - this.options.onAnimationComplete.call(this); - } - return this; - }, - generateLegend : function(){ - return template(this.options.legendTemplate,this); - }, - destroy : function(){ - this.clear(); - unbindEvents(this, this.events); - var canvas = this.chart.canvas; - - // Reset canvas height/width attributes starts a fresh with the canvas context - canvas.width = this.chart.width; - canvas.height = this.chart.height; - - // < IE9 doesn't support removeProperty - if (canvas.style.removeProperty) { - canvas.style.removeProperty('width'); - canvas.style.removeProperty('height'); - } else { - canvas.style.removeAttribute('width'); - canvas.style.removeAttribute('height'); - } - - delete Chart.instances[this.id]; - }, - showTooltip : function(ChartElements, forceRedraw){ - // Only redraw the chart if we've actually changed what we're hovering on. - if (typeof this.activeElements === 'undefined') this.activeElements = []; - - var isChanged = (function(Elements){ - var changed = false; - - if (Elements.length !== this.activeElements.length){ - changed = true; - return changed; - } - - each(Elements, function(element, index){ - if (element !== this.activeElements[index]){ - changed = true; - } - }, this); - return changed; - }).call(this, ChartElements); - - if (!isChanged && !forceRedraw){ - return; - } - else{ - this.activeElements = ChartElements; - } - this.draw(); - if(this.options.customTooltips){ - this.options.customTooltips(false); - } - if (ChartElements.length > 0){ - // If we have multiple datasets, show a MultiTooltip for all of the data points at that index - if (this.datasets && this.datasets.length > 1) { - var dataArray, - dataIndex; - - for (var i = this.datasets.length - 1; i >= 0; i--) { - dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments; - dataIndex = indexOf(dataArray, ChartElements[0]); - if (dataIndex !== -1){ - break; - } - } - var tooltipLabels = [], - tooltipColors = [], - medianPosition = (function(index) { - - // Get all the points at that particular index - var Elements = [], - dataCollection, - xPositions = [], - yPositions = [], - xMax, - yMax, - xMin, - yMin; - helpers.each(this.datasets, function(dataset){ - dataCollection = dataset.points || dataset.bars || dataset.segments; - if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){ - Elements.push(dataCollection[dataIndex]); - } - }); - - helpers.each(Elements, function(element) { - xPositions.push(element.x); - yPositions.push(element.y); - - - //Include any colour information about the element - tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element)); - tooltipColors.push({ - fill: element._saved.fillColor || element.fillColor, - stroke: element._saved.strokeColor || element.strokeColor - }); - - }, this); - - yMin = min(yPositions); - yMax = max(yPositions); - - xMin = min(xPositions); - xMax = max(xPositions); - - return { - x: (xMin > this.chart.width/2) ? xMin : xMax, - y: (yMin + yMax)/2 - }; - }).call(this, dataIndex); - - new Chart.MultiTooltip({ - x: medianPosition.x, - y: medianPosition.y, - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - xOffset: this.options.tooltipXOffset, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - titleTextColor: this.options.tooltipTitleFontColor, - titleFontFamily: this.options.tooltipTitleFontFamily, - titleFontStyle: this.options.tooltipTitleFontStyle, - titleFontSize: this.options.tooltipTitleFontSize, - cornerRadius: this.options.tooltipCornerRadius, - labels: tooltipLabels, - legendColors: tooltipColors, - legendColorBackground : this.options.multiTooltipKeyBackground, - title: ChartElements[0].label, - chart: this.chart, - ctx: this.chart.ctx, - custom: this.options.customTooltips - }).draw(); - - } else { - each(ChartElements, function(Element) { - var tooltipPosition = Element.tooltipPosition(); - new Chart.Tooltip({ - x: Math.round(tooltipPosition.x), - y: Math.round(tooltipPosition.y), - xPadding: this.options.tooltipXPadding, - yPadding: this.options.tooltipYPadding, - fillColor: this.options.tooltipFillColor, - textColor: this.options.tooltipFontColor, - fontFamily: this.options.tooltipFontFamily, - fontStyle: this.options.tooltipFontStyle, - fontSize: this.options.tooltipFontSize, - caretHeight: this.options.tooltipCaretSize, - cornerRadius: this.options.tooltipCornerRadius, - text: template(this.options.tooltipTemplate, Element), - chart: this.chart, - custom: this.options.customTooltips - }).draw(); - }, this); - } - } - return this; - }, - toBase64Image : function(){ - return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); - } - }); - - Chart.Type.extend = function(extensions){ - - var parent = this; - - var ChartType = function(){ - return parent.apply(this,arguments); - }; - - //Copy the prototype object of the this class - ChartType.prototype = clone(parent.prototype); - //Now overwrite some of the properties in the base class with the new extensions - extend(ChartType.prototype, extensions); - - ChartType.extend = Chart.Type.extend; - - if (extensions.name || parent.prototype.name){ - - var chartName = extensions.name || parent.prototype.name; - //Assign any potential default values of the new chart type - - //If none are defined, we'll use a clone of the chart type this is being extended from. - //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart - //doesn't define some defaults of their own. - - var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {}; - - Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults); - - Chart.types[chartName] = ChartType; - - //Register this new chart type in the Chart prototype - Chart.prototype[chartName] = function(data,options){ - var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {}); - return new ChartType(data,config,this); - }; - } else{ - warn("Name not provided for this chart, so it hasn't been registered"); - } - return parent; - }; - - Chart.Element = function(configuration){ - extend(this,configuration); - this.initialize.apply(this,arguments); - this.save(); - }; - extend(Chart.Element.prototype,{ - initialize : function(){}, - restore : function(props){ - if (!props){ - extend(this,this._saved); - } else { - each(props,function(key){ - this[key] = this._saved[key]; - },this); - } - return this; - }, - save : function(){ - this._saved = clone(this); - delete this._saved._saved; - return this; - }, - update : function(newProps){ - each(newProps,function(value,key){ - this._saved[key] = this[key]; - this[key] = value; - },this); - return this; - }, - transition : function(props,ease){ - each(props,function(value,key){ - this[key] = ((value - this._saved[key]) * ease) + this._saved[key]; - },this); - return this; - }, - tooltipPosition : function(){ - return { - x : this.x, - y : this.y - }; - }, - hasValue: function(){ - return isNumber(this.value); - } - }); - - Chart.Element.extend = inherits; - - - Chart.Point = Chart.Element.extend({ - display: true, - inRange: function(chartX,chartY){ - var hitDetectionRange = this.hitDetectionRadius + this.radius; - return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2)); - }, - draw : function(){ - if (this.display){ - var ctx = this.ctx; - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); - ctx.closePath(); - - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.stroke(); - } - - - //Quick debug for bezier curve splining - //Highlights control points and the line between them. - //Handy for dev - stripped in the min version. - - // ctx.save(); - // ctx.fillStyle = "black"; - // ctx.strokeStyle = "black" - // ctx.beginPath(); - // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.beginPath(); - // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2); - // ctx.fill(); - - // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y); - // ctx.lineTo(this.x, this.y); - // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y); - // ctx.stroke(); - - // ctx.restore(); - - - - } - }); - - Chart.Arc = Chart.Element.extend({ - inRange : function(chartX,chartY){ - - var pointRelativePosition = helpers.getAngleFromPoint(this, { - x: chartX, - y: chartY - }); - - //Check if within the range of the open/close angle - var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle), - withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius); - - return (betweenAngles && withinRadius); - //Ensure within the outside of the arc centre, but inside arc outer - }, - tooltipPosition : function(){ - var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2), - rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius; - return { - x : this.x + (Math.cos(centreAngle) * rangeFromCentre), - y : this.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - draw : function(animationPercent){ - - var easingDecimal = animationPercent || 1; - - var ctx = this.ctx; - - ctx.beginPath(); - - ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle); - - ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true); - - ctx.closePath(); - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - ctx.fillStyle = this.fillColor; - - ctx.fill(); - ctx.lineJoin = 'bevel'; - - if (this.showStroke){ - ctx.stroke(); - } - } - }); - - Chart.Rectangle = Chart.Element.extend({ - draw : function(){ - var ctx = this.ctx, - halfWidth = this.width/2, - leftX = this.x - halfWidth, - rightX = this.x + halfWidth, - top = this.base - (this.base - this.y), - halfStroke = this.strokeWidth / 2; - - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (this.showStroke){ - leftX += halfStroke; - rightX -= halfStroke; - top += halfStroke; - } - - ctx.beginPath(); - - ctx.fillStyle = this.fillColor; - ctx.strokeStyle = this.strokeColor; - ctx.lineWidth = this.strokeWidth; - - // It'd be nice to keep this class totally generic to any rectangle - // and simply specify which border to miss out. - ctx.moveTo(leftX, this.base); - ctx.lineTo(leftX, top); - ctx.lineTo(rightX, top); - ctx.lineTo(rightX, this.base); - ctx.fill(); - if (this.showStroke){ - ctx.stroke(); - } - }, - height : function(){ - return this.base - this.y; - }, - inRange : function(chartX,chartY){ - return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base); - } - }); - - Chart.Tooltip = Chart.Element.extend({ - draw : function(){ - - var ctx = this.chart.ctx; - - ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.xAlign = "center"; - this.yAlign = "above"; - - //Distance between the actual element.y position and the start of the tooltip caret - var caretPadding = this.caretPadding = 2; - - var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, - tooltipRectHeight = this.fontSize + 2*this.yPadding, - tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding; - - if (this.x + tooltipWidth/2 >this.chart.width){ - this.xAlign = "left"; - } else if (this.x - tooltipWidth/2 < 0){ - this.xAlign = "right"; - } - - if (this.y - tooltipHeight < 0){ - this.yAlign = "below"; - } - - - var tooltipX = this.x - tooltipWidth/2, - tooltipY = this.y - tooltipHeight; - - ctx.fillStyle = this.fillColor; - - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - switch(this.yAlign) - { - case "above": - //Draw a caret above the x/y - ctx.beginPath(); - ctx.moveTo(this.x,this.y - caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.closePath(); - ctx.fill(); - break; - case "below": - tooltipY = this.y + caretPadding + this.caretHeight; - //Draw a caret below the x/y - ctx.beginPath(); - ctx.moveTo(this.x, this.y + caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.closePath(); - ctx.fill(); - break; - } - - switch(this.xAlign) - { - case "left": - tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); - break; - case "right": - tooltipX = this.x - (this.cornerRadius + this.caretHeight); - break; - } - - drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); - - ctx.fill(); - - ctx.fillStyle = this.textColor; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); - } - } - }); - - Chart.MultiTooltip = Chart.Element.extend({ - initialize : function(){ - this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - - this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily); - - this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5; - - this.ctx.font = this.titleFont; - - var titleWidth = this.ctx.measureText(this.title).width, - //Label has a legend square as well so account for this. - labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3, - longestTextWidth = max([labelWidth,titleWidth]); - - this.width = longestTextWidth + (this.xPadding*2); - - - var halfHeight = this.height/2; - - //Check to ensure the height will fit on the canvas - if (this.y - halfHeight < 0 ){ - this.y = halfHeight; - } else if (this.y + halfHeight > this.chart.height){ - this.y = this.chart.height - halfHeight; - } - - //Decide whether to align left or right based on position on canvas - if (this.x > this.chart.width/2){ - this.x -= this.xOffset + this.width; - } else { - this.x += this.xOffset; - } - - - }, - getLineHeight : function(index){ - var baseLineHeight = this.y - (this.height/2) + this.yPadding, - afterTitleIndex = index-1; - - //If the index is zero, we're getting the title - if (index === 0){ - return baseLineHeight + this.titleFontSize/2; - } else{ - return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5; - } - - }, - draw : function(){ - // Custom Tooltips - if(this.custom){ - this.custom(this); - } - else{ - drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); - var ctx = this.ctx; - ctx.fillStyle = this.fillColor; - ctx.fill(); - ctx.closePath(); - - ctx.textAlign = "left"; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.titleTextColor; - ctx.font = this.titleFont; - - ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); - - ctx.font = this.font; - helpers.each(this.labels,function(label,index){ - ctx.fillStyle = this.textColor; - ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); - - //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) - //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - //Instead we'll make a white filled block to put the legendColour palette over. - - ctx.fillStyle = this.legendColorBackground; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - ctx.fillStyle = this.legendColors[index].fill; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - - - },this); - } - } - }); - - Chart.Scale = Chart.Element.extend({ - initialize : function(){ - this.fit(); - }, - buildYLabels : function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0; - }, - addXLabel : function(label){ - this.xLabels.push(label); - this.valuesCount++; - this.fit(); - }, - removeXLabel : function(){ - this.xLabels.shift(); - this.valuesCount--; - this.fit(); - }, - // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use - fit: function(){ - // First we need the width of the yLabels, assuming the xLabels aren't rotated - - // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation - this.startPoint = (this.display) ? this.fontSize : 0; - this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels - - // Apply padding settings to the start and end point. - this.startPoint += this.padding; - this.endPoint -= this.padding; - - // Cache the starting height, so can determine if we need to recalculate the scale yAxis - var cachedHeight = this.endPoint - this.startPoint, - cachedYLabelWidth; - - // Build the current yLabels so we have an idea of what size they'll be to start - /* - * This sets what is returned from calculateScaleRange as static properties of this class: - * - this.steps; - this.stepValue; - this.min; - this.max; - * - */ - this.calculateYRange(cachedHeight); - - // With these properties set we can now build the array of yLabels - // and also the width of the largest yLabel - this.buildYLabels(); - - this.calculateXLabelRotation(); - - while((cachedHeight > this.endPoint - this.startPoint)){ - cachedHeight = this.endPoint - this.startPoint; - cachedYLabelWidth = this.yLabelWidth; - - this.calculateYRange(cachedHeight); - this.buildYLabels(); - - // Only go through the xLabel loop again if the yLabel width has changed - if (cachedYLabelWidth < this.yLabelWidth){ - this.calculateXLabelRotation(); - } - } - - }, - calculateXLabelRotation : function(){ - //Get the width of each grid by calculating the difference - //between x offsets between 0 and 1. - - this.ctx.font = this.font; - - var firstWidth = this.ctx.measureText(this.xLabels[0]).width, - lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width, - firstRotated, - lastRotated; - - - this.xScalePaddingRight = lastWidth/2 + 3; - this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10; - - this.xLabelRotation = 0; - if (this.display){ - var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels), - cosRotation, - firstRotatedWidth; - this.xLabelWidth = originalLabelWidth; - //Allow 3 pixels x2 padding either side for label readability - var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6; - - //Max label rotate should be 90 - also act as a loop counter - while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){ - cosRotation = Math.cos(toRadians(this.xLabelRotation)); - - firstRotated = cosRotation * firstWidth; - lastRotated = cosRotation * lastWidth; - - // We're right aligning the text now. - if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){ - this.xScalePaddingLeft = firstRotated + this.fontSize / 2; - } - this.xScalePaddingRight = this.fontSize/2; - - - this.xLabelRotation++; - this.xLabelWidth = cosRotation * originalLabelWidth; - - } - if (this.xLabelRotation > 0){ - this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3; - } - } - else{ - this.xLabelWidth = 0; - this.xScalePaddingRight = this.padding; - this.xScalePaddingLeft = this.padding; - } - - }, - // Needs to be overidden in each Chart type - // Otherwise we need to pass all the data into the scale class - calculateYRange: noop, - drawingArea: function(){ - return this.startPoint - this.endPoint; - }, - calculateY : function(value){ - var scalingFactor = this.drawingArea() / (this.min - this.max); - return this.endPoint - (scalingFactor * (value - this.min)); - }, - calculateX : function(index){ - var isRotated = (this.xLabelRotation > 0), - // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding, - innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight), - valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1), - valueOffset = (valueWidth * index) + this.xScalePaddingLeft; - - if (this.offsetGridLines){ - valueOffset += (valueWidth/2); - } - - return Math.round(valueOffset); - }, - update : function(newProps){ - helpers.extend(this, newProps); - this.fit(); - }, - draw : function(){ - var ctx = this.ctx, - yLabelGap = (this.endPoint - this.startPoint) / this.steps, - xStart = Math.round(this.xScalePaddingLeft); - if (this.display){ - ctx.fillStyle = this.textColor; - ctx.font = this.font; - each(this.yLabels,function(labelString,index){ - var yLabelCenter = this.endPoint - (yLabelGap * index), - linePositionY = Math.round(yLabelCenter), - drawHorizontalLine = this.showHorizontalLines; - - ctx.textAlign = "right"; - ctx.textBaseline = "middle"; - if (this.showLabels){ - ctx.fillText(labelString,xStart - 10,yLabelCenter); - } - - // This is X axis, so draw it - if (index === 0 && !drawHorizontalLine){ - drawHorizontalLine = true; - } - - if (drawHorizontalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - linePositionY += helpers.aliasPixel(ctx.lineWidth); - - if(drawHorizontalLine){ - ctx.moveTo(xStart, linePositionY); - ctx.lineTo(this.width, linePositionY); - ctx.stroke(); - ctx.closePath(); - } - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - ctx.beginPath(); - ctx.moveTo(xStart - 5, linePositionY); - ctx.lineTo(xStart, linePositionY); - ctx.stroke(); - ctx.closePath(); - - },this); - - each(this.xLabels,function(label,index){ - var xPos = this.calculateX(index) + aliasPixel(this.lineWidth), - // Check to see if line/bar here and decide where to place the line - linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth), - isRotated = (this.xLabelRotation > 0), - drawVerticalLine = this.showVerticalLines; - - // This is Y axis, so draw it - if (index === 0 && !drawVerticalLine){ - drawVerticalLine = true; - } - - if (drawVerticalLine){ - ctx.beginPath(); - } - - if (index > 0){ - // This is a grid line in the centre, so drop that - ctx.lineWidth = this.gridLineWidth; - ctx.strokeStyle = this.gridLineColor; - } else { - // This is the first line on the scale - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - } - - if (drawVerticalLine){ - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.startPoint - 3); - ctx.stroke(); - ctx.closePath(); - } - - - ctx.lineWidth = this.lineWidth; - ctx.strokeStyle = this.lineColor; - - - // Small lines at the bottom of the base grid line - ctx.beginPath(); - ctx.moveTo(linePos,this.endPoint); - ctx.lineTo(linePos,this.endPoint + 5); - ctx.stroke(); - ctx.closePath(); - - ctx.save(); - ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8); - ctx.rotate(toRadians(this.xLabelRotation)*-1); - ctx.font = this.font; - ctx.textAlign = (isRotated) ? "right" : "center"; - ctx.textBaseline = (isRotated) ? "middle" : "top"; - ctx.fillText(label, 0, 0); - ctx.restore(); - },this); - - } - } - - }); - - Chart.RadialScale = Chart.Element.extend({ - initialize: function(){ - this.size = min([this.height, this.width]); - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - }, - calculateCenterOffset: function(value){ - // Take into account half font size + the yPadding of the top value - var scalingFactor = this.drawingArea / (this.max - this.min); - - return (value - this.min) * scalingFactor; - }, - update : function(){ - if (!this.lineArc){ - this.setScaleSize(); - } else { - this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2); - } - this.buildYLabels(); - }, - buildYLabels: function(){ - this.yLabels = []; - - var stepDecimalPlaces = getDecimalPlaces(this.stepValue); - - for (var i=0; i<=this.steps; i++){ - this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)})); - } - }, - getCircumference : function(){ - return ((Math.PI*2) / this.valuesCount); - }, - setScaleSize: function(){ - /* - * Right, this is really confusing and there is a lot of maths going on here - * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 - * - * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif - * - * Solution: - * - * We assume the radius of the polygon is half the size of the canvas at first - * at each index we check if the text overlaps. - * - * Where it does, we store that angle and that index. - * - * After finding the largest index and angle we calculate how much we need to remove - * from the shape radius to move the point inwards by that x. - * - * We average the left and right distances to get the maximum shape radius that can fit in the box - * along with labels. - * - * Once we have that, we can find the centre point for the chart, by taking the x text protrusion - * on each side, removing that from the size, halving it and adding the left x protrusion width. - * - * This will mean we have a shape fitted to the canvas, as large as it can be with the labels - * and position it in the most space efficient manner - * - * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif - */ - - - // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. - // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points - var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]), - pointPosition, - i, - textWidth, - halfTextWidth, - furthestRight = this.width, - furthestRightIndex, - furthestRightAngle, - furthestLeft = 0, - furthestLeftIndex, - furthestLeftAngle, - xProtrusionLeft, - xProtrusionRight, - radiusReductionRight, - radiusReductionLeft, - maxWidthRadius; - this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - for (i=0;i<this.valuesCount;i++){ - // 5px to space the text slightly out - similar to what we do in the draw function. - pointPosition = this.getPointPosition(i, largestPossibleRadius); - textWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5; - if (i === 0 || i === this.valuesCount/2){ - // If we're at index zero, or exactly the middle, we're at exactly the top/bottom - // of the radar chart, so text will be aligned centrally, so we'll half it and compare - // w/left and right text sizes - halfTextWidth = textWidth/2; - if (pointPosition.x + halfTextWidth > furthestRight) { - furthestRight = pointPosition.x + halfTextWidth; - furthestRightIndex = i; - } - if (pointPosition.x - halfTextWidth < furthestLeft) { - furthestLeft = pointPosition.x - halfTextWidth; - furthestLeftIndex = i; - } - } - else if (i < this.valuesCount/2) { - // Less than half the values means we'll left align the text - if (pointPosition.x + textWidth > furthestRight) { - furthestRight = pointPosition.x + textWidth; - furthestRightIndex = i; - } - } - else if (i > this.valuesCount/2){ - // More than half the values means we'll right align the text - if (pointPosition.x - textWidth < furthestLeft) { - furthestLeft = pointPosition.x - textWidth; - furthestLeftIndex = i; - } - } - } - - xProtrusionLeft = furthestLeft; - - xProtrusionRight = Math.ceil(furthestRight - this.width); - - furthestRightAngle = this.getIndexAngle(furthestRightIndex); - - furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); - - radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2); - - radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2); - - // Ensure we actually need to reduce the size of the chart - radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0; - radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; - - this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2; - - //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2]) - this.setCenterPoint(radiusReductionLeft, radiusReductionRight); - - }, - setCenterPoint: function(leftMovement, rightMovement){ - - var maxRight = this.width - rightMovement - this.drawingArea, - maxLeft = leftMovement + this.drawingArea; - - this.xCenter = (maxLeft + maxRight)/2; - // Always vertically in the centre as the text height doesn't change - this.yCenter = (this.height/2); - }, - - getIndexAngle : function(index){ - var angleMultiplier = (Math.PI * 2) / this.valuesCount; - // Start from the top instead of right, so remove a quarter of the circle - - return index * angleMultiplier - (Math.PI/2); - }, - getPointPosition : function(index, distanceFromCenter){ - var thisAngle = this.getIndexAngle(index); - return { - x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter, - y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter - }; - }, - draw: function(){ - if (this.display){ - var ctx = this.ctx; - each(this.yLabels, function(label, index){ - // Don't draw a centre value - if (index > 0){ - var yCenterOffset = index * (this.drawingArea/this.steps), - yHeight = this.yCenter - yCenterOffset, - pointPosition; - - // Draw circular lines around the scale - if (this.lineWidth > 0){ - ctx.strokeStyle = this.lineColor; - ctx.lineWidth = this.lineWidth; - - if(this.lineArc){ - ctx.beginPath(); - ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2); - ctx.closePath(); - ctx.stroke(); - } else{ - ctx.beginPath(); - for (var i=0;i<this.valuesCount;i++) - { - pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue))); - if (i === 0){ - ctx.moveTo(pointPosition.x, pointPosition.y); - } else { - ctx.lineTo(pointPosition.x, pointPosition.y); - } - } - ctx.closePath(); - ctx.stroke(); - } - } - if(this.showLabels){ - ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily); - if (this.showLabelBackdrop){ - var labelWidth = ctx.measureText(label).width; - ctx.fillStyle = this.backdropColor; - ctx.fillRect( - this.xCenter - labelWidth/2 - this.backdropPaddingX, - yHeight - this.fontSize/2 - this.backdropPaddingY, - labelWidth + this.backdropPaddingX*2, - this.fontSize + this.backdropPaddingY*2 - ); - } - ctx.textAlign = 'center'; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.fontColor; - ctx.fillText(label, this.xCenter, yHeight); - } - } - }, this); - - if (!this.lineArc){ - ctx.lineWidth = this.angleLineWidth; - ctx.strokeStyle = this.angleLineColor; - for (var i = this.valuesCount - 1; i >= 0; i--) { - if (this.angleLineWidth > 0){ - var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max)); - ctx.beginPath(); - ctx.moveTo(this.xCenter, this.yCenter); - ctx.lineTo(outerPosition.x, outerPosition.y); - ctx.stroke(); - ctx.closePath(); - } - // Extra 3px out for some label spacing - var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5); - ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily); - ctx.fillStyle = this.pointLabelFontColor; - - var labelsCount = this.labels.length, - halfLabelsCount = this.labels.length/2, - quarterLabelsCount = halfLabelsCount/2, - upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), - exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); - if (i === 0){ - ctx.textAlign = 'center'; - } else if(i === halfLabelsCount){ - ctx.textAlign = 'center'; - } else if (i < halfLabelsCount){ - ctx.textAlign = 'left'; - } else { - ctx.textAlign = 'right'; - } - - // Set the correct text baseline based on outer positioning - if (exactQuarter){ - ctx.textBaseline = 'middle'; - } else if (upperHalf){ - ctx.textBaseline = 'bottom'; - } else { - ctx.textBaseline = 'top'; - } - - ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y); - } - } - } - } - }); - - // Attach global event to resize each chart instance when the browser resizes - helpers.addEvent(window, "resize", (function(){ - // Basic debounce of resize function so it doesn't hurt performance when resizing browser. - var timeout; - return function(){ - clearTimeout(timeout); - timeout = setTimeout(function(){ - each(Chart.instances,function(instance){ - // If the responsive flag is set in the chart instance config - // Cascade the resize event down to the chart. - if (instance.options.responsive){ - instance.resize(instance.render, true); - } - }); - }, 50); - }; - })()); - - - if (amd) { - define(function(){ - return Chart; - }); - } else if (typeof module === 'object' && module.exports) { - module.exports = Chart; - } - - root.Chart = Chart; - - Chart.noConflict = function(){ - root.Chart = previous; - return Chart; - }; - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - var defaultConfig = { - //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value - scaleBeginAtZero : true, - - //Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - If there is a stroke on each bar - barShowStroke : true, - - //Number - Pixel width of the bar stroke - barStrokeWidth : 2, - - //Number - Spacing between each of the X value sets - barValueSpacing : 5, - - //Number - Spacing between data sets within X values - barDatasetSpacing : 1, - - //String - A legend template - legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>" - - }; - - - Chart.Type.extend({ - name: "Bar", - defaults : defaultConfig, - initialize: function(data){ - - //Expose options as a scope variable here so we can access it in the ScaleClass - var options = this.options; - - this.ScaleClass = Chart.Scale.extend({ - offsetGridLines : true, - calculateBarX : function(datasetCount, datasetIndex, barIndex){ - //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar - var xWidth = this.calculateBaseWidth(), - xAbsolute = this.calculateX(barIndex) - (xWidth/2), - barWidth = this.calculateBarWidth(datasetCount); - - return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2; - }, - calculateBaseWidth : function(){ - return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing); - }, - calculateBarWidth : function(datasetCount){ - //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset - var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing); - - return (baseWidth / datasetCount); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : []; - - this.eachBars(function(bar){ - bar.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activeBars, function(activeBar){ - activeBar.fillColor = activeBar.highlightFill; - activeBar.strokeColor = activeBar.highlightStroke; - }); - this.showTooltip(activeBars); - }); - } - - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.BarClass = Chart.Rectangle.extend({ - strokeWidth : this.options.barStrokeWidth, - showStroke : this.options.barShowStroke, - ctx : this.chart.ctx - }); - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset,datasetIndex){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - bars : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.bars.push(new this.BarClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.strokeColor, - fillColor : dataset.fillColor, - highlightFill : dataset.highlightFill || dataset.fillColor, - highlightStroke : dataset.highlightStroke || dataset.strokeColor - })); - },this); - - },this); - - this.buildScale(data.labels); - - this.BarClass.prototype.base = this.scale.endPoint; - - this.eachBars(function(bar, index, datasetIndex){ - helpers.extend(bar, { - width : this.scale.calculateBarWidth(this.datasets.length), - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y: this.scale.endPoint - }); - bar.save(); - }, this); - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - - this.eachBars(function(bar){ - bar.save(); - }); - this.render(); - }, - eachBars : function(callback){ - helpers.each(this.datasets,function(dataset, datasetIndex){ - helpers.each(dataset.bars, callback, this, datasetIndex); - },this); - }, - getBarsAtEvent : function(e){ - var barsArray = [], - eventPosition = helpers.getRelativePosition(e), - datasetIterator = function(dataset){ - barsArray.push(dataset.bars[barIndex]); - }, - barIndex; - - for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) { - for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) { - if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){ - helpers.each(this.datasets, datasetIterator); - return barsArray; - } - } - } - - return barsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachBars(function(bar){ - values.push(bar.value); - }); - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange: function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - this.scale = new this.ScaleClass(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].bars.push(new this.BarClass({ - value : value, - label : label, - x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1), - y: this.scale.endPoint, - width : this.scale.calculateBarWidth(this.datasets.length), - base : this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].strokeColor, - fillColor : this.datasets[datasetIndex].fillColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.bars.shift(); - },this); - this.update(); - }, - reflow : function(){ - helpers.extend(this.BarClass.prototype,{ - y: this.scale.endPoint, - base : this.scale.endPoint - }); - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - this.scale.draw(easingDecimal); - - //Draw all the bars for each dataset - helpers.each(this.datasets,function(dataset,datasetIndex){ - helpers.each(dataset.bars,function(bar,index){ - if (bar.hasValue()){ - bar.base = this.scale.endPoint; - //Transition then draw - bar.transition({ - x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index), - y : this.scale.calculateY(bar.value), - width : this.scale.calculateBarWidth(this.datasets.length) - }, easingDecimal).draw(); - } - },this); - - },this); - } - }); - - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Whether we should show a stroke on each segment - segmentShowStroke : true, - - //String - The colour of each segment stroke - segmentStrokeColor : "#fff", - - //Number - The width of each segment stroke - segmentStrokeWidth : 2, - - //The percentage of the chart that we cut out of the middle. - percentageInnerCutout : 50, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect - animationEasing : "easeOutBounce", - - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate : true, - - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>" - - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "Doughnut", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - - //Declare segments as a static property to prevent inheriting across the Chart type prototype - this.segments = []; - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - - this.SegmentArc = Chart.Arc.extend({ - ctx : this.chart.ctx, - x : this.chart.width/2, - y : this.chart.height/2 - }); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - this.calculateTotal(data); - - helpers.each(data,function(datapoint, index){ - this.addData(datapoint, index, true); - },this); - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - this.segments.splice(index, 0, new this.SegmentArc({ - value : segment.value, - outerRadius : (this.options.animateScale) ? 0 : this.outerRadius, - innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout, - fillColor : segment.color, - highlightColor : segment.highlight || segment.color, - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - startAngle : Math.PI * 1.5, - circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), - label : segment.label - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - calculateCircumference : function(value){ - return (Math.PI*2)*(Math.abs(value) / this.total); - }, - calculateTotal : function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += Math.abs(segment.value); - },this); - }, - update : function(){ - this.calculateTotal(this.segments); - - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor']); - }); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - this.render(); - }, - - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - }); - }, this); - }, - draw : function(easeDecimal){ - var animDecimal = (easeDecimal) ? easeDecimal : 1; - this.clear(); - helpers.each(this.segments,function(segment,index){ - segment.transition({ - circumference : this.calculateCircumference(segment.value), - outerRadius : this.outerRadius, - innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout - },animDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - segment.draw(); - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length-1){ - this.segments[index+1].startAngle = segment.endAngle; - } - },this); - - } - }); - - Chart.types.Doughnut.extend({ - name : "Pie", - defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0}) - }); - -}).call(this); -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - var defaultConfig = { - - ///Boolean - Whether grid lines are shown across the chart - scaleShowGridLines : true, - - //String - Colour of the grid lines - scaleGridLineColor : "rgba(0,0,0,.05)", - - //Number - Width of the grid lines - scaleGridLineWidth : 1, - - //Boolean - Whether to show horizontal lines (except X axis) - scaleShowHorizontalLines: true, - - //Boolean - Whether to show vertical lines (except Y axis) - scaleShowVerticalLines: true, - - //Boolean - Whether the line is curved between points - bezierCurve : true, - - //Number - Tension of the bezier curve between points - bezierCurveTension : 0.4, - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 4, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>" - - }; - - - Chart.Type.extend({ - name: "Line", - defaults : defaultConfig, - initialize: function(data){ - //Declare the extension of the default point, to cater for the options passed in to the constructor - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx, - inRange : function(mouseX){ - return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2)); - } - }); - - this.datasets = []; - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePoints, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - this.showTooltip(activePoints); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label : dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - this.buildScale(data.labels); - - - this.eachPoints(function(point, index){ - helpers.extend(point, { - x: this.scale.calculateX(index), - y: this.scale.endPoint - }); - point.save(); - }, this); - - },this); - - - this.render(); - }, - update : function(){ - this.scale.update(); - // Reset any highlight colours before updating. - helpers.each(this.activeElements, function(activeElement){ - activeElement.restore(['fillColor', 'strokeColor']); - }); - this.eachPoints(function(point){ - point.save(); - }); - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - getPointsAtEvent : function(e){ - var pointsArray = [], - eventPosition = helpers.getRelativePosition(e); - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,function(point){ - if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point); - }); - },this); - return pointsArray; - }, - buildScale : function(labels){ - var self = this; - - var dataTotal = function(){ - var values = []; - self.eachPoints(function(point){ - values.push(point.value); - }); - - return values; - }; - - var scaleOptions = { - templateString : this.options.scaleLabel, - height : this.chart.height, - width : this.chart.width, - ctx : this.chart.ctx, - textColor : this.options.scaleFontColor, - fontSize : this.options.scaleFontSize, - fontStyle : this.options.scaleFontStyle, - fontFamily : this.options.scaleFontFamily, - valuesCount : labels.length, - beginAtZero : this.options.scaleBeginAtZero, - integersOnly : this.options.scaleIntegersOnly, - calculateYRange : function(currentHeight){ - var updatedRanges = helpers.calculateScaleRange( - dataTotal(), - currentHeight, - this.fontSize, - this.beginAtZero, - this.integersOnly - ); - helpers.extend(this, updatedRanges); - }, - xLabels : labels, - font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily), - lineWidth : this.options.scaleLineWidth, - lineColor : this.options.scaleLineColor, - showHorizontalLines : this.options.scaleShowHorizontalLines, - showVerticalLines : this.options.scaleShowVerticalLines, - gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0, - gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)", - padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth, - showLabels : this.options.scaleShowLabels, - display : this.options.showScale - }; - - if (this.options.scaleOverride){ - helpers.extend(scaleOptions, { - calculateYRange: helpers.noop, - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - }); - } - - - this.scale = new Chart.Scale(scaleOptions); - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - - helpers.each(valuesArray,function(value,datasetIndex){ - //Add a new point for each piece of data, passing any required data to draw. - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: this.scale.calculateX(this.scale.valuesCount+1), - y: this.scale.endPoint, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.addXLabel(label); - //Then re-render the chart. - this.update(); - }, - removeData : function(){ - this.scale.removeXLabel(); - //Then re-render the chart. - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.update(); - }, - reflow : function(){ - var newScaleProps = helpers.extend({ - height : this.chart.height, - width : this.chart.width - }); - this.scale.update(newScaleProps); - }, - draw : function(ease){ - var easingDecimal = ease || 1; - this.clear(); - - var ctx = this.chart.ctx; - - // Some helper methods for getting the next/prev points - var hasValue = function(item){ - return item.value !== null; - }, - nextPoint = function(point, collection, index){ - return helpers.findNextWhere(collection, hasValue, index) || point; - }, - previousPoint = function(point, collection, index){ - return helpers.findPreviousWhere(collection, hasValue, index) || point; - }; - - this.scale.draw(easingDecimal); - - - helpers.each(this.datasets,function(dataset){ - var pointsWithValues = helpers.where(dataset.points, hasValue); - - //Transition each point first so that the line and point drawing isn't out of sync - //We can use this extra loop to calculate the control points of this dataset also in this loop - - helpers.each(dataset.points, function(point, index){ - if (point.hasValue()){ - point.transition({ - y : this.scale.calculateY(point.value), - x : this.scale.calculateX(index) - }, easingDecimal); - } - },this); - - - // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point - // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed - if (this.options.bezierCurve){ - helpers.each(pointsWithValues, function(point, index){ - var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0; - point.controlPoints = helpers.splineCurve( - previousPoint(point, pointsWithValues, index), - point, - nextPoint(point, pointsWithValues, index), - tension - ); - - // Prevent the bezier going outside of the bounds of the graph - - // Cap puter bezier handles to the upper/lower scale bounds - if (point.controlPoints.outer.y > this.scale.endPoint){ - point.controlPoints.outer.y = this.scale.endPoint; - } - else if (point.controlPoints.outer.y < this.scale.startPoint){ - point.controlPoints.outer.y = this.scale.startPoint; - } - - // Cap inner bezier handles to the upper/lower scale bounds - if (point.controlPoints.inner.y > this.scale.endPoint){ - point.controlPoints.inner.y = this.scale.endPoint; - } - else if (point.controlPoints.inner.y < this.scale.startPoint){ - point.controlPoints.inner.y = this.scale.startPoint; - } - },this); - } - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - - helpers.each(pointsWithValues, function(point, index){ - if (index === 0){ - ctx.moveTo(point.x, point.y); - } - else{ - if(this.options.bezierCurve){ - var previous = previousPoint(point, pointsWithValues, index); - - ctx.bezierCurveTo( - previous.controlPoints.outer.x, - previous.controlPoints.outer.y, - point.controlPoints.inner.x, - point.controlPoints.inner.y, - point.x, - point.y - ); - } - else{ - ctx.lineTo(point.x,point.y); - } - } - }, this); - - ctx.stroke(); - - if (this.options.datasetFill && pointsWithValues.length > 0){ - //Round off the line by going to the base of the chart, back to the start, then fill. - ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint); - ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint); - ctx.fillStyle = dataset.fillColor; - ctx.closePath(); - ctx.fill(); - } - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(pointsWithValues,function(point){ - point.draw(); - }); - },this); - } - }); - - -}).call(this); - -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - //Cache a local reference to Chart.helpers - helpers = Chart.helpers; - - var defaultConfig = { - //Boolean - Show a backdrop to the scale label - scaleShowLabelBackdrop : true, - - //String - The colour of the label backdrop - scaleBackdropColor : "rgba(255,255,255,0.75)", - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //Number - The backdrop padding above & below the label in pixels - scaleBackdropPaddingY : 2, - - //Number - The backdrop padding to the side of the label in pixels - scaleBackdropPaddingX : 2, - - //Boolean - Show line for each value in the scale - scaleShowLine : true, - - //Boolean - Stroke a line around each segment in the chart - segmentShowStroke : true, - - //String - The colour of the stroke on each segement. - segmentStrokeColor : "#fff", - - //Number - The width of the stroke value in pixels - segmentStrokeWidth : 2, - - //Number - Amount of animation steps - animationSteps : 100, - - //String - Animation easing effect. - animationEasing : "easeOutBounce", - - //Boolean - Whether to animate the rotation of the chart - animateRotate : true, - - //Boolean - Whether to animate scaling the chart from the centre - animateScale : false, - - //String - A legend template - legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>" - }; - - - Chart.Type.extend({ - //Passing in a name registers this chart in the Chart namespace - name: "PolarArea", - //Providing a defaults will also register the deafults in the chart namespace - defaults : defaultConfig, - //Initialize is fired when the chart is initialized - Data is passed in as a parameter - //Config is automatically merged by the core of Chart.js, and is available at this.options - initialize: function(data){ - this.segments = []; - //Declare segment class as a chart instance specific class, so it can share props for this instance - this.SegmentArc = Chart.Arc.extend({ - showStroke : this.options.segmentShowStroke, - strokeWidth : this.options.segmentStrokeWidth, - strokeColor : this.options.segmentStrokeColor, - ctx : this.chart.ctx, - innerRadius : 0, - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - lineArc: true, - width: this.chart.width, - height: this.chart.height, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - valuesCount: data.length - }); - - this.updateScaleRange(data); - - this.scale.update(); - - helpers.each(data,function(segment,index){ - this.addData(segment,index,true); - },this); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; - helpers.each(this.segments,function(segment){ - segment.restore(["fillColor"]); - }); - helpers.each(activeSegments,function(activeSegment){ - activeSegment.fillColor = activeSegment.highlightColor; - }); - this.showTooltip(activeSegments); - }); - } - - this.render(); - }, - getSegmentsAtEvent : function(e){ - var segmentsArray = []; - - var location = helpers.getRelativePosition(e); - - helpers.each(this.segments,function(segment){ - if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); - },this); - return segmentsArray; - }, - addData : function(segment, atIndex, silent){ - var index = atIndex || this.segments.length; - - this.segments.splice(index, 0, new this.SegmentArc({ - fillColor: segment.color, - highlightColor: segment.highlight || segment.color, - label: segment.label, - value: segment.value, - outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value), - circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(), - startAngle: Math.PI * 1.5 - })); - if (!silent){ - this.reflow(); - this.update(); - } - }, - removeData: function(atIndex){ - var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; - this.segments.splice(indexToDelete, 1); - this.reflow(); - this.update(); - }, - calculateTotal: function(data){ - this.total = 0; - helpers.each(data,function(segment){ - this.total += segment.value; - },this); - this.scale.valuesCount = this.segments.length; - }, - updateScaleRange: function(datapoints){ - var valuesArray = []; - helpers.each(datapoints,function(segment){ - valuesArray.push(segment.value); - }); - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes, - { - size: helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - } - ); - - }, - update : function(){ - this.calculateTotal(this.segments); - - helpers.each(this.segments,function(segment){ - segment.save(); - }); - - this.reflow(); - this.render(); - }, - reflow : function(){ - helpers.extend(this.SegmentArc.prototype,{ - x : this.chart.width/2, - y : this.chart.height/2 - }); - this.updateScaleRange(this.segments); - this.scale.update(); - - helpers.extend(this.scale,{ - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - - helpers.each(this.segments, function(segment){ - segment.update({ - outerRadius : this.scale.calculateCenterOffset(segment.value) - }); - }, this); - - }, - draw : function(ease){ - var easingDecimal = ease || 1; - //Clear & draw the canvas - this.clear(); - helpers.each(this.segments,function(segment, index){ - segment.transition({ - circumference : this.scale.getCircumference(), - outerRadius : this.scale.calculateCenterOffset(segment.value) - },easingDecimal); - - segment.endAngle = segment.startAngle + segment.circumference; - - // If we've removed the first segment we need to set the first one to - // start at the top. - if (index === 0){ - segment.startAngle = Math.PI * 1.5; - } - - //Check to see if it's the last segment, if not get the next and update the start angle - if (index < this.segments.length - 1){ - this.segments[index+1].startAngle = segment.endAngle; - } - segment.draw(); - }, this); - this.scale.draw(); - } - }); - -}).call(this); -(function(){ - "use strict"; - - var root = this, - Chart = root.Chart, - helpers = Chart.helpers; - - - - Chart.Type.extend({ - name: "Radar", - defaults:{ - //Boolean - Whether to show lines for each scale point - scaleShowLine : true, - - //Boolean - Whether we show the angle lines out of the radar - angleShowLineOut : true, - - //Boolean - Whether to show labels on the scale - scaleShowLabels : false, - - // Boolean - Whether the scale should begin at zero - scaleBeginAtZero : true, - - //String - Colour of the angle line - angleLineColor : "rgba(0,0,0,.1)", - - //Number - Pixel width of the angle line - angleLineWidth : 1, - - //String - Point label font declaration - pointLabelFontFamily : "'Arial'", - - //String - Point label font weight - pointLabelFontStyle : "normal", - - //Number - Point label font size in pixels - pointLabelFontSize : 10, - - //String - Point label font colour - pointLabelFontColor : "#666", - - //Boolean - Whether to show a dot for each point - pointDot : true, - - //Number - Radius of each point dot in pixels - pointDotRadius : 3, - - //Number - Pixel width of point dot stroke - pointDotStrokeWidth : 1, - - //Number - amount extra to add to the radius to cater for hit detection outside the drawn point - pointHitDetectionRadius : 20, - - //Boolean - Whether to show a stroke for datasets - datasetStroke : true, - - //Number - Pixel width of dataset stroke - datasetStrokeWidth : 2, - - //Boolean - Whether to fill the dataset with a colour - datasetFill : true, - - //String - A legend template - legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>" - - }, - - initialize: function(data){ - this.PointClass = Chart.Point.extend({ - strokeWidth : this.options.pointDotStrokeWidth, - radius : this.options.pointDotRadius, - display: this.options.pointDot, - hitDetectionRadius : this.options.pointHitDetectionRadius, - ctx : this.chart.ctx - }); - - this.datasets = []; - - this.buildScale(data); - - //Set up tooltip events on the chart - if (this.options.showTooltips){ - helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ - var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : []; - - this.eachPoints(function(point){ - point.restore(['fillColor', 'strokeColor']); - }); - helpers.each(activePointsCollection, function(activePoint){ - activePoint.fillColor = activePoint.highlightFill; - activePoint.strokeColor = activePoint.highlightStroke; - }); - - this.showTooltip(activePointsCollection); - }); - } - - //Iterate through each of the datasets, and build this into a property of the chart - helpers.each(data.datasets,function(dataset){ - - var datasetObject = { - label: dataset.label || null, - fillColor : dataset.fillColor, - strokeColor : dataset.strokeColor, - pointColor : dataset.pointColor, - pointStrokeColor : dataset.pointStrokeColor, - points : [] - }; - - this.datasets.push(datasetObject); - - helpers.each(dataset.data,function(dataPoint,index){ - //Add a new point for each piece of data, passing any required data to draw. - var pointPosition; - if (!this.scale.animation){ - pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint)); - } - datasetObject.points.push(new this.PointClass({ - value : dataPoint, - label : data.labels[index], - datasetLabel: dataset.label, - x: (this.options.animation) ? this.scale.xCenter : pointPosition.x, - y: (this.options.animation) ? this.scale.yCenter : pointPosition.y, - strokeColor : dataset.pointStrokeColor, - fillColor : dataset.pointColor, - highlightFill : dataset.pointHighlightFill || dataset.pointColor, - highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor - })); - },this); - - },this); - - this.render(); - }, - eachPoints : function(callback){ - helpers.each(this.datasets,function(dataset){ - helpers.each(dataset.points,callback,this); - },this); - }, - - getPointsAtEvent : function(evt){ - var mousePosition = helpers.getRelativePosition(evt), - fromCenter = helpers.getAngleFromPoint({ - x: this.scale.xCenter, - y: this.scale.yCenter - }, mousePosition); - - var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount, - pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex), - activePointsCollection = []; - - // If we're at the top, make the pointIndex 0 to get the first of the array. - if (pointIndex >= this.scale.valuesCount || pointIndex < 0){ - pointIndex = 0; - } - - if (fromCenter.distance <= this.scale.drawingArea){ - helpers.each(this.datasets, function(dataset){ - activePointsCollection.push(dataset.points[pointIndex]); - }); - } - - return activePointsCollection; - }, - - buildScale : function(data){ - this.scale = new Chart.RadialScale({ - display: this.options.showScale, - fontStyle: this.options.scaleFontStyle, - fontSize: this.options.scaleFontSize, - fontFamily: this.options.scaleFontFamily, - fontColor: this.options.scaleFontColor, - showLabels: this.options.scaleShowLabels, - showLabelBackdrop: this.options.scaleShowLabelBackdrop, - backdropColor: this.options.scaleBackdropColor, - backdropPaddingY : this.options.scaleBackdropPaddingY, - backdropPaddingX: this.options.scaleBackdropPaddingX, - lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0, - lineColor: this.options.scaleLineColor, - angleLineColor : this.options.angleLineColor, - angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0, - // Point labels at the edge of each line - pointLabelFontColor : this.options.pointLabelFontColor, - pointLabelFontSize : this.options.pointLabelFontSize, - pointLabelFontFamily : this.options.pointLabelFontFamily, - pointLabelFontStyle : this.options.pointLabelFontStyle, - height : this.chart.height, - width: this.chart.width, - xCenter: this.chart.width/2, - yCenter: this.chart.height/2, - ctx : this.chart.ctx, - templateString: this.options.scaleLabel, - labels: data.labels, - valuesCount: data.datasets[0].data.length - }); - - this.scale.setScaleSize(); - this.updateScaleRange(data.datasets); - this.scale.buildYLabels(); - }, - updateScaleRange: function(datasets){ - var valuesArray = (function(){ - var totalDataArray = []; - helpers.each(datasets,function(dataset){ - if (dataset.data){ - totalDataArray = totalDataArray.concat(dataset.data); - } - else { - helpers.each(dataset.points, function(point){ - totalDataArray.push(point.value); - }); - } - }); - return totalDataArray; - })(); - - - var scaleSizes = (this.options.scaleOverride) ? - { - steps: this.options.scaleSteps, - stepValue: this.options.scaleStepWidth, - min: this.options.scaleStartValue, - max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth) - } : - helpers.calculateScaleRange( - valuesArray, - helpers.min([this.chart.width, this.chart.height])/2, - this.options.scaleFontSize, - this.options.scaleBeginAtZero, - this.options.scaleIntegersOnly - ); - - helpers.extend( - this.scale, - scaleSizes - ); - - }, - addData : function(valuesArray,label){ - //Map the values array for each of the datasets - this.scale.valuesCount++; - helpers.each(valuesArray,function(value,datasetIndex){ - var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value)); - this.datasets[datasetIndex].points.push(new this.PointClass({ - value : value, - label : label, - x: pointPosition.x, - y: pointPosition.y, - strokeColor : this.datasets[datasetIndex].pointStrokeColor, - fillColor : this.datasets[datasetIndex].pointColor - })); - },this); - - this.scale.labels.push(label); - - this.reflow(); - - this.update(); - }, - removeData : function(){ - this.scale.valuesCount--; - this.scale.labels.shift(); - helpers.each(this.datasets,function(dataset){ - dataset.points.shift(); - },this); - this.reflow(); - this.update(); - }, - update : function(){ - this.eachPoints(function(point){ - point.save(); - }); - this.reflow(); - this.render(); - }, - reflow: function(){ - helpers.extend(this.scale, { - width : this.chart.width, - height: this.chart.height, - size : helpers.min([this.chart.width, this.chart.height]), - xCenter: this.chart.width/2, - yCenter: this.chart.height/2 - }); - this.updateScaleRange(this.datasets); - this.scale.setScaleSize(); - this.scale.buildYLabels(); - }, - draw : function(ease){ - var easeDecimal = ease || 1, - ctx = this.chart.ctx; - this.clear(); - this.scale.draw(); - - helpers.each(this.datasets,function(dataset){ - - //Transition each point first so that the line and point drawing isn't out of sync - helpers.each(dataset.points,function(point,index){ - if (point.hasValue()){ - point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal); - } - },this); - - - - //Draw the line between all the points - ctx.lineWidth = this.options.datasetStrokeWidth; - ctx.strokeStyle = dataset.strokeColor; - ctx.beginPath(); - helpers.each(dataset.points,function(point,index){ - if (index === 0){ - ctx.moveTo(point.x,point.y); - } - else{ - ctx.lineTo(point.x,point.y); - } - },this); - ctx.closePath(); - ctx.stroke(); - - ctx.fillStyle = dataset.fillColor; - ctx.fill(); - - //Now draw the points over the line - //A little inefficient double looping, but better than the line - //lagging behind the point positions - helpers.each(dataset.points,function(point){ - if (point.hasValue()){ - point.draw(); - } - }); - - },this); - - } - - }); - - - - - -}).call(this);
\ No newline at end of file diff --git a/vendor/assets/javascripts/jasmine-jquery.js b/vendor/assets/javascripts/jasmine-jquery.js new file mode 100644 index 00000000000..9b7b245009d --- /dev/null +++ b/vendor/assets/javascripts/jasmine-jquery.js @@ -0,0 +1,851 @@ +/* eslint-disable */ +/* + Jasmine JQuery 3.0 patched version from this fork : https://github.com/cmrd-senya/jasmine-jquery/blob/jquery3/lib/jasmine-jquery.js +*/ +/*! +Jasmine-jQuery: a set of jQuery helpers for Jasmine tests. + +Version 2.1.1 + +https://github.com/velesin/jasmine-jquery + +Copyright (c) 2010-2014 Wojciech Zawistowski, Travis Jeffery + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +(function (root, factory) { + if (typeof module !== 'undefined' && module.exports) { + factory(root, root.jasmine, require('jquery')); + } else { + factory(root, root.jasmine, root.jQuery); + } +}((function() {return this; })(), function (window, jasmine, $) { "use strict"; + + jasmine.spiedEventsKey = function (selector, eventName) { + for(var i = 0;; i++) { + var key = [eventName, i].toString() + if (data.spiedEvents[key] === undefined || data.spiedEvents[key].selector.is($(selector))) { + return key + } + } + } + + jasmine.getFixtures = function () { + return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures() + } + + jasmine.getStyleFixtures = function () { + return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures() + } + + jasmine.Fixtures = function () { + this.containerId = 'jasmine-fixtures' + this.fixturesCache_ = {} + this.fixturesPath = 'spec/javascripts/fixtures' + } + + jasmine.Fixtures.prototype.set = function (html) { + this.cleanUp() + return this.createContainer_(html) + } + + jasmine.Fixtures.prototype.appendSet= function (html) { + this.addToContainer_(html) + } + + jasmine.Fixtures.prototype.preload = function () { + this.read.apply(this, arguments) + } + + jasmine.Fixtures.prototype.load = function () { + this.cleanUp() + this.createContainer_(this.read.apply(this, arguments)) + } + + jasmine.Fixtures.prototype.appendLoad = function () { + this.addToContainer_(this.read.apply(this, arguments)) + } + + jasmine.Fixtures.prototype.read = function () { + var htmlChunks = [] + , fixtureUrls = arguments + + for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { + htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])) + } + + return htmlChunks.join('') + } + + jasmine.Fixtures.prototype.clearCache = function () { + this.fixturesCache_ = {} + } + + jasmine.Fixtures.prototype.cleanUp = function () { + $('#' + this.containerId).remove() + } + + jasmine.Fixtures.prototype.sandbox = function (attributes) { + var attributesToSet = attributes || {} + return $('<div id="sandbox" />').attr(attributesToSet) + } + + jasmine.Fixtures.prototype.createContainer_ = function (html) { + var container = $('<div>') + .attr('id', this.containerId) + .html(html) + + $(document.body).append(container) + return container + } + + jasmine.Fixtures.prototype.addToContainer_ = function (html){ + var container = $(document.body).find('#'+this.containerId).append(html) + + if (!container.length) { + this.createContainer_(html) + } + } + + jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) { + if (typeof this.fixturesCache_[url] === 'undefined') { + this.loadFixtureIntoCache_(url) + } + return this.fixturesCache_[url] + } + + jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { + var self = this + , url = this.makeFixtureUrl_(relativeUrl) + , htmlText = '' + , request = $.ajax({ + async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded + cache: false, + url: url, + dataType: 'html', + success: function (data, status, $xhr) { + htmlText = $xhr.responseText + } + }).fail(function ($xhr, status, err) { + throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') + }) + + var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || []; + + scripts.each(function(){ + $.ajax({ + async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded + cache: false, + dataType: 'script', + url: $(this).attr('src'), + success: function (data, status, $xhr) { + htmlText += '<script>' + $xhr.responseText + '</script>' + }, + error: function ($xhr, status, err) { + throw new Error('Script could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') + } + }); + }) + + self.fixturesCache_[relativeUrl] = htmlText; + } + + jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){ + return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl + } + + jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { + return this[methodName].apply(this, passedArguments) + } + + + jasmine.StyleFixtures = function () { + this.fixturesCache_ = {} + this.fixturesNodes_ = [] + this.fixturesPath = 'spec/javascripts/fixtures' + } + + jasmine.StyleFixtures.prototype.set = function (css) { + this.cleanUp() + this.createStyle_(css) + } + + jasmine.StyleFixtures.prototype.appendSet = function (css) { + this.createStyle_(css) + } + + jasmine.StyleFixtures.prototype.preload = function () { + this.read_.apply(this, arguments) + } + + jasmine.StyleFixtures.prototype.load = function () { + this.cleanUp() + this.createStyle_(this.read_.apply(this, arguments)) + } + + jasmine.StyleFixtures.prototype.appendLoad = function () { + this.createStyle_(this.read_.apply(this, arguments)) + } + + jasmine.StyleFixtures.prototype.cleanUp = function () { + while(this.fixturesNodes_.length) { + this.fixturesNodes_.pop().remove() + } + } + + jasmine.StyleFixtures.prototype.createStyle_ = function (html) { + var styleText = $('<div></div>').html(html).text() + , style = $('<style>' + styleText + '</style>') + + this.fixturesNodes_.push(style) + $('head').append(style) + } + + jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache + jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read + jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_ + jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_ + jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_ + jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_ + + jasmine.getJSONFixtures = function () { + return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures() + } + + jasmine.JSONFixtures = function () { + this.fixturesCache_ = {} + this.fixturesPath = 'spec/javascripts/fixtures/json' + } + + jasmine.JSONFixtures.prototype.load = function () { + this.read.apply(this, arguments) + return this.fixturesCache_ + } + + jasmine.JSONFixtures.prototype.read = function () { + var fixtureUrls = arguments + + for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { + this.getFixtureData_(fixtureUrls[urlIndex]) + } + + return this.fixturesCache_ + } + + jasmine.JSONFixtures.prototype.clearCache = function () { + this.fixturesCache_ = {} + } + + jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) { + if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url) + return this.fixturesCache_[url] + } + + jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) { + var self = this + , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl + + $.ajax({ + async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded + cache: false, + dataType: 'json', + url: url, + success: function (data) { + self.fixturesCache_[relativeUrl] = data + }, + error: function ($xhr, status, err) { + throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')') + } + }) + } + + jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) { + return this[methodName].apply(this, passedArguments) + } + + jasmine.jQuery = function () {} + + jasmine.jQuery.browserTagCaseIndependentHtml = function (html) { + return $('<div/>').append(html).html() + } + + jasmine.jQuery.elementToString = function (element) { + return $(element).map(function () { return this.outerHTML; }).toArray().join(', ') + } + + var data = { + spiedEvents: {} + , handlers: [] + } + + jasmine.jQuery.events = { + spyOn: function (selector, eventName) { + var handler = function (e) { + var calls = (typeof data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] !== 'undefined') ? data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0 + data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = { + selector: $(selector), + args: jasmine.util.argsToArray(arguments), + calls: ++calls + } + } + + $(selector).on(eventName, handler) + data.handlers.push(handler) + + return { + selector: selector, + eventName: eventName, + handler: handler, + reset: function (){ + delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] + }, + calls: { + count: function () { + return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ? + data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0; + }, + any: function () { + return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ? + !!data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : false; + } + } + } + }, + + args: function (selector, eventName) { + var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].args + + if (!actualArgs) { + throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent." + } + + return actualArgs + }, + + wasTriggered: function (selector, eventName) { + return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]) + }, + + wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) { + var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1) + + if (Object.prototype.toString.call(expectedArgs) !== '[object Array]') + actualArgs = actualArgs[0] + + return util.equals(actualArgs, expectedArgs, customEqualityTesters) + }, + + wasPrevented: function (selector, eventName) { + var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] + , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args + , e = args ? args[0] : undefined + + return e && e.isDefaultPrevented() + }, + + wasStopped: function (selector, eventName) { + var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] + , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args + , e = args ? args[0] : undefined + + return e && e.isPropagationStopped() + }, + + cleanUp: function () { + data.spiedEvents = {} + data.handlers = [] + } + } + + var hasProperty = function (actualValue, expectedValue) { + if (expectedValue === undefined) + return actualValue !== undefined + + return actualValue === expectedValue + } + + beforeEach(function () { + jasmine.addMatchers({ + toHaveClass: function () { + return { + compare: function (actual, className) { + return { pass: $(actual).hasClass(className) } + } + } + }, + + toHaveCss: function () { + return { + compare: function (actual, css) { + var stripCharsRegex = /[\s;\"\']/g + for (var prop in css) { + var value = css[prop] + // see issue #147 on gh + ;if ((value === 'auto') && ($(actual).get(0).style[prop] === 'auto')) continue + var actualStripped = $(actual).css(prop).replace(stripCharsRegex, '') + var valueStripped = value.replace(stripCharsRegex, '') + if (actualStripped !== valueStripped) return { pass: false } + } + return { pass: true } + } + } + }, + + toBeVisible: function () { + return { + compare: function (actual) { + return { pass: $(actual).is(':visible') } + } + } + }, + + toBeHidden: function () { + return { + compare: function (actual) { + return { pass: $(actual).is(':hidden') } + } + } + }, + + toBeSelected: function () { + return { + compare: function (actual) { + return { pass: $(actual).is(':selected') } + } + } + }, + + toBeChecked: function () { + return { + compare: function (actual) { + return { pass: $(actual).is(':checked') } + } + } + }, + + toBeEmpty: function () { + return { + compare: function (actual) { + return { pass: $(actual).is(':empty') } + } + } + }, + + toBeInDOM: function () { + return { + compare: function (actual) { + return { pass: $.contains(document.documentElement, $(actual)[0]) } + } + } + }, + + toExist: function () { + return { + compare: function (actual) { + return { pass: $(actual).length } + } + } + }, + + toHaveLength: function () { + return { + compare: function (actual, length) { + return { pass: $(actual).length === length } + } + } + }, + + toHaveAttr: function () { + return { + compare: function (actual, attributeName, expectedAttributeValue) { + return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) } + } + } + }, + + toHaveProp: function () { + return { + compare: function (actual, propertyName, expectedPropertyValue) { + return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) } + } + } + }, + + toHaveId: function () { + return { + compare: function (actual, id) { + return { pass: $(actual).attr('id') == id } + } + } + }, + + toHaveHtml: function () { + return { + compare: function (actual, html) { + return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) } + } + } + }, + + toContainHtml: function () { + return { + compare: function (actual, html) { + var actualHtml = $(actual).html() + , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html) + + return { pass: (actualHtml.indexOf(expectedHtml) >= 0) } + } + } + }, + + toHaveText: function () { + return { + compare: function (actual, text) { + var actualText = $(actual).text() + var trimmedText = $.trim(actualText) + + if (text && $.isFunction(text.test)) { + return { pass: text.test(actualText) || text.test(trimmedText) } + } else { + return { pass: (actualText == text || trimmedText == text) } + } + } + } + }, + + toContainText: function () { + return { + compare: function (actual, text) { + var trimmedText = $.trim($(actual).text()) + + if (text && $.isFunction(text.test)) { + return { pass: text.test(trimmedText) } + } else { + return { pass: trimmedText.indexOf(text) != -1 } + } + } + } + }, + + toHaveValue: function () { + return { + compare: function (actual, value) { + return { pass: $(actual).val() === value } + } + } + }, + + toHaveData: function () { + return { + compare: function (actual, key, expectedValue) { + return { pass: hasProperty($(actual).data(key), expectedValue) } + } + } + }, + + toContainElement: function () { + return { + compare: function (actual, selector) { + return { pass: $(actual).find(selector).length } + } + } + }, + + toBeMatchedBy: function () { + return { + compare: function (actual, selector) { + return { pass: $(actual).filter(selector).length } + } + } + }, + + toBeDisabled: function () { + return { + compare: function (actual, selector) { + return { pass: $(actual).is(':disabled') } + } + } + }, + + toBeFocused: function (selector) { + return { + compare: function (actual, selector) { + return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement } + } + } + }, + + toHandle: function () { + return { + compare: function (actual, event) { + if ( !actual || actual.length === 0 ) return { pass: false }; + var events = $._data($(actual).get(0), "events") + + if (!events || !event || typeof event !== "string") { + return { pass: false } + } + + var namespaces = event.split(".") + , eventType = namespaces.shift() + , sortedNamespaces = namespaces.slice(0).sort() + , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") + + if (events[eventType] && namespaces.length) { + for (var i = 0; i < events[eventType].length; i++) { + var namespace = events[eventType][i].namespace + + if (namespaceRegExp.test(namespace)) + return { pass: true } + } + } else { + return { pass: (events[eventType] && events[eventType].length > 0) } + } + + return { pass: false } + } + } + }, + + toHandleWith: function () { + return { + compare: function (actual, eventName, eventHandler) { + if ( !actual || actual.length === 0 ) return { pass: false }; + var normalizedEventName = eventName.split('.')[0] + , stack = $._data($(actual).get(0), "events")[normalizedEventName] + + for (var i = 0; i < stack.length; i++) { + if (stack[i].handler == eventHandler) return { pass: true } + } + + return { pass: false } + } + } + }, + + toHaveBeenTriggeredOn: function () { + return { + compare: function (actual, selector) { + var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) } + + result.message = result.pass ? + "Expected event " + $(actual) + " not to have been triggered on " + selector : + "Expected event " + $(actual) + " to have been triggered on " + selector + + return result; + } + } + }, + + toHaveBeenTriggered: function (){ + return { + compare: function (actual) { + var eventName = actual.eventName + , selector = actual.selector + , result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) } + + result.message = result.pass ? + "Expected event " + eventName + " not to have been triggered on " + selector : + "Expected event " + eventName + " to have been triggered on " + selector + + return result + } + } + }, + + toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) { + return { + compare: function (actual, selector, expectedArgs) { + var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual) + , result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) } + + if (wasTriggered) { + var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1] + result.message = result.pass ? + "Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) : + "Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) + + } else { + // todo check on this + result.message = result.pass ? + "Expected event " + actual + " not to have been triggered on " + selector : + "Expected event " + actual + " to have been triggered on " + selector + } + + return result + } + } + }, + + toHaveBeenPreventedOn: function () { + return { + compare: function (actual, selector) { + var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) } + + result.message = result.pass ? + "Expected event " + actual + " not to have been prevented on " + selector : + "Expected event " + actual + " to have been prevented on " + selector + + return result + } + } + }, + + toHaveBeenPrevented: function () { + return { + compare: function (actual) { + var eventName = actual.eventName + , selector = actual.selector + , result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) } + + result.message = result.pass ? + "Expected event " + eventName + " not to have been prevented on " + selector : + "Expected event " + eventName + " to have been prevented on " + selector + + return result + } + } + }, + + toHaveBeenStoppedOn: function () { + return { + compare: function (actual, selector) { + var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) } + + result.message = result.pass ? + "Expected event " + actual + " not to have been stopped on " + selector : + "Expected event " + actual + " to have been stopped on " + selector + + return result; + } + } + }, + + toHaveBeenStopped: function () { + return { + compare: function (actual) { + var eventName = actual.eventName + , selector = actual.selector + , result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) } + + result.message = result.pass ? + "Expected event " + eventName + " not to have been stopped on " + selector : + "Expected event " + eventName + " to have been stopped on " + selector + + return result + } + } + } + }) + + jasmine.getEnv().addCustomEqualityTester(function(a, b) { + if (a && b) { + if (a instanceof $ || jasmine.isDomNode(a)) { + var $a = $(a) + + if (b instanceof $) + return $a.length == b.length && a.is(b) + + return $a.is(b); + } + + if (b instanceof $ || jasmine.isDomNode(b)) { + var $b = $(b) + + if (a instanceof $) + return a.length == $b.length && $b.is(a) + + return $(b).is(a); + } + } + }) + + jasmine.getEnv().addCustomEqualityTester(function (a, b) { + if (a instanceof $ && b instanceof $ && a.size() == b.size()) + return a.is(b) + }) + }) + + afterEach(function () { + jasmine.getFixtures().cleanUp() + jasmine.getStyleFixtures().cleanUp() + jasmine.jQuery.events.cleanUp() + }) + + window.readFixtures = function () { + return jasmine.getFixtures().proxyCallTo_('read', arguments) + } + + window.preloadFixtures = function () { + jasmine.getFixtures().proxyCallTo_('preload', arguments) + } + + window.loadFixtures = function () { + jasmine.getFixtures().proxyCallTo_('load', arguments) + } + + window.appendLoadFixtures = function () { + jasmine.getFixtures().proxyCallTo_('appendLoad', arguments) + } + + window.setFixtures = function (html) { + return jasmine.getFixtures().proxyCallTo_('set', arguments) + } + + window.appendSetFixtures = function () { + jasmine.getFixtures().proxyCallTo_('appendSet', arguments) + } + + window.sandbox = function (attributes) { + return jasmine.getFixtures().sandbox(attributes) + } + + window.spyOnEvent = function (selector, eventName) { + return jasmine.jQuery.events.spyOn(selector, eventName) + } + + window.preloadStyleFixtures = function () { + jasmine.getStyleFixtures().proxyCallTo_('preload', arguments) + } + + window.loadStyleFixtures = function () { + jasmine.getStyleFixtures().proxyCallTo_('load', arguments) + } + + window.appendLoadStyleFixtures = function () { + jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments) + } + + window.setStyleFixtures = function (html) { + jasmine.getStyleFixtures().proxyCallTo_('set', arguments) + } + + window.appendSetStyleFixtures = function (html) { + jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments) + } + + window.loadJSONFixtures = function () { + return jasmine.getJSONFixtures().proxyCallTo_('load', arguments) + } + + window.getJSONFixture = function (url) { + return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url] + } +})); diff --git a/vendor/assets/javascripts/jquery.atwho.js b/vendor/assets/javascripts/jquery.atwho.js index 0d295ebe5af..e058e13303a 100644 --- a/vendor/assets/javascripts/jquery.atwho.js +++ b/vendor/assets/javascripts/jquery.atwho.js @@ -448,7 +448,7 @@ Controller = (function() { Controller.prototype.insertContentFor = function($li) { var data, tpl; tpl = this.getOpt('insertTpl'); - data = $.extend({}, $li.data('item-data'), { + data = $.extend({}, $li.data('itemData'), { 'atwho-at': this.at }); return this.callbacks("tplEval").call(this, tpl, data, "onInsert"); @@ -824,7 +824,7 @@ EditableController = (function(superClass) { this.$inputor.focus(); } suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0"; - data = $li.data('item-data'); + data = $li.data('itemData'); this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text); if (range = this._getRange()) { range.setEndAfter(this.query.el[0]); diff --git a/vendor/assets/javascripts/jquery.endless-scroll.js b/vendor/assets/javascripts/jquery.endless-scroll.js index 38db6b05101..f022d9a5d06 100644 --- a/vendor/assets/javascripts/jquery.endless-scroll.js +++ b/vendor/assets/javascripts/jquery.endless-scroll.js @@ -125,4 +125,4 @@ } }; -})(jQuery);
\ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js index 6a341a3f0fe..695eeb27c17 100644 --- a/vendor/assets/javascripts/peek.js +++ b/vendor/assets/javascripts/peek.js @@ -10,7 +10,7 @@ (function($) { var fetchRequestResults, getRequestId, peekEnabled, updatePerformanceBar; getRequestId = function() { - return $('#peek').data('request-id'); + return $('#peek').data('requestId'); }; peekEnabled = function() { return $('#peek').length; diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 094d6791505..0ac8885405e 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -321,7 +321,15 @@ production: # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" SAST_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - docker run --volume "$PWD:/code" \ + # Deprecation notice for CONFIDENCE_LEVEL variable + if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then + SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL" + echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL" + fi + + docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \ + --env SAST_DISABLE_REMOTE_CHECKS="${SAST_DISABLE_REMOTE_CHECKS:-false}" \ + --volume "$PWD:/code" \ --volume /var/run/docker.sock:/var/run/docker.sock \ "registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code ;; diff --git a/yarn.lock b/yarn.lock index 3285fdae331..4d7dc1be854 100644 --- a/yarn.lock +++ b/yarn.lock @@ -69,17 +69,28 @@ JSONStream@^1.0.3: jsonparse "^1.2.0" through ">=2.2.7 <3" -abbrev@1, abbrev@1.0.x: +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@1.3.3, accepts@~1.3.3: +accepts@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" dependencies: mime-types "~2.1.11" negotiator "0.6.1" +accepts@~1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + dependencies: + mime-types "~2.1.16" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -92,6 +103,13 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" +acorn-node@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.3.0.tgz#5f86d73346743810ef1269b901dbcbded020861b" + dependencies: + acorn "^5.4.1" + xtend "^4.0.1" + acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" @@ -100,13 +118,17 @@ acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.1: +acorn@^5.0.0, acorn@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" -acorn@^5.2.1: - version "5.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" +acorn@^5.2.1, acorn@^5.3.0, acorn@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" + +address@1.0.3, address@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" addressparser@1.0.1: version "1.0.1" @@ -127,9 +149,9 @@ ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" -ajv-keywords@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0" +ajv-keywords@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" ajv@^4.7.0, ajv@^4.9.1: version "4.11.8" @@ -138,7 +160,7 @@ ajv@^4.7.0, ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.0.0, ajv@^5.1.5: +ajv@^5.0.0: version "5.2.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" dependencies: @@ -156,6 +178,14 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e" + dependencies: + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -172,13 +202,29 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +amqplib@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63" + dependencies: + bitsyntax "~0.0.4" + bluebird "^3.4.6" + buffer-more-ints "0.0.2" + readable-stream "1.x >=1.1.9" + safe-buffer "^5.0.1" + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + dependencies: + string-width "^2.0.0" + ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" -ansi-html@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.5.tgz#0dcaa5a081206866bc240a3b773a184ea3b88b64" +ansi-escapes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" ansi-html@0.0.7: version "0.0.7" @@ -196,7 +242,7 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.1.0: +ansi-styles@^3.1.0, ansi-styles@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: @@ -209,15 +255,22 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + 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" dependencies: default-require-extensions "^1.0.0" -aproba@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" are-we-there-yet@~1.1.2: version "1.1.4" @@ -238,9 +291,17 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" -arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" array-filter@~0.0.0: version "0.0.1" @@ -262,6 +323,13 @@ array-flatten@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" @@ -288,6 +356,10 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -297,8 +369,8 @@ arrify@^1.0.0, arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" asn1.js@^4.0.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" dependencies: bn.js "^4.0.0" inherits "^2.0.1" @@ -322,9 +394,13 @@ assert@^1.1.1, assert@^1.4.0: dependencies: util "0.10.3" +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + ast-types@0.x.x: - version "0.10.1" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" + version "0.11.1" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.1.tgz#5bb3a8d5ba292c3f4ae94d46df37afc30300b990" astw@^2.0.0: version "2.2.0" @@ -344,12 +420,18 @@ async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@2.4.1, async@^2.1.2, async@^2.1.4: +async@^2.1.2, async@^2.1.4: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" dependencies: lodash "^4.14.0" +async@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + dependencies: + lodash "^4.14.0" + async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -364,6 +446,10 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atob@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" @@ -410,7 +496,7 @@ axios@^0.17.1: follow-redirects "^1.2.5" is-buffer "^1.1.5" -babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: +babel-code-frame@6.26.0, babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -1026,13 +1112,25 @@ base64-arraybuffer@0.1.5: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" base64-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + version "1.2.3" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801" base64id@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -1049,13 +1147,27 @@ better-assert@~1.0.0: dependencies: callsite "1.0.0" +bfj-node4@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.2.1.tgz#3a6aa2730cf6911ba2afb836c2f88f015d718f3f" + dependencies: + bluebird "^3.5.1" + check-types "^7.3.0" + tryer "^1.0.0" + big.js@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" binary-extensions@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bitsyntax@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" + dependencies: + buffer-more-ints "0.0.2" bl@~1.1.2: version "1.1.2" @@ -1084,31 +1196,31 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^2.10.2: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - -bluebird@^3.1.1, bluebird@^3.3.0: +bluebird@^3.1.1: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" +bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.6" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" -body-parser@^1.16.1: - version "1.17.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee" +body-parser@1.18.2, body-parser@^1.16.1: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" dependencies: - bytes "2.4.0" - content-type "~1.0.2" - debug "2.6.7" - depd "~1.1.0" - http-errors "~1.6.1" - iconv-lite "0.4.15" + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" on-finished "~2.3.0" - qs "6.4.0" - raw-body "~2.2.0" + qs "6.5.1" + raw-body "2.3.2" type-is "~1.6.15" bonjour@^3.5.0: @@ -1144,13 +1256,32 @@ bootstrap-sass@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.6.tgz#363b0d300e868d3e70134c1a742bb17288444fd1" -brace-expansion@^1.0.0, brace-expansion@^1.1.7, brace-expansion@^1.1.8: +boxen@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +brace-expansion@^1.0.0, brace-expansion@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + braces@^0.1.2: version "0.1.5" resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" @@ -1165,17 +1296,35 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.0, braces@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + define-property "^1.0.0" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + kind-of "^6.0.2" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + brorand@^1.0.1: - version "1.0.7" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.7.tgz#6677fa5e4901bdbf9c9ec2a748e28dca407a9bfc" + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" browser-pack@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531" + version "6.0.4" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f" dependencies: JSONStream "^1.0.3" - combine-source-map "~0.7.1" + combine-source-map "~0.8.0" defined "^1.0.0" + safe-buffer "^5.1.1" through2 "^2.0.0" umd "^3.0.0" @@ -1186,14 +1335,15 @@ browser-resolve@^1.11.0, browser-resolve@^1.7.0: resolve "1.1.7" browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + version "1.1.1" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" dependencies: - buffer-xor "^1.0.2" + buffer-xor "^1.0.3" cipher-base "^1.0.0" create-hash "^1.1.0" - evp_bytestokey "^1.0.0" + evp_bytestokey "^1.0.3" inherits "^2.0.1" + safe-buffer "^5.0.1" browserify-cipher@^1.0.0: version "1.0.0" @@ -1219,8 +1369,8 @@ browserify-rsa@^4.0.0: randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" dependencies: bn.js "^4.1.1" browserify-rsa "^4.0.0" @@ -1305,7 +1455,11 @@ buffer-indexof@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.0.tgz#f54f647c4f4e25228baa656a2e57e43d5f270982" -buffer-xor@^1.0.2: +buffer-more-ints@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" + +buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1318,8 +1472,8 @@ buffer@^4.3.0: isarray "^1.0.0" buffer@^5.0.2: - version "5.0.8" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24" + version "5.1.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe" dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1344,10 +1498,6 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" -bytes@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" - bytes@2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a" @@ -1356,6 +1506,38 @@ bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" +cacache@^10.0.1: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + cached-path-relative@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7" @@ -1393,7 +1575,7 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.1.0: +camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1410,6 +1592,10 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000649" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a" +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" + caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" @@ -1443,7 +1629,27 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0: +chalk@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796" + dependencies: + ansi-styles "^3.2.0" + escape-string-regexp "^1.0.5" + supports-color "^5.2.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + +chart.js@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-1.0.2.tgz#ad57d2229cfd8ccf5955147e8121b4911e69dfe7" + +check-types@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d" + +chokidar@^1.4.1, chokidar@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: @@ -1458,19 +1664,42 @@ chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" -cipher-base@^1.0.0, cipher-base@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" +chokidar@^2.0.0, chokidar@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" dependencies: inherits "^2.0.1" + safe-buffer "^5.0.1" circular-json@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" -circular-json@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.4.0.tgz#c448ea998b7fe31ecf472ec29c6b608e2e2a62fd" +circular-json@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f" clap@^1.0.9: version "1.1.3" @@ -1478,16 +1707,35 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + classlist-polyfill@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e" +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" dependencies: restore-cursor "^1.0.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -1538,6 +1786,13 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" @@ -1589,17 +1844,24 @@ combine-source-map@~0.7.1: lodash.memoize "~3.0.3" source-map "~0.5.3" -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" +combine-source-map@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" dependencies: - delayed-stream "~1.0.0" + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" -commander@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" +combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: - graceful-readlink ">= 1.0.0" + delayed-stream "~1.0.0" + +commander@^2.13.0, commander@^2.9.0: + version "2.14.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" commondir@^1.0.1: version "1.0.1" @@ -1609,7 +1871,7 @@ component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" -component-emitter@1.2.1: +component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -1623,11 +1885,14 @@ compressible@~2.0.10: dependencies: mime-db ">= 1.29.0 < 2" -compression-webpack-plugin@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.0.0.tgz#5c5eb6afd08ca6a5d66006eeef71da17b01bd676" +compression-webpack-plugin@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.1.7.tgz#b0dfb97cf1d26baab997b584b8c36fe91872abe2" dependencies: - async "2.4.1" + async "^2.4.1" + cacache "^10.0.1" + find-cache-dir "^1.0.0" + serialize-javascript "^1.4.0" webpack-sources "^1.0.1" compression@^1.5.2: @@ -1646,7 +1911,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.5.2: +concat-stream@^1.5.0, concat-stream@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1662,31 +1927,29 @@ concat-stream@~1.5.0, concat-stream@~1.5.1: readable-stream "~2.0.0" typedarray "~0.0.5" -configstore@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" +configstore@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" dependencies: + dot-prop "^4.1.0" graceful-fs "^4.1.2" - mkdirp "^0.5.0" - object-assign "^4.0.1" - os-tmpdir "^1.0.0" - osenv "^0.1.0" - uuid "^2.0.1" - write-file-atomic "^1.1.2" - xdg-basedir "^2.0.0" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" connect@^3.6.0: - version "3.6.3" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.3.tgz#f7320d46a25b4be7b483a2236517f24b1e27e301" + version "3.6.6" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" dependencies: - debug "2.6.8" - finalhandler "1.0.4" - parseurl "~1.3.1" - utils-merge "1.0.0" + debug "2.6.9" + finalhandler "1.1.0" + parseurl "~1.3.2" + utils-merge "1.0.1" console-browserify@^1.1.0: version "1.1.0" @@ -1716,9 +1979,9 @@ content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" -content-type@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" +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.0: version "1.5.0" @@ -1736,27 +1999,46 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -copy-webpack-plugin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.0.1.tgz#9728e383b94316050d0c7463958f2b85c0aa8200" +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" dependencies: - bluebird "^2.10.2" - fs-extra "^0.26.4" - glob "^6.0.4" - is-glob "^3.1.0" + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +copy-webpack-plugin@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.4.1.tgz#1e8c366211db6dc2ddee40e5a3e4fc661dd149e8" + dependencies: + cacache "^10.0.1" + find-cache-dir "^1.0.0" + globby "^7.1.1" + is-glob "^4.0.0" loader-utils "^0.2.15" - lodash "^4.3.0" - minimatch "^3.0.0" - node-dir "^0.1.10" + minimatch "^3.0.4" + p-limit "^1.0.0" + serialize-javascript "^1.4.0" -core-js@^2.2.0, core-js@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" +core-js@^2.2.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" core-js@^2.4.0, core-js@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" +core-js@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + core-js@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" @@ -1783,21 +2065,31 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" -create-hash@^1.1.0, create-hash@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" dependencies: cipher-base "^1.0.1" inherits "^2.0.1" - ripemd160 "^1.0.0" - sha.js "^2.3.6" + ripemd160 "^2.0.0" + sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" dependencies: + cipher-base "^1.0.3" create-hash "^1.1.0" inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" cropper@^2.3.0: version "2.3.0" @@ -1805,7 +2097,7 @@ cropper@^2.3.0: dependencies: jquery ">= 1.9.1" -cross-spawn@^5.0.1: +cross-spawn@5.1.0, cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: @@ -1856,34 +2148,32 @@ crypto-browserify@^3.11.0: public-encrypt "^4.0.0" randombytes "^2.0.0" +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + css-color-names@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" -css-loader@^0.28.0: - version "0.28.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.0.tgz#417cfa9789f8cde59a30ccbf3e4da7a806889bad" +css-loader@^0.28.9: + version "0.28.9" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.9.tgz#68064b85f4e271d7ce4c48a58300928e535d1c95" dependencies: - babel-code-frame "^6.11.0" + babel-code-frame "^6.26.0" css-selector-tokenizer "^0.7.0" - cssnano ">=2.6.1 <4" + cssnano "^3.10.0" + icss-utils "^2.1.0" loader-utils "^1.0.2" lodash.camelcase "^4.3.0" - object-assign "^4.0.1" + object-assign "^4.1.1" postcss "^5.0.6" - postcss-modules-extract-imports "^1.0.0" - postcss-modules-local-by-default "^1.0.1" - postcss-modules-scope "^1.0.0" - postcss-modules-values "^1.1.0" - source-list-map "^0.1.7" - -css-selector-tokenizer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152" - dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - regexpu-core "^1.0.0" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" css-selector-tokenizer@^0.7.0: version "0.7.0" @@ -1897,7 +2187,7 @@ cssesc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" -"cssnano@>=2.6.1 <4": +cssnano@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" dependencies: @@ -1951,6 +2241,10 @@ custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + d3-array@^1.2.0, d3-array@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc" @@ -2107,7 +2401,7 @@ de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" -debug@2, debug@~2.6.4, debug@~2.6.6, debug@~2.6.9: +debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@~2.6.4, debug@~2.6.6: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2119,19 +2413,13 @@ debug@2.2.0, debug@~2.2.0: dependencies: ms "0.7.1" -debug@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" - dependencies: - ms "2.0.0" - -debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8: +debug@2.6.8, debug@^2.1.1, debug@^2.6.6, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@^3.0.1, debug@^3.1.0: +debug@^3.0.1, debug@^3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2145,13 +2433,17 @@ deckar01-task_list@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9" +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + decompress-response@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" dependencies: mimic-response "^1.0.0" -deep-equal@^1.0.1, deep-equal@~1.0.1: +deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2176,7 +2468,26 @@ define-properties@^1.1.2: foreach "^2.0.5" object-keys "^1.0.8" -defined@^1.0.0, defined@~1.0.0: +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -2223,11 +2534,7 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" - -depd@1.1.1, depd@~1.1.0, depd@~1.1.1: +depd@1.1.1, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" @@ -2257,10 +2564,21 @@ detect-indent@^4.0.0: 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" + detect-node@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" +detect-port-alt@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.5.tgz#a1aa8fc805a4a5df9b905b7ddc7eed036bcce889" + dependencies: + address "^1.0.1" + debug "^2.6.0" + detective@^4.0.0: version "4.7.1" resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" @@ -2284,6 +2602,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -2360,6 +2685,12 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +dot-prop@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + dependencies: + is-obj "^1.0.0" + double-ended-queue@^2.1.0-0: version "2.1.0-0" resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" @@ -2382,9 +2713,9 @@ duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" -duplexify@^3.2.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd" +duplexify@^3.4.2, duplexify@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e" dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -2401,22 +2732,25 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -ejs@^2.5.6: - version "2.5.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88" +ejs@^2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" electron-to-chromium@^1.2.7: version "1.3.3" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e" elliptic@^6.0.0: - version "6.3.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" dependencies: bn.js "^4.4.0" brorand "^1.0.1" hash.js "^1.0.0" + hmac-drbg "^1.0.0" inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" emoji-unicode-version@^0.2.1: version "0.2.1" @@ -2427,8 +2761,8 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" encodeurl@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" end-of-stream@^1.0.0: version "1.4.0" @@ -2436,13 +2770,19 @@ end-of-stream@^1.0.0: dependencies: once "^1.4.0" +end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + engine.io-client@~3.1.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.4.tgz#4fcf1370b47163bd2ce9be2733972430350d4ea1" + version "3.1.5" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.5.tgz#85de17666560327ef1817978f6e3f8101ded2c47" dependencies: component-emitter "1.2.1" component-inherit "0.0.3" - debug "~2.6.9" + debug "~3.1.0" engine.io-parser "~2.1.1" has-cors "1.1.0" indexof "0.0.1" @@ -2463,17 +2803,17 @@ engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: has-binary2 "~1.0.2" engine.io@~3.1.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.4.tgz#3d0211b70a552ce841ffc7da8627b301a9a4162e" + version "3.1.5" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.5.tgz#0e7ef9d690eb0b35597f1d4ad02a26ca2dba3845" dependencies: - accepts "1.3.3" + accepts "~1.3.4" base64id "1.0.0" cookie "0.3.1" - debug "~2.6.9" + debug "~3.1.0" engine.io-parser "~2.1.0" ws "~3.3.1" optionalDependencies: - uws "~0.14.4" + uws "~9.14.0" enhanced-resolve@^3.4.0: version "3.4.1" @@ -2512,9 +2852,9 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.2.tgz#25103263dc4decbda60e0c737ca32313518027ee" +es-abstract@^1.7.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" dependencies: es-to-primitive "^1.1.1" function-bind "^1.1.1" @@ -2556,7 +2896,7 @@ es6-map@^0.1.3: es6-symbol "~3.1.1" event-emitter "~0.3.5" -es6-promise@^3.0.2, es6-promise@~3.0.2: +es6-promise@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" @@ -2810,9 +3150,9 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -etag@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" eve-raphael@0.5.0: version "0.5.0" @@ -2845,17 +3185,18 @@ events@^1.0.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" -eventsource@0.1.6, eventsource@^0.1.3: +eventsource@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" dependencies: original ">=0.0.5" -evp_bytestokey@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" dependencies: - create-hash "^1.1.1" + md5.js "^1.3.4" + safe-buffer "^5.1.1" execa@^0.7.0: version "0.7.0" @@ -2887,6 +3228,18 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" @@ -2900,60 +3253,106 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -exports-loader@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754be2740fc886" +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" dependencies: - loader-utils "^1.0.2" - source-map "0.5.x" + homedir-polyfill "^1.0.1" + +exports-loader@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5" + dependencies: + loader-utils "^1.1.0" + source-map "0.5.0" -express@^4.13.3, express@^4.15.2: - version "4.15.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" +express@^4.16.2: + version "4.16.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" dependencies: - accepts "~1.3.3" + accepts "~1.3.4" array-flatten "1.1.1" + body-parser "1.18.2" content-disposition "0.5.2" - content-type "~1.0.2" + content-type "~1.0.4" cookie "0.3.1" cookie-signature "1.0.6" - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.4" - fresh "0.5.0" + etag "~1.8.1" + finalhandler "1.1.0" + fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.2" path-to-regexp "0.1.7" - proxy-addr "~1.1.5" - qs "6.5.0" + proxy-addr "~2.0.2" + qs "6.5.1" range-parser "~1.2.0" - send "0.15.4" - serve-static "1.12.4" - setprototypeof "1.0.3" + safe-buffer "5.1.1" + send "0.16.1" + serve-static "1.13.1" + setprototypeof "1.1.0" statuses "~1.3.1" type-is "~1.6.15" - utils-merge "1.0.0" - vary "~1.1.1" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +external-editor@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" dependencies: is-extglob "^1.0.0" -extsprintf@1.3.0, extsprintf@^1.2.0: +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" @@ -2982,12 +3381,6 @@ faye-websocket@~0.11.0: dependencies: websocket-driver ">=0.5.1" -faye-websocket@~0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.7.3.tgz#cc4074c7f4a4dfd03af54dd65c354b135132ce11" - dependencies: - websocket-driver ">=0.3.6" - figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2995,6 +3388,12 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -3002,19 +3401,20 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-loader@^0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.1.tgz#6b328ee1234a729e4e47d36375dd6d35c0e1db84" +file-loader@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.8.tgz#a62592ed732667d7482dc3268c381c7f0c913086" dependencies: loader-utils "^1.0.2" + schema-utils "^0.4.5" file-uri-to-path@1: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" filename-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" fileset@^2.0.2: version "2.0.3" @@ -3023,13 +3423,13 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -filesize@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.3.0.tgz#53149ea3460e3b2e024962a51648aa572cf98122" +filesize@3.5.11: + version "3.5.11" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.11.tgz#1919326749433bb3cf77368bd158caabcc19e9ee" -filesize@^3.5.9: - version "3.5.10" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.10.tgz#fc8fa23ddb4ef9e5e0ab6e1e64f679a24a56761f" +filesize@^3.5.11: + version "3.6.0" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa" fill-range@^2.1.0: version "2.2.3" @@ -3041,15 +3441,24 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@1.0.4, finalhandler@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" dependencies: - debug "2.6.8" + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + dependencies: + debug "2.6.9" encodeurl "~1.0.1" escape-html "~1.0.3" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.2" statuses "~1.3.1" unpipe "~1.0.0" @@ -3091,6 +3500,13 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" +flush-write-stream@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + follow-redirects@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" @@ -3103,21 +3519,15 @@ follow-redirects@^1.2.5: dependencies: debug "^3.1.0" -for-each@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" - dependencies: - is-function "~1.0.0" - -for-in@^0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" for-own@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" dependencies: - for-in "^0.1.5" + for-in "^1.0.1" foreach@^2.0.5: version "2.0.5" @@ -3144,20 +3554,33 @@ form-data@~2.1.1: mime-types "^2.1.12" form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" dependencies: asynckit "^0.4.0" - combined-stream "^1.0.5" + combined-stream "1.0.6" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" from@~0: version "0.1.7" @@ -3169,26 +3592,25 @@ fs-access@^1.0.0: dependencies: null-check "^1.0.0" -fs-extra@^0.26.4: - version "0.26.7" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9" +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" dependencies: graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" + version "1.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" dependencies: nan "^2.3.0" - node-pre-gyp "^0.6.36" + node-pre-gyp "^0.6.39" fstream-ignore@^1.0.5: version "1.0.5" @@ -3214,11 +3636,7 @@ ftp@~0.3.10: readable-stream "1.1.x" xregexp "2.0.0" -function-bind@^1.0.2, function-bind@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" - -function-bind@^1.1.1: +function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -3272,6 +3690,10 @@ get-uri@2: ftp "~0.3.10" readable-stream "2" +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -3291,6 +3713,13 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -3301,17 +3730,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: +glob@^7.0.0, glob@^7.0.3: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -3322,7 +3741,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.0, glob@^7.1.2, glob@~7.1.2: +glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -3333,6 +3752,30 @@ glob@^7.1.0, glob@^7.1.2, glob@~7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + dependencies: + ini "^1.3.4" + +global-modules@1.0.0, global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + globals@^10.0.0: version "10.4.0" resolved "https://registry.yarnpkg.com/globals/-/globals-10.4.0.tgz#5c477388b128a9e4c5c5d01c7a2aca68c68b2da7" @@ -3362,26 +3805,38 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" dependencies: delegate "^3.1.2" -got@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" dependencies: - duplexify "^3.2.0" - infinity-agent "^2.0.0" + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" is-stream "^1.0.0" lowercase-keys "^1.0.0" - nested-error-stacks "^1.0.0" - object-assign "^3.0.0" - prepend-http "^1.0.0" - read-all-stream "^3.0.0" - timed-out "^2.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" got@^7.1.0: version "7.1.0" @@ -3402,26 +3857,29 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - graphlib@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.1.tgz#42352c52ba2f4d035cb566eb91f7395f76ebc951" dependencies: lodash "^4.11.1" -gzip-size@3.0.0, gzip-size@^3.0.0: +gzip-size@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520" dependencies: duplexer "^0.1.1" +gzip-size@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c" + dependencies: + duplexer "^0.1.1" + pify "^3.0.0" + handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -3491,6 +3949,10 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-symbol-support-x@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.3.0.tgz#588bd6927eaa0e296afae24160659167fc2be4f8" @@ -3505,23 +3967,64 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.0, has@^1.0.1, has@~1.0.1: +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" dependencies: function-bind "^1.0.2" +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + hash-sum@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" -hash.js@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" dependencies: - inherits "^2.0.1" + inherits "^2.0.3" + minimalistic-assert "^1.0.0" -hawk@~3.1.3: +hawk@3.1.3, hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" dependencies: @@ -3550,13 +4053,21 @@ hipchat-notifier@^1.1.0: lodash "^4.0.0" request "^2.0.0" +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" hoek@4.x.x: - version "4.2.0" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" home-or-tmp@^2.0.0: version "2.0.0" @@ -3565,6 +4076,12 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +homedir-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" + hosted-git-info@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" @@ -3582,7 +4099,7 @@ html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" -html-entities@1.2.0, html-entities@^1.2.0: +html-entities@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" @@ -3605,7 +4122,7 @@ http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" -http-errors@1.6.2, http-errors@~1.6.2: +http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -3614,15 +4131,6 @@ http-errors@1.6.2, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" -http-errors@~1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" - dependencies: - depd "1.1.0" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - http-proxy-agent@1: version "1.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" @@ -3694,19 +4202,29 @@ iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" -iconv-lite@0.4.19: +iconv-lite@0.4.19, iconv-lite@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -icss-replace-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5" +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + dependencies: + postcss "^6.0.1" ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" -ignore-by-default@^1.0.0: +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -3714,16 +4232,31 @@ ignore@^3.2.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" +ignore@^3.3.5: + version "3.3.7" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" -imports-loader@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.7.1.tgz#f204b5f34702a32c1db7d48d89d5e867a0441253" +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +imports-loader@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.8.0.tgz#030ea51b8ca05977c40a3abfd9b4088fe0be9a69" dependencies: loader-utils "^1.0.2" - source-map "^0.5.6" + source-map "^0.6.1" imurmurhash@^0.1.4: version "0.1.4" @@ -3743,10 +4276,6 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" -infinity-agent@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216" - inflection@~1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" @@ -3770,9 +4299,9 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" +ini@^1.3.4, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" inline-source-map@~0.6.0: version "0.6.2" @@ -3780,6 +4309,25 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" +inquirer@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -3811,7 +4359,7 @@ insert-module-globals@^7.0.0: through2 "^2.0.0" xtend "^4.0.0" -internal-ip@^1.2.0: +internal-ip@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" dependencies: @@ -3839,9 +4387,9 @@ ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" -ipaddr.js@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" is-absolute-url@^2.0.0: version "2.1.0" @@ -3854,6 +4402,18 @@ is-absolute@^0.2.3: is-relative "^0.2.1" is-windows "^0.2.0" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3864,14 +4424,14 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" - is-buffer@^1.1.0: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" +is-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" @@ -3882,13 +4442,41 @@ is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" -is-dotfile@^1.0.0: +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" is-equal-shallow@^0.1.3: version "0.1.3" @@ -3896,15 +4484,21 @@ is-equal-shallow@^0.1.3: dependencies: is-primitive "^2.0.0" -is-extendable@^0.1.1: +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" -is-extglob@^2.1.0: +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" @@ -3924,10 +4518,6 @@ is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" -is-function@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" @@ -3940,6 +4530,23 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + is-my-json-valid@^2.10.0: version "2.16.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" @@ -3950,11 +4557,12 @@ is-my-json-valid@^2.10.0: xtend "^4.0.0" is-my-json-valid@^2.12.4: - version "2.17.1" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471" + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" jsonpointer "^4.0.0" xtend "^4.0.0" @@ -3966,16 +4574,36 @@ is-number@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" -is-number@^2.0.2, is-number@^2.1.0: +is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + is-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -3996,6 +4624,12 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -4004,6 +4638,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -4034,6 +4672,10 @@ is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" +is-root@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5" + is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4066,6 +4708,14 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4086,12 +4736,20 @@ isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" dependencies: isarray "1.0.0" +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -4207,9 +4865,9 @@ jquery.waitforimages@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b" -"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02" +"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" js-base64@^2.1.9: version "2.1.9" @@ -4285,12 +4943,6 @@ json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - optionalDependencies: - graceful-fs "^4.1.6" - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4333,9 +4985,9 @@ karma-chrome-launcher@^2.2.0: fs-access "^1.0.0" which "^1.2.1" -karma-coverage-istanbul-reporter@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.3.tgz#daf26051d5a0daa5838a4ce81aa4a41724bdf36b" +karma-coverage-istanbul-reporter@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.1.tgz#2b42d145ddbb4868d85d52888c495a21c61d873c" dependencies: istanbul-api "^1.1.14" minimatch "^3.0.4" @@ -4407,17 +5059,29 @@ katex@^0.8.3: dependencies: match-at "^0.1.0" -kind-of@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" +killable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: - is-buffer "^1.0.2" + is-buffer "^1.1.5" -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - optionalDependencies: - graceful-fs "^4.1.9" +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" labeled-stream-splicer@^2.0.0: version "2.0.0" @@ -4427,16 +5091,22 @@ labeled-stream-splicer@^2.0.0: isarray "~0.0.1" stream-splicer "^2.0.0" -latest-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb" +latest-version@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" dependencies: - package-json "^1.0.0" + package-json "^4.0.0" lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" +lazy-cache@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" + dependencies: + set-getter "^0.1.0" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -4525,55 +5195,16 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" -lodash._baseassign@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" - dependencies: - lodash._basecopy "^3.0.0" - lodash.keys "^3.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - lodash._baseget@^3.0.0: version "3.7.2" resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4" -lodash._bindcallback@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - -lodash._createassigner@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" - dependencies: - lodash._bindcallback "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash.restparam "^3.0.0" - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - lodash._topath@^3.0.0: version "3.8.1" resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac" dependencies: lodash.isarray "^3.0.0" -lodash.assign@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" - dependencies: - lodash._baseassign "^3.0.0" - lodash._createassigner "^3.0.0" - lodash.keys "^3.0.0" - lodash.camelcase@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.1.1.tgz#065b3ff08f0b7662f389934c46a5504c90e0b2d8" @@ -4602,12 +5233,9 @@ lodash.deburr@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b" -lodash.defaults@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c" - dependencies: - lodash.assign "^3.0.0" - lodash.restparam "^3.0.0" +lodash.endswith@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" lodash.escaperegexp@^4.1.2: version "4.1.2" @@ -4620,14 +5248,18 @@ lodash.get@^3.7.0: lodash._baseget "^3.0.0" lodash._topath "^3.0.0" -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" +lodash.isfunction@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + lodash.kebabcase@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz#5e63bc9aa2a5562ff3b97ca7af2f803de1bcb90e" @@ -4635,14 +5267,6 @@ lodash.kebabcase@4.0.1: lodash.deburr "^4.0.0" lodash.words "^4.0.0" -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4655,10 +5279,6 @@ lodash.mergewith@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - lodash.snakecase@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.0.1.tgz#bd012e5d2f93f7b58b9303e9a7fbfd5db13d6281" @@ -4666,6 +5286,10 @@ lodash.snakecase@4.0.1: lodash.deburr "^4.0.0" lodash.words "^4.0.0" +lodash.startswith@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -4674,7 +5298,7 @@ lodash.words@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036" -lodash@4.17.4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: +lodash@4.17.4, lodash@^4.11.1, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -4682,6 +5306,10 @@ lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.5.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + log-symbols@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6" @@ -4689,15 +5317,16 @@ log-symbols@^2.1.0: chalk "^2.0.1" log4js@^2.3.9: - version "2.4.1" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.4.1.tgz#b0c4e88133e0e3056afdc6f91f7f377576158778" + version "2.5.3" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1" dependencies: - circular-json "^0.4.0" + circular-json "^0.5.1" date-format "^1.2.0" debug "^3.1.0" semver "^5.3.0" streamroller "^0.7.0" optionalDependencies: + amqplib "^0.5.2" axios "^0.15.3" hipchat-notifier "^1.1.0" loggly "^1.1.0" @@ -4739,11 +5368,7 @@ lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" -lru-cache@2.2.x: - version "2.2.4" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" - -lru-cache@^4.0.1, lru-cache@^4.1.1: +lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" dependencies: @@ -4785,6 +5410,10 @@ make-dir@^1.0.0: dependencies: pify "^2.3.0" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -4793,6 +5422,12 @@ map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + marked@^0.3.12: version "0.3.12" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519" @@ -4805,6 +5440,13 @@ math-expression-evaluator@^1.2.14: version "1.2.16" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9" +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4867,9 +5509,27 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.4: + version "3.1.6" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.6.tgz#8d7c043b48156f408ca07a4715182b79b99420bf" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + miller-rabin@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" dependencies: bn.js "^4.0.0" brorand "^1.0.1" @@ -4878,31 +5538,21 @@ miller-rabin@^4.0.0: version "1.29.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878" -mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" - -mime-db@~1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" - -mime-types@^2.1.11, mime-types@~2.1.17: - version "2.1.17" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" - dependencies: - mime-db "~1.30.0" +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: - version "2.1.15" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" +mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: - mime-db "~1.27.0" + mime-db "~1.33.0" -mime@1.3.4, mime@1.3.x, mime@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" -mime@^1.5.0: +mime@^1.3.4, mime@^1.4.1, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -4918,6 +5568,10 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -4930,14 +5584,40 @@ minimatch@3.0.3: dependencies: brace-expansion "^1.0.0" -minimist@0.0.8, minimist@~0.0.1: +minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0: +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -4976,6 +5656,17 @@ mousetrap@^1.4.6: version "1.4.6" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a" +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -4999,13 +5690,34 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + name-all-modules-plugin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz#0abfb6ad835718b9fb4def0674e06657a954375c" nan@^2.3.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + version "2.8.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" natural-compare@^1.4.0: version "1.4.0" @@ -5015,22 +5727,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" -nested-error-stacks@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf" - dependencies: - inherits "~2.0.1" - netmask@~1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" -node-dir@^0.1.10: - version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - dependencies: - minimatch "^3.0.2" - node-forge@0.6.33: version "0.6.33" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" @@ -5091,18 +5791,19 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" -node-pre-gyp@^0.6.36: - version "0.6.37" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.37.tgz#3c872b236b2e266e4140578fe1ee88f693323a05" +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" mkdirp "^0.5.1" nopt "^4.0.1" npmlog "^4.0.2" rc "^1.1.7" - request "^2.81.0" + request "2.81.0" rimraf "^2.6.1" semver "^5.3.0" - tape "^4.6.3" tar "^2.2.1" tar-pack "^3.4.0" @@ -5159,20 +5860,19 @@ nodemailer@^2.5.0: nodemailer-smtp-transport "2.7.2" socks "1.1.9" -nodemon@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c" +nodemon@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.15.1.tgz#54daa72443d8d5a548f130866b92e65cded0ed58" dependencies: - chokidar "^1.4.3" - debug "^2.2.0" - es6-promise "^3.0.2" - ignore-by-default "^1.0.0" - lodash.defaults "^3.1.2" - minimatch "^3.0.0" - ps-tree "^1.0.1" - touch "1.0.0" - undefsafe "0.0.3" - update-notifier "0.5.0" + chokidar "^2.0.2" + debug "^3.1.0" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.0" + semver "^5.4.1" + touch "^3.1.0" + undefsafe "^2.0.1" + update-notifier "^2.3.0" nopt@3.x: version "3.0.6" @@ -5202,7 +5902,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1: +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -5252,11 +5952,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5264,14 +5960,24 @@ object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" -object-inspect@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.3.0.tgz#5b1eb8e6742e2ee83342a637034d844928ba2f6d" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -5279,6 +5985,12 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + obuf@^1.0.0, obuf@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" @@ -5293,7 +6005,7 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@1.x, once@^1.3.0, once@^1.3.3, once@^1.4.0: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -5303,16 +6015,21 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + opener@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" -opn@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" +opn@5.2.0, opn@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" + is-wsl "^1.1.0" optimist@^0.6.1: version "0.6.1" @@ -5364,13 +6081,13 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@^0.1.0, osenv@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -5383,6 +6100,12 @@ p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +p-limit@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" + dependencies: + p-try "^1.0.0" + p-limit@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" @@ -5403,6 +6126,10 @@ p-timeout@^1.1.1: dependencies: p-finally "^1.0.0" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + pac-proxy-agent@1: version "1.1.0" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" @@ -5427,12 +6154,14 @@ pac-resolver@~2.0.0: netmask "~1.0.4" thunkify "~2.1.1" -package-json@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" +package-json@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" dependencies: - got "^3.2.0" - registry-url "^3.0.0" + got "^6.7.1" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" pako@~0.2.0: version "0.2.9" @@ -5446,6 +6175,14 @@ pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + parents@^1.0.0, parents@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" @@ -5453,8 +6190,8 @@ parents@^1.0.0, parents@^1.0.1: path-platform "~0.11.15" parse-asn1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -5477,6 +6214,10 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -5489,14 +6230,22 @@ parseuri@0.0.5: dependencies: better-assert "~1.0.0" -parseurl@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" +parseurl@~1.3.1, parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" path-browserify@0.0.0, path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -5551,6 +6300,12 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -5558,10 +6313,14 @@ pause-stream@0.0.11: through "~2.3" pbkdf2@^3.0.3: - version "3.0.9" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + version "3.0.14" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade" dependencies: - create-hmac "^1.1.2" + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" performance-now@^0.2.0: version "0.2.0" @@ -5625,6 +6384,10 @@ portfinder@^1.0.9: debug "^2.2.0" mkdirp "0.5.x" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" @@ -5770,32 +6533,32 @@ postcss-minify-selectors@^2.0.4: postcss "^5.0.14" postcss-selector-parser "^2.0.0" -postcss-modules-extract-imports@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" +postcss-modules-extract-imports@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" dependencies: - postcss "^5.0.4" + postcss "^6.0.1" -postcss-modules-local-by-default@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce" +postcss-modules-local-by-default@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" dependencies: - css-selector-tokenizer "^0.6.0" - postcss "^5.0.4" + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" -postcss-modules-scope@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29" +postcss-modules-scope@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" dependencies: - css-selector-tokenizer "^0.6.0" - postcss "^5.0.4" + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" -postcss-modules-values@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1" +postcss-modules-values@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" dependencies: - icss-replace-symbols "^1.0.2" - postcss "^5.0.14" + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" postcss-normalize-charset@^1.1.0: version "1.1.1" @@ -5886,6 +6649,14 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" +postcss@^6.0.1: + version "6.0.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" + dependencies: + chalk "^2.3.1" + source-map "^0.6.1" + supports-color "^5.2.0" + postcss@^6.0.14: version "6.0.15" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.15.tgz#f460cd6269fede0d1bf6defff0b934a9845d974d" @@ -5936,20 +6707,32 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -process@^0.11.0, process@~0.11.0: +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.0: version "0.11.9" resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" +process@~0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" -proxy-addr@~1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + +proxy-addr@~2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" dependencies: - forwarded "~0.1.0" - ipaddr.js "1.4.0" + forwarded "~0.1.2" + ipaddr.js "1.6.0" proxy-agent@~2.0.0: version "2.0.0" @@ -5968,7 +6751,7 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" -ps-tree@^1.0.1: +ps-tree@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" dependencies: @@ -5978,6 +6761,12 @@ pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +pstree.remy@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.0.tgz#f2af27265bd3e5b32bbfcc10e80bac55ba78688b" + dependencies: + ps-tree "^1.1.0" + public-encrypt@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" @@ -5988,6 +6777,21 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb" + dependencies: + duplexify "^3.5.3" + inherits "^2.0.3" + pump "^2.0.0" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -6005,24 +6809,20 @@ q@~1.4.0: resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" qjobs@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" - -qs@6.4.0, qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" -qs@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" +qs@6.5.1, qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" qs@~6.2.0: version "6.2.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" -qs@~6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" query-string@^4.1.0: version "4.3.2" @@ -6048,25 +6848,21 @@ querystringify@~1.0.0: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" randomatic@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" + is-number "^3.0.0" + kind-of "^4.0.0" -randombytes@^2.0.0, randombytes@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" - -randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62" + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" @@ -6085,7 +6881,7 @@ raven-js@^3.22.1: version "3.22.1" resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da" -raw-body@2: +raw-body@2, raw-body@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" dependencies: @@ -6094,19 +6890,11 @@ raw-body@2: iconv-lite "0.4.19" unpipe "1.0.0" -raw-body@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" - dependencies: - bytes "2.4.0" - iconv-lite "0.4.15" - unpipe "1.0.0" - raw-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" -rc@^1.0.1, rc@^1.1.7: +rc@^1.0.1: version "1.2.1" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" dependencies: @@ -6115,27 +6903,41 @@ rc@^1.0.1, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dev-utils@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-0.5.2.tgz#50d0b962d3a94b6c2e8f2011ed6468e4124bc410" +rc@^1.1.6, rc@^1.1.7: + version "1.2.5" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd" dependencies: - ansi-html "0.0.5" + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dev-utils@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.0.tgz#425ac7c9c40c2603bc4f7ab8836c1406e96bb473" + dependencies: + address "1.0.3" + babel-code-frame "6.26.0" chalk "1.1.3" + cross-spawn "5.1.0" + detect-port-alt "1.1.5" escape-string-regexp "1.0.5" - filesize "3.3.0" + filesize "3.5.11" + global-modules "1.0.0" gzip-size "3.0.0" - html-entities "1.2.0" - opn "4.0.2" - recursive-readdir "2.1.1" - sockjs-client "1.0.1" + inquirer "3.3.0" + is-root "1.0.0" + opn "5.2.0" + react-error-overlay "^4.0.0" + recursive-readdir "2.2.1" + shell-quote "1.6.1" + sockjs-client "1.1.4" strip-ansi "3.0.1" + text-table "0.2.0" -read-all-stream@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" - dependencies: - pinkie-promise "^2.0.0" - readable-stream "^2.0.0" +react-error-overlay@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4" read-only-stream@^2.0.0: version "2.0.0" @@ -6173,7 +6975,19 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@1.1.x: +"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.3.0, readable-stream@^2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readable-stream@1.1.x, "readable-stream@1.x >=1.1.9": version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: @@ -6182,7 +6996,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.0, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.0: +readable-stream@^2.0.0, readable-stream@^2.1.0, readable-stream@^2.2.2, readable-stream@^2.2.9: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -6194,7 +7008,7 @@ readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stre string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6: +readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: @@ -6228,9 +7042,9 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -recursive-readdir@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.1.1.tgz#a01cfc7f7f38a53ec096a096f63a50489c3e297c" +recursive-readdir@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" dependencies: minimatch "3.0.3" @@ -6288,11 +7102,17 @@ regenerator-transform@^0.10.0: private "^0.1.6" regex-cache@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" dependencies: is-equal-shallow "^0.1.3" - is-primitive "^2.0.0" + +regex-not@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" regexpu-core@^1.0.0: version "1.0.0" @@ -6310,7 +7130,14 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -registry-url@^3.0.0: +registry-auth-token@^3.0.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" dependencies: @@ -6338,16 +7165,10 @@ repeat-string@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" - dependencies: - is-finite "^1.0.0" - repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" @@ -6380,6 +7201,33 @@ request@2.75.x: tough-cookie "~2.3.0" tunnel-agent "~0.4.1" +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + request@^2.0.0, request@^2.74.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" @@ -6407,36 +7255,9 @@ request@^2.0.0, request@^2.74.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@^2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - requestretry@^1.2.2: - version "1.12.2" - resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.12.2.tgz#13ce38a4ce4e809f3c9ec6d4ca3b7b9ba4acf26c" + version "1.13.0" + resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94" dependencies: extend "^3.0.0" lodash "^4.15.0" @@ -6470,10 +7291,31 @@ requires-port@1.0.x, requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + +resolve-dir@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -6484,12 +7326,6 @@ resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.2.0, resolve@^1.4.0: dependencies: path-parse "^1.0.5" -resolve@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" - dependencies: - path-parse "^1.0.5" - restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -6497,11 +7333,16 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" -resumer@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" dependencies: - through "~2.3.4" + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" right-align@^0.1.1: version "0.1.3" @@ -6509,15 +7350,24 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1: +rimraf@2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +rimraf@^2.2.8: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: glob "^7.0.5" -ripemd160@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" run-async@^0.1.0: version "0.1.0" @@ -6525,17 +7375,41 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -safe-buffer@5.1.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -safe-buffer@^5.0.1, safe-buffer@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" sanitize-html@^1.16.1: version "1.16.3" @@ -6559,6 +7433,13 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" +schema-utils@^0.4.3, schema-utils@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -6583,32 +7464,40 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.3: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" +semver@^5.1.0, semver@^5.3.0, semver@^5.4.1: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" -send@0.15.4: - version "0.15.4" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" +send@0.16.1: + version "0.16.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" dependencies: - debug "2.6.8" + debug "2.6.9" depd "~1.1.1" destroy "~1.0.4" encodeurl "~1.0.1" escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" + etag "~1.8.1" + fresh "0.5.2" http-errors "~1.6.2" - mime "1.3.4" + mime "1.4.1" ms "2.0.0" on-finished "~2.3.0" range-parser "~1.2.0" statuses "~1.3.1" +serialize-javascript@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005" + serve-index@^1.7.2: version "1.9.0" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7" @@ -6621,23 +7510,47 @@ serve-index@^1.7.2: mime-types "~2.1.15" parseurl "~1.3.1" -serve-static@1.12.4: - version "1.12.4" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" +serve-static@1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.4" + parseurl "~1.3.2" + send "0.16.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" +set-getter@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" + dependencies: + to-object-path "^0.3.0" + set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -6646,15 +7559,13 @@ setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" -sha.js@^2.3.6: - version "2.4.8" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" - dependencies: - inherits "^2.0.1" +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" -sha.js@~2.4.4: - version "2.4.9" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" +sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: + version "2.4.10" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -6676,7 +7587,7 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -shell-quote@^1.6.1: +shell-quote@1.6.1, shell-quote@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" dependencies: @@ -6693,7 +7604,7 @@ shelljs@^0.7.5: interpret "^1.0.0" rechoir "^0.6.2" -signal-exit@^3.0.0: +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" @@ -6711,10 +7622,6 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -slide@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - smart-buffer@^1.0.13, smart-buffer@^1.0.4: version "1.1.15" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" @@ -6726,6 +7633,33 @@ smtp-connection@2.12.0: httpntlm "1.6.1" nodemailer-shared "1.1.0" +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^2.0.0" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -6779,17 +7713,6 @@ socket.io@2.0.4: socket.io-client "2.0.4" socket.io-parser "~3.1.1" -sockjs-client@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.0.1.tgz#8943ae05b46547bc2054816c409002cf5e2fe026" - dependencies: - debug "^2.1.0" - eventsource "^0.1.3" - faye-websocket "~0.7.3" - inherits "^2.0.1" - json3 "^3.3.2" - url-parse "^1.0.1" - sockjs-client@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" @@ -6801,12 +7724,12 @@ sockjs-client@1.1.4: json3 "^3.3.2" url-parse "^1.1.8" -sockjs@0.3.18: - version "0.3.18" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" dependencies: faye-websocket "^0.10.0" - uuid "^2.0.2" + uuid "^3.0.1" socks-proxy-agent@2: version "2.1.1" @@ -6836,23 +7759,33 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" -source-list-map@^0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" - source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" + dependencies: + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + 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@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +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" + +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.4.4: version "0.4.4" @@ -6860,6 +7793,10 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" +source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -6870,7 +7807,7 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" -source-map@~0.5.6: +source-map@~0.5.3, source-map@~0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -6911,6 +7848,12 @@ spdy@^3.4.1: select-hose "^2.0.0" spdy-transport "^2.0.18" +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" @@ -6946,7 +7889,24 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -"statuses@>= 1.3.1 < 2", statuses@~1.3.1: +ssri@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52" + dependencies: + safe-buffer "^5.1.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.3.1 < 2": + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -6970,13 +7930,20 @@ stream-combiner@~0.0.4: dependencies: duplexer "~0.1.1" +stream-each@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + stream-http@^2.0.0: - version "2.7.2" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" + version "2.8.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" - readable-stream "^2.2.6" + readable-stream "^2.3.3" to-arraybuffer "^1.0.0" xtend "^4.0.0" @@ -7014,12 +7981,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" - dependencies: - strip-ansi "^3.0.0" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -7035,13 +7996,12 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^3.0.0" -string.prototype.trim@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" +string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.0" - function-bind "^1.0.2" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" @@ -7093,12 +8053,12 @@ strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" -style-loader@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.1.tgz#591ffc80bcefe268b77c5d9ebc0505d772619f85" +style-loader@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.20.2.tgz#851b373c187890331776e9cde359eea9c95ecd00" dependencies: - loader-utils "^1.0.2" - schema-utils "^0.3.0" + loader-utils "^1.1.0" + schema-utils "^0.4.3" subarg@^1.0.0: version "1.0.0" @@ -7110,7 +8070,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.1, supports-color@^3.1.2, supports-color@^3.2.3: +supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -7134,6 +8094,12 @@ supports-color@^5.1.0: dependencies: has-flag "^2.0.0" +supports-color@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a" + dependencies: + has-flag "^3.0.0" + svg4everybody@2.1.9: version "2.1.9" resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d" @@ -7151,10 +8117,10 @@ svgo@^0.7.0: whet.extend "~0.9.9" syntax-error@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1" + version "1.4.0" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" dependencies: - acorn "^4.0.3" + acorn-node "^1.2.0" table@^3.7.8: version "3.8.3" @@ -7175,27 +8141,9 @@ tapable@^0.2.7: version "0.2.8" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" -tape@^4.6.3: - version "4.8.0" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.8.0.tgz#f6a9fec41cc50a1de50fa33603ab580991f6068e" - dependencies: - deep-equal "~1.0.1" - defined "~1.0.0" - for-each "~0.3.2" - function-bind "~1.1.0" - glob "~7.1.2" - has "~1.0.1" - inherits "~2.0.3" - minimist "~1.2.0" - object-inspect "~1.3.0" - resolve "~1.4.0" - resumer "~0.0.0" - string.prototype.trim "~1.1.2" - through "~2.3.8" - tar-pack@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" dependencies: debug "^2.2.0" fstream "^1.0.10" @@ -7214,6 +8162,12 @@ tar@^2.2.1: fstream "^1.0.2" inherits "2" +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + dependencies: + execa "^0.7.0" + test-exclude@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" @@ -7224,7 +8178,7 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -text-table@~0.2.0: +text-table@0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -7247,7 +8201,7 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4, through@~2.3.8: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -7269,10 +8223,6 @@ timeago.js@^3.0.2: dependencies: "@types/jquery" "^2.0.40" -timed-out@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" - timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -7297,18 +8247,12 @@ tiny-emitter@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c" -tmp@0.0.33: +tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: os-tmpdir "~1.0.2" -tmp@0.0.x: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - dependencies: - os-tmpdir "~1.0.1" - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -7325,19 +8269,34 @@ to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" -touch@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" dependencies: - nopt "~1.0.10" + kind-of "^3.0.2" -tough-cookie@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" dependencies: - punycode "^1.4.1" + is-number "^3.0.0" + repeat-string "^1.6.1" -tough-cookie@~2.3.3: +to-regex@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" + dependencies: + define-property "^0.2.5" + extend-shallow "^2.0.1" + regex-not "^1.0.0" + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + dependencies: + nopt "~1.0.10" + +tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" dependencies: @@ -7355,6 +8314,10 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +tryer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7" + tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" @@ -7363,10 +8326,14 @@ tsscmp@~1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" -tty-browserify@0.0.0, tty-browserify@~0.0.0: +tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" +tty-browserify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -7388,11 +8355,11 @@ type-check@~0.3.2: prelude-ls "~1.1.2" type-is@~1.6.15: - version "1.6.15" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" dependencies: media-typer "0.3.0" - mime-types "~2.1.15" + mime-types "~2.1.18" typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" @@ -7424,8 +8391,8 @@ uid-number@^0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" ultron@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" umd@^3.0.0: version "3.0.1" @@ -7435,9 +8402,11 @@ unc-path-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" -undefsafe@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" +undefsafe@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" + dependencies: + debug "^2.2.0" underscore@^1.8.3: version "1.8.3" @@ -7447,6 +8416,15 @@ underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -7461,28 +8439,73 @@ uniqs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" +unique-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + dependencies: + crypto-random-string "^1.0.0" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -update-notifier@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" dependencies: - chalk "^1.0.0" - configstore "^1.0.0" + has-value "^0.3.1" + isobject "^3.0.0" + +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + +upath@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.2.tgz#80aaae5395abc5fd402933ae2f58694f0860204c" + dependencies: + lodash.endswith "^4.2.1" + lodash.isfunction "^3.0.8" + lodash.isstring "^4.0.1" + lodash.startswith "^4.2.1" + +update-notifier@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" + dependencies: + boxen "^1.2.1" + chalk "^2.0.1" + configstore "^3.0.0" + import-lazy "^2.1.0" + is-installed-globally "^0.1.0" is-npm "^1.0.0" - latest-version "^1.0.0" - repeating "^1.1.2" + latest-version "^3.0.0" semver-diff "^2.0.0" - string-length "^1.0.0" + xdg-basedir "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" -url-loader@^0.5.8: - version "0.5.8" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.8.tgz#b9183b1801e0f847718673673040bc9dc1c715c5" +url-loader@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7" dependencies: loader-utils "^1.0.2" - mime "1.3.x" + mime "^1.4.1" + schema-utils "^0.3.0" url-parse-lax@^1.0.0: version "1.0.0" @@ -7497,13 +8520,6 @@ url-parse@1.0.x: querystringify "0.0.x" requires-port "1.0.x" -url-parse@^1.0.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" - dependencies: - querystringify "0.0.x" - requires-port "1.0.x" - url-parse@^1.1.8: version "1.1.9" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19" @@ -7522,6 +8538,14 @@ url@^0.11.0, url@~0.11.0: punycode "1.3.2" querystring "0.2.0" +use@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" + dependencies: + define-property "^0.2.5" + isobject "^3.0.0" + lazy-cache "^2.0.2" + user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -7529,10 +8553,10 @@ user-home@^2.0.0: os-homedir "^1.0.0" useragent@^2.1.12: - version "2.2.1" - resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.2.1.tgz#cf593ef4f2d175875e8bb658ea92e18a4fd06d8e" + version "2.3.0" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972" dependencies: - lru-cache "2.2.x" + lru-cache "4.1.x" tmp "0.0.x" util-deprecate@~1.0.1: @@ -7545,21 +8569,17 @@ util@0.10.3, util@^0.10.3, util@~0.10.1: dependencies: inherits "2.0.1" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - -uuid@^2.0.1, uuid@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" -uuid@^3.0.0, uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" -uws@~0.14.4: - version "0.14.5" - resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc" +uws@~9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95" validate-npm-package-license@^3.0.1: version "3.0.1" @@ -7572,6 +8592,10 @@ vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" @@ -7613,9 +8637,9 @@ vue-hot-reload-api@^2.2.0: version "2.2.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz#683bd1d026c0d3b3c937d5875679e9a87ec6cd8f" -vue-loader@^13.7.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.7.0.tgz#4d6a35b169c2a0a488842fb95c85052105fa9729" +vue-loader@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-14.1.1.tgz#331f197fcea790d6b8662c29b850806e7eb29342" dependencies: consolidate "^0.14.0" hash-sum "^1.0.2" @@ -7628,7 +8652,7 @@ vue-loader@^13.7.0: resolve "^1.4.0" source-map "^0.6.1" vue-hot-reload-api "^2.2.0" - vue-style-loader "^3.0.0" + vue-style-loader "^4.0.1" vue-template-es2015-compiler "^1.6.0" vue-resource@^1.3.5: @@ -7641,9 +8665,9 @@ vue-router@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9" -vue-style-loader@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.0.3.tgz#623658f81506aef9d121cdc113a4f5c9cac32df7" +vue-style-loader@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.0.2.tgz#e89aa4702a0c6b9630d8de70b1cbddb06b9ad254" dependencies: hash-sum "^1.0.2" loader-utils "^1.0.2" @@ -7681,32 +8705,24 @@ wbuf@^1.1.0, wbuf@^1.7.2: dependencies: minimalistic-assert "^1.0.0" -webpack-bundle-analyzer@^2.8.2: - version "2.8.2" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.8.2.tgz#8b6240c29a9d63bc72f09d920fb050adbcce9fe8" +webpack-bundle-analyzer@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.10.0.tgz#d0646cda342939f6f05eb632a090abbd90317446" dependencies: - acorn "^5.0.3" - chalk "^1.1.3" - commander "^2.9.0" - ejs "^2.5.6" - express "^4.15.2" - filesize "^3.5.9" - gzip-size "^3.0.0" + acorn "^5.3.0" + bfj-node4 "^5.2.0" + chalk "^2.3.0" + commander "^2.13.0" + ejs "^2.5.7" + express "^4.16.2" + filesize "^3.5.11" + gzip-size "^4.1.0" lodash "^4.17.4" mkdirp "^0.5.1" opener "^1.4.3" - ws "^2.3.1" + ws "^4.0.0" -webpack-dev-middleware@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9" - dependencies: - memory-fs "~0.4.1" - mime "^1.3.4" - path-is-absolute "^1.0.0" - range-parser "^1.0.3" - -webpack-dev-middleware@^1.12.0: +webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0: version "1.12.2" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" dependencies: @@ -7716,33 +8732,37 @@ webpack-dev-middleware@^1.12.0: range-parser "^1.0.3" time-stamp "^2.0.0" -webpack-dev-server@^2.6.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.7.1.tgz#21580f5a08cd065c71144cf6f61c345bca59a8b8" +webpack-dev-server@^2.11.1: + version "2.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz#6f9358a002db8403f016e336816f4485384e5ec0" dependencies: ansi-html "0.0.7" + array-includes "^3.0.3" bonjour "^3.5.0" - chokidar "^1.6.0" + chokidar "^2.0.0" compression "^1.5.2" connect-history-api-fallback "^1.3.0" + debug "^3.1.0" del "^3.0.0" - express "^4.13.3" + express "^4.16.2" html-entities "^1.2.0" http-proxy-middleware "~0.17.4" - internal-ip "^1.2.0" + import-local "^1.0.0" + internal-ip "1.2.0" ip "^1.1.5" + killable "^1.0.0" loglevel "^1.4.1" - opn "4.0.2" + opn "^5.1.0" portfinder "^1.0.9" selfsigned "^1.9.1" serve-index "^1.7.2" - sockjs "0.3.18" + sockjs "0.3.19" sockjs-client "1.1.4" spdy "^3.4.1" strip-ansi "^3.0.0" - supports-color "^3.1.1" - webpack-dev-middleware "^1.11.0" - yargs "^6.0.0" + supports-color "^5.1.0" + webpack-dev-middleware "1.12.2" + yargs "6.6.0" webpack-sources@^1.0.1: version "1.0.1" @@ -7755,14 +8775,14 @@ webpack-stats-plugin@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.1.5.tgz#29e5f12ebfd53158d31d656a113ac1f7b86179d9" -webpack@^3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.5.5.tgz#3226f09fc8b3e435ff781e7af34f82b68b26996c" +webpack@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" - ajv "^5.1.5" - ajv-keywords "^2.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" async "^2.1.2" enhanced-resolve "^3.4.0" escope "^3.6.0" @@ -7782,7 +8802,7 @@ webpack@^3.5.5: webpack-sources "^1.0.1" yargs "^8.0.2" -websocket-driver@>=0.3.6, websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1: version "0.6.5" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" dependencies: @@ -7814,12 +8834,24 @@ which@^1.1.1, which@^1.2.1, which@^1.2.9: dependencies: isexe "^1.1.1" +which@^1.2.14: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" dependencies: string-width "^1.0.2" +widest-line@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" + dependencies: + string-width "^2.1.1" + window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" @@ -7854,13 +8886,13 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -write-file-atomic@^1.1.2: - version "1.3.4" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" +write-file-atomic@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" - slide "^1.1.5" + signal-exit "^3.0.2" write@^0.2.1: version "0.2.1" @@ -7868,11 +8900,12 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80" +ws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-4.0.0.tgz#bfe1da4c08eeb9780b986e0e4d10eccd7345999f" dependencies: - safe-buffer "~5.0.1" + async-limiter "~1.0.0" + safe-buffer "~5.1.0" ultron "~1.1.0" ws@~3.3.1: @@ -7883,11 +8916,9 @@ ws@~3.3.1: safe-buffer "~5.1.0" ultron "~1.1.0" -xdg-basedir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" - dependencies: - os-homedir "^1.0.0" +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" xmlhttprequest-ssl@~1.5.4: version "1.5.5" @@ -7897,7 +8928,7 @@ xregexp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" -xtend@^4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -7905,6 +8936,10 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -7921,7 +8956,7 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" -yargs@^6.0.0: +yargs@6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" dependencies: |