diff --git a/.babelrc b/.babelrc index b93bef72de..8cf07b7342 100644 --- a/.babelrc +++ b/.babelrc @@ -1,20 +1,20 @@ { - "presets": [ - ["latest", { "es2015": { "modules": false } }], - "stage-2" - ], + "presets": [["latest", { "es2015": { "modules": false } }], "stage-2"], "env": { "coverage": { "plugins": [ - ["istanbul", { - "exclude": [ - "spec/javascripts/**/*", - "app/assets/javascripts/locale/**/app.js" - ] - }], - ["transform-define", { - "process.env.BABEL_ENV": "coverage" - }] + [ + "istanbul", + { + "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"] + } + ], + [ + "transform-define", + { + "process.env.BABEL_ENV": "coverage" + } + ] ] } } diff --git a/.codeclimate.yml b/.codeclimate.yml index 216ecf43be..8699a903f2 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -10,12 +10,6 @@ engines: - javascript exclude_paths: - "lib/api/v3/*" - eslint: - enabled: true - channel: "eslint-4" - rubocop: - enabled: true - channel: "gitlab-rubocop-0-52-1" ratings: paths: - Gemfile.lock diff --git a/.eslintignore b/.eslintignore index 1623b99621..33a8186fad 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,11 +1,12 @@ +/app/assets/javascripts/locale/**/app.js +/config/ /builds/ /coverage/ /coverage-javascript/ /node_modules/ /public/ +/scripts/ /tmp/ /vendor/ karma.config.js webpack.config.js -svg.config.js -/app/assets/javascripts/locale/**/app.js diff --git a/.eslintrc b/.eslintrc index 8f9cdfb14a..3f187db0c0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,5 @@ { "env": { - "jquery": true, "browser": true, "es6": true }, @@ -36,15 +35,22 @@ "import/no-commonjs": "error", "no-multiple-empty-lines": ["error", { "max": 1 }], "promise/catch-or-return": "error", - "no-underscore-dangle": ["error", { "allow": ["__", "_links"]}], - "vue/html-self-closing": ["error", { - "html": { - "void": "always", - "normal": "never", - "component": "always" - }, - "svg": "always", - "math": "always" - }] + "no-underscore-dangle": ["error", { "allow": ["__", "_links"] }], + "no-mixed-operators": 0, + "space-before-function-paren": 0, + "curly": 0, + "arrow-parens": 0, + "vue/html-self-closing": [ + "error", + { + "html": { + "void": "always", + "normal": "never", + "component": "always" + }, + "svg": "always", + "math": "always" + } + ] } } diff --git a/.flayignore b/.flayignore index 87cb3507b0..3d69bb2c98 100644 --- a/.flayignore +++ b/.flayignore @@ -8,3 +8,4 @@ lib/gitlab/redis/*.rb lib/gitlab/gitaly_client/operation_service.rb lib/gitlab/background_migration/* app/models/project_services/kubernetes_service.rb +lib/gitlab/workhorse.rb diff --git a/.gitignore b/.gitignore index fa39ae01ff..e9ff0048c1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ eslint-report.html /.gitlab_shell_secret .idea +/.vscode/* /.rbenv-version .rbx/ /.ruby-gemset @@ -22,6 +23,9 @@ eslint-report.html /.yarn-cache /.byebug_history /Vagrantfile +/app/assets/images/icons.json +/app/assets/images/icons.svg +/app/assets/images/illustrations/ /app/assets/javascripts/locale/**/app.js /backups/* /config/aws.yml @@ -46,6 +50,7 @@ eslint-report.html /db/data.yml /doc/code/* /dump.rdb +/jsconfig.json /log/*.log* /node_modules/ /nohup.out diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f3ebcb10fb..26e442ccea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,6 +78,19 @@ stages: - mysql:5.7 - redis:alpine +.rails5-variables: &rails5-variables + script: + - export RAILS5=${RAILS5} + - export BUNDLE_GEMFILE=${BUNDLE_GEMFILE} + +.rails5: &rails5 + allow_failure: true + only: + - /rails5/ + variables: + BUNDLE_GEMFILE: "Gemfile.rails5" + RAILS5: "true" + # Skip all jobs except the ones that begin with 'docs/'. # Used for commits including ONLY documentation changes. # https://docs.gitlab.com/ce/development/writing_documentation.html#testing @@ -118,6 +131,7 @@ stages: <<: *dedicated-runner <<: *except-docs-and-qa <<: *pull-cache + <<: *rails5-variables stage: test script: - JOB_NAME=( $CI_JOB_NAME ) @@ -148,14 +162,23 @@ stages: <<: *rspec-metadata <<: *use-pg +.rspec-metadata-pg-rails5: &rspec-metadata-pg-rails5 + <<: *rspec-metadata-pg + <<: *rails5 + .rspec-metadata-mysql: &rspec-metadata-mysql <<: *rspec-metadata <<: *use-mysql +.rspec-metadata-mysql-rails5: &rspec-metadata-mysql-rails5 + <<: *rspec-metadata-mysql + <<: *rails5 + .spinach-metadata: &spinach-metadata <<: *dedicated-runner <<: *except-docs-and-qa <<: *pull-cache + <<: *rails5-variables stage: test script: - JOB_NAME=( $CI_JOB_NAME ) @@ -179,10 +202,18 @@ stages: <<: *spinach-metadata <<: *use-pg +.spinach-metadata-pg-rails5: &spinach-metadata-pg-rails5 + <<: *spinach-metadata-pg + <<: *rails5 + .spinach-metadata-mysql: &spinach-metadata-mysql <<: *spinach-metadata <<: *use-mysql +.spinach-metadata-mysql-rails5: &spinach-metadata-mysql-rails5 + <<: *spinach-metadata-mysql + <<: *rails5 + .only-canonical-masters: &only-canonical-masters only: - master@gitlab-org/gitlab-ce @@ -257,15 +288,25 @@ stages: ## # Trigger a package build in omnibus-gitlab repository # -package-qa: +package-and-qa: <<: *dedicated-runner image: ruby:2.4-alpine before_script: [] stage: build cache: {} when: manual + variables: + GIT_STRATEGY: none + retry: 0 + before_script: + # We need to download the script rather than clone the repo since the + # package-and-qa job will not be able to run when the branch gets + # deleted (when merging the MR). + - apk add --update openssl + - wget https://gitlab.com/$CI_PROJECT_PATH/raw/$CI_COMMIT_SHA/scripts/trigger-build-omnibus + - chmod 755 trigger-build-omnibus script: - - scripts/trigger-build-omnibus + - ./trigger-build-omnibus only: - //@gitlab-org/gitlab-ce - //@gitlab-org/gitlab-ee @@ -458,6 +499,70 @@ spinach-pg 1 2: *spinach-metadata-pg spinach-mysql 0 2: *spinach-metadata-mysql spinach-mysql 1 2: *spinach-metadata-mysql +rspec-pg-rails5 0 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 1 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 2 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 3 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 4 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 5 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 6 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 7 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 8 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 9 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 10 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 11 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 12 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 13 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 14 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 15 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 16 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 17 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 18 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 19 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 20 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 21 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 22 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 23 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 24 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 25 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 26 28: *rspec-metadata-pg-rails5 +rspec-pg-rails5 27 28: *rspec-metadata-pg-rails5 + +rspec-mysql-rails5 0 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 1 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 2 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 3 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 4 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 5 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 6 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 7 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 8 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 9 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 10 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 11 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 12 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 13 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 14 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 15 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 16 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 17 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 18 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 19 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 20 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 21 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 22 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 23 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 24 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 25 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 26 28: *rspec-metadata-mysql-rails5 +rspec-mysql-rails5 27 28: *rspec-metadata-mysql-rails5 + +spinach-pg-rails5 0 2: *spinach-metadata-pg-rails5 +spinach-pg-rails5 1 2: *spinach-metadata-pg-rails5 + +spinach-mysql-rails5 0 2: *spinach-metadata-mysql-rails5 +spinach-mysql-rails5 1 2: *spinach-metadata-mysql-rails5 + static-analysis: <<: *dedicated-no-docs-no-db-pull-cache-job dependencies: @@ -544,7 +649,7 @@ migration:path-mysql: .db-rollback: &db-rollback <<: *dedicated-no-docs-pull-cache-job script: - - bundle exec rake db:rollback STEP=119 + - bundle exec rake db:migrate VERSION=20170523121229 - bundle exec rake db:migrate db:rollback-pg: @@ -608,21 +713,23 @@ karma: codequality: <<: *dedicated-no-docs-no-db-pull-cache-job - image: docker:latest + image: docker:stable + allow_failure: true + # gitlab-org runners set `privileged: false` but we need to have it set to true + # since we're using Docker in Docker + tags: [] before_script: [] services: - docker:dind variables: SETUP_DB: "false" DOCKER_DRIVER: overlay2 - CODECLIMATE_FORMAT: json cache: {} dependencies: [] script: - - apk update && apk add jq - - ./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 - - jq -c 'map({check_name,description,fingerprint,location})' raw_codeclimate.json > codeclimate.json + # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products + - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') + - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code artifacts: paths: [codeclimate.json] expire_in: 1 week @@ -655,7 +762,13 @@ qa:selectors: - bundle exec bin/qa Test::Sanity::Selectors coverage: - <<: *dedicated-no-docs-no-db-pull-cache-job + # Don't include dedicated-no-docs-no-db-pull-cache-job here since we need to + # download artifacts from all the rspec jobs instead of from setup-test-env only + <<: *dedicated-runner + <<: *except-docs-and-qa + <<: *pull-cache + variables: + SETUP_DB: "false" stage: post-test script: - bundle exec scripts/merge-simplecov diff --git a/.gitlab/merge_request_templates/Database Changes.md b/.gitlab/merge_request_templates/Database Changes.md index 8302b3b30c..68bc0fd1c7 100644 --- a/.gitlab/merge_request_templates/Database Changes.md +++ b/.gitlab/merge_request_templates/Database Changes.md @@ -33,13 +33,16 @@ When removing columns, tables, indexes or other structures: ## General Checklist -- [ ] [Changelog entry](https://docs.gitlab.com/ce/development/changelog.html) added, if necessary -- [ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md) +- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary +- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html) - [ ] API support added - [ ] Tests added for this feature/bug - Review - [ ] Has been reviewed by Backend - [ ] Has been reviewed by Database -- [ ] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html) -- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) +- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html) +- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides) - [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) +- [ ] Internationalization required/considered +- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan +- [ ] End-to-end tests pass (`package-qa` manual pipeline job) diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md index 102eb7e795..da38a703c3 100644 --- a/.gitlab/merge_request_templates/Documentation.md +++ b/.gitlab/merge_request_templates/Documentation.md @@ -1,16 +1,29 @@ -See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html + ## What does this MR do? -(briefly describe what this MR is about) + + +## Related issues + + + +Closes ## Moving docs to a new location? -See the guidelines: http://docs.gitlab.com/ce/development/doc_styleguide.html#changing-document-location +Read the guidelines: +https://docs.gitlab.com/ce/development/writing_documentation.html#changing-document-location -- [ ] Make sure the old link is not removed and has its contents replaced with a link to the new location. +- [ ] Make sure the old link is not removed and has its contents replaced with + a link to the new location. - [ ] Make sure internal links pointing to the document in question are not broken. -- [ ] Search and replace any links referring to old docs in GitLab Rails app, specifically under the `app/views/` directory. -- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/doc_styleguide.html#redirections-for-pages-with-disqus-comments) to the new document if there are any Disqus comments on the old document thread. -- [ ] If working on CE, submit an MR to EE with the changes as well. +- [ ] Search and replace any links referring to old docs in GitLab Rails app, + specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories. +- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments) + to the new document if there are any Disqus comments on the old document thread. +- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE + with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee). - [ ] Ping one of the technical writers for review. + +/label ~Documentation diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..b674ccd50c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +/app/assets/javascripts/locale/**/app.js +/node_modules/ +/public/ +/vendor/ +/tmp/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..3384551aea --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "printWidth": 100, + "singleQuote": true, + "trailingComma": "es5", + "overrides": [ + { + "files": ["**/app/**/*", "**/spec/**/*"], + "options": { + "trailingComma": "all" + } + } + ] +} diff --git a/.rubocop.yml b/.rubocop.yml index 90dac88453..0582bfe8d7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,6 +31,78 @@ Style/MutableConstant: - 'ee/db/post_migrate/**/*' - 'ee/db/geo/migrate/**/*' +Naming/FileName: + ExpectMatchingDefinition: true + Exclude: + - 'spec/**/*' + - 'features/**/*' + - 'ee/spec/**/*' + - 'qa/spec/**/*' + - 'qa/qa/specs/**/*' + - 'qa/bin/*' + - 'config/**/*' + - 'lib/generators/**/*' + - 'ee/lib/generators/**/*' + IgnoreExecutableScripts: true + AllowedAcronyms: + - EE + - JSON + - LDAP + - IO + - HMAC + - QA + - ENV + - STL + - PDF + - SVG + - CTE + - DN + - RSA + - CI + - CD + - OAuth + # default ones: + - CLI + - DSL + - ACL + - API + - ASCII + - CPU + - CSS + - DNS + - EOF + - GUID + - HTML + - HTTP + - HTTPS + - ID + - IP + - JSON + - LHS + - QPS + - RAM + - RHS + - RPC + - SLA + - SMTP + - SQL + - SSH + - TCP + - TLS + - TTL + - UDP + - UI + - UID + - UUID + - URI + - URL + - UTF8 + - VM + - XML + - XMPP + - XSRF + - XSS + # Gitlab ################################################################### Gitlab/ModuleWithInstanceVariables: diff --git a/.scss-lint.yml b/.scss-lint.yml index dcd4cac780..180d377d6f 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -59,6 +59,8 @@ linters: # Reports when you define the same property twice in a single rule set. DuplicateProperty: enabled: true + ignore_consecutive: + - cursor # Separate rule, function, and mixin declarations with empty lines. EmptyLineBetweenBlocks: diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc4225afd..6371b38775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,29 +2,261 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. -## 10.6.5 (2018-04-24) +## 10.7.3 (2018-05-02) -### Security (1 change) +### Fixed (8 changes) +- Fixed wrong avatar URL when the avatar is on object storage. !18092 +- Fix errors on pushing to an empty repository. !18462 +- Update doorkeeper to 4.3.2 to fix GitLab OAuth authentication. !18543 +- Ports omniauth-jwt gem onto GitLab OmniAuth Strategies suite. !18580 +- Fix redirection error for applications using OpenID. !18599 +- Fix commit trailer rendering when Gravatar is disabled. +- Fix file_store for artifacts and lfs when saving. +- Fix users not seeing labels from private groups when being a member of a child project. + + +## 10.7.2 (2018-04-25) + +### Security (2 changes) + +- Serve archive requests with the correct file in all cases. - Sanitizes user name to avoid XSS attacks. -## 10.6.4 (2018-04-09) +## 10.7.1 (2018-04-23) -### Fixed (8 changes, 1 of them is from the community) +### Fixed (11 changes) +- [API] Fix URLs in the `Link` header for `GET /projects/:id/repository/contributors` when no value is passed for `order_by` or `sort`. !18393 +- Fix a case with secret variables being empty sometimes. !18400 +- Fix `Trace::HttpIO` can not render multi-byte chars. !18417 +- Fix specifying a non-default ref when requesting an archive using the legacy URL. !18468 +- Respect visibility options and description when importing project from template. !18473 +- Removes 'No Job log' message from build trace. !18523 +- Align action icons in pipeline graph. +- Fix direct_upload when records with null file_store are used. +- Removed alert box in IDE when redirecting to new merge request. +- Fixed IDE not loading for sub groups. +- Fixed IDE not showing loading state when tree is loading. + +### Performance (4 changes) + +- Validate project path prior to hitting the database. !18322 +- Add index to file_store on ci_job_artifacts. !18444 +- Fix N+1 queries when loading participants for a commit note. +- Support Markdown rendering using multiple projects. + +### Added (1 change) + +- Add an API endpoint to download git repository snapshots. !18173 + + +## 10.7.0 (2018-04-22) + +### Security (6 changes, 2 of them are from the community) + +- Fixed some SSRF vulnerabilities in services, hooks and integrations. !2337 +- Update ruby-saml to 1.7.2 and omniauth-saml to 1.10.0. !17734 (Takuya Noguchi) +- Update rack-protection to 2.0.1. !17835 (Takuya Noguchi) +- Adds confidential notes channel for Slack/Mattermost. +- Fix XSS on diff view stored on filenames. +- Fix GitLab Auth0 integration signing in the wrong user. + +### Fixed (65 changes, 20 of them are from the community) + +- File uploads in remote storage now support project renaming. !4597 +- Fixed bug in dropdown selector when selecting the same selection again. !14631 (bitsapien) +- Fixed group deletion linked to Mattermost. !16209 (Julien Millau) +- Create commit API and Web IDE obey LFS filters. !16718 +- Set breadcrumb for admin/runners/show. !17431 (Takuya Noguchi) +- Enable restore rake task to handle nested storage directories. !17516 (Balasankar C) +- Fix hover style of dropdown items in the right sidebar. !17519 +- Improve empty state for canceled job. !17646 +- Fix generated URL when listing repoitories for import. !17692 +- Use singular in the diff stats if only one line has been changed. !17697 (Jan Beckmann) +- Long instance urls do not overflow anymore during project creation. !17717 +- Fix importing multiple assignees from GitLab export. !17718 - Correct copy text for the promote milestone and label modals. !17726 +- Fix search results stripping last endline when parsing the results. !17777 (Jasper Maes) +- Add read-only banner to all pages. !17798 +- Fix viewing diffs on old merge requests. !17805 +- Fix forking to subgroup via API when namespace is given by name. !17815 (Jan Beckmann) +- Fix UI breakdown for Create merge request button. !17821 (Takuya Noguchi) +- Unify format for nested non-task lists. !17823 (Takuya Noguchi) +- UX re-design branch items with flexbox. !17832 (Takuya Noguchi) +- Use porcelain commit lookup method on CI::CreatePipelineService. !17911 +- Update dashboard milestones breadcrumb link. !17933 (George Tsiolis) +- Deleting a MR you are assigned to should decrements counter. !17951 (m b) +- Update no repository placeholder. !17964 (George Tsiolis) +- Drop JSON response in Project Milestone along with avoiding error. !17977 (Takuya Noguchi) +- Fix personal access token clipboard button style. !17978 (Fabian Schneider) - Avoid validation errors when running the Pages domain verification service. !17992 +- Project creation will now raise an error if a service template is invalid. !18013 +- Add better LDAP connection handling. !18039 - Fix autolinking URLs containing ampersands. !18045 - Fix exceptions raised when migrating pipeline stages in the background. !18076 +- Always display Labels section in issuable sidebar, even when the project has no labels. !18081 (Branka Martinovic) +- Fixed gitlab:uploads:migrate task ignoring some uploads. !18082 +- Fixed gitlab:uploads:migrate task failing for Groups' avatar. !18088 +- Increase dropdown width in pipeline graph & center action icon. !18089 +- Fix `JobsController#raw` endpoint can not read traces in database. !18101 +- Fix `gitlab-rake gitlab:two_factor:disable_for_all_users`. !18154 +- Adjust 404's for LegacyDiffNote discussion rendering. !18201 - Work around Prometheus Helm chart name changes to fix integration. !18206 (joshlambert) -- Don't show Jump to Discussion button on Issues. -- Fix listing commit branch/tags that contain special characters. +- Prioritize weight over title when sorting charts. !18233 +- Verify that deploy token has valid access when pulling container registry image. !18260 +- Stop redirecting the page in pipeline main actions. +- Fixed IDE button opening the wrong URL in tree list. +- Ensure hooks run when a deploy key without a user pushes. - Fix 404 in group boards when moving issue between lists. +- Display state indicator for issuable references in non-project scope (e.g. when referencing issuables from group scope). +- Add missing port to artifact links. +- Fix data race between ObjectStorage background_upload and Pages publishing. +- Fixes unresolved discussions rendering the error state instead of the diff. +- Don't show Jump to Discussion button on Issues. +- Fix bug rendering group icons when forking. +- Automatically cleanup stale worktrees and lock files upon a push. +- Use the GitLab version as part of the appearances cache key. +- Fix Firefox stealing formatting characters on issue notes. +- Include matching branches and tags in protected branches / tags count. (Jan Beckmann) +- Fix 500 error when a merge request from a fork has conflicts and has not yet been updated. +- Test if remote repository exists when importing wikis. +- Hide emoji popup after multiple spaces. (Jan Beckmann) +- Fix relative uri when "#" is in branch name. (Jan) +- Escape Markdown characters properly when using autocomplete. +- Ignore project internal references in group context. +- Fix finding wiki file when Gitaly is enabled. +- Fix listing commit branch/tags that contain special characters. +- Ensure internal users (ghost, support bot) get assigned a namespace. +- Fix links to subdirectories of a directory with a plus character in its path. -### Performance (1 change) +### Deprecated (1 change) +- Remove support for legacy tar.gz pages artifacts. !18090 + +### Changed (22 changes, 2 of them are from the community) + +- Add yellow favicon when `CANARY=true` to differientate canary environment. !12477 +- Use human readable value build_timeout in Project. !17386 +- Improved visual styles and consistency for commit hash and possible actions across commit lists. !17406 +- Don't create permanent redirect routes. !17521 +- Add empty repo check before running AutoDevOps pipeline. !17605 +- Update wording to specify create/manage project vs group labels in labels dropdown. !17640 +- Add tooltips to icons in lists of issues and merge requests. !17700 +- Change avatar error message to include allowed file formats. !17747 (Fabian Schneider) +- Polish design for verifying domains. !17767 +- Move email footer info to a single line. !17916 +- Add average and maximum summary statistics to the prometheus dashboard. !17921 +- Add additional cluster usage metrics to usage ping. !17922 +- Move 'Registry' after 'CI/CD' in project navigation sidebar. !18018 (Elias Werberich) +- Redesign application settings to match project settings. !18019 +- Allow HTTP(s) when git request is made by GitLab CI. !18021 +- Added hover background color to IDE file list rows. +- Make project avatar in IDE consistent with the rest of GitLab. +- Show issues of subgroups in group-level issue board. +- Repository checksum calculation is handled by Gitaly when feature is enabled. +- Allow viewing timings for AJAX requests in the performance bar. +- Fixes remove source branch checkbox being visible when user cannot remove the branch. +- Make /-/ delimiter optional for search endpoints. + +### Performance (24 changes, 11 of them are from the community) + +- Move AssigneeTitle vue component. !17397 (George Tsiolis) +- Move TimeTrackingCollapsedState vue component. !17399 (George Tsiolis) +- Move MemoryGraph and MemoryUsage vue components. !17533 (George Tsiolis) +- Move UnresolvedDiscussions vue component. !17538 (George Tsiolis) +- Move NothingToMerge vue component. !17544 (George Tsiolis) +- Move ShaMismatch vue component. !17546 (George Tsiolis) +- Stop caching highlighted diffs in Redis unnecessarily. !17746 +- Add i18n and update specs for ShaMismatch vue component. !17870 (George Tsiolis) +- Update spec import path for vue mount component helper. !17880 (George Tsiolis) +- Move TimeTrackingComparisonPane vue component. !17931 (George Tsiolis) +- Improves the performance of projects list page. !17934 +- Remove N+1 query for Noteable association. !17956 +- Improve performance of loading issues with lots of references to merge requests. !17986 +- Reuse root_ref_hash for performance on Branches. !17998 (Takuya Noguchi) +- Update asciidoctor-plantuml to 0.0.8. !18022 (Takuya Noguchi) +- Cache personal projects count. !18197 +- Reduce complexity of issuable finder query. !18219 +- Reduce number of queries when viewing a merge request. - Free open file descriptors and libgit2 buffers in UpdatePagesService. +- Memoize Git::Repository#has_visible_content?. +- Require at least one filter when listing issues or merge requests on dashboard page. +- lazy load diffs on merge request discussions. +- Bulk deleting refs is handled by Gitaly by default. +- ListCommitsByOid is executed by Gitaly by default. + +### Added (38 changes, 7 of them are from the community) + +- Add HTTPS-only pages. !16273 (rfwatson) +- adds closed by informations in issue api. !17042 (haseebeqx) +- Projects and groups badges settings UI. !17114 +- Add per-runner configured job timeout. !17221 +- Add alternate archive route for simplified packaging. !17225 +- Add support for pipeline variables expressions in only/except. !17316 +- Add object storage support for LFS objects, CI artifacts, and uploads. !17358 +- Added confirmation modal for changing username. !17405 +- Implement foreground verification of CI artifacts. !17578 +- Extend API for exporting a project with direct upload URL. !17686 +- Move ci/lint under project's namespace. !17729 +- Add Total CPU/Memory consumption metrics for Kubernetes. !17731 +- Adds the option to the project export API to override the project description and display GitLab export description once imported. !17744 +- Port direct upload of LFS artifacts from EE. !17752 +- Adds support for OmniAuth JWT provider. !17774 +- Display error message on job's tooltip if this one fails. !17782 +- Add 'Assigned Issues' and 'Assigned Merge Requests' as dashboard view choices for users. !17860 (Elias Werberich) +- Extend API for importing a project export with overwrite support. !17883 +- Create Deploy Tokens to allow permanent access to repository and registry. !17894 +- Detect commit message trailers and link users properly to their accounts on Gitlab. !17919 (cousine) +- Adds cancel btn to new pages domain page. !18026 (Jacopo Beschi @jacopo-beschi) +- API: Add parameter merge_method to projects. !18031 (Jan Beckmann) +- Introduce simpler env vars for auto devops REPLICAS and CANARY_REPLICAS #41436. !18036 +- Allow overriding params on project import through API. !18086 +- Support LFS objects when importing/exporting GitLab project archives. !18115 +- Store sha256 checksum of artifact metadata. !18149 +- Limit the number of failed logins when using LDAP for authentication. !43525 +- Allow assigning and filtering issuables by ancestor group labels. +- Include subgroup issues when searching for group issues using the API. +- Allow to store uploads by default on Object Storage. +- Add slash command for moving issues. (Adam Pahlevi) +- Render MR commit SHA instead "diffs" when viable. +- Send @mention notifications even if a user has explicitly unsubscribed from item. +- Add support for Sidekiq JSON logging. +- Add Gitaly call details to performance bar. +- Add support for patch link extension for commit links on GitLab Flavored Markdown. +- Allow feature gates to be removed through the API. +- Allow merge requests related to a commit to be found via API. + +### Other (27 changes, 11 of them are from the community) + +- Send notification emails when push to a merge request. !7610 (YarNayar) +- Rename modal.vue to deprecated_modal.vue. !17438 +- Atomic generation of internal ids for issues. !17580 +- Use object ID to prevent duplicate keys Vue warning on Issue Boards page during development. !17682 +- Update foreman from 0.78.0 to 0.84.0. !17690 (Takuya Noguchi) +- Add realtime pipeline status for adding/viewing files. !17705 +- Update documentation to reflect current minimum required versions of node and yarn. !17706 +- Update knapsack to 1.16.0. !17735 (Takuya Noguchi) +- Update CI services documnetation. !17749 +- Added i18n support for the prometheus memory widget. !17753 +- Use specific names for filtered CI variable controller parameters. !17796 +- Apply NestingDepth (level 5) (framework/dropdowns.scss). !17820 (Takuya Noguchi) +- Clean up selectors in framework/header.scss. !17822 (Takuya Noguchi) +- Bump `state_machines-activerecord` to 0.5.1. !17924 (blackst0ne) +- Increase the memory limits used in the unicorn killer. !17948 +- Replace the spinach test with an rspec analog. !17950 (blackst0ne) +- Remove unused index from events table. !18014 +- Make all workhorse gitaly calls opt-out, take 2. !18043 +- Update brakeman 3.6.1 to 4.2.1. !18122 (Takuya Noguchi) +- Replace the `project/issues/labels.feature` spinach test with an rspec analog. !18126 (blackst0ne) +- Bump html-pipeline to 2.7.1. !18132 (@blackst0ne) +- Remove test_ci rake task. !18139 (Takuya Noguchi) +- Add documentation for Pipelines failure reasons. !18352 +- Improve JIRA event descriptions. +- Add query counts to profiler output. +- Move Sidekiq exporter logs to log/sidekiq_exporter.log. +- Upgrade Gitaly to upgrade its charlock_holmes. ## 10.6.3 (2018-04-03) @@ -224,7 +456,6 @@ entry. - Enable privileged mode for GitLab Runner. !17528 - Expose GITLAB_FEATURES as CI/CD variable (fixes #40994). - Upgrade GitLab Workhorse to 4.0.0. -- Allow CI/CD Jobs being grouped on version strings. - Add discussions API for Issues and Snippets. - Add one group board to Libre. - Add support for filtering by source and target branch to merge requests API. @@ -251,6 +482,57 @@ entry. - Use host URL to build JIRA remote link icon. +## 10.5.7 (2018-04-03) + +### Security (2 changes) + +- Fix XSS on diff view stored on filenames. +- Adds confidential notes channel for Slack/Mattermost. + + +## 10.5.6 (2018-03-16) + +### Security (2 changes) + +- Fixed some SSRF vulnerabilities in services, hooks and integrations. !2337 +- Fix GitLab Auth0 integration signing in the wrong user. + + +## 10.5.5 (2018-03-15) + +### Fixed (3 changes) + +- Fix missing uploads after group transfer. !17658 +- Fix code and wiki search results when filename is non-ASCII. +- Remove double caching of Repository#empty?. + +### Performance (2 changes) + +- Adding missing indexes on taggings table. +- Add index on section_name_id on ci_build_trace_sections table. + + +## 10.5.4 (2018-03-08) + +### Fixed (11 changes) + +- Encode branch name as binary before creating a RPC request to copy attributes. !17291 +- Restart Unicorn and Sidekiq when GRPC throws 14:Endpoint read failed. !17293 +- Ensure group issues and merge requests pages show results from subgroups when there are no results from the current group. !17312 +- Prevent trace artifact migration to incur data loss. !17313 +- Return a 404 instead of 403 if the repository does not exist on disk. !17341 +- Allow Prometheus application to be installed from Cluster applications. !17372 +- Fixes Prometheus admin configuration page. !17377 +- Fix code and wiki search results pages when non-ASCII text is displayed. !17413 +- Fix pages flaky failure by reloading stale object. !17522 +- Fixed issue edit shortcut not opening edit form. +- Revert Project.public_or_visible_to_user changes and only apply to snippets. + +### Performance (1 change) + +- Don't use ProjectsFinder in TodosFinder. + + ## 10.5.3 (2018-03-01) ### Security (1 change) @@ -475,6 +757,22 @@ entry. - Adds empty state illustration for pending job. +## 10.4.7 (2018-04-03) + +### Security (2 changes) + +- Fix XSS on diff view stored on filenames. +- Adds confidential notes channel for Slack/Mattermost. + + +## 10.4.6 (2018-03-16) + +### Security (2 changes) + +- Fixed some SSRF vulnerabilities in services, hooks and integrations. !2337 +- Fix GitLab Auth0 integration signing in the wrong user. + + ## 10.4.5 (2018-03-01) ### Security (1 change) @@ -706,6 +1004,15 @@ entry. - Use a background migration for issues.closed_at. +## 10.3.9 (2018-03-16) + +### Security (3 changes) + +- Fixed some SSRF vulnerabilities in services, hooks and integrations. !2337 +- Update nokogiri to 1.8.2. !16807 +- Fix GitLab Auth0 integration signing in the wrong user. + + ## 10.3.8 (2018-03-01) ### Security (1 change) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76ee6265c5..9c8fdc1275 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._ - [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc) - [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc) - [Team labels (~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.)](#team-labels-cicd-discussion-edge-platform-etc) - - [Priority labels (~Deliverable and ~Stretch)](#priority-labels-deliverable-and-stretch) + - [Priority labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#priority-labels-deliverable-stretch-next-patch-release) - [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests) - [Implement design & UI elements](#implement-design-ui-elements) - [Issue tracker](#issue-tracker) @@ -98,8 +98,8 @@ coach is going to finish the merge request we assign the ## Helping others -Please help other GitLab users when you can. The channels people will reach out -on can be found on the [getting help page][getting-help]. +Please help other GitLab users when you can. +The methods people will use to seek help can be found on the [getting help page][getting-help]. Sign up for the mailing list, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with @@ -126,7 +126,7 @@ Most issues will have labels for at least one of the following: - Type: ~"feature proposal", ~bug, ~customer, etc. - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc. - Team: ~"CI/CD", ~Discussion, ~Edge, ~Platform, etc. -- Priority: ~Deliverable, ~Stretch +- Priority: ~Deliverable, ~Stretch, ~"Next Patch Release" All labels, their meaning and priority are defined on the [labels page][labels-page]. @@ -185,7 +185,7 @@ indicate if an issue needs backend work, frontend work, or both. Team labels are always capitalized so that they show up as the first label for any issue. -### Priority labels (~Deliverable and ~Stretch) +### Priority labels (~Deliverable, ~Stretch, ~"Next Patch Release") Priority labels help us clearly communicate expectations of the work for the release. There are two levels of priority labels: @@ -195,6 +195,13 @@ release. There are two levels of priority labels: - ~Stretch: Issues that are a stretch goal for delivering in the current milestone. If these issues are not done in the current release, they will strongly be considered for the next release. +- ~"Next Patch Release": Issues to put in the next patch release. Work on these + first, and add the "Pick Into X" label to the merge request, along with the + appropriate milestone. + +Each issue scheduled for the current milestone should be labeled ~Deliverable +or ~"Stretch". Any open issue for a previous milestone should be labeled +~"Next Patch Release", or otherwise rescheduled to a different milestone. ### Severity labels (~S1, ~S2, etc.) @@ -686,3 +693,4 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [^1]: Please note that specs other than JavaScript specs are considered backend code. + \ No newline at end of file diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 8f63f4f9a1..483b771941 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.91.0 +0.96.1 diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 39e898a4f9..6f4eebdf6f 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.7.1 +0.8.1 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 1aa5e414fd..a8a1887568 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -6.0.4 +7.1.2 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index fcdb2e109f..ee74734aa2 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -4.0.0 +4.1.0 diff --git a/Gemfile b/Gemfile index ac06a938d0..e61dfd8e47 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,18 @@ +# --- Special code for migrating to Rails 5.0 --- +def rails5? + %w[1 true].include?(ENV["RAILS5"]) +end + +gem_versions = {} +gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2' +gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0' +gem_versions['rails'] = rails5? ? '5.0.6' : '4.2.10' +gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9' +# --- The end of special code for migrating to Rails 5.0 --- + source 'https://rubygems.org' -gem 'rails', '4.2.10' +gem 'rails', gem_versions['rails'] gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with @@ -9,34 +21,34 @@ gem 'responders', '~> 2.0' gem 'sprockets', '~> 3.7.0' # Default values for AR models -gem 'default_value_for', '~> 3.0.0' +gem 'default_value_for', gem_versions['default_value_for'] # Supported DBs gem 'mysql2', '~> 0.4.10', group: :mysql gem 'pg', '~> 0.18.2', group: :postgres -gem 'rugged', '~> 0.26.0' +gem 'rugged', '~> 0.27' gem 'grape-route-helpers', '~> 2.1.0' gem 'faraday', '~> 0.12' # Authentication libraries gem 'devise', '~> 4.2' -gem 'doorkeeper', '~> 4.2.0' -gem 'doorkeeper-openid_connect', '~> 1.2.0' -gem 'omniauth', '~> 1.4.2' +gem 'doorkeeper', '~> 4.3' +gem 'doorkeeper-openid_connect', '~> 1.3' +gem 'omniauth', '~> 1.8' gem 'omniauth-auth0', '~> 2.0.0' gem 'omniauth-azure-oauth2', '~> 0.0.9' gem 'omniauth-cas3', '~> 1.1.4' gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.2' -gem 'omniauth-google-oauth2', '~> 0.5.2' +gem 'omniauth-google-oauth2', '~> 0.5.3' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-oauth2-generic', '~> 0.2.2' -gem 'omniauth-saml', '~> 1.7.0' +gem 'omniauth-saml', '~> 1.10' gem 'omniauth-shibboleth', '~> 1.2.0' -gem 'omniauth-twitter', '~> 1.2.0' +gem 'omniauth-twitter', '~> 1.4' gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth-authentiq', '~> 0.3.1' gem 'rack-oauth2', '~> 1.2.1' @@ -49,7 +61,7 @@ gem 'akismet', '~> 2.0' # Two-factor authentication gem 'devise-two-factor', '~> 3.0.0' gem 'rqrcode-rails3', '~> 0.1.7' -gem 'attr_encrypted', '~> 3.0.0' +gem 'attr_encrypted', '~> 3.1.0' gem 'u2f', '~> 0.2.1' # GitLab Pages @@ -104,16 +116,16 @@ gem 'carrierwave', '~> 1.2' gem 'dropzonejs-rails', '~> 0.7.1' # for backups -gem 'fog-aws', '~> 1.4' +gem 'fog-aws', '~> 2.0.1' gem 'fog-core', '~> 1.44' -gem 'fog-google', '~> 0.5' +gem 'fog-google', '~> 1.3.3' gem 'fog-local', '~> 0.3' gem 'fog-openstack', '~> 0.1' gem 'fog-rackspace', '~> 0.1.1' gem 'fog-aliyun', '~> 0.2.0' # for Google storage -gem 'google-api-client', '~> 0.13.6' +gem 'google-api-client', '~> 0.19.8' # for aws storage gem 'unf', '~> 0.1.4' @@ -122,7 +134,7 @@ gem 'unf', '~> 0.1.4' gem 'seed-fu', '~> 2.3.7' # Markdown and HTML processing -gem 'html-pipeline', '~> 1.11.0' +gem 'html-pipeline', '~> 2.7.1' gem 'deckar01-task_list', '2.0.0' gem 'gitlab-markup', '~> 1.6.2' gem 'redcarpet', '~> 3.4' @@ -132,8 +144,8 @@ gem 'rdoc', '~> 4.2' gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' -gem 'asciidoctor', '~> 1.5.2' -gem 'asciidoctor-plantuml', '0.0.7' +gem 'asciidoctor', '~> 1.5.6' +gem 'asciidoctor-plantuml', '0.0.8' gem 'rouge', '~> 2.0' gem 'truncato', '~> 0.7.9' gem 'bootstrap_form', '~> 2.7.0' @@ -149,10 +161,10 @@ group :unicorn do end # State machine -gem 'state_machines-activerecord', '~> 0.4.0' +gem 'state_machines-activerecord', '~> 0.5.1' # Issue tags -gem 'acts-as-taggable-on', '~> 4.0' +gem 'acts-as-taggable-on', '~> 5.0' # Background jobs gem 'sidekiq', '~> 5.0' @@ -208,7 +220,7 @@ gem 'asana', '~> 0.6.0' gem 'ruby-fogbugz', '~> 0.2.1' # Kubernetes integration -gem 'kubeclient', '~> 2.2.0' +gem 'kubeclient', '~> 3.0' # d3 gem 'd3_rails', '~> 3.5.0' @@ -221,7 +233,7 @@ gem 'babosa', '~> 1.0.2' gem 'loofah', '~> 2.2' # Working with license -gem 'licensee', '~> 8.7.0' +gem 'licensee', '~> 8.9' # Protect against bruteforcing gem 'rack-attack', '~> 4.4.1' @@ -235,9 +247,6 @@ gem 'mousetrap-rails', '~> 1.4.6' # Detect and convert string character encoding gem 'charlock_holmes', '~> 0.7.5' -# Faster JSON -gem 'oj', '~> 2.17.4' - # Faster blank gem 'fast_blank' @@ -257,22 +266,21 @@ gem 'font-awesome-rails', '~> 4.7' gem 'gemojione', '~> 3.3' gem 'gon', '~> 6.1.0' gem 'jquery-atwho-rails', '~> 1.3.2' -gem 'jquery-rails', '~> 4.3.1' gem 'request_store', '~> 1.3' gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' gem 'base32', '~> 0.3.0' # Sentry integration -gem 'sentry-raven', '~> 2.5.3' +gem 'sentry-raven', '~> 2.7' gem 'premailer-rails', '~> 1.9.7' # I18n gem 'ruby_parser', '~> 3.8', require: false -gem 'rails-i18n', '~> 4.0.9' +gem 'rails-i18n', gem_versions['rails-i18n'] gem 'gettext_i18n_rails', '~> 1.8.0' -gem 'gettext_i18n_rails_js', '~> 1.2.0' +gem 'gettext_i18n_rails_js', '~> 1.3' gem 'gettext', '~> 3.2.2', require: false, group: :development gem 'batch-loader', '~> 1.2.1' @@ -280,7 +288,6 @@ gem 'batch-loader', '~> 1.2.1' # Perf bar gem 'peek', '~> 1.0.1' gem 'peek-gc', '~> 0.0.2' -gem 'peek-host', '~> 1.0.0' gem 'peek-mysql2', '~> 1.1.0', group: :mysql gem 'peek-performance_bar', '~> 1.3.0' gem 'peek-pg', '~> 1.3.0', group: :postgres @@ -300,8 +307,8 @@ group :metrics do end group :development do - gem 'foreman', '~> 0.78.0' - gem 'brakeman', '~> 3.6.0', require: false + gem 'foreman', '~> 0.84.0' + gem 'brakeman', '~> 4.2', require: false gem 'letter_opener_web', '~> 1.3.0' gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false @@ -360,13 +367,15 @@ group :development, :test do gem 'benchmark-ips', '~> 2.3.0', require: false gem 'license_finder', '~> 3.1', require: false - gem 'knapsack', '~> 1.11.0' + gem 'knapsack', '~> 1.16' - gem 'activerecord_sane_schema_dumper', '0.2' + gem 'activerecord_sane_schema_dumper', gem_versions['activerecord_sane_schema_dumper'] gem 'stackprof', '~> 0.2.10', require: false gem 'simple_po_parser', '~> 1.1.2', require: false + + gem 'timecop', '~> 0.8.0' end group :test do @@ -374,21 +383,20 @@ group :test do gem 'email_spec', '~> 1.6.0' gem 'json-schema', '~> 2.8.0' gem 'webmock', '~> 2.3.2' - gem 'test_after_commit', '~> 1.1' + gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0. gem 'sham_rack', '~> 1.3.6' - gem 'timecop', '~> 0.8.0' gem 'concurrent-ruby', '~> 1.0.5' gem 'test-prof', '~> 0.2.5' end -gem 'octokit', '~> 4.6.2' +gem 'octokit', '~> 4.8' gem 'mail_room', '~> 0.9.1' gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' -gem 'ruby-prof', '~> 0.16.2' +gem 'ruby-prof', '~> 0.17.0' # OAuth gem 'oauth2', '~> 1.4' @@ -401,7 +409,7 @@ gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' # SSH host key support -gem 'net-ssh', '~> 4.1.0' +gem 'net-ssh', '~> 4.2.0' gem 'sshkey', '~> 1.9.0' # Required for ED25519 SSH host key support @@ -412,7 +420,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.88.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.96.0', require: 'gitaly' gem 'grpc', '~> 1.10.0' # Locked until https://github.com/google/protobuf/issues/4210 is closed @@ -421,9 +429,9 @@ gem 'google-protobuf', '= 3.5.1' gem 'toml-rb', '~> 1.0.0', require: false # Feature toggles -gem 'flipper', '~> 0.11.0' -gem 'flipper-active_record', '~> 0.11.0' -gem 'flipper-active_support_cache_store', '~> 0.11.0' +gem 'flipper', '~> 0.13.0' +gem 'flipper-active_record', '~> 0.13.0' +gem 'flipper-active_support_cache_store', '~> 0.13.0' # Structured logging gem 'lograge', '~> 0.5' diff --git a/Gemfile.lock b/Gemfile.lock index 41e6f2cc52..31b0dc9c0e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,13 +40,14 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - acts-as-taggable-on (4.0.0) - activerecord (>= 4.0) + acts-as-taggable-on (5.0.0) + activerecord (>= 4.2.8) adamantium (0.2.0) ice_nine (~> 0.11.0) memoizable (~> 0.4.0) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) + aes_key_wrap (1.0.1) akismet (2.0.0) allocations (1.0.5) arel (6.0.4) @@ -55,17 +56,17 @@ GEM faraday_middleware (~> 0.9) faraday_middleware-multi_json (~> 0.0) oauth2 (~> 1.0) - asciidoctor (1.5.3) - asciidoctor-plantuml (0.0.7) + asciidoctor (1.5.6.2) + asciidoctor-plantuml (0.0.8) asciidoctor (~> 1.5) asset_sync (2.2.0) activemodel (>= 4.1.0) fog-core mime-types (>= 2.99) unf - ast (2.3.0) + ast (2.4.0) atomic (1.1.99) - attr_encrypted (3.0.3) + attr_encrypted (3.1.0) encryptor (~> 3.0.0) attr_required (1.0.0) autoprefixer-rails (6.2.3) @@ -86,7 +87,7 @@ GEM coderay (>= 1.0.0) erubis (>= 2.6.6) rack (>= 0.9.0) - bindata (2.4.1) + bindata (2.4.3) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) blankslate (2.1.2.4) @@ -94,7 +95,7 @@ GEM autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) bootstrap_form (2.7.0) - brakeman (3.6.1) + brakeman (4.2.1) browser (2.2.0) builder (3.2.3) bullet (5.5.1) @@ -119,7 +120,7 @@ GEM activesupport (>= 4.0.0) mime-types (>= 1.16) cause (0.1) - charlock_holmes (0.7.5) + charlock_holmes (0.7.6) childprocess (0.7.0) ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) @@ -175,12 +176,12 @@ GEM diff-lcs (1.3) diffy (3.1.0) docile (1.1.5) - domain_name (0.5.20161021) + domain_name (0.5.20170404) unf (>= 0.0.5, < 1.0.0) - doorkeeper (4.2.6) + doorkeeper (4.3.2) railties (>= 4.2) - doorkeeper-openid_connect (1.2.0) - doorkeeper (~> 4.0) + doorkeeper-openid_connect (1.3.0) + doorkeeper (~> 4.3) json-jwt (~> 1.6) dropzonejs-rails (0.7.2) rails (> 3.1) @@ -195,7 +196,7 @@ GEM et-orbi (1.0.3) tzinfo eventmachine (1.0.8) - excon (0.57.1) + excon (0.60.0) execjs (2.6.0) expression_parser (0.9.0) factory_bot (4.8.2) @@ -211,7 +212,7 @@ GEM faraday_middleware multi_json fast_blank (1.0.0) - fast_gettext (1.4.0) + fast_gettext (1.6.0) ffaker (2.4.0) ffi (1.9.18) flay (2.10.0) @@ -219,13 +220,13 @@ GEM path_expander (~> 1.0) ruby_parser (~> 3.0) sexp_processor (~> 4.0) - flipper (0.11.0) - flipper-active_record (0.11.0) + flipper (0.13.0) + flipper-active_record (0.13.0) activerecord (>= 3.2, < 6) - flipper (~> 0.11.0) - flipper-active_support_cache_store (0.11.0) + flipper (~> 0.13.0) + flipper-active_support_cache_store (0.13.0) activesupport (>= 3.2, < 6) - flipper (~> 0.11.0) + flipper (~> 0.13.0) flowdock (0.7.1) httparty (~> 0.7) multi_json @@ -234,19 +235,20 @@ GEM fog-json (~> 1.0) ipaddress (~> 0.8) xml-simple (~> 1.1) - fog-aws (1.4.0) + fog-aws (2.0.1) fog-core (~> 1.38) fog-json (~> 1.0) fog-xml (~> 0.1) ipaddress (~> 0.8) - fog-core (1.44.3) + fog-core (1.45.0) builder - excon (~> 0.49) + excon (~> 0.58) formatador (~> 0.2) - fog-google (0.5.3) + fog-google (1.3.3) fog-core fog-json fog-xml + google-api-client (~> 0.19.1) fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) @@ -266,7 +268,7 @@ GEM nokogiri (>= 1.5.11, < 2.0.0) font-awesome-rails (4.7.0.1) railties (>= 3.2, < 5.1) - foreman (0.78.0) + foreman (0.84.0) thor (~> 0.19.1) formatador (0.2.5) fuubar (2.2.0) @@ -277,20 +279,20 @@ GEM gemojione (3.3.0) json get_process_mem (0.2.0) - gettext (3.2.2) + gettext (3.2.9) locale (>= 2.0.5) text (>= 1.3.0) gettext_i18n_rails (1.8.0) fast_gettext (>= 0.9.0) - gettext_i18n_rails_js (1.2.0) + gettext_i18n_rails_js (1.3.0) gettext (>= 3.0.2) gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.88.0) + gitaly-proto (0.96.0) google-protobuf (~> 3.1) - grpc (~> 1.0) + grpc (~> 1.10) github-linguist (5.3.3) charlock_holmes (~> 0.7.5) escape_utils (~> 1.1.0) @@ -336,9 +338,9 @@ GEM json multi_json request_store (>= 1.0) - google-api-client (0.13.6) + google-api-client (0.19.8) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.5) + googleauth (>= 0.5, < 0.7.0) httpclient (>= 2.8.1, < 3.0) mime-types (~> 3.0) representable (~> 3.0) @@ -356,7 +358,7 @@ GEM signet (~> 0.7) gpgme (2.0.13) mini_portile2 (~> 2.1) - grape (1.0.0) + grape (1.0.2) activesupport builder mustermann-grape (~> 1.0.0) @@ -389,7 +391,7 @@ GEM thor tilt hashdiff (0.3.4) - hashie (3.5.6) + hashie (3.5.7) hashie-forbidden_attributes (0.1.1) hashie (>= 3.0) health_check (2.6.0) @@ -397,26 +399,26 @@ GEM hipchat (1.5.2) httparty mimemagic - html-pipeline (1.11.0) + html-pipeline (2.7.1) activesupport (>= 2) - nokogiri (~> 1.4) + nokogiri (>= 1.4) html2text (0.2.0) nokogiri (~> 1.6) htmlentities (4.3.4) - http (0.9.8) + http (2.2.2) addressable (~> 2.3) http-cookie (~> 1.0) http-form_data (~> 1.0.1) http_parser.rb (~> 0.6.0) http-cookie (1.0.3) domain_name (~> 0.5) - http-form_data (1.0.1) + http-form_data (1.0.3) http_parser.rb (0.6.0) httparty (0.13.7) json (~> 1.8) multi_xml (>= 0.5.2) - httpclient (2.8.2) - i18n (0.9.1) + httpclient (2.8.3) + i18n (0.9.5) concurrent-ruby (~> 1.0) ice_nine (0.11.2) influxdb (0.2.3) @@ -428,15 +430,11 @@ GEM multipart-post oauth (~> 0.5, >= 0.5.0) jquery-atwho-rails (1.3.2) - jquery-rails (4.3.1) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) json (1.8.6) - json-jwt (1.7.2) + json-jwt (1.9.2) activesupport + aes_key_wrap bindata - multi_json (>= 1.3) securecompare url_safe_base64 json-schema (2.8.0) @@ -455,13 +453,13 @@ GEM kaminari-core (= 1.0.1) kaminari-core (1.0.1) kgio (2.10.0) - knapsack (1.11.0) + knapsack (1.16.0) rake timecop (>= 0.1.0) - kubeclient (2.2.0) - http (= 0.9.8) - recursive-open-struct (= 1.0.0) - rest-client + kubeclient (3.0.0) + http (~> 2.2.2) + recursive-open-struct (~> 1.0.4) + rest-client (~> 2.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.4.1) @@ -478,7 +476,7 @@ GEM toml (= 0.1.2) with_env (> 1.0) xml-simple - licensee (8.7.0) + licensee (8.9.2) rugged (~> 0.24) little-plugger (1.1.4) locale (2.1.2) @@ -503,36 +501,35 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mimemagic (0.3.0) - mini_mime (0.1.4) + mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.7.0) mousetrap-rails (1.4.6) multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) - mustermann (1.0.0) + mustermann (1.0.2) mustermann-grape (1.0.0) mustermann (~> 1.0.0) mysql2 (0.4.10) net-ldap (0.16.0) - net-ssh (4.1.0) + net-ssh (4.2.0) netrc (0.11.0) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) numerizer (0.1.1) - oauth (0.5.1) + oauth (0.5.4) oauth2 (1.4.0) faraday (>= 0.8, < 0.13) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - octokit (4.6.2) + octokit (4.8.0) sawyer (~> 0.8.0, >= 0.5.3) - oj (2.17.5) - omniauth (1.4.2) - hashie (>= 1.2, < 4) - rack (>= 1.0, < 3) + omniauth (1.8.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) omniauth-auth0 (2.0.0) omniauth-oauth2 (~> 1.4) omniauth-authentiq (0.3.1) @@ -553,11 +550,10 @@ GEM omniauth-gitlab (1.0.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.0) - omniauth-google-oauth2 (0.5.2) - jwt (~> 1.5) - multi_json (~> 1.3) + omniauth-google-oauth2 (0.5.3) + jwt (>= 1.5) omniauth (>= 1.1.1) - omniauth-oauth2 (>= 1.3.1) + omniauth-oauth2 (>= 1.5) omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) @@ -566,19 +562,19 @@ GEM omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.4.0) - oauth2 (~> 1.0) + omniauth-oauth2 (1.5.0) + oauth2 (~> 1.1) omniauth (~> 1.2) omniauth-oauth2-generic (0.2.2) omniauth-oauth2 (~> 1.0) - omniauth-saml (1.7.0) - omniauth (~> 1.3) - ruby-saml (~> 1.4) + omniauth-saml (1.10.0) + omniauth (~> 1.3, >= 1.3.2) + ruby-saml (~> 1.7) omniauth-shibboleth (1.2.1) omniauth (>= 1.0.0) - omniauth-twitter (1.2.1) - json (~> 1.3) + omniauth-twitter (1.4.0) omniauth-oauth (~> 1.1) + rack omniauth_crowd (2.2.3) activesupport nokogiri (>= 1.4.4) @@ -588,8 +584,8 @@ GEM orm_adapter (0.5.0) os (0.9.6) parallel (1.12.1) - parser (2.4.0.2) - ast (~> 2.3) + parser (2.5.0.5) + ast (~> 2.4.0) parslet (1.5.0) blankslate (~> 2.0) path_expander (1.0.2) @@ -599,8 +595,6 @@ GEM railties (>= 4.0.0) peek-gc (0.0.2) peek - peek-host (1.0.0) - peek peek-mysql2 (1.1.0) atomic (>= 1.0.0) mysql2 @@ -652,7 +646,7 @@ GEM pry (>= 0.9.10) public_suffix (3.0.2) pyu-ruby-sasl (0.0.3.3) - rack (1.6.8) + rack (1.6.9) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.4.1) @@ -664,7 +658,7 @@ GEM httpclient (>= 2.4) multi_json (>= 1.3.6) rack (>= 1.1) - rack-protection (1.5.3) + rack-protection (2.0.1) rack rack-proxy (0.6.0) rack @@ -683,8 +677,8 @@ GEM sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.8) - activesupport (>= 4.2.0.beta, < 5.0) + rails-dom-testing (1.0.9) + activesupport (>= 4.2.0, < 5.0) nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.4) @@ -715,7 +709,7 @@ GEM re2 (1.1.1) recaptcha (3.0.0) json - recursive-open-struct (1.0.0) + recursive-open-struct (1.0.5) redcarpet (3.4.0) redis (3.3.5) redis-actionpack (5.0.2) @@ -743,7 +737,7 @@ GEM request_store (1.3.1) responders (2.3.0) railties (>= 4.2.0, < 5.1) - rest-client (2.0.0) + rest-client (2.0.2) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) @@ -805,9 +799,9 @@ GEM i18n ruby-fogbugz (0.2.1) crack (~> 0.4) - ruby-prof (0.16.2) + ruby-prof (0.17.0) ruby-progressbar (1.9.0) - ruby-saml (1.4.1) + ruby-saml (1.7.2) nokogiri (>= 1.5.10) ruby_parser (3.9.0) sexp_processor (~> 4.1) @@ -816,7 +810,7 @@ GEM rubyzip (1.2.1) rufus-scheduler (3.4.0) et-orbi (~> 1.0) - rugged (0.26.0) + rugged (0.27.0) safe_yaml (1.0.4) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -846,7 +840,7 @@ GEM selenium-webdriver (3.5.0) childprocess (~> 0.5) rubyzip (~> 1.0) - sentry-raven (2.5.3) + sentry-raven (2.7.2) faraday (>= 0.7.6, < 1.0) settingslogic (2.0.9) sexp_processor (4.9.0) @@ -903,13 +897,13 @@ GEM sqlite3 (1.3.13) sshkey (1.9.0) stackprof (0.2.10) - state_machines (0.4.0) - state_machines-activemodel (0.4.0) - activemodel (>= 4.1, < 5.1) - state_machines (>= 0.4.0) - state_machines-activerecord (0.4.0) - activerecord (>= 4.1, < 5.1) - state_machines-activemodel (>= 0.3.0) + state_machines (0.5.0) + state_machines-activemodel (0.5.1) + activemodel (>= 4.1, < 6.0) + state_machines (>= 0.5.0) + state_machines-activerecord (0.5.1) + activerecord (>= 4.1, < 6.0) + state_machines-activemodel (>= 0.5.0) stringex (2.7.1) sys-filesystem (1.1.6) ffi @@ -935,7 +929,7 @@ GEM truncato (0.7.10) htmlentities (~> 4.3.1) nokogiri (~> 1.8.0, >= 1.7.0) - tzinfo (1.2.4) + tzinfo (1.2.5) thread_safe (~> 0.1) u2f (0.2.1) uber (0.1.0) @@ -944,7 +938,7 @@ GEM json (>= 1.8.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.4) + unf_ext (0.0.7.5) unicode-display_width (1.3.0) unicorn (5.1.0) kgio (~> 2.6) @@ -953,13 +947,13 @@ GEM get_process_mem (~> 0) unicorn (>= 4, < 6) uniform_notifier (1.10.0) - unparser (0.2.6) + unparser (0.2.7) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (>= 2.3.1.2, < 2.5) + parser (>= 2.3.1.2, < 2.6) procto (~> 0.0.2) url_safe_base64 (0.2.2) validates_hostname (1.0.6) @@ -996,15 +990,15 @@ DEPENDENCIES RedCloth (~> 4.3.2) ace-rails-ap (~> 4.1.0) activerecord_sane_schema_dumper (= 0.2) - acts-as-taggable-on (~> 4.0) + acts-as-taggable-on (~> 5.0) addressable (~> 2.5.2) akismet (~> 2.0) allocations (~> 1.0) asana (~> 0.6.0) - asciidoctor (~> 1.5.2) - asciidoctor-plantuml (= 0.0.7) + asciidoctor (~> 1.5.6) + asciidoctor-plantuml (= 0.0.8) asset_sync (~> 2.2.0) - attr_encrypted (~> 3.0.0) + attr_encrypted (~> 3.1.0) awesome_print (~> 1.2.0) babosa (~> 1.0.2) base32 (~> 0.3.0) @@ -1015,7 +1009,7 @@ DEPENDENCIES binding_of_caller (~> 0.7.2) bootstrap-sass (~> 3.3.0) bootstrap_form (~> 2.7.0) - brakeman (~> 3.6.0) + brakeman (~> 4.2) browser (~> 2.2) bullet (~> 5.5.0) bundler-audit (~> 0.5.0) @@ -1036,8 +1030,8 @@ DEPENDENCIES devise (~> 4.2) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) - doorkeeper (~> 4.2.0) - doorkeeper-openid_connect (~> 1.2.0) + doorkeeper (~> 4.3) + doorkeeper-openid_connect (~> 1.3) dropzonejs-rails (~> 0.7.1) email_reply_trimmer (~> 0.1) email_spec (~> 1.6.0) @@ -1046,25 +1040,25 @@ DEPENDENCIES fast_blank ffaker (~> 2.4) flay (~> 2.10.0) - flipper (~> 0.11.0) - flipper-active_record (~> 0.11.0) - flipper-active_support_cache_store (~> 0.11.0) + flipper (~> 0.13.0) + flipper-active_record (~> 0.13.0) + flipper-active_support_cache_store (~> 0.13.0) fog-aliyun (~> 0.2.0) - fog-aws (~> 1.4) + fog-aws (~> 2.0.1) fog-core (~> 1.44) - fog-google (~> 0.5) + fog-google (~> 1.3.3) fog-local (~> 0.3) fog-openstack (~> 0.1) fog-rackspace (~> 0.1.1) font-awesome-rails (~> 4.7) - foreman (~> 0.78.0) + foreman (~> 0.84.0) fuubar (~> 2.2.0) gemnasium-gitlab-service (~> 0.2) gemojione (~> 3.3) gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) - gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.88.0) + gettext_i18n_rails_js (~> 1.3) + gitaly-proto (~> 0.96.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) @@ -1073,7 +1067,7 @@ DEPENDENCIES gollum-lib (~> 4.2) gollum-rugged_adapter (~> 0.4.4) gon (~> 6.1.0) - google-api-client (~> 0.13.6) + google-api-client (~> 0.19.8) google-protobuf (= 3.5.1) gpgme grape (~> 1.0) @@ -1086,21 +1080,20 @@ DEPENDENCIES hashie-forbidden_attributes health_check (~> 2.6.0) hipchat (~> 1.5.0) - html-pipeline (~> 1.11.0) + html-pipeline (~> 2.7.1) html2text httparty (~> 0.13.3) influxdb (~> 0.2) jira-ruby (~> 1.4) jquery-atwho-rails (~> 1.3.2) - jquery-rails (~> 4.3.1) json-schema (~> 2.8.0) jwt (~> 1.5.6) kaminari (~> 1.0) - knapsack (~> 1.11.0) - kubeclient (~> 2.2.0) + knapsack (~> 1.16) + kubeclient (~> 3.0) letter_opener_web (~> 1.3.0) license_finder (~> 3.1) - licensee (~> 8.7.0) + licensee (~> 8.9) lograge (~> 0.5) loofah (~> 2.2) mail_room (~> 0.9.1) @@ -1109,12 +1102,11 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.10) net-ldap - net-ssh (~> 4.1.0) + net-ssh (~> 4.2.0) nokogiri (~> 1.8.2) oauth2 (~> 1.4) - octokit (~> 4.6.2) - oj (~> 2.17.4) - omniauth (~> 1.4.2) + octokit (~> 4.8) + omniauth (~> 1.8) omniauth-auth0 (~> 2.0.0) omniauth-authentiq (~> 0.3.1) omniauth-azure-oauth2 (~> 0.0.9) @@ -1122,17 +1114,16 @@ DEPENDENCIES omniauth-facebook (~> 4.0.0) omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.2) - omniauth-google-oauth2 (~> 0.5.2) + omniauth-google-oauth2 (~> 0.5.3) omniauth-kerberos (~> 0.3.0) omniauth-oauth2-generic (~> 0.2.2) - omniauth-saml (~> 1.7.0) + omniauth-saml (~> 1.10) omniauth-shibboleth (~> 1.2.0) - omniauth-twitter (~> 1.2.0) + omniauth-twitter (~> 1.4) omniauth_crowd (~> 2.2.0) org-ruby (~> 0.9.12) peek (~> 1.0.1) peek-gc (~> 0.0.2) - peek-host (~> 1.0.0) peek-mysql2 (~> 1.1.0) peek-performance_bar (~> 1.3.0) peek-pg (~> 1.3.0) @@ -1175,17 +1166,17 @@ DEPENDENCIES rubocop (~> 0.52.1) rubocop-rspec (~> 1.22.1) ruby-fogbugz (~> 0.2.1) - ruby-prof (~> 0.16.2) + ruby-prof (~> 0.17.0) ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) - rugged (~> 0.26.0) + rugged (~> 0.27) sanitize (~> 2.0) sass-rails (~> 5.0.6) scss_lint (~> 0.56.0) seed-fu (~> 2.3.7) select2-rails (~> 3.5.9) selenium-webdriver (~> 3.5) - sentry-raven (~> 2.5.3) + sentry-raven (~> 2.7) settingslogic (~> 2.0.9) sham_rack (~> 1.3.6) shoulda-matchers (~> 3.1.2) @@ -1203,7 +1194,7 @@ DEPENDENCIES sprockets (~> 3.7.0) sshkey (~> 1.9.0) stackprof (~> 0.2.10) - state_machines-activerecord (~> 0.4.0) + state_machines-activerecord (~> 0.5.1) sys-filesystem (~> 1.1.6) test-prof (~> 0.2.5) test_after_commit (~> 1.1) diff --git a/Gemfile.rails5 b/Gemfile.rails5 new file mode 100644 index 0000000000..2b526b19ba --- /dev/null +++ b/Gemfile.rails5 @@ -0,0 +1,7 @@ +# BUNDLE_GEMFILE=Gemfile.rails5 bundle install + +ENV["RAILS5"] = "true" + +gemfile = File.expand_path("../Gemfile", __FILE__) + +eval(File.read(gemfile), nil, gemfile) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock new file mode 100644 index 0000000000..18bbad88ec --- /dev/null +++ b/Gemfile.rails5.lock @@ -0,0 +1,1223 @@ +GEM + remote: https://rubygems.org/ + specs: + RedCloth (4.3.2) + abstract_type (0.0.7) + ace-rails-ap (4.1.4) + actioncable (5.0.6) + actionpack (= 5.0.6) + nio4r (>= 1.2, < 3.0) + websocket-driver (~> 0.6.1) + actionmailer (5.0.6) + actionpack (= 5.0.6) + actionview (= 5.0.6) + activejob (= 5.0.6) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.6) + actionview (= 5.0.6) + activesupport (= 5.0.6) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.6) + activesupport (= 5.0.6) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.0.6) + activesupport (= 5.0.6) + globalid (>= 0.3.6) + activemodel (5.0.6) + activesupport (= 5.0.6) + activerecord (5.0.6) + activemodel (= 5.0.6) + activesupport (= 5.0.6) + arel (~> 7.0) + activerecord_sane_schema_dumper (1.0) + rails (>= 5, < 6) + activesupport (5.0.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + acts-as-taggable-on (5.0.0) + activerecord (>= 4.2.8) + adamantium (0.2.0) + ice_nine (~> 0.11.0) + memoizable (~> 0.4.0) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + aes_key_wrap (1.0.1) + akismet (2.0.0) + allocations (1.0.5) + arel (7.1.4) + asana (0.6.3) + faraday (~> 0.9) + faraday_middleware (~> 0.9) + faraday_middleware-multi_json (~> 0.0) + oauth2 (~> 1.0) + asciidoctor (1.5.6.1) + asciidoctor-plantuml (0.0.8) + asciidoctor (~> 1.5) + asset_sync (2.2.0) + activemodel (>= 4.1.0) + fog-core + mime-types (>= 2.99) + unf + ast (2.4.0) + atomic (1.1.100) + attr_encrypted (3.0.3) + encryptor (~> 3.0.0) + attr_required (1.0.1) + autoprefixer-rails (8.1.0.1) + execjs + awesome_print (1.2.0) + axiom-types (0.1.1) + descendants_tracker (~> 0.0.4) + ice_nine (~> 0.11.0) + thread_safe (~> 0.3, >= 0.3.1) + babosa (1.0.2) + base32 (0.3.2) + batch-loader (1.2.1) + bcrypt (3.1.11) + bcrypt_pbkdf (1.0.0) + benchmark-ips (2.3.0) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) + bindata (2.4.3) + binding_of_caller (0.7.3) + debug_inspector (>= 0.0.1) + blankslate (2.1.2.4) + bootstrap-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) + bootstrap_form (2.7.0) + brakeman (4.2.1) + browser (2.5.3) + builder (3.2.3) + bullet (5.5.1) + activesupport (>= 3.0.0) + uniform_notifier (~> 1.10.0) + bundler-audit (0.5.0) + bundler (~> 1.2) + thor (~> 0.18) + byebug (9.0.6) + capybara (2.18.0) + addressable + mini_mime (>= 0.1.3) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (>= 2.0, < 4.0) + capybara-screenshot (1.0.18) + capybara (>= 1.0, < 3) + launchy + carrierwave (1.2.2) + activemodel (>= 4.0.0) + activesupport (>= 4.0.0) + mime-types (>= 1.16) + charlock_holmes (0.7.5) + childprocess (0.9.0) + ffi (~> 1.0, >= 1.0.11) + chronic (0.10.2) + chronic_duration (0.10.6) + numerizer (~> 0.1.1) + chunky_png (1.3.10) + citrus (3.0.2) + coderay (1.1.2) + coercible (1.0.0) + descendants_tracker (~> 0.0.1) + colorize (0.8.1) + commonmarker (0.17.9) + ruby-enum (~> 0.5) + concord (0.1.5) + adamantium (~> 0.2.0) + equalizer (~> 0.0.9) + concurrent-ruby (1.0.5) + concurrent-ruby-ext (1.0.5) + concurrent-ruby (= 1.0.5) + connection_pool (2.2.1) + crack (0.4.3) + safe_yaml (~> 1.0.0) + crass (1.0.3) + creole (0.5.0) + css_parser (1.6.0) + addressable + d3_rails (3.5.17) + railties (>= 3.1.0) + daemons (1.2.6) + database_cleaner (1.5.3) + debug_inspector (0.0.3) + debugger-ruby_core_source (1.3.8) + deckar01-task_list (2.0.0) + html-pipeline + declarative (0.0.10) + declarative-option (0.1.0) + default_value_for (3.0.5) + activerecord (>= 3.2.0, < 5.2) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + devise (4.4.1) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0, < 5.2) + responders + warden (~> 1.2.3) + devise-two-factor (3.0.2) + activesupport (< 5.2) + attr_encrypted (>= 1.3, < 4, != 2) + devise (~> 4.0) + railties (< 5.2) + rotp (~> 2.0) + diff-lcs (1.3) + diffy (3.1.0) + docile (1.1.5) + domain_name (0.5.20170404) + unf (>= 0.0.5, < 1.0.0) + doorkeeper (4.3.1) + railties (>= 4.2) + doorkeeper-openid_connect (1.3.0) + doorkeeper (~> 4.3) + json-jwt (~> 1.6) + dropzonejs-rails (0.7.4) + rails (> 3.1) + email_reply_trimmer (0.1.10) + email_spec (1.6.0) + launchy (~> 2.1) + mail (~> 2.2) + encryptor (3.0.0) + equalizer (0.0.11) + erubis (2.7.0) + escape_utils (1.1.1) + et-orbi (1.0.9) + tzinfo + eventmachine (1.2.5) + excon (0.60.0) + execjs (2.7.0) + expression_parser (0.9.0) + factory_bot (4.8.2) + activesupport (>= 3.0.0) + factory_bot_rails (4.8.2) + factory_bot (~> 4.8.2) + railties (>= 3.0.0) + faraday (0.12.2) + multipart-post (>= 1.2, < 3) + faraday_middleware (0.12.2) + faraday (>= 0.7.4, < 1.0) + faraday_middleware-multi_json (0.0.6) + faraday_middleware + multi_json + fast_blank (1.0.0) + fast_gettext (1.6.0) + ffaker (2.8.1) + ffi (1.9.23) + flay (2.10.0) + erubis (~> 2.7.0) + path_expander (~> 1.0) + ruby_parser (~> 3.0) + sexp_processor (~> 4.0) + flipper (0.13.0) + flipper-active_record (0.13.0) + activerecord (>= 3.2, < 6) + flipper (~> 0.13.0) + flipper-active_support_cache_store (0.13.0) + activesupport (>= 3.2, < 6) + flipper (~> 0.13.0) + flowdock (0.7.1) + httparty (~> 0.7) + multi_json + fog-aliyun (0.2.0) + fog-core (~> 1.27) + fog-json (~> 1.0) + ipaddress (~> 0.8) + xml-simple (~> 1.1) + fog-aws (2.0.1) + fog-core (~> 1.38) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) + fog-core (1.45.0) + builder + excon (~> 0.58) + formatador (~> 0.2) + fog-google (1.3.3) + fog-core + fog-json + fog-xml + google-api-client (~> 0.19.1) + fog-json (1.0.2) + fog-core (~> 1.0) + multi_json (~> 1.10) + fog-local (0.5.0) + fog-core (>= 1.27, < 3.0) + fog-openstack (0.1.24) + fog-core (~> 1.40) + fog-json (>= 1.0) + ipaddress (>= 0.8) + fog-rackspace (0.1.5) + fog-core (>= 1.35) + fog-json (>= 1.0) + fog-xml (>= 0.1) + ipaddress (>= 0.8) + fog-xml (0.1.3) + fog-core + nokogiri (>= 1.5.11, < 2.0.0) + font-awesome-rails (4.7.0.3) + railties (>= 3.2, < 5.2) + foreman (0.84.0) + thor (~> 0.19.1) + formatador (0.2.5) + fuubar (2.2.0) + rspec-core (~> 3.0) + ruby-progressbar (~> 1.4) + gemnasium-gitlab-service (0.2.6) + rugged (~> 0.21) + gemojione (3.3.0) + json + get_process_mem (0.2.1) + gettext (3.2.9) + locale (>= 2.0.5) + text (>= 1.3.0) + gettext_i18n_rails (1.8.0) + fast_gettext (>= 0.9.0) + gettext_i18n_rails_js (1.3.0) + gettext (>= 3.0.2) + gettext_i18n_rails (>= 0.7.1) + po_to_json (>= 1.0.0) + rails (>= 3.2.0) + gherkin-ruby (0.3.2) + gitaly-proto (0.94.0) + google-protobuf (~> 3.1) + grpc (~> 1.0) + github-linguist (5.3.3) + charlock_holmes (~> 0.7.5) + escape_utils (~> 1.1.0) + mime-types (>= 1.19) + rugged (>= 0.25.1) + github-markup (1.7.0) + gitlab-flowdock-git-hook (1.0.1) + flowdock (~> 0.7) + gitlab-grit (>= 2.4.1) + multi_json + gitlab-grit (2.8.2) + charlock_holmes (~> 0.6) + diff-lcs (~> 1.1) + mime-types (>= 1.16) + posix-spawn (~> 0.3) + gitlab-markup (1.6.3) + gitlab-styles (2.3.2) + rubocop (~> 0.51) + rubocop-gitlab-security (~> 0.1.0) + rubocop-rspec (~> 1.19) + gitlab_omniauth-ldap (2.0.4) + net-ldap (~> 0.16) + omniauth (~> 1.3) + pyu-ruby-sasl (>= 0.0.3.3, < 0.1) + rubyntlm (~> 0.5) + globalid (0.4.1) + activesupport (>= 4.2.0) + gollum-grit_adapter (1.0.1) + gitlab-grit (~> 2.7, >= 2.7.1) + gollum-lib (4.2.7) + gemojione (~> 3.2) + github-markup (~> 1.6) + gollum-grit_adapter (~> 1.0) + nokogiri (>= 1.6.1, < 2.0) + rouge (~> 2.1) + sanitize (~> 2.1) + stringex (~> 2.6) + gollum-rugged_adapter (0.4.4) + mime-types (>= 1.15) + rugged (~> 0.25) + gon (6.1.0) + actionpack (>= 3.0) + json + multi_json + request_store (>= 1.0) + google-api-client (0.19.8) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.7.0) + httpclient (>= 2.8.1, < 3.0) + mime-types (~> 3.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + google-protobuf (3.5.1) + googleapis-common-protos-types (1.0.1) + google-protobuf (~> 3.0) + googleauth (0.6.2) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + logging (~> 2.0) + memoist (~> 0.12) + multi_json (~> 1.11) + os (~> 0.9) + signet (~> 0.7) + gpgme (2.0.16) + mini_portile2 (~> 2.3) + grape (1.0.2) + activesupport + builder + mustermann-grape (~> 1.0.0) + rack (>= 1.3.0) + rack-accept + virtus (>= 1.0.0) + grape-entity (0.6.1) + activesupport (>= 5.0.0) + multi_json (>= 1.3.2) + grape-route-helpers (2.1.0) + activesupport + grape (>= 0.16.0) + rake + grape_logging (1.7.0) + grape + grpc (1.10.0) + google-protobuf (~> 3.1) + googleapis-common-protos-types (~> 1.0.0) + googleauth (>= 0.5.1, < 0.7) + haml (4.0.7) + tilt + haml_lint (0.26.0) + haml (>= 4.0, < 5.1) + rainbow + rake (>= 10, < 13) + rubocop (>= 0.49.0) + sysexits (~> 1.1) + hamlit (2.6.2) + temple (~> 0.7.6) + thor + tilt + hashdiff (0.3.7) + hashie (3.5.7) + hashie-forbidden_attributes (0.1.1) + hashie (>= 3.0) + health_check (2.6.0) + rails (>= 4.0) + hipchat (1.5.4) + httparty + mimemagic + html-pipeline (2.7.1) + activesupport (>= 2) + nokogiri (>= 1.4) + html2text (0.2.1) + nokogiri (~> 1.6) + htmlentities (4.3.4) + http (2.2.2) + addressable (~> 2.3) + http-cookie (~> 1.0) + http-form_data (~> 1.0.1) + http_parser.rb (~> 0.6.0) + http-cookie (1.0.3) + domain_name (~> 0.5) + http-form_data (1.0.3) + http_parser.rb (0.6.0) + httparty (0.13.7) + json (~> 1.8) + multi_xml (>= 0.5.2) + httpclient (2.8.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + ice_nine (0.11.2) + influxdb (0.5.3) + ipaddress (0.8.3) + jira-ruby (1.5.0) + activesupport + multipart-post + oauth (~> 0.5, >= 0.5.0) + jquery-atwho-rails (1.3.2) + json (1.8.6) + json-jwt (1.9.2) + activesupport + aes_key_wrap + bindata + securecompare + url_safe_base64 + json-schema (2.8.0) + addressable (>= 2.4) + jwt (1.5.6) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) + kgio (2.11.2) + knapsack (1.16.0) + rake + kubeclient (3.0.0) + http (~> 2.2.2) + recursive-open-struct (~> 1.0.4) + rest-client (~> 2.0) + launchy (2.4.3) + addressable (~> 2.3) + letter_opener (1.6.0) + launchy (~> 2.2) + letter_opener_web (1.3.3) + actionmailer (>= 3.2) + letter_opener (~> 1.0) + railties (>= 3.2) + license_finder (3.1.1) + bundler + httparty + rubyzip + thor + toml (= 0.1.2) + with_env (> 1.0) + xml-simple + licensee (8.9.2) + rugged (~> 0.24) + little-plugger (1.1.4) + locale (2.1.2) + logging (2.2.2) + little-plugger (~> 1.1) + multi_json (~> 1.10) + lograge (0.9.0) + actionpack (>= 4) + activesupport (>= 4) + railties (>= 4) + request_store (~> 1.0) + loofah (2.2.2) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.7.0) + mini_mime (>= 0.1.1) + mail_room (0.9.1) + memoist (0.16.0) + memoizable (0.4.2) + thread_safe (~> 0.3, >= 0.3.1) + method_source (0.9.0) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mimemagic (0.3.2) + mini_mime (1.0.0) + mini_portile2 (2.3.0) + minitest (5.7.0) + mousetrap-rails (1.4.6) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + mustermann (1.0.2) + mustermann-grape (1.0.0) + mustermann (~> 1.0.0) + mysql2 (0.4.10) + net-ldap (0.16.1) + net-ssh (4.2.0) + netrc (0.11.0) + nio4r (2.2.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) + numerizer (0.1.1) + oauth (0.5.4) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + octokit (4.8.0) + sawyer (~> 0.8.0, >= 0.5.3) + omniauth (1.8.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-auth0 (2.0.0) + omniauth-oauth2 (~> 1.4) + omniauth-authentiq (0.3.1) + omniauth-oauth2 (~> 1.3, >= 1.3.1) + omniauth-azure-oauth2 (0.0.9) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.4) + omniauth-cas3 (1.1.4) + addressable (~> 2.3) + nokogiri (~> 1.7, >= 1.7.1) + omniauth (~> 1.2) + omniauth-facebook (4.0.0) + omniauth-oauth2 (~> 1.2) + omniauth-github (1.1.2) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-gitlab (1.0.3) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) + omniauth-google-oauth2 (0.5.3) + jwt (>= 1.5) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.5) + omniauth-jwt (0.0.2) + jwt + omniauth (~> 1.1) + omniauth-kerberos (0.3.0) + omniauth-multipassword + timfel-krb5-auth (~> 0.8) + omniauth-multipassword (0.4.2) + omniauth (~> 1.0) + omniauth-oauth (1.1.0) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.5.0) + oauth2 (~> 1.1) + omniauth (~> 1.2) + omniauth-oauth2-generic (0.2.4) + omniauth-oauth2 (~> 1.0) + omniauth-saml (1.10.0) + omniauth (~> 1.3, >= 1.3.2) + ruby-saml (~> 1.7) + omniauth-shibboleth (1.2.1) + omniauth (>= 1.0.0) + omniauth-twitter (1.4.0) + omniauth-oauth (~> 1.1) + rack + omniauth_crowd (2.2.3) + activesupport + nokogiri (>= 1.4.4) + omniauth (~> 1.0) + org-ruby (0.9.12) + rubypants (~> 0.2) + orm_adapter (0.5.0) + os (0.9.6) + parallel (1.12.1) + parser (2.5.0.5) + ast (~> 2.4.0) + parslet (1.5.0) + blankslate (~> 2.0) + path_expander (1.0.2) + peek (1.0.1) + concurrent-ruby (>= 0.9.0) + concurrent-ruby-ext (>= 0.9.0) + railties (>= 4.0.0) + peek-gc (0.0.2) + peek + peek-mysql2 (1.1.0) + atomic (>= 1.0.0) + mysql2 + peek + peek-performance_bar (1.3.1) + peek (>= 0.1.0) + peek-pg (1.3.0) + concurrent-ruby + concurrent-ruby-ext + peek + pg + peek-rblineprof (0.2.0) + peek + rblineprof + peek-redis (1.2.0) + atomic (>= 1.0.0) + peek + redis + peek-sidekiq (1.0.3) + atomic (>= 1.0.0) + peek + sidekiq + pg (0.18.4) + po_to_json (1.0.1) + json (>= 1.6.0) + posix-spawn (0.3.13) + powerpack (0.1.1) + premailer (1.11.1) + addressable + css_parser (>= 1.6.0) + htmlentities (>= 4.0.0) + premailer-rails (1.9.7) + actionmailer (>= 3, < 6) + premailer (~> 1.7, >= 1.7.9) + proc_to_ast (0.1.0) + coderay + parser + unparser + procto (0.0.3) + prometheus-client-mmap (0.9.1) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry-byebug (3.4.3) + byebug (>= 9.0, < 9.1) + pry (~> 0.10) + pry-rails (0.3.6) + pry (>= 0.10.4) + public_suffix (3.0.2) + pyu-ruby-sasl (0.0.3.3) + rack (2.0.4) + rack-accept (0.4.5) + rack (>= 0.4) + rack-attack (4.4.1) + rack + rack-cors (1.0.2) + rack-oauth2 (1.2.3) + activesupport (>= 2.3) + attr_required (>= 0.0.5) + httpclient (>= 2.4) + multi_json (>= 1.3.6) + rack (>= 1.1) + rack-protection (2.0.1) + rack + rack-proxy (0.6.4) + rack + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.6) + actioncable (= 5.0.6) + actionmailer (= 5.0.6) + actionpack (= 5.0.6) + actionview (= 5.0.6) + activejob (= 5.0.6) + activemodel (= 5.0.6) + activerecord (= 5.0.6) + activesupport (= 5.0.6) + bundler (>= 1.3.0) + railties (= 5.0.6) + sprockets-rails (>= 2.0.0) + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + rails-i18n (5.1.1) + i18n (>= 0.7, < 2) + railties (>= 5.0, < 6) + railties (5.0.6) + actionpack (= 5.0.6) + activesupport (= 5.0.6) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rainbow (2.2.2) + rake + raindrops (0.19.0) + rake (12.3.0) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + rblineprof (0.3.7) + debugger-ruby_core_source (~> 1.3) + rbnacl (4.0.2) + ffi + rbnacl-libsodium (1.0.16) + rbnacl (>= 3.0.1) + rdoc (4.3.0) + re2 (1.1.1) + recaptcha (3.4.0) + json + recursive-open-struct (1.0.5) + redcarpet (3.4.0) + redis (3.3.5) + redis-actionpack (5.0.2) + actionpack (>= 4.0, < 6) + redis-rack (>= 1, < 3) + redis-store (>= 1.1.0, < 2) + redis-activesupport (5.0.4) + activesupport (>= 3, < 6) + redis-store (>= 1.3, < 2) + redis-namespace (1.5.3) + redis (~> 3.0, >= 3.0.4) + redis-rack (2.0.4) + rack (>= 1.5, < 3) + redis-store (>= 1.2, < 2) + redis-rails (5.0.2) + redis-actionpack (>= 5.0, < 6) + redis-activesupport (>= 5.0, < 6) + redis-store (>= 1.2, < 2) + redis-store (1.4.1) + redis (>= 2.2, < 5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + request_store (1.4.0) + rack (>= 1.4) + responders (2.4.0) + actionpack (>= 4.2.0, < 5.3) + railties (>= 4.2.0, < 5.3) + rest-client (2.0.2) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + retriable (3.1.1) + rinku (2.0.4) + rotp (2.1.2) + rouge (2.2.1) + rqrcode (0.10.1) + chunky_png (~> 1.0) + rqrcode-rails3 (0.1.7) + rqrcode (>= 0.4.2) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-mocks (3.6.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.6.0) + rspec-parameterized (0.4.0) + binding_of_caller + parser + proc_to_ast + rspec (>= 2.13, < 4) + unparser + rspec-rails (3.6.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-support (~> 3.6.0) + rspec-retry (0.4.6) + rspec-core + rspec-set (0.1.3) + rspec-support (3.6.0) + rspec_profiling (0.0.5) + activerecord + pg + rails + sqlite3 + rubocop (0.52.1) + parallel (~> 1.10) + parser (>= 2.4.0.2, < 3.0) + powerpack (~> 0.1) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (~> 1.0, >= 1.0.1) + rubocop-gitlab-security (0.1.1) + rubocop (>= 0.51) + rubocop-rspec (1.22.2) + rubocop (>= 0.52.1) + ruby-enum (0.7.2) + i18n + ruby-fogbugz (0.2.1) + crack (~> 0.4) + ruby-prof (0.17.0) + ruby-progressbar (1.9.0) + ruby-saml (1.7.2) + nokogiri (>= 1.5.10) + ruby_parser (3.11.0) + sexp_processor (~> 4.9) + rubyntlm (0.6.2) + rubypants (0.7.0) + rubyzip (1.2.1) + rufus-scheduler (3.4.2) + et-orbi (~> 1.0) + rugged (0.27.0) + safe_yaml (1.0.4) + sanitize (2.1.0) + nokogiri (>= 1.4.4) + sass (3.5.5) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + sawyer (0.8.1) + addressable (>= 2.3.5, < 2.6) + faraday (~> 0.8, < 1.0) + scss_lint (0.56.0) + rake (>= 0.9, < 13) + sass (~> 3.5.3) + securecompare (1.0.0) + seed-fu (2.3.7) + activerecord (>= 3.1) + activesupport (>= 3.1) + select2-rails (3.5.10) + thor (~> 0.14) + selenium-webdriver (3.11.0) + childprocess (~> 0.5) + rubyzip (~> 1.2) + sentry-raven (2.7.2) + faraday (>= 0.7.6, < 1.0) + settingslogic (2.0.9) + sexp_processor (4.10.1) + sham_rack (1.3.6) + rack + shoulda-matchers (3.1.2) + activesupport (>= 4.0.0) + sidekiq (5.1.1) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (>= 3.3.5, < 5) + sidekiq-cron (0.6.3) + rufus-scheduler (>= 3.3.0) + sidekiq (>= 4.2.1) + sidekiq-limit_fetch (3.4.0) + sidekiq (>= 4) + signet (0.8.1) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simple_po_parser (1.1.3) + simplecov (0.14.1) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) + slack-notifier (1.5.1) + spinach (0.10.1) + colorize + gherkin-ruby (>= 0.3.2) + json + spinach-rails (0.2.1) + capybara (>= 2.0.0) + railties (>= 3) + spinach (>= 0.4) + spinach-rerun-reporter (0.0.2) + spinach (~> 0.8) + spring (2.0.2) + activesupport (>= 4.2) + spring-commands-rspec (1.0.4) + spring (>= 0.9.1) + spring-commands-spinach (1.1.0) + spring (>= 0.9.1) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + sqlite3 (1.3.13) + sshkey (1.9.0) + stackprof (0.2.11) + state_machines (0.5.0) + state_machines-activemodel (0.5.1) + activemodel (>= 4.1, < 6.0) + state_machines (>= 0.5.0) + state_machines-activerecord (0.5.1) + activerecord (>= 4.1, < 6.0) + state_machines-activemodel (>= 0.5.0) + stringex (2.8.4) + sys-filesystem (1.1.9) + ffi + sysexits (1.2.0) + temple (0.7.7) + test-prof (0.2.5) + text (1.3.1) + thin (1.7.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (0.19.4) + thread_safe (0.3.6) + tilt (2.0.8) + timecop (0.8.1) + timfel-krb5-auth (0.8.3) + toml (0.1.2) + parslet (~> 1.5.0) + toml-rb (1.0.0) + citrus (~> 3.0, > 3.0) + truncato (0.7.10) + htmlentities (~> 4.3.1) + nokogiri (~> 1.8.0, >= 1.7.0) + tzinfo (1.2.5) + thread_safe (~> 0.1) + u2f (0.2.1) + uber (0.1.0) + uglifier (2.7.2) + execjs (>= 0.3.0) + json (>= 1.8.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.5) + unicode-display_width (1.3.0) + unicorn (5.1.0) + kgio (~> 2.6) + raindrops (~> 0.7) + unicorn-worker-killer (0.4.4) + get_process_mem (~> 0) + unicorn (>= 4, < 6) + uniform_notifier (1.10.0) + unparser (0.2.7) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + parser (>= 2.3.1.2, < 2.6) + procto (~> 0.0.2) + url_safe_base64 (0.2.2) + validates_hostname (1.0.8) + activerecord (>= 3.0) + activesupport (>= 3.0) + version_sorter (2.1.0) + virtus (1.0.5) + axiom-types (~> 0.1) + coercible (~> 1.0) + descendants_tracker (~> 0.0, >= 0.0.3) + equalizer (~> 0.0, >= 0.0.9) + vmstat (2.3.0) + warden (1.2.7) + rack (>= 1.0) + webmock (2.3.2) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff + webpack-rails (0.9.11) + railties (>= 3.2.0) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.3) + wikicloth (0.8.1) + builder + expression_parser + rinku + with_env (1.1.0) + xml-simple (1.1.5) + xpath (3.0.0) + nokogiri (~> 1.8) + +PLATFORMS + ruby + +DEPENDENCIES + RedCloth (~> 4.3.2) + ace-rails-ap (~> 4.1.0) + activerecord_sane_schema_dumper (= 1.0) + acts-as-taggable-on (~> 5.0) + addressable (~> 2.5.2) + akismet (~> 2.0) + allocations (~> 1.0) + asana (~> 0.6.0) + asciidoctor (~> 1.5.6) + asciidoctor-plantuml (= 0.0.8) + asset_sync (~> 2.2.0) + attr_encrypted (~> 3.0.0) + awesome_print (~> 1.2.0) + babosa (~> 1.0.2) + base32 (~> 0.3.0) + batch-loader (~> 1.2.1) + bcrypt_pbkdf (~> 1.0) + benchmark-ips (~> 2.3.0) + better_errors (~> 2.1.0) + binding_of_caller (~> 0.7.2) + bootstrap-sass (~> 3.3.0) + bootstrap_form (~> 2.7.0) + brakeman (~> 4.2) + browser (~> 2.2) + bullet (~> 5.5.0) + bundler-audit (~> 0.5.0) + capybara (~> 2.15) + capybara-screenshot (~> 1.0.0) + carrierwave (~> 1.2) + charlock_holmes (~> 0.7.5) + chronic (~> 0.10.2) + chronic_duration (~> 0.10.6) + commonmarker (~> 0.17) + concurrent-ruby (~> 1.0.5) + connection_pool (~> 2.0) + creole (~> 0.5.0) + d3_rails (~> 3.5.0) + database_cleaner (~> 1.5.0) + deckar01-task_list (= 2.0.0) + default_value_for (~> 3.0.5) + devise (~> 4.2) + devise-two-factor (~> 3.0.0) + diffy (~> 3.1.0) + doorkeeper (~> 4.3) + doorkeeper-openid_connect (~> 1.3) + dropzonejs-rails (~> 0.7.1) + email_reply_trimmer (~> 0.1) + email_spec (~> 1.6.0) + factory_bot_rails (~> 4.8.2) + faraday (~> 0.12) + fast_blank + ffaker (~> 2.4) + flay (~> 2.10.0) + flipper (~> 0.13.0) + flipper-active_record (~> 0.13.0) + flipper-active_support_cache_store (~> 0.13.0) + fog-aliyun (~> 0.2.0) + fog-aws (~> 2.0.1) + fog-core (~> 1.44) + fog-google (~> 1.3.3) + fog-local (~> 0.3) + fog-openstack (~> 0.1) + fog-rackspace (~> 0.1.1) + font-awesome-rails (~> 4.7) + foreman (~> 0.84.0) + fuubar (~> 2.2.0) + gemnasium-gitlab-service (~> 0.2) + gemojione (~> 3.3) + gettext (~> 3.2.2) + gettext_i18n_rails (~> 1.8.0) + gettext_i18n_rails_js (~> 1.3) + gitaly-proto (~> 0.94.0) + github-linguist (~> 5.3.3) + gitlab-flowdock-git-hook (~> 1.0.1) + gitlab-markup (~> 1.6.2) + gitlab-styles (~> 2.3) + gitlab_omniauth-ldap (~> 2.0.4) + gollum-lib (~> 4.2) + gollum-rugged_adapter (~> 0.4.4) + gon (~> 6.1.0) + google-api-client (~> 0.19.8) + google-protobuf (= 3.5.1) + gpgme + grape (~> 1.0) + grape-entity (~> 0.6.0) + grape-route-helpers (~> 2.1.0) + grape_logging (~> 1.7) + grpc (~> 1.10.0) + haml_lint (~> 0.26.0) + hamlit (~> 2.6.1) + hashie-forbidden_attributes + health_check (~> 2.6.0) + hipchat (~> 1.5.0) + html-pipeline (~> 2.7.1) + html2text + httparty (~> 0.13.3) + influxdb (~> 0.2) + jira-ruby (~> 1.4) + jquery-atwho-rails (~> 1.3.2) + json-schema (~> 2.8.0) + jwt (~> 1.5.6) + kaminari (~> 1.0) + knapsack (~> 1.16) + kubeclient (~> 3.0) + letter_opener_web (~> 1.3.0) + license_finder (~> 3.1) + licensee (~> 8.9) + lograge (~> 0.5) + loofah (~> 2.2) + mail_room (~> 0.9.1) + method_source (~> 0.8) + minitest (~> 5.7.0) + mousetrap-rails (~> 1.4.6) + mysql2 (~> 0.4.10) + net-ldap + net-ssh (~> 4.2.0) + nokogiri (~> 1.8.2) + oauth2 (~> 1.4) + octokit (~> 4.8) + omniauth (~> 1.8) + omniauth-auth0 (~> 2.0.0) + omniauth-authentiq (~> 0.3.1) + omniauth-azure-oauth2 (~> 0.0.9) + omniauth-cas3 (~> 1.1.4) + omniauth-facebook (~> 4.0.0) + omniauth-github (~> 1.1.1) + omniauth-gitlab (~> 1.0.2) + omniauth-google-oauth2 (~> 0.5.3) + omniauth-jwt (~> 0.0.2) + omniauth-kerberos (~> 0.3.0) + omniauth-oauth2-generic (~> 0.2.2) + omniauth-saml (~> 1.10) + omniauth-shibboleth (~> 1.2.0) + omniauth-twitter (~> 1.4) + omniauth_crowd (~> 2.2.0) + org-ruby (~> 0.9.12) + peek (~> 1.0.1) + peek-gc (~> 0.0.2) + peek-mysql2 (~> 1.1.0) + peek-performance_bar (~> 1.3.0) + peek-pg (~> 1.3.0) + peek-rblineprof (~> 0.2.0) + peek-redis (~> 1.2.0) + peek-sidekiq (~> 1.0.3) + pg (~> 0.18.2) + premailer-rails (~> 1.9.7) + prometheus-client-mmap (~> 0.9.1) + pry-byebug (~> 3.4.1) + pry-rails (~> 0.3.4) + rack-attack (~> 4.4.1) + rack-cors (~> 1.0.0) + rack-oauth2 (~> 1.2.1) + rack-proxy (~> 0.6.0) + rails (= 5.0.6) + rails-deprecated_sanitizer (~> 1.0.3) + rails-i18n (~> 5.1) + rainbow (~> 2.2) + raindrops (~> 0.18) + rblineprof (~> 0.3.6) + rbnacl (~> 4.0) + rbnacl-libsodium + rdoc (~> 4.2) + re2 (~> 1.1.1) + recaptcha (~> 3.0) + redcarpet (~> 3.4) + redis (~> 3.2) + redis-namespace (~> 1.5.2) + redis-rails (~> 5.0.2) + request_store (~> 1.3) + responders (~> 2.0) + rouge (~> 2.0) + rqrcode-rails3 (~> 0.1.7) + rspec-parameterized + rspec-rails (~> 3.6.0) + rspec-retry (~> 0.4.5) + rspec-set (~> 0.1.3) + rspec_profiling (~> 0.0.5) + rubocop (~> 0.52.1) + rubocop-rspec (~> 1.22.1) + ruby-fogbugz (~> 0.2.1) + ruby-prof (~> 0.17.0) + ruby_parser (~> 3.8) + rufus-scheduler (~> 3.4) + rugged (~> 0.27) + sanitize (~> 2.0) + sass-rails (~> 5.0.6) + scss_lint (~> 0.56.0) + seed-fu (~> 2.3.7) + select2-rails (~> 3.5.9) + selenium-webdriver (~> 3.5) + sentry-raven (~> 2.7) + settingslogic (~> 2.0.9) + sham_rack (~> 1.3.6) + shoulda-matchers (~> 3.1.2) + sidekiq (~> 5.0) + sidekiq-cron (~> 0.6.0) + sidekiq-limit_fetch (~> 3.4) + simple_po_parser (~> 1.1.2) + simplecov (~> 0.14.0) + slack-notifier (~> 1.5.1) + spinach-rails (~> 0.2.1) + spinach-rerun-reporter (~> 0.0.2) + spring (~> 2.0.0) + 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.5.1) + sys-filesystem (~> 1.1.6) + test-prof (~> 0.2.5) + thin (~> 1.7.0) + timecop (~> 0.8.0) + toml-rb (~> 1.0.0) + truncato (~> 0.7.9) + u2f (~> 0.2.1) + uglifier (~> 2.7.2) + unf (~> 0.1.4) + unicorn (~> 5.1.0) + unicorn-worker-killer (~> 0.4.4) + validates_hostname (~> 1.0.6) + version_sorter (~> 2.1.0) + virtus (~> 1.0.1) + vmstat (~> 2.3.0) + webmock (~> 2.3.2) + webpack-rails (~> 0.9.10) + wikicloth (= 0.8.1) + +BUNDLED WITH + 1.16.1 diff --git a/PROCESS.md b/PROCESS.md index 5ae191840f..f206506f7c 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -53,7 +53,7 @@ Below we describe the contributing process to GitLab for two reasons: Several people from the [GitLab team][team] are helping community members to get their contributions accepted by meeting our [Definition of done][done]. -What you can expect from them is described at https://about.gitlab.com/jobs/merge-request-coach/. +What you can expect from them is described at https://about.gitlab.com/roles/merge-request-coach/. ## Assigning issues @@ -202,6 +202,9 @@ you can ask for an exception to be made. Go to [Release tasks issue tracker](https://gitlab.com/gitlab-org/release/tasks/issues/new) and create an issue using the `Exception-request` issue template. +**Do not** set the relevant `Pick into X.Y` label (see above) before request an +exception; this should be done after the exception is approved. + You can find who is who on the [team page](https://about.gitlab.com/team/). Whether an exception is made is determined by weighing the benefit and urgency of the change diff --git a/Procfile b/Procfile index cad738d429..1776fd9794 100644 --- a/Procfile +++ b/Procfile @@ -4,4 +4,3 @@ # web: RAILS_ENV=development bin/web start_foreground worker: RAILS_ENV=development bin/background_jobs start_foreground -# mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/VERSION b/VERSION index cac93e55f5..992a0120b2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.6.5 +10.7.3 diff --git a/app/assets/images/ci_favicons/canary/favicon_status_canceled.ico b/app/assets/images/ci_favicons/canary/favicon_status_canceled.ico new file mode 100644 index 0000000000..48b1095370 Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_canceled.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_created.ico b/app/assets/images/ci_favicons/canary/favicon_status_created.ico new file mode 100644 index 0000000000..623c728faf Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_created.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_failed.ico b/app/assets/images/ci_favicons/canary/favicon_status_failed.ico new file mode 100644 index 0000000000..3073fe5a76 Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_failed.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_manual.ico b/app/assets/images/ci_favicons/canary/favicon_status_manual.ico new file mode 100644 index 0000000000..6c713d7b67 Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_manual.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_not_found.ico b/app/assets/images/ci_favicons/canary/favicon_status_not_found.ico new file mode 100644 index 0000000000..dbf855fdaf Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_not_found.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_pending.ico b/app/assets/images/ci_favicons/canary/favicon_status_pending.ico new file mode 100644 index 0000000000..ccd00606ae Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_pending.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_running.ico b/app/assets/images/ci_favicons/canary/favicon_status_running.ico new file mode 100644 index 0000000000..968e7c4c2d Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_running.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_skipped.ico b/app/assets/images/ci_favicons/canary/favicon_status_skipped.ico new file mode 100644 index 0000000000..7e3be35cc3 Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_skipped.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_success.ico b/app/assets/images/ci_favicons/canary/favicon_status_success.ico new file mode 100644 index 0000000000..a1fb6e91d6 Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_success.ico differ diff --git a/app/assets/images/ci_favicons/canary/favicon_status_warning.ico b/app/assets/images/ci_favicons/canary/favicon_status_warning.ico new file mode 100644 index 0000000000..5d931619fb Binary files /dev/null and b/app/assets/images/ci_favicons/canary/favicon_status_warning.ico differ diff --git a/app/assets/images/favicon-yellow.ico b/app/assets/images/favicon-yellow.ico new file mode 100644 index 0000000000..b650f277fb Binary files /dev/null and b/app/assets/images/favicon-yellow.ico differ diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json deleted file mode 100644 index 19843d24e2..0000000000 --- a/app/assets/images/icons.json +++ /dev/null @@ -1 +0,0 @@ -{"iconCount":191,"spriteSize":86607,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","bookmark","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder-o","folder-open","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-external","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","podcast","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","soft-unwrap","soft-wrap","spam","spinner","staged","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_notfound","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","unstaged","user","users","volume-up","warning","work"]} \ No newline at end of file diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg deleted file mode 100644 index 6aec54d054..0000000000 --- a/app/assets/images/icons.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/cluster_popover.svg b/app/assets/images/illustrations/cluster_popover.svg deleted file mode 100644 index 202231373f..0000000000 --- a/app/assets/images/illustrations/cluster_popover.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/clusters_empty.svg b/app/assets/images/illustrations/clusters_empty.svg deleted file mode 100644 index 39627a1c31..0000000000 --- a/app/assets/images/illustrations/clusters_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/convdev_no_data.svg b/app/assets/images/illustrations/convdev/convdev_no_data.svg deleted file mode 100644 index b90eddcccf..0000000000 --- a/app/assets/images/illustrations/convdev/convdev_no_data.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/convdev_no_index.svg b/app/assets/images/illustrations/convdev/convdev_no_index.svg deleted file mode 100644 index 4aaf505e0b..0000000000 --- a/app/assets/images/illustrations/convdev/convdev_no_index.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/convdev_overview.svg b/app/assets/images/illustrations/convdev/convdev_overview.svg deleted file mode 100644 index a06d70812c..0000000000 --- a/app/assets/images/illustrations/convdev/convdev_overview.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_1.svg b/app/assets/images/illustrations/convdev/i2p_step_1.svg deleted file mode 100644 index 67467b1513..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_10.svg b/app/assets/images/illustrations/convdev/i2p_step_10.svg deleted file mode 100644 index 588ecd8141..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_10.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_2.svg b/app/assets/images/illustrations/convdev/i2p_step_2.svg deleted file mode 100644 index 4280024c23..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_2.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_3.svg b/app/assets/images/illustrations/convdev/i2p_step_3.svg deleted file mode 100644 index 7690f91b42..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_3.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_4.svg b/app/assets/images/illustrations/convdev/i2p_step_4.svg deleted file mode 100644 index ba21b9e2c3..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_4.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_5.svg b/app/assets/images/illustrations/convdev/i2p_step_5.svg deleted file mode 100644 index 3c8f8422a9..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_5.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_6.svg b/app/assets/images/illustrations/convdev/i2p_step_6.svg deleted file mode 100644 index 933860798a..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_6.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_7.svg b/app/assets/images/illustrations/convdev/i2p_step_7.svg deleted file mode 100644 index d97c8f7c2d..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_7.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_8.svg b/app/assets/images/illustrations/convdev/i2p_step_8.svg deleted file mode 100644 index 919bbeff31..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_8.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/convdev/i2p_step_9.svg b/app/assets/images/illustrations/convdev/i2p_step_9.svg deleted file mode 100644 index 2d1b10d430..0000000000 --- a/app/assets/images/illustrations/convdev/i2p_step_9.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/epics.svg b/app/assets/images/illustrations/epics.svg deleted file mode 100644 index 1a37e6bba5..0000000000 --- a/app/assets/images/illustrations/epics.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/gitlab_logo.svg b/app/assets/images/illustrations/gitlab_logo.svg deleted file mode 100644 index 8dbd75a340..0000000000 --- a/app/assets/images/illustrations/gitlab_logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/image_comment_light_cursor.svg b/app/assets/images/illustrations/image_comment_light_cursor.svg deleted file mode 100644 index ac712ea0c9..0000000000 --- a/app/assets/images/illustrations/image_comment_light_cursor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/image_comment_light_cursor@2x.svg b/app/assets/images/illustrations/image_comment_light_cursor@2x.svg deleted file mode 100644 index 02943acd9d..0000000000 --- a/app/assets/images/illustrations/image_comment_light_cursor@2x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/issues.svg b/app/assets/images/illustrations/issues.svg deleted file mode 100644 index c8e0504732..0000000000 --- a/app/assets/images/illustrations/issues.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/job_not_triggered.svg b/app/assets/images/illustrations/job_not_triggered.svg deleted file mode 100644 index e13c1cb0a7..0000000000 --- a/app/assets/images/illustrations/job_not_triggered.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/labels.svg b/app/assets/images/illustrations/labels.svg deleted file mode 100644 index 3a2d521323..0000000000 --- a/app/assets/images/illustrations/labels.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/logos/go_logo.svg b/app/assets/images/illustrations/logos/go_logo.svg deleted file mode 100644 index 7fd4911800..0000000000 --- a/app/assets/images/illustrations/logos/go_logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/logos/mattermost_logo.svg b/app/assets/images/illustrations/logos/mattermost_logo.svg deleted file mode 100644 index b577c0599a..0000000000 --- a/app/assets/images/illustrations/logos/mattermost_logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/manual_action.svg b/app/assets/images/illustrations/manual_action.svg deleted file mode 100644 index 85735855b4..0000000000 --- a/app/assets/images/illustrations/manual_action.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/merge_request_changes_empty.svg b/app/assets/images/illustrations/merge_request_changes_empty.svg deleted file mode 100644 index 40efeb2de5..0000000000 --- a/app/assets/images/illustrations/merge_request_changes_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/merge_requests.svg b/app/assets/images/illustrations/merge_requests.svg deleted file mode 100644 index b9b8f0058e..0000000000 --- a/app/assets/images/illustrations/merge_requests.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/monitoring/getting_started.svg b/app/assets/images/illustrations/monitoring/getting_started.svg deleted file mode 100644 index ff783bdd38..0000000000 --- a/app/assets/images/illustrations/monitoring/getting_started.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/monitoring/loading.svg b/app/assets/images/illustrations/monitoring/loading.svg deleted file mode 100644 index 1e196fc8ad..0000000000 --- a/app/assets/images/illustrations/monitoring/loading.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/monitoring/unable_to_connect.svg b/app/assets/images/illustrations/monitoring/unable_to_connect.svg deleted file mode 100644 index 314c052f93..0000000000 --- a/app/assets/images/illustrations/monitoring/unable_to_connect.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg b/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg deleted file mode 100644 index 06d73941c3..0000000000 --- a/app/assets/images/illustrations/multi-editor_all_changes_committed_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/multi-editor_no_changes_empty.svg b/app/assets/images/illustrations/multi-editor_no_changes_empty.svg deleted file mode 100644 index ebeea1f3dd..0000000000 --- a/app/assets/images/illustrations/multi-editor_no_changes_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg b/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg deleted file mode 100644 index 08321ef526..0000000000 --- a/app/assets/images/illustrations/multi-editor_no_staged_files_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/multi_file_editor_empty.svg b/app/assets/images/illustrations/multi_file_editor_empty.svg deleted file mode 100644 index bd376f0a05..0000000000 --- a/app/assets/images/illustrations/multi_file_editor_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/no_commits.svg b/app/assets/images/illustrations/no_commits.svg deleted file mode 100644 index 76fa25156d..0000000000 --- a/app/assets/images/illustrations/no_commits.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/pending_job_empty.svg b/app/assets/images/illustrations/pending_job_empty.svg deleted file mode 100644 index 8de695afa1..0000000000 --- a/app/assets/images/illustrations/pending_job_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/pipelines_empty.svg b/app/assets/images/illustrations/pipelines_empty.svg deleted file mode 100644 index f3107c8f06..0000000000 --- a/app/assets/images/illustrations/pipelines_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/pipelines_failed.svg b/app/assets/images/illustrations/pipelines_failed.svg deleted file mode 100644 index 8daf7da86e..0000000000 --- a/app/assets/images/illustrations/pipelines_failed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/pipelines_pending.svg b/app/assets/images/illustrations/pipelines_pending.svg deleted file mode 100644 index 25038366e9..0000000000 --- a/app/assets/images/illustrations/pipelines_pending.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/priority_labels.svg b/app/assets/images/illustrations/priority_labels.svg deleted file mode 100644 index b79c551d3d..0000000000 --- a/app/assets/images/illustrations/priority_labels.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_callout.svg b/app/assets/images/illustrations/service_desk_callout.svg deleted file mode 100644 index 2886388279..0000000000 --- a/app/assets/images/illustrations/service_desk_callout.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/service_desk_empty.svg b/app/assets/images/illustrations/service_desk_empty.svg deleted file mode 100644 index daaaeae6a1..0000000000 --- a/app/assets/images/illustrations/service_desk_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/slack_logo.svg b/app/assets/images/illustrations/slack_logo.svg deleted file mode 100644 index b8d7906c2e..0000000000 --- a/app/assets/images/illustrations/slack_logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/todos_all_done.svg b/app/assets/images/illustrations/todos_all_done.svg deleted file mode 100644 index 6387497a6f..0000000000 --- a/app/assets/images/illustrations/todos_all_done.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/todos_empty.svg b/app/assets/images/illustrations/todos_empty.svg deleted file mode 100644 index 4de6cb403b..0000000000 --- a/app/assets/images/illustrations/todos_empty.svg +++ /dev/null @@ -1 +0,0 @@ -@ \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/add_new_group.svg b/app/assets/images/illustrations/welcome/add_new_group.svg deleted file mode 100644 index b10a3ae881..0000000000 --- a/app/assets/images/illustrations/welcome/add_new_group.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/add_new_project.svg b/app/assets/images/illustrations/welcome/add_new_project.svg deleted file mode 100644 index 4b8dc34c08..0000000000 --- a/app/assets/images/illustrations/welcome/add_new_project.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/add_new_user.svg b/app/assets/images/illustrations/welcome/add_new_user.svg deleted file mode 100644 index d4c184989b..0000000000 --- a/app/assets/images/illustrations/welcome/add_new_user.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/configure_server.svg b/app/assets/images/illustrations/welcome/configure_server.svg deleted file mode 100644 index f9dda816f1..0000000000 --- a/app/assets/images/illustrations/welcome/configure_server.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/ee_trial.svg b/app/assets/images/illustrations/welcome/ee_trial.svg deleted file mode 100644 index 6d0dcf0020..0000000000 --- a/app/assets/images/illustrations/welcome/ee_trial.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/globe.svg b/app/assets/images/illustrations/welcome/globe.svg deleted file mode 100644 index c2daae5f31..0000000000 --- a/app/assets/images/illustrations/welcome/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/welcome/lightbulb.svg b/app/assets/images/illustrations/welcome/lightbulb.svg deleted file mode 100644 index fce1031208..0000000000 --- a/app/assets/images/illustrations/welcome/lightbulb.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/wiki-fro-logged-out-users.svg b/app/assets/images/illustrations/wiki-fro-logged-out-users.svg deleted file mode 100644 index c71841f72e..0000000000 --- a/app/assets/images/illustrations/wiki-fro-logged-out-users.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/wiki_login_empty.svg b/app/assets/images/illustrations/wiki_login_empty.svg deleted file mode 100644 index 1cfa47220a..0000000000 --- a/app/assets/images/illustrations/wiki_login_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/illustrations/wiki_logout_empty.svg b/app/assets/images/illustrations/wiki_logout_empty.svg deleted file mode 100644 index c71841f72e..0000000000 --- a/app/assets/images/illustrations/wiki_logout_empty.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 6a0662ba90..c117d080bd 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -1,5 +1,6 @@ /* eslint-disable no-param-reassign, class-methods-use-this */ +import $ from 'jquery'; import Cookies from 'js-cookie'; import Pager from './pager'; import { localTimeAgo } from './lib/utils/datetime_utility'; diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js index 2bc77859c2..bd08308904 100644 --- a/app/assets/javascripts/ajax_loading_spinner.js +++ b/app/assets/javascripts/ajax_loading_spinner.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + export default class AjaxLoadingSpinner { static init() { const $elements = $('.js-ajax-loading-spinner'); diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 464611f66f..8ad3d18b30 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import _ from 'underscore'; import axios from './lib/utils/axios_utils'; @@ -9,6 +10,9 @@ const Api = { projectsPath: '/api/:version/projects.json', projectPath: '/api/:version/projects/:id', projectLabelsPath: '/:namespace_path/:project_path/labels', + mergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid', + mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes', + mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions', groupLabelsPath: '/groups/:namespace_path/-/labels', licensePath: '/api/:version/templates/licenses/:key', gitignorePath: '/api/:version/templates/gitignores/:key', @@ -21,25 +25,27 @@ const Api = { createBranchPath: '/api/:version/projects/:id/repository/branches', group(groupId, callback) { - const url = Api.buildUrl(Api.groupPath) - .replace(':id', groupId); - return axios.get(url) - .then(({ data }) => { - callback(data); + const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); + return axios.get(url).then(({ data }) => { + callback(data); - return data; - }); + return data; + }); }, // Return groups list. Filtered by query groups(query, options, callback = $.noop) { const url = Api.buildUrl(Api.groupsPath); - return axios.get(url, { - params: Object.assign({ - search: query, - per_page: 20, - }, options), - }) + return axios + .get(url, { + params: Object.assign( + { + search: query, + per_page: 20, + }, + options, + ), + }) .then(({ data }) => { callback(data); @@ -50,12 +56,13 @@ const Api = { // Return namespaces list. Filtered by query namespaces(query, callback) { const url = Api.buildUrl(Api.namespacesPath); - return axios.get(url, { - params: { - search: query, - per_page: 20, - }, - }) + return axios + .get(url, { + params: { + search: query, + per_page: 20, + }, + }) .then(({ data }) => callback(data)); }, @@ -72,9 +79,10 @@ const Api = { defaults.membership = true; } - return axios.get(url, { - params: Object.assign(defaults, options), - }) + return axios + .get(url, { + params: Object.assign(defaults, options), + }) .then(({ data }) => { callback(data); @@ -84,8 +92,32 @@ const Api = { // Return single project project(projectPath) { - const url = Api.buildUrl(Api.projectPath) - .replace(':id', encodeURIComponent(projectPath)); + const url = Api.buildUrl(Api.projectPath).replace(':id', encodeURIComponent(projectPath)); + + return axios.get(url); + }, + + // Return Merge Request for project + mergeRequest(projectPath, mergeRequestId) { + const url = Api.buildUrl(Api.mergeRequestPath) + .replace(':id', encodeURIComponent(projectPath)) + .replace(':mrid', mergeRequestId); + + return axios.get(url); + }, + + mergeRequestChanges(projectPath, mergeRequestId) { + const url = Api.buildUrl(Api.mergeRequestChangesPath) + .replace(':id', encodeURIComponent(projectPath)) + .replace(':mrid', mergeRequestId); + + return axios.get(url); + }, + + mergeRequestVersions(projectPath, mergeRequestId) { + const url = Api.buildUrl(Api.mergeRequestVersionsPath) + .replace(':id', encodeURIComponent(projectPath)) + .replace(':mrid', mergeRequestId); return axios.get(url); }, @@ -101,30 +133,30 @@ const Api = { url = Api.buildUrl(Api.groupLabelsPath).replace(':namespace_path', namespacePath); } - return axios.post(url, { - label: data, - }) + return axios + .post(url, { + label: data, + }) .then(res => callback(res.data)) .catch(e => callback(e.response.data)); }, // Return group projects list. Filtered by query groupProjects(groupId, query, callback) { - const url = Api.buildUrl(Api.groupProjectsPath) - .replace(':id', groupId); - return axios.get(url, { - params: { - search: query, - per_page: 20, - }, - }) + const url = Api.buildUrl(Api.groupProjectsPath).replace(':id', groupId); + return axios + .get(url, { + params: { + search: query, + per_page: 20, + }, + }) .then(({ data }) => callback(data)); }, commitMultiple(id, data) { // see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions - const url = Api.buildUrl(Api.commitPath) - .replace(':id', encodeURIComponent(id)); + const url = Api.buildUrl(Api.commitPath).replace(':id', encodeURIComponent(id)); return axios.post(url, JSON.stringify(data), { headers: { 'Content-Type': 'application/json; charset=utf-8', @@ -135,39 +167,34 @@ const Api = { branchSingle(id, branch) { const url = Api.buildUrl(Api.branchSinglePath) .replace(':id', encodeURIComponent(id)) - .replace(':branch', branch); + .replace(':branch', encodeURIComponent(branch)); return axios.get(url); }, // Return text for a specific license licenseText(key, data, callback) { - const url = Api.buildUrl(Api.licensePath) - .replace(':key', key); - return axios.get(url, { - params: data, - }) + const url = Api.buildUrl(Api.licensePath).replace(':key', key); + return axios + .get(url, { + params: data, + }) .then(res => callback(res.data)); }, gitignoreText(key, callback) { - const url = Api.buildUrl(Api.gitignorePath) - .replace(':key', key); - return axios.get(url) - .then(({ data }) => callback(data)); + const url = Api.buildUrl(Api.gitignorePath).replace(':key', key); + return axios.get(url).then(({ data }) => callback(data)); }, gitlabCiYml(key, callback) { - const url = Api.buildUrl(Api.gitlabCiYmlPath) - .replace(':key', key); - return axios.get(url) - .then(({ data }) => callback(data)); + const url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key); + return axios.get(url).then(({ data }) => callback(data)); }, dockerfileYml(key, callback) { const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); - return axios.get(url) - .then(({ data }) => callback(data)); + return axios.get(url).then(({ data }) => callback(data)); }, issueTemplate(namespacePath, projectPath, key, type, callback) { @@ -176,7 +203,8 @@ const Api = { .replace(':type', type) .replace(':project_path', projectPath) .replace(':namespace_path', namespacePath); - return axios.get(url) + return axios + .get(url) .then(({ data }) => callback(null, data)) .catch(callback); }, @@ -184,10 +212,13 @@ const Api = { users(query, options) { const url = Api.buildUrl(this.usersPath); return axios.get(url, { - params: Object.assign({ - search: query, - per_page: 20, - }, options), + params: Object.assign( + { + search: query, + per_page: 20, + }, + options, + ), }); }, diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 26e62732b3..976d32abe9 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,8 +1,11 @@ /* eslint-disable class-methods-use-this */ + +import $ from 'jquery'; import _ from 'underscore'; import Cookies from 'js-cookie'; import { __ } from './locale'; -import { isInIssuePage, isInMRPage, hasVueMRDiscussionsCookie, updateTooltipTitle } from './lib/utils/common_utils'; +import { updateTooltipTitle } from './lib/utils/common_utils'; +import { isInVueNoteablePage } from './lib/utils/dom_utils'; import flash from './flash'; import axios from './lib/utils/axios_utils'; @@ -241,7 +244,7 @@ class AwardsHandler { addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) { const isMainAwardsBlock = votesBlock.closest('.js-noteable-awards').length; - if (this.isInVueNoteablePage() && !isMainAwardsBlock) { + if (isInVueNoteablePage() && !isMainAwardsBlock) { const id = votesBlock.attr('id').replace('note_', ''); this.hideMenuElement($('.emoji-menu')); @@ -293,16 +296,8 @@ class AwardsHandler { } } - isVueMRDiscussions() { - return isInMRPage() && hasVueMRDiscussionsCookie() && !$('#diffs').is(':visible'); - } - - isInVueNoteablePage() { - return isInIssuePage() || this.isVueMRDiscussions(); - } - getVotesBlock() { - if (this.isInVueNoteablePage()) { + if (isInVueNoteablePage()) { const $el = $('.js-add-award.is-active').closest('.note.timeline-entry'); if ($el.length) { diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue new file mode 100644 index 0000000000..6e6cb31e3a --- /dev/null +++ b/app/assets/javascripts/badges/components/badge.vue @@ -0,0 +1,121 @@ + + + diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue new file mode 100644 index 0000000000..ae942b2c1a --- /dev/null +++ b/app/assets/javascripts/badges/components/badge_form.vue @@ -0,0 +1,219 @@ + + + diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue new file mode 100644 index 0000000000..ca7197e1e0 --- /dev/null +++ b/app/assets/javascripts/badges/components/badge_list.vue @@ -0,0 +1,57 @@ + + + diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue new file mode 100644 index 0000000000..af062bdf8c --- /dev/null +++ b/app/assets/javascripts/badges/components/badge_list_row.vue @@ -0,0 +1,89 @@ + + + diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue new file mode 100644 index 0000000000..83f7839423 --- /dev/null +++ b/app/assets/javascripts/badges/components/badge_settings.vue @@ -0,0 +1,70 @@ + + + diff --git a/app/assets/javascripts/badges/constants.js b/app/assets/javascripts/badges/constants.js new file mode 100644 index 0000000000..8fbe3db5ef --- /dev/null +++ b/app/assets/javascripts/badges/constants.js @@ -0,0 +1,2 @@ +export const GROUP_BADGE = 'group'; +export const PROJECT_BADGE = 'project'; diff --git a/app/assets/javascripts/badges/empty_badge.js b/app/assets/javascripts/badges/empty_badge.js new file mode 100644 index 0000000000..49a9b5e1be --- /dev/null +++ b/app/assets/javascripts/badges/empty_badge.js @@ -0,0 +1,7 @@ +export default () => ({ + imageUrl: '', + isDeleting: false, + linkUrl: '', + renderedImageUrl: '', + renderedLinkUrl: '', +}); diff --git a/app/assets/javascripts/badges/store/actions.js b/app/assets/javascripts/badges/store/actions.js new file mode 100644 index 0000000000..5542278b3e --- /dev/null +++ b/app/assets/javascripts/badges/store/actions.js @@ -0,0 +1,167 @@ +import axios from '~/lib/utils/axios_utils'; +import types from './mutation_types'; + +export const transformBackendBadge = badge => ({ + id: badge.id, + imageUrl: badge.image_url, + kind: badge.kind, + linkUrl: badge.link_url, + renderedImageUrl: badge.rendered_image_url, + renderedLinkUrl: badge.rendered_link_url, + isDeleting: false, +}); + +export default { + requestNewBadge({ commit }) { + commit(types.REQUEST_NEW_BADGE); + }, + receiveNewBadge({ commit }, newBadge) { + commit(types.RECEIVE_NEW_BADGE, newBadge); + }, + receiveNewBadgeError({ commit }) { + commit(types.RECEIVE_NEW_BADGE_ERROR); + }, + addBadge({ dispatch, state }) { + const newBadge = state.badgeInAddForm; + const endpoint = state.apiEndpointUrl; + dispatch('requestNewBadge'); + return axios + .post(endpoint, { + image_url: newBadge.imageUrl, + link_url: newBadge.linkUrl, + }) + .catch(error => { + dispatch('receiveNewBadgeError'); + throw error; + }) + .then(res => { + dispatch('receiveNewBadge', transformBackendBadge(res.data)); + }); + }, + requestDeleteBadge({ commit }, badgeId) { + commit(types.REQUEST_DELETE_BADGE, badgeId); + }, + receiveDeleteBadge({ commit }, badgeId) { + commit(types.RECEIVE_DELETE_BADGE, badgeId); + }, + receiveDeleteBadgeError({ commit }, badgeId) { + commit(types.RECEIVE_DELETE_BADGE_ERROR, badgeId); + }, + deleteBadge({ dispatch, state }, badge) { + const badgeId = badge.id; + dispatch('requestDeleteBadge', badgeId); + const endpoint = `${state.apiEndpointUrl}/${badgeId}`; + return axios + .delete(endpoint) + .catch(error => { + dispatch('receiveDeleteBadgeError', badgeId); + throw error; + }) + .then(() => { + dispatch('receiveDeleteBadge', badgeId); + }); + }, + + editBadge({ commit }, badge) { + commit(types.START_EDITING, badge); + }, + + requestLoadBadges({ commit }, data) { + commit(types.REQUEST_LOAD_BADGES, data); + }, + receiveLoadBadges({ commit }, badges) { + commit(types.RECEIVE_LOAD_BADGES, badges); + }, + receiveLoadBadgesError({ commit }) { + commit(types.RECEIVE_LOAD_BADGES_ERROR); + }, + + loadBadges({ dispatch, state }, data) { + dispatch('requestLoadBadges', data); + const endpoint = state.apiEndpointUrl; + return axios + .get(endpoint) + .catch(error => { + dispatch('receiveLoadBadgesError'); + throw error; + }) + .then(res => { + dispatch('receiveLoadBadges', res.data.map(transformBackendBadge)); + }); + }, + + requestRenderedBadge({ commit }) { + commit(types.REQUEST_RENDERED_BADGE); + }, + receiveRenderedBadge({ commit }, renderedBadge) { + commit(types.RECEIVE_RENDERED_BADGE, renderedBadge); + }, + receiveRenderedBadgeError({ commit }) { + commit(types.RECEIVE_RENDERED_BADGE_ERROR); + }, + + renderBadge({ dispatch, state }) { + const badge = state.isEditing ? state.badgeInEditForm : state.badgeInAddForm; + const { linkUrl, imageUrl } = badge; + if (!linkUrl || linkUrl.trim() === '' || !imageUrl || imageUrl.trim() === '') { + return Promise.resolve(badge); + } + + dispatch('requestRenderedBadge'); + + const parameters = [ + `link_url=${encodeURIComponent(linkUrl)}`, + `image_url=${encodeURIComponent(imageUrl)}`, + ].join('&'); + const renderEndpoint = `${state.apiEndpointUrl}/render?${parameters}`; + return axios + .get(renderEndpoint) + .catch(error => { + dispatch('receiveRenderedBadgeError'); + throw error; + }) + .then(res => { + dispatch('receiveRenderedBadge', transformBackendBadge(res.data)); + }); + }, + + requestUpdatedBadge({ commit }) { + commit(types.REQUEST_UPDATED_BADGE); + }, + receiveUpdatedBadge({ commit }, updatedBadge) { + commit(types.RECEIVE_UPDATED_BADGE, updatedBadge); + }, + receiveUpdatedBadgeError({ commit }) { + commit(types.RECEIVE_UPDATED_BADGE_ERROR); + }, + + saveBadge({ dispatch, state }) { + const badge = state.badgeInEditForm; + const endpoint = `${state.apiEndpointUrl}/${badge.id}`; + dispatch('requestUpdatedBadge'); + return axios + .put(endpoint, { + image_url: badge.imageUrl, + link_url: badge.linkUrl, + }) + .catch(error => { + dispatch('receiveUpdatedBadgeError'); + throw error; + }) + .then(res => { + dispatch('receiveUpdatedBadge', transformBackendBadge(res.data)); + }); + }, + + stopEditing({ commit }) { + commit(types.STOP_EDITING); + }, + + updateBadgeInForm({ commit }, badge) { + commit(types.UPDATE_BADGE_IN_FORM, badge); + }, + + updateBadgeInModal({ commit }, badge) { + commit(types.UPDATE_BADGE_IN_MODAL, badge); + }, +}; diff --git a/app/assets/javascripts/badges/store/index.js b/app/assets/javascripts/badges/store/index.js new file mode 100644 index 0000000000..7a5df403a0 --- /dev/null +++ b/app/assets/javascripts/badges/store/index.js @@ -0,0 +1,13 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import createState from './state'; +import actions from './actions'; +import mutations from './mutations'; + +Vue.use(Vuex); + +export default new Vuex.Store({ + state: createState(), + actions, + mutations, +}); diff --git a/app/assets/javascripts/badges/store/mutation_types.js b/app/assets/javascripts/badges/store/mutation_types.js new file mode 100644 index 0000000000..d73f91b600 --- /dev/null +++ b/app/assets/javascripts/badges/store/mutation_types.js @@ -0,0 +1,21 @@ +export default { + RECEIVE_DELETE_BADGE: 'RECEIVE_DELETE_BADGE', + RECEIVE_DELETE_BADGE_ERROR: 'RECEIVE_DELETE_BADGE_ERROR', + RECEIVE_LOAD_BADGES: 'RECEIVE_LOAD_BADGES', + RECEIVE_LOAD_BADGES_ERROR: 'RECEIVE_LOAD_BADGES_ERROR', + RECEIVE_NEW_BADGE: 'RECEIVE_NEW_BADGE', + RECEIVE_NEW_BADGE_ERROR: 'RECEIVE_NEW_BADGE_ERROR', + RECEIVE_RENDERED_BADGE: 'RECEIVE_RENDERED_BADGE', + RECEIVE_RENDERED_BADGE_ERROR: 'RECEIVE_RENDERED_BADGE_ERROR', + RECEIVE_UPDATED_BADGE: 'RECEIVE_UPDATED_BADGE', + RECEIVE_UPDATED_BADGE_ERROR: 'RECEIVE_UPDATED_BADGE_ERROR', + REQUEST_DELETE_BADGE: 'REQUEST_DELETE_BADGE', + REQUEST_LOAD_BADGES: 'REQUEST_LOAD_BADGES', + REQUEST_NEW_BADGE: 'REQUEST_NEW_BADGE', + REQUEST_RENDERED_BADGE: 'REQUEST_RENDERED_BADGE', + REQUEST_UPDATED_BADGE: 'REQUEST_UPDATED_BADGE', + START_EDITING: 'START_EDITING', + STOP_EDITING: 'STOP_EDITING', + UPDATE_BADGE_IN_FORM: 'UPDATE_BADGE_IN_FORM', + UPDATE_BADGE_IN_MODAL: 'UPDATE_BADGE_IN_MODAL', +}; diff --git a/app/assets/javascripts/badges/store/mutations.js b/app/assets/javascripts/badges/store/mutations.js new file mode 100644 index 0000000000..bd84e68c00 --- /dev/null +++ b/app/assets/javascripts/badges/store/mutations.js @@ -0,0 +1,158 @@ +import types from './mutation_types'; +import { PROJECT_BADGE } from '../constants'; + +const reorderBadges = badges => + badges.sort((a, b) => { + if (a.kind !== b.kind) { + return a.kind === PROJECT_BADGE ? 1 : -1; + } + + return a.id - b.id; + }); + +export default { + [types.RECEIVE_NEW_BADGE](state, newBadge) { + Object.assign(state, { + badgeInAddForm: null, + badges: reorderBadges(state.badges.concat(newBadge)), + isSaving: false, + renderedBadge: null, + }); + }, + [types.RECEIVE_NEW_BADGE_ERROR](state) { + Object.assign(state, { + isSaving: false, + }); + }, + [types.REQUEST_NEW_BADGE](state) { + Object.assign(state, { + isSaving: true, + }); + }, + + [types.RECEIVE_UPDATED_BADGE](state, updatedBadge) { + const badges = state.badges.map(badge => { + if (badge.id === updatedBadge.id) { + return updatedBadge; + } + return badge; + }); + Object.assign(state, { + badgeInEditForm: null, + badges, + isEditing: false, + isSaving: false, + renderedBadge: null, + }); + }, + [types.RECEIVE_UPDATED_BADGE_ERROR](state) { + Object.assign(state, { + isSaving: false, + }); + }, + [types.REQUEST_UPDATED_BADGE](state) { + Object.assign(state, { + isSaving: true, + }); + }, + + [types.RECEIVE_LOAD_BADGES](state, badges) { + Object.assign(state, { + badges: reorderBadges(badges), + isLoading: false, + }); + }, + [types.RECEIVE_LOAD_BADGES_ERROR](state) { + Object.assign(state, { + isLoading: false, + }); + }, + [types.REQUEST_LOAD_BADGES](state, data) { + Object.assign(state, { + kind: data.kind, // project or group + apiEndpointUrl: data.apiEndpointUrl, + docsUrl: data.docsUrl, + isLoading: true, + }); + }, + + [types.RECEIVE_DELETE_BADGE](state, badgeId) { + const badges = state.badges.filter(badge => badge.id !== badgeId); + Object.assign(state, { + badges, + }); + }, + [types.RECEIVE_DELETE_BADGE_ERROR](state, badgeId) { + const badges = state.badges.map(badge => { + if (badge.id === badgeId) { + return { + ...badge, + isDeleting: false, + }; + } + + return badge; + }); + Object.assign(state, { + badges, + }); + }, + [types.REQUEST_DELETE_BADGE](state, badgeId) { + const badges = state.badges.map(badge => { + if (badge.id === badgeId) { + return { + ...badge, + isDeleting: true, + }; + } + + return badge; + }); + Object.assign(state, { + badges, + }); + }, + + [types.RECEIVE_RENDERED_BADGE](state, renderedBadge) { + Object.assign(state, { isRendering: false, renderedBadge }); + }, + [types.RECEIVE_RENDERED_BADGE_ERROR](state) { + Object.assign(state, { isRendering: false }); + }, + [types.REQUEST_RENDERED_BADGE](state) { + Object.assign(state, { isRendering: true }); + }, + + [types.START_EDITING](state, badge) { + Object.assign(state, { + badgeInEditForm: { ...badge }, + isEditing: true, + renderedBadge: { ...badge }, + }); + }, + [types.STOP_EDITING](state) { + Object.assign(state, { + badgeInEditForm: null, + isEditing: false, + renderedBadge: null, + }); + }, + + [types.UPDATE_BADGE_IN_FORM](state, badge) { + if (state.isEditing) { + Object.assign(state, { + badgeInEditForm: badge, + }); + } else { + Object.assign(state, { + badgeInAddForm: badge, + }); + } + }, + + [types.UPDATE_BADGE_IN_MODAL](state, badge) { + Object.assign(state, { + badgeInModal: badge, + }); + }, +}; diff --git a/app/assets/javascripts/badges/store/state.js b/app/assets/javascripts/badges/store/state.js new file mode 100644 index 0000000000..43413aeb5b --- /dev/null +++ b/app/assets/javascripts/badges/store/state.js @@ -0,0 +1,13 @@ +export default () => ({ + apiEndpointUrl: null, + badgeInAddForm: null, + badgeInEditForm: null, + badgeInModal: null, + badges: [], + docsUrl: null, + renderedBadge: null, + isEditing: false, + isLoading: false, + isRendering: false, + isSaving: false, +}); diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js index b669b63d23..e2a73a1797 100644 --- a/app/assets/javascripts/behaviors/copy_to_clipboard.js +++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import Clipboard from 'clipboard'; function showTooltip(target, title) { diff --git a/app/assets/javascripts/behaviors/details_behavior.js b/app/assets/javascripts/behaviors/details_behavior.js index 7c9dbcc8d6..1d63f5baee 100644 --- a/app/assets/javascripts/behaviors/details_behavior.js +++ b/app/assets/javascripts/behaviors/details_behavior.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; $(() => { $('body').on('click', '.js-details-target', function target() { diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js index 8d021de799..84fef4d8b4 100644 --- a/app/assets/javascripts/behaviors/index.js +++ b/app/assets/javascripts/behaviors/index.js @@ -1,6 +1,7 @@ import './autosize'; import './bind_in_out'; -import initCopyAsGFM from './copy_as_gfm'; +import './markdown/render_gfm'; +import initCopyAsGFM from './markdown/copy_as_gfm'; import initCopyToClipboard from './copy_to_clipboard'; import './details_behavior'; import installGlEmojiElement from './gl_emoji'; diff --git a/app/assets/javascripts/behaviors/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js similarity index 99% rename from app/assets/javascripts/behaviors/copy_as_gfm.js rename to app/assets/javascripts/behaviors/markdown/copy_as_gfm.js index ffe90595b5..75cf90de0b 100644 --- a/app/assets/javascripts/behaviors/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js @@ -1,8 +1,9 @@ /* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */ +import $ from 'jquery'; import _ from 'underscore'; -import { insertText, getSelectedFragment, nodeMatchesSelector } from '../lib/utils/common_utils'; -import { placeholderImage } from '../lazy_loader'; +import { insertText, getSelectedFragment, nodeMatchesSelector } from '~/lib/utils/common_utils'; +import { placeholderImage } from '~/lazy_loader'; const gfmRules = { // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js similarity index 85% rename from app/assets/javascripts/render_gfm.js rename to app/assets/javascripts/behaviors/markdown/render_gfm.js index 05a623ca6d..dbff2bd4b1 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -1,6 +1,7 @@ +import $ from 'jquery'; +import syntaxHighlight from '~/syntax_highlight'; import renderMath from './render_math'; import renderMermaid from './render_mermaid'; -import syntaxHighlight from './syntax_highlight'; // Render Gitlab flavoured Markdown // diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js similarity index 85% rename from app/assets/javascripts/render_math.js rename to app/assets/javascripts/behaviors/markdown/render_math.js index eabdb01b2a..eb4e59d12b 100644 --- a/app/assets/javascripts/render_math.js +++ b/app/assets/javascripts/behaviors/markdown/render_math.js @@ -1,5 +1,6 @@ -import { __ } from './locale'; -import flash from './flash'; +import $ from 'jquery'; +import { __ } from '~/locale'; +import flash from '~/flash'; // Renders math using KaTeX in any element with the // `js-render-math` class @@ -30,7 +31,7 @@ export default function renderMath($els) { if (!$els.length) return; Promise.all([ import(/* webpackChunkName: 'katex' */ 'katex'), - import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.css'), + import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'), ]).then(([katex]) => { renderWithKaTeX($els, katex); }).catch(() => flash(__('An error occurred while rendering KaTeX'))); diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js similarity index 94% rename from app/assets/javascripts/render_mermaid.js rename to app/assets/javascripts/behaviors/markdown/render_mermaid.js index d4f18955bd..56b1896e9f 100644 --- a/app/assets/javascripts/render_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js @@ -1,3 +1,5 @@ +import flash from '~/flash'; + // Renders diagrams and flowcharts from text using Mermaid in any element with the // `js-render-mermaid` class. // @@ -12,8 +14,6 @@ // // -import Flash from './flash'; - export default function renderMermaid($els) { if (!$els.length) return; @@ -52,6 +52,6 @@ export default function renderMermaid($els) { }); }); }).catch((err) => { - Flash(`Can't load mermaid module: ${err}`); + flash(`Can't load mermaid module: ${err}`); }); } diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js index 312edc0cd6..3ec932bdb7 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js +++ b/app/assets/javascripts/behaviors/quick_submit.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import '../commons/bootstrap'; import { isInIssuePage } from '../lib/utils/common_utils'; @@ -72,5 +73,5 @@ $(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-q title, trigger: 'manual', }); - $this.tooltip('show').one('blur', () => $this.tooltip('hide')); + $this.tooltip('show').one('blur click', () => $this.tooltip('hide')); }); diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js index e10cb2e3dc..ffff4ddb71 100644 --- a/app/assets/javascripts/behaviors/requires_input.js +++ b/app/assets/javascripts/behaviors/requires_input.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import _ from 'underscore'; import '../commons/bootstrap'; diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index 81c8944142..4446be0e52 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -1,3 +1,6 @@ +import $ from 'jquery'; +import { getLocationHash } from '../lib/utils/url_utility'; + // Toggle button. Show/hide content inside parent container. // Button does not change visibility. If button has icon - it changes chevron style. // @@ -5,7 +8,6 @@ // %button.js-toggle-button // %div.js-toggle-content // -import { getLocationHash } from '../lib/utils/url_utility'; $(() => { function toggleContainer(container, toggleState) { diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js index 83cac896f8..ff1739b167 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js +++ b/app/assets/javascripts/blob/blob_file_dropzone.js @@ -1,4 +1,6 @@ /* eslint-disable func-names, object-shorthand, prefer-arrow-callback */ + +import $ from 'jquery'; import Dropzone from 'dropzone'; import { visitUrl } from '../lib/utils/url_utility'; import { HIDDEN_CLASS } from '../lib/utils/constants'; diff --git a/app/assets/javascripts/blob/blob_fork_suggestion.js b/app/assets/javascripts/blob/blob_fork_suggestion.js index 47c431fb80..476b9405a9 100644 --- a/app/assets/javascripts/blob/blob_fork_suggestion.js +++ b/app/assets/javascripts/blob/blob_fork_suggestion.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + const defaults = { // Buttons that will show the `suggestionSections` // has `data-fork-path`, and `data-action` diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js index 37074301b5..030ca1907e 100644 --- a/app/assets/javascripts/blob/file_template_mediator.js +++ b/app/assets/javascripts/blob/file_template_mediator.js @@ -1,4 +1,6 @@ /* eslint-disable class-methods-use-this */ + +import $ from 'jquery'; import Flash from '../flash'; import FileTemplateTypeSelector from './template_selectors/type_selector'; import BlobCiYamlSelector from './template_selectors/ci_yaml_selector'; diff --git a/app/assets/javascripts/blob/file_template_selector.js b/app/assets/javascripts/blob/file_template_selector.js index 5ae30990ae..e52cf249f3 100644 --- a/app/assets/javascripts/blob/file_template_selector.js +++ b/app/assets/javascripts/blob/file_template_selector.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + export default class FileTemplateSelector { constructor(mediator) { this.mediator = mediator; diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js index 888883163c..9dfdb06007 100644 --- a/app/assets/javascripts/blob/template_selector.js +++ b/app/assets/javascripts/blob/template_selector.js @@ -1,5 +1,7 @@ /* eslint-disable class-methods-use-this, no-unused-vars */ +import $ from 'jquery'; + export default class TemplateSelector { constructor({ dropdown, data, pattern, wrapper, editor, $input } = {}) { this.pattern = pattern; @@ -76,7 +78,7 @@ export default class TemplateSelector { if (!skipFocus) this.editor.focus(); - if (this.editor instanceof jQuery) { + if (this.editor instanceof $) { this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent); } } diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js index 92ea91c45a..137e1f5a09 100644 --- a/app/assets/javascripts/blob/viewer/index.js +++ b/app/assets/javascripts/blob/viewer/index.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import Flash from '../../flash'; import { handleLocationHash } from '../../lib/utils/common_utils'; import axios from '../../lib/utils/axios_utils'; diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 931ed042df..4424232f64 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-new, max-len */ /* global EditBlob */ + +import $ from 'jquery'; import NewCommitForm from '../new_commit_form'; import EditBlob from './edit_blob'; import BlobFileDropzone from '../blob/blob_file_dropzone'; diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index d4f6adaccb..82a3d494b6 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -1,5 +1,6 @@ /* global ace */ +import $ from 'jquery'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; import { __ } from '~/locale'; diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 9c4cc2338c..3cffd91716 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -1,4 +1,6 @@ /* eslint-disable comma-dangle, space-before-function-paren, one-var */ + +import $ from 'jquery'; import Sortable from 'vendor/Sortable'; import Vue from 'vue'; import AccessorUtilities from '../../lib/utils/accessor'; diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js index 8a1b177bba..7be98825fd 100644 --- a/app/assets/javascripts/boards/components/board_delete.js +++ b/app/assets/javascripts/boards/components/board_delete.js @@ -1,5 +1,6 @@ /* eslint-disable comma-dangle, space-before-function-paren, no-alert */ +import $ from 'jquery'; import Vue from 'vue'; window.gl = window.gl || {}; diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index 870d242e77..8d84c1735b 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -1,4 +1,5 @@ + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue new file mode 100644 index 0000000000..2cbd982af1 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -0,0 +1,65 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue new file mode 100644 index 0000000000..453208f3f1 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -0,0 +1,66 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue new file mode 100644 index 0000000000..15918ac963 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue @@ -0,0 +1,35 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue new file mode 100644 index 0000000000..560cdd941c --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -0,0 +1,58 @@ + + + diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue new file mode 100644 index 0000000000..4310d762c7 --- /dev/null +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -0,0 +1,94 @@ + + + diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue new file mode 100644 index 0000000000..0c44a755f5 --- /dev/null +++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue @@ -0,0 +1,130 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue new file mode 100644 index 0000000000..1c237c0ec9 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide.vue @@ -0,0 +1,108 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue new file mode 100644 index 0000000000..79a83b4799 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_context_bar.vue @@ -0,0 +1,84 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_external_links.vue b/app/assets/javascripts/ide/components/ide_external_links.vue new file mode 100644 index 0000000000..c6f6e0d234 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_external_links.vue @@ -0,0 +1,43 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_file_buttons.vue b/app/assets/javascripts/ide/components/ide_file_buttons.vue new file mode 100644 index 0000000000..a6c6f46a14 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_file_buttons.vue @@ -0,0 +1,84 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue new file mode 100644 index 0000000000..eb2749e615 --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue @@ -0,0 +1,47 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue new file mode 100644 index 0000000000..a6f40286ac --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_project_tree.vue @@ -0,0 +1,65 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue new file mode 100644 index 0000000000..e6af88e04b --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_repo_tree.vue @@ -0,0 +1,41 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue new file mode 100644 index 0000000000..8cf1ccb4fc --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -0,0 +1,51 @@ + + + diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue new file mode 100644 index 0000000000..152a5f632a --- /dev/null +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -0,0 +1,60 @@ + + + diff --git a/app/assets/javascripts/ide/components/mr_file_icon.vue b/app/assets/javascripts/ide/components/mr_file_icon.vue new file mode 100644 index 0000000000..8a440902df --- /dev/null +++ b/app/assets/javascripts/ide/components/mr_file_icon.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue new file mode 100644 index 0000000000..769e9b79ca --- /dev/null +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -0,0 +1,111 @@ + + + diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue new file mode 100644 index 0000000000..4b5a50785b --- /dev/null +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -0,0 +1,99 @@ + + + diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue new file mode 100644 index 0000000000..c165af5ce5 --- /dev/null +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -0,0 +1,75 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue new file mode 100644 index 0000000000..d885ed5e30 --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -0,0 +1,172 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue new file mode 100644 index 0000000000..711bafa17a --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -0,0 +1,219 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue new file mode 100644 index 0000000000..3b5068d491 --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -0,0 +1,127 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_file_status_icon.vue b/app/assets/javascripts/ide/components/repo_file_status_icon.vue new file mode 100644 index 0000000000..97589e116c --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_file_status_icon.vue @@ -0,0 +1,39 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue new file mode 100644 index 0000000000..79af8c0b0c --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_loading_file.vue @@ -0,0 +1,42 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue new file mode 100644 index 0000000000..304a73ed1a --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -0,0 +1,104 @@ + + + diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue new file mode 100644 index 0000000000..7bd646ba9b --- /dev/null +++ b/app/assets/javascripts/ide/components/repo_tabs.vue @@ -0,0 +1,82 @@ + + + diff --git a/app/assets/javascripts/ide/components/resizable_panel.vue b/app/assets/javascripts/ide/components/resizable_panel.vue new file mode 100644 index 0000000000..5ea2a2f682 --- /dev/null +++ b/app/assets/javascripts/ide/components/resizable_panel.vue @@ -0,0 +1,85 @@ + + + diff --git a/app/assets/javascripts/ide/eventhub.js b/app/assets/javascripts/ide/eventhub.js new file mode 100644 index 0000000000..0948c2e535 --- /dev/null +++ b/app/assets/javascripts/ide/eventhub.js @@ -0,0 +1,3 @@ +import Vue from 'vue'; + +export default new Vue(); diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js new file mode 100644 index 0000000000..4a0a303d5a --- /dev/null +++ b/app/assets/javascripts/ide/ide_router.js @@ -0,0 +1,173 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import flash from '~/flash'; +import store from './stores'; + +Vue.use(VueRouter); + +/** + * Routes below /-/ide/: + +/project/h5bp/html5-boilerplate/blob/master +/project/h5bp/html5-boilerplate/blob/master/app/js/test.js + +/project/h5bp/html5-boilerplate/mr/123 +/project/h5bp/html5-boilerplate/mr/123/app/js/test.js + +/workspace/123 +/workspace/project/h5bp/html5-boilerplate/blob/my-special-branch +/workspace/project/h5bp/html5-boilerplate/mr/123 + +/ = /workspace + +/settings +*/ + +// Unfortunately Vue Router doesn't work without at least a fake component +// If you do only data handling +const EmptyRouterComponent = { + render(createElement) { + return createElement('div'); + }, +}; + +const router = new VueRouter({ + mode: 'history', + base: `${gon.relative_url_root}/-/ide/`, + routes: [ + { + path: '/project/:namespace/:project+', + component: EmptyRouterComponent, + children: [ + { + path: ':targetmode(edit|tree|blob)/:branch/*', + component: EmptyRouterComponent, + }, + { + path: 'merge_requests/:mrid', + component: EmptyRouterComponent, + }, + ], + }, + ], +}); + +router.beforeEach((to, from, next) => { + if (to.params.namespace && to.params.project) { + store + .dispatch('getProjectData', { + namespace: to.params.namespace, + projectId: to.params.project, + }) + .then(() => { + const fullProjectId = `${to.params.namespace}/${to.params.project}`; + + if (to.params.branch) { + store.dispatch('getBranchData', { + projectId: fullProjectId, + branchId: to.params.branch, + }); + + store + .dispatch('getFiles', { + projectId: fullProjectId, + branchId: to.params.branch, + }) + .then(() => { + if (to.params[0]) { + const path = + to.params[0].slice(-1) === '/' ? to.params[0].slice(0, -1) : to.params[0]; + const treeEntryKey = Object.keys(store.state.entries).find( + key => key === path && !store.state.entries[key].pending, + ); + const treeEntry = store.state.entries[treeEntryKey]; + + if (treeEntry) { + store.dispatch('handleTreeEntryAction', treeEntry); + } + } + }) + .catch(e => { + flash( + 'Error while loading the branch files. Please try again.', + 'alert', + document, + null, + false, + true, + ); + throw e; + }); + } else if (to.params.mrid) { + store.dispatch('updateViewer', 'mrdiff'); + + store + .dispatch('getMergeRequestData', { + projectId: fullProjectId, + mergeRequestId: to.params.mrid, + }) + .then(mr => { + store.dispatch('getBranchData', { + projectId: fullProjectId, + branchId: mr.source_branch, + }); + + return store.dispatch('getFiles', { + projectId: fullProjectId, + branchId: mr.source_branch, + }); + }) + .then(() => + store.dispatch('getMergeRequestVersions', { + projectId: fullProjectId, + mergeRequestId: to.params.mrid, + }), + ) + .then(() => + store.dispatch('getMergeRequestChanges', { + projectId: fullProjectId, + mergeRequestId: to.params.mrid, + }), + ) + .then(mrChanges => { + mrChanges.changes.forEach((change, ind) => { + const changeTreeEntry = store.state.entries[change.new_path]; + + if (changeTreeEntry) { + store.dispatch('setFileMrChange', { + file: changeTreeEntry, + mrChange: change, + }); + + if (ind < 10) { + store.dispatch('getFileData', { + path: change.new_path, + makeFileActive: ind === 0, + }); + } + } + }); + }) + .catch(e => { + flash('Error while loading the merge request. Please try again.'); + throw e; + }); + } + }) + .catch(e => { + flash( + 'Error while loading the project data. Please try again.', + 'alert', + document, + null, + false, + true, + ); + throw e; + }); + } + + next(); +}); + +export default router; diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js new file mode 100644 index 0000000000..cbfb3dc54f --- /dev/null +++ b/app/assets/javascripts/ide/index.js @@ -0,0 +1,33 @@ +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import ide from './components/ide.vue'; +import store from './stores'; +import router from './ide_router'; + +function initIde(el) { + if (!el) return null; + + return new Vue({ + el, + store, + router, + components: { + ide, + }, + render(createElement) { + return createElement('ide', { + props: { + emptyStateSvgPath: el.dataset.emptyStateSvgPath, + noChangesStateSvgPath: el.dataset.noChangesStateSvgPath, + committedStateSvgPath: el.dataset.committedStateSvgPath, + }, + }); + }, + }); +} + +const ideElement = document.getElementById('ide'); + +Vue.use(Translate); + +initIde(ideElement); diff --git a/app/assets/javascripts/ide/lib/common/disposable.js b/app/assets/javascripts/ide/lib/common/disposable.js new file mode 100644 index 0000000000..84b29bdb60 --- /dev/null +++ b/app/assets/javascripts/ide/lib/common/disposable.js @@ -0,0 +1,14 @@ +export default class Disposable { + constructor() { + this.disposers = new Set(); + } + + add(...disposers) { + disposers.forEach(disposer => this.disposers.add(disposer)); + } + + dispose() { + this.disposers.forEach(disposer => disposer.dispose()); + this.disposers.clear(); + } +} diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js new file mode 100644 index 0000000000..e47adae99e --- /dev/null +++ b/app/assets/javascripts/ide/lib/common/model.js @@ -0,0 +1,94 @@ +/* global monaco */ +import Disposable from './disposable'; +import eventHub from '../../eventhub'; + +export default class Model { + constructor(monaco, file) { + this.monaco = monaco; + this.disposable = new Disposable(); + this.file = file; + this.content = file.content !== '' ? file.content : file.raw; + + this.disposable.add( + (this.originalModel = this.monaco.editor.createModel( + this.file.raw, + undefined, + new this.monaco.Uri(null, null, `original/${this.file.key}`), + )), + (this.model = this.monaco.editor.createModel( + this.content, + undefined, + new this.monaco.Uri(null, null, this.file.key), + )), + ); + if (this.file.mrChange) { + this.disposable.add( + (this.baseModel = this.monaco.editor.createModel( + this.file.baseRaw, + undefined, + new this.monaco.Uri(null, null, `target/${this.file.path}`), + )), + ); + } + + this.events = new Map(); + + this.updateContent = this.updateContent.bind(this); + this.dispose = this.dispose.bind(this); + + eventHub.$on(`editor.update.model.dispose.${this.file.key}`, this.dispose); + eventHub.$on(`editor.update.model.content.${this.file.path}`, this.updateContent); + } + + get url() { + return this.model.uri.toString(); + } + + get language() { + return this.model.getModeId(); + } + + get eol() { + return this.model.getEOL() === '\n' ? 'LF' : 'CRLF'; + } + + get path() { + return this.file.key; + } + + getModel() { + return this.model; + } + + getOriginalModel() { + return this.originalModel; + } + + getBaseModel() { + return this.baseModel; + } + + setValue(value) { + this.getModel().setValue(value); + } + + onChange(cb) { + this.events.set( + this.path, + this.disposable.add(this.model.onDidChangeContent(e => cb(this, e))), + ); + } + + updateContent(content) { + this.getOriginalModel().setValue(content); + this.getModel().setValue(content); + } + + dispose() { + this.disposable.dispose(); + this.events.clear(); + + eventHub.$off(`editor.update.model.dispose.${this.file.key}`, this.dispose); + eventHub.$off(`editor.update.model.content.${this.file.path}`, this.updateContent); + } +} diff --git a/app/assets/javascripts/ide/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js new file mode 100644 index 0000000000..0e7b563b5d --- /dev/null +++ b/app/assets/javascripts/ide/lib/common/model_manager.js @@ -0,0 +1,48 @@ +import eventHub from '../../eventhub'; +import Disposable from './disposable'; +import Model from './model'; + +export default class ModelManager { + constructor(monaco) { + this.monaco = monaco; + this.disposable = new Disposable(); + this.models = new Map(); + } + + hasCachedModel(key) { + return this.models.has(key); + } + + getModel(key) { + return this.models.get(key); + } + + addModel(file) { + if (this.hasCachedModel(file.key)) { + return this.getModel(file.key); + } + + const model = new Model(this.monaco, file); + this.models.set(model.path, model); + this.disposable.add(model); + + eventHub.$on( + `editor.update.model.dispose.${file.key}`, + this.removeCachedModel.bind(this, file), + ); + + return model; + } + + removeCachedModel(file) { + this.models.delete(file.key); + + eventHub.$off(`editor.update.model.dispose.${file.key}`, this.removeCachedModel); + } + + dispose() { + // dispose of all the models + this.disposable.dispose(); + this.models.clear(); + } +} diff --git a/app/assets/javascripts/ide/lib/decorations/controller.js b/app/assets/javascripts/ide/lib/decorations/controller.js new file mode 100644 index 0000000000..4290477474 --- /dev/null +++ b/app/assets/javascripts/ide/lib/decorations/controller.js @@ -0,0 +1,45 @@ +export default class DecorationsController { + constructor(editor) { + this.editor = editor; + this.decorations = new Map(); + this.editorDecorations = new Map(); + } + + getAllDecorationsForModel(model) { + if (!this.decorations.has(model.url)) return []; + + const modelDecorations = this.decorations.get(model.url); + const decorations = []; + + modelDecorations.forEach(val => decorations.push(...val)); + + return decorations; + } + + addDecorations(model, decorationsKey, decorations) { + const decorationMap = this.decorations.get(model.url) || new Map(); + + decorationMap.set(decorationsKey, decorations); + + this.decorations.set(model.url, decorationMap); + + this.decorate(model); + } + + decorate(model) { + if (!this.editor.instance) return; + + const decorations = this.getAllDecorationsForModel(model); + const oldDecorations = this.editorDecorations.get(model.url) || []; + + this.editorDecorations.set( + model.url, + this.editor.instance.deltaDecorations(oldDecorations, decorations), + ); + } + + dispose() { + this.decorations.clear(); + this.editorDecorations.clear(); + } +} diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js new file mode 100644 index 0000000000..b136545ad1 --- /dev/null +++ b/app/assets/javascripts/ide/lib/diff/controller.js @@ -0,0 +1,72 @@ +/* global monaco */ +import { throttle } from 'underscore'; +import DirtyDiffWorker from './diff_worker'; +import Disposable from '../common/disposable'; + +export const getDiffChangeType = (change) => { + if (change.modified) { + return 'modified'; + } else if (change.added) { + return 'added'; + } else if (change.removed) { + return 'removed'; + } + + return ''; +}; + +export const getDecorator = change => ({ + range: new monaco.Range( + change.lineNumber, + 1, + change.endLineNumber, + 1, + ), + options: { + isWholeLine: true, + linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`, + }, +}); + +export default class DirtyDiffController { + constructor(modelManager, decorationsController) { + this.disposable = new Disposable(); + this.editorSimpleWorker = null; + this.modelManager = modelManager; + this.decorationsController = decorationsController; + this.dirtyDiffWorker = new DirtyDiffWorker(); + this.throttledComputeDiff = throttle(this.computeDiff, 250); + this.decorate = this.decorate.bind(this); + + this.dirtyDiffWorker.addEventListener('message', this.decorate); + } + + attachModel(model) { + model.onChange(() => this.throttledComputeDiff(model)); + } + + computeDiff(model) { + this.dirtyDiffWorker.postMessage({ + path: model.path, + originalContent: model.getOriginalModel().getValue(), + newContent: model.getModel().getValue(), + }); + } + + reDecorate(model) { + this.decorationsController.decorate(model); + } + + decorate({ data }) { + const decorations = data.changes.map(change => getDecorator(change)); + const model = this.modelManager.getModel(data.path); + this.decorationsController.addDecorations(model, 'dirtyDiff', decorations); + } + + dispose() { + this.disposable.dispose(); + + this.dirtyDiffWorker.removeEventListener('message', this.decorate); + this.dirtyDiffWorker.terminate(); + } +} diff --git a/app/assets/javascripts/ide/lib/diff/diff.js b/app/assets/javascripts/ide/lib/diff/diff.js new file mode 100644 index 0000000000..0e37f5c470 --- /dev/null +++ b/app/assets/javascripts/ide/lib/diff/diff.js @@ -0,0 +1,30 @@ +import { diffLines } from 'diff'; + +// eslint-disable-next-line import/prefer-default-export +export const computeDiff = (originalContent, newContent) => { + const changes = diffLines(originalContent, newContent); + + let lineNumber = 1; + return changes.reduce((acc, change) => { + const findOnLine = acc.find(c => c.lineNumber === lineNumber); + + if (findOnLine) { + Object.assign(findOnLine, change, { + modified: true, + endLineNumber: (lineNumber + change.count) - 1, + }); + } else if ('added' in change || 'removed' in change) { + acc.push(Object.assign({}, change, { + lineNumber, + modified: undefined, + endLineNumber: (lineNumber + change.count) - 1, + })); + } + + if (!change.removed) { + lineNumber += change.count; + } + + return acc; + }, []); +}; diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js new file mode 100644 index 0000000000..e74c404633 --- /dev/null +++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js @@ -0,0 +1,10 @@ +import { computeDiff } from './diff'; + +self.addEventListener('message', (e) => { + const data = e.data; + + self.postMessage({ + path: data.path, + changes: computeDiff(data.originalContent, data.newContent), + }); +}); diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js new file mode 100644 index 0000000000..001737d6ee --- /dev/null +++ b/app/assets/javascripts/ide/lib/editor.js @@ -0,0 +1,192 @@ +import _ from 'underscore'; +import DecorationsController from './decorations/controller'; +import DirtyDiffController from './diff/controller'; +import Disposable from './common/disposable'; +import ModelManager from './common/model_manager'; +import editorOptions, { defaultEditorOptions } from './editor_options'; +import gitlabTheme from './themes/gl_theme'; + +export const clearDomElement = el => { + if (!el || !el.firstChild) return; + + while (el.firstChild) { + el.removeChild(el.firstChild); + } +}; + +export default class Editor { + static create(monaco) { + if (this.editorInstance) return this.editorInstance; + + this.editorInstance = new Editor(monaco); + + return this.editorInstance; + } + + constructor(monaco) { + this.monaco = monaco; + this.currentModel = null; + this.instance = null; + this.dirtyDiffController = null; + this.disposable = new Disposable(); + this.modelManager = new ModelManager(this.monaco); + this.decorationsController = new DecorationsController(this); + + this.setupMonacoTheme(); + + this.debouncedUpdate = _.debounce(() => { + this.updateDimensions(); + }, 200); + } + + createInstance(domElement) { + if (!this.instance) { + clearDomElement(domElement); + + this.disposable.add( + (this.instance = this.monaco.editor.create(domElement, { + ...defaultEditorOptions, + })), + (this.dirtyDiffController = new DirtyDiffController( + this.modelManager, + this.decorationsController, + )), + ); + + window.addEventListener('resize', this.debouncedUpdate, false); + } + } + + createDiffInstance(domElement) { + if (!this.instance) { + clearDomElement(domElement); + + this.disposable.add( + (this.instance = this.monaco.editor.createDiffEditor(domElement, { + ...defaultEditorOptions, + readOnly: true, + quickSuggestions: false, + occurrencesHighlight: false, + renderLineHighlight: 'none', + hideCursorInOverviewRuler: true, + renderSideBySide: Editor.renderSideBySide(domElement), + })), + ); + + window.addEventListener('resize', this.debouncedUpdate, false); + } + } + + createModel(file) { + return this.modelManager.addModel(file); + } + + attachModel(model) { + if (this.isDiffEditorType) { + this.instance.setModel({ + original: model.getOriginalModel(), + modified: model.getModel(), + }); + + return; + } + + this.instance.setModel(model.getModel()); + if (this.dirtyDiffController) this.dirtyDiffController.attachModel(model); + + this.currentModel = model; + + this.instance.updateOptions( + editorOptions.reduce((acc, obj) => { + Object.keys(obj).forEach(key => { + Object.assign(acc, { + [key]: obj[key](model), + }); + }); + return acc; + }, {}), + ); + + if (this.dirtyDiffController) this.dirtyDiffController.reDecorate(model); + } + + attachMergeRequestModel(model) { + this.instance.setModel({ + original: model.getBaseModel(), + modified: model.getModel(), + }); + + this.monaco.editor.createDiffNavigator(this.instance, { + alwaysRevealFirst: true, + }); + } + + setupMonacoTheme() { + this.monaco.editor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme); + + this.monaco.editor.setTheme('gitlab'); + } + + clearEditor() { + if (this.instance) { + this.instance.setModel(null); + } + } + + dispose() { + window.removeEventListener('resize', this.debouncedUpdate); + + // catch any potential errors with disposing the error + // this is mainly for tests caused by elements not existing + try { + this.disposable.dispose(); + + this.instance = null; + } catch (e) { + this.instance = null; + + if (process.env.NODE_ENV !== 'test') { + // eslint-disable-next-line no-console + console.error(e); + } + } + } + + updateDimensions() { + this.instance.layout(); + this.updateDiffView(); + } + + setPosition({ lineNumber, column }) { + this.instance.revealPositionInCenter({ + lineNumber, + column, + }); + this.instance.setPosition({ + lineNumber, + column, + }); + } + + onPositionChange(cb) { + if (!this.instance.onDidChangeCursorPosition) return; + + this.disposable.add(this.instance.onDidChangeCursorPosition(e => cb(this.instance, e))); + } + + updateDiffView() { + if (!this.isDiffEditorType) return; + + this.instance.updateOptions({ + renderSideBySide: Editor.renderSideBySide(this.instance.getDomNode()), + }); + } + + get isDiffEditorType() { + return this.instance.getEditorType() === 'vs.editor.IDiffEditor'; + } + + static renderSideBySide(domElement) { + return domElement.offsetWidth >= 700; + } +} diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js new file mode 100644 index 0000000000..9f895d49f2 --- /dev/null +++ b/app/assets/javascripts/ide/lib/editor_options.js @@ -0,0 +1,16 @@ +export const defaultEditorOptions = { + model: null, + readOnly: false, + contextmenu: true, + scrollBeyondLastLine: false, + minimap: { + enabled: false, + }, + wordWrap: 'on', +}; + +export default [ + { + readOnly: model => !!model.file.file_lock, + }, +]; diff --git a/app/assets/javascripts/ide/lib/themes/gl_theme.js b/app/assets/javascripts/ide/lib/themes/gl_theme.js new file mode 100644 index 0000000000..2fc96250c7 --- /dev/null +++ b/app/assets/javascripts/ide/lib/themes/gl_theme.js @@ -0,0 +1,14 @@ +export default { + themeName: 'gitlab', + monacoTheme: { + base: 'vs', + inherit: true, + rules: [], + colors: { + 'editorLineNumber.foreground': '#CCCCCC', + 'diffEditor.insertedTextBackground': '#ddfbe6', + 'diffEditor.removedTextBackground': '#f9d7dc', + 'editor.selectionBackground': '#aad6f8', + }, + }, +}; diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js new file mode 100644 index 0000000000..142a220097 --- /dev/null +++ b/app/assets/javascripts/ide/monaco_loader.js @@ -0,0 +1,16 @@ +import monacoContext from 'monaco-editor/dev/vs/loader'; + +monacoContext.require.config({ + paths: { + vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase + }, +}); + +// 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/ide/services/index.js b/app/assets/javascripts/ide/services/index.js new file mode 100644 index 0000000000..a12e637616 --- /dev/null +++ b/app/assets/javascripts/ide/services/index.js @@ -0,0 +1,78 @@ +import Vue from 'vue'; +import VueResource from 'vue-resource'; +import Api from '~/api'; + +Vue.use(VueResource); + +export default { + getTreeData(endpoint) { + return Vue.http.get(endpoint, { params: { format: 'json' } }); + }, + getFileData(endpoint) { + return Vue.http.get(endpoint, { params: { format: 'json' } }); + }, + getRawFileData(file) { + if (file.tempFile) { + return Promise.resolve(file.content); + } + + if (file.raw) { + return Promise.resolve(file.raw); + } + + return Vue.http.get(file.rawPath, { params: { format: 'json' } }).then(res => res.text()); + }, + getBaseRawFileData(file, sha) { + if (file.tempFile) { + return Promise.resolve(file.baseRaw); + } + + if (file.baseRaw) { + return Promise.resolve(file.baseRaw); + } + + return Vue.http + .get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), { + params: { format: 'json' }, + }) + .then(res => res.text()); + }, + getProjectData(namespace, project) { + return Api.project(`${namespace}/${project}`); + }, + getProjectMergeRequestData(projectId, mergeRequestId) { + return Api.mergeRequest(projectId, mergeRequestId); + }, + getProjectMergeRequestChanges(projectId, mergeRequestId) { + return Api.mergeRequestChanges(projectId, mergeRequestId); + }, + getProjectMergeRequestVersions(projectId, mergeRequestId) { + return Api.mergeRequestVersions(projectId, mergeRequestId); + }, + getBranchData(projectId, currentBranchId) { + return Api.branchSingle(projectId, currentBranchId); + }, + createBranch(projectId, payload) { + const url = Api.buildUrl(Api.createBranchPath).replace(':id', projectId); + + return Vue.http.post(url, payload); + }, + commit(projectId, payload) { + return Api.commitMultiple(projectId, payload); + }, + getTreeLastCommit(endpoint) { + return Vue.http.get(endpoint, { + params: { + format: 'json', + }, + }); + }, + getFiles(projectUrl, branchId) { + const url = `${projectUrl}/files/${branchId}`; + return Vue.http.get(url, { + params: { + format: 'json', + }, + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js new file mode 100644 index 0000000000..c6ba679d99 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions.js @@ -0,0 +1,118 @@ +import Vue from 'vue'; +import { visitUrl } from '~/lib/utils/url_utility'; +import flash from '~/flash'; +import * as types from './mutation_types'; +import FilesDecoratorWorker from './workers/files_decorator_worker'; + +export const redirectToUrl = (_, url) => visitUrl(url); + +export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data); + +export const discardAllChanges = ({ state, commit, dispatch }) => { + state.changedFiles.forEach(file => { + commit(types.DISCARD_FILE_CHANGES, file.path); + + if (file.tempFile) { + dispatch('closeFile', file.path); + } + }); + + commit(types.REMOVE_ALL_CHANGES_FILES); +}; + +export const closeAllFiles = ({ state, dispatch }) => { + state.openFiles.forEach(file => dispatch('closeFile', file)); +}; + +export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => { + if (side === 'left') { + commit(types.SET_LEFT_PANEL_COLLAPSED, collapsed); + } else { + commit(types.SET_RIGHT_PANEL_COLLAPSED, collapsed); + } +}; + +export const setResizingStatus = ({ commit }, resizing) => { + commit(types.SET_RESIZING_STATUS, resizing); +}; + +export const createTempEntry = ( + { state, commit, dispatch }, + { branchId, name, type, content = '', base64 = false }, +) => + new Promise(resolve => { + const worker = new FilesDecoratorWorker(); + const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name; + + if (state.entries[name]) { + flash( + `The name "${name.split('/').pop()}" is already taken in this directory.`, + 'alert', + document, + null, + false, + true, + ); + + resolve(); + + return null; + } + + worker.addEventListener('message', ({ data }) => { + const { file } = data; + + worker.terminate(); + + commit(types.CREATE_TMP_ENTRY, { + data, + projectId: state.currentProjectId, + branchId, + }); + + if (type === 'blob') { + commit(types.TOGGLE_FILE_OPEN, file.path); + commit(types.ADD_FILE_TO_CHANGED, file.path); + dispatch('setFileActive', file.path); + } + + resolve(file); + }); + + worker.postMessage({ + data: [fullName], + projectId: state.currentProjectId, + branchId, + type, + tempFile: true, + base64, + content, + }); + + return null; + }); + +export const scrollToTab = () => { + Vue.nextTick(() => { + const tabs = document.getElementById('tabs'); + + if (tabs) { + const tabEl = tabs.querySelector('.active .repo-tab'); + + tabEl.focus(); + } + }); +}; + +export const updateViewer = ({ commit }, viewer) => { + commit(types.UPDATE_VIEWER, viewer); +}; + +export const updateDelayViewerUpdated = ({ commit }, delay) => { + commit(types.UPDATE_DELAY_VIEWER_CHANGE, delay); +}; + +export * from './actions/tree'; +export * from './actions/file'; +export * from './actions/project'; +export * from './actions/merge_request'; diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js new file mode 100644 index 0000000000..66c60ad605 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -0,0 +1,187 @@ +import { normalizeHeaders } from '~/lib/utils/common_utils'; +import flash from '~/flash'; +import eventHub from '../../eventhub'; +import service from '../../services'; +import * as types from '../mutation_types'; +import router from '../../ide_router'; +import { setPageTitle } from '../utils'; + +export const closeFile = ({ commit, state, dispatch }, file) => { + const path = file.path; + const indexOfClosedFile = state.openFiles.findIndex(f => f.key === file.key); + const fileWasActive = file.active; + + if (file.pending) { + commit(types.REMOVE_PENDING_TAB, file); + } else { + commit(types.TOGGLE_FILE_OPEN, path); + commit(types.SET_FILE_ACTIVE, { path, active: false }); + } + + if (state.openFiles.length > 0 && fileWasActive) { + const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1; + const nextFileToOpen = state.openFiles[nextIndexToOpen]; + + if (nextFileToOpen.pending) { + dispatch('updateViewer', 'diff'); + dispatch('openPendingTab', nextFileToOpen); + } else { + dispatch('updateDelayViewerUpdated', true); + router.push(`/project${nextFileToOpen.url}`); + } + } else if (!state.openFiles.length) { + router.push(`/project/${file.projectId}/tree/${file.branchId}/`); + } + + eventHub.$emit(`editor.update.model.dispose.${file.key}`); +}; + +export const setFileActive = ({ commit, state, getters, dispatch }, path) => { + const file = state.entries[path]; + const currentActiveFile = getters.activeFile; + + if (file.active) return; + + if (currentActiveFile) { + commit(types.SET_FILE_ACTIVE, { + path: currentActiveFile.path, + active: false, + }); + } + + commit(types.SET_FILE_ACTIVE, { path, active: true }); + dispatch('scrollToTab'); + + commit(types.SET_CURRENT_PROJECT, file.projectId); + commit(types.SET_CURRENT_BRANCH, file.branchId); +}; + +export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => { + const file = state.entries[path]; + commit(types.TOGGLE_LOADING, { entry: file }); + return service + .getFileData(`${gon.relative_url_root ? gon.relative_url_root : ''}${file.url}`) + .then(res => { + const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); + setPageTitle(pageTitle); + + return res.json(); + }) + .then(data => { + commit(types.SET_FILE_DATA, { data, file }); + commit(types.TOGGLE_FILE_OPEN, path); + if (makeFileActive) dispatch('setFileActive', path); + commit(types.TOGGLE_LOADING, { entry: file }); + }) + .catch(() => { + commit(types.TOGGLE_LOADING, { entry: file }); + flash('Error loading file data. Please try again.', 'alert', document, null, false, true); + }); +}; + +export const setFileMrChange = ({ state, commit }, { file, mrChange }) => { + commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange }); +}; + +export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) => { + const file = state.entries[path]; + return new Promise((resolve, reject) => { + service + .getRawFileData(file) + .then(raw => { + commit(types.SET_FILE_RAW_DATA, { file, raw }); + if (file.mrChange && file.mrChange.new_file === false) { + service + .getBaseRawFileData(file, baseSha) + .then(baseRaw => { + commit(types.SET_FILE_BASE_RAW_DATA, { + file, + baseRaw, + }); + resolve(raw); + }) + .catch(e => { + reject(e); + }); + } else { + resolve(raw); + } + }) + .catch(() => { + flash('Error loading file content. Please try again.'); + reject(); + }); + }); +}; + +export const changeFileContent = ({ state, commit }, { path, content }) => { + const file = state.entries[path]; + commit(types.UPDATE_FILE_CONTENT, { path, content }); + + const indexOfChangedFile = state.changedFiles.findIndex(f => f.path === path); + + if (file.changed && indexOfChangedFile === -1) { + commit(types.ADD_FILE_TO_CHANGED, path); + } else if (!file.changed && indexOfChangedFile !== -1) { + commit(types.REMOVE_FILE_FROM_CHANGED, path); + } +}; + +export const setFileLanguage = ({ getters, commit }, { fileLanguage }) => { + if (getters.activeFile) { + commit(types.SET_FILE_LANGUAGE, { file: getters.activeFile, fileLanguage }); + } +}; + +export const setFileEOL = ({ getters, commit }, { eol }) => { + if (getters.activeFile) { + commit(types.SET_FILE_EOL, { file: getters.activeFile, eol }); + } +}; + +export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn }) => { + if (getters.activeFile) { + commit(types.SET_FILE_POSITION, { + file: getters.activeFile, + editorRow, + editorColumn, + }); + } +}; + +export const setFileViewMode = ({ state, commit }, { file, viewMode }) => { + commit(types.SET_FILE_VIEWMODE, { file, viewMode }); +}; + +export const discardFileChanges = ({ state, commit }, path) => { + const file = state.entries[path]; + + commit(types.DISCARD_FILE_CHANGES, path); + commit(types.REMOVE_FILE_FROM_CHANGED, path); + + if (file.tempFile && file.opened) { + commit(types.TOGGLE_FILE_OPEN, path); + } + + eventHub.$emit(`editor.update.model.content.${file.path}`, file.raw); +}; + +export const openPendingTab = ({ commit, getters, dispatch, state }, file) => { + if (getters.activeFile && getters.activeFile.path === file.path && state.viewer === 'diff') { + return false; + } + + commit(types.ADD_PENDING_TAB, { file }); + + dispatch('scrollToTab'); + + router.push(`/project/${file.projectId}/tree/${state.currentBranchId}/`); + + return true; +}; + +export const removePendingTab = ({ commit }, file) => { + commit(types.REMOVE_PENDING_TAB, file); + + eventHub.$emit(`editor.update.model.dispose.${file.key}`); +}; diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js new file mode 100644 index 0000000000..da73034fd7 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/merge_request.js @@ -0,0 +1,84 @@ +import flash from '~/flash'; +import service from '../../services'; +import * as types from '../mutation_types'; + +export const getMergeRequestData = ( + { commit, state, dispatch }, + { projectId, mergeRequestId, force = false } = {}, +) => + new Promise((resolve, reject) => { + if (!state.projects[projectId].mergeRequests[mergeRequestId] || force) { + service + .getProjectMergeRequestData(projectId, mergeRequestId) + .then(res => res.data) + .then(data => { + commit(types.SET_MERGE_REQUEST, { + projectPath: projectId, + mergeRequestId, + mergeRequest: data, + }); + if (!state.currentMergeRequestId) { + commit(types.SET_CURRENT_MERGE_REQUEST, mergeRequestId); + } + resolve(data); + }) + .catch(() => { + flash('Error loading merge request data. Please try again.'); + reject(new Error(`Merge Request not loaded ${projectId}`)); + }); + } else { + resolve(state.projects[projectId].mergeRequests[mergeRequestId]); + } + }); + +export const getMergeRequestChanges = ( + { commit, state, dispatch }, + { projectId, mergeRequestId, force = false } = {}, +) => + new Promise((resolve, reject) => { + if (!state.projects[projectId].mergeRequests[mergeRequestId].changes.length || force) { + service + .getProjectMergeRequestChanges(projectId, mergeRequestId) + .then(res => res.data) + .then(data => { + commit(types.SET_MERGE_REQUEST_CHANGES, { + projectPath: projectId, + mergeRequestId, + changes: data, + }); + resolve(data); + }) + .catch(() => { + flash('Error loading merge request changes. Please try again.'); + reject(new Error(`Merge Request Changes not loaded ${projectId}`)); + }); + } else { + resolve(state.projects[projectId].mergeRequests[mergeRequestId].changes); + } + }); + +export const getMergeRequestVersions = ( + { commit, state, dispatch }, + { projectId, mergeRequestId, force = false } = {}, +) => + new Promise((resolve, reject) => { + if (!state.projects[projectId].mergeRequests[mergeRequestId].versions.length || force) { + service + .getProjectMergeRequestVersions(projectId, mergeRequestId) + .then(res => res.data) + .then(data => { + commit(types.SET_MERGE_REQUEST_VERSIONS, { + projectPath: projectId, + mergeRequestId, + versions: data, + }); + resolve(data); + }) + .catch(() => { + flash('Error loading merge request versions. Please try again.'); + reject(new Error(`Merge Request Versions not loaded ${projectId}`)); + }); + } else { + resolve(state.projects[projectId].mergeRequests[mergeRequestId].versions); + } + }); diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js new file mode 100644 index 0000000000..b3882cb8d2 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -0,0 +1,49 @@ +import flash from '~/flash'; +import service from '../../services'; +import * as types from '../mutation_types'; + +export const getProjectData = ( + { commit, state, dispatch }, + { namespace, projectId, force = false } = {}, +) => new Promise((resolve, reject) => { + if (!state.projects[`${namespace}/${projectId}`] || force) { + commit(types.TOGGLE_LOADING, { entry: state }); + service.getProjectData(namespace, projectId) + .then(res => res.data) + .then((data) => { + commit(types.TOGGLE_LOADING, { entry: state }); + commit(types.SET_PROJECT, { projectPath: `${namespace}/${projectId}`, project: data }); + if (!state.currentProjectId) commit(types.SET_CURRENT_PROJECT, `${namespace}/${projectId}`); + resolve(data); + }) + .catch(() => { + flash('Error loading project data. Please try again.', 'alert', document, null, false, true); + reject(new Error(`Project not loaded ${namespace}/${projectId}`)); + }); + } else { + resolve(state.projects[`${namespace}/${projectId}`]); + } +}); + +export const getBranchData = ( + { commit, state, dispatch }, + { projectId, branchId, force = false } = {}, +) => new Promise((resolve, reject) => { + if ((typeof state.projects[`${projectId}`] === 'undefined' || + !state.projects[`${projectId}`].branches[branchId]) + || force) { + service.getBranchData(`${projectId}`, branchId) + .then(({ data }) => { + const { id } = data.commit; + commit(types.SET_BRANCH, { projectPath: `${projectId}`, branchName: branchId, branch: data }); + commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id }); + resolve(data); + }) + .catch(() => { + flash('Error loading branch data. Please try again.', 'alert', document, null, false, true); + reject(new Error(`Branch not loaded - ${projectId}/${branchId}`)); + }); + } else { + resolve(state.projects[`${projectId}`].branches[branchId]); + } +}); diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js new file mode 100644 index 0000000000..6536be04f0 --- /dev/null +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -0,0 +1,95 @@ +import { normalizeHeaders } from '~/lib/utils/common_utils'; +import flash from '~/flash'; +import service from '../../services'; +import * as types from '../mutation_types'; +import { findEntry } from '../utils'; +import FilesDecoratorWorker from '../workers/files_decorator_worker'; + +export const toggleTreeOpen = ({ commit, dispatch }, path) => { + commit(types.TOGGLE_TREE_OPEN, path); +}; + +export const handleTreeEntryAction = ({ commit, dispatch }, row) => { + if (row.type === 'tree') { + dispatch('toggleTreeOpen', row.path); + } else if (row.type === 'blob' && (row.opened || row.changed)) { + if (row.changed && !row.opened) { + commit(types.TOGGLE_FILE_OPEN, row.path); + } + + dispatch('setFileActive', row.path); + } else { + dispatch('getFileData', { path: row.path }); + } +}; + +export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { + if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return; + + service + .getTreeLastCommit(tree.lastCommitPath) + .then(res => { + const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null; + + commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath }); + + return res.json(); + }) + .then(data => { + data.forEach(lastCommit => { + const entry = findEntry(tree.tree, lastCommit.type, lastCommit.file_name); + + if (entry) { + commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit }); + } + }); + + dispatch('getLastCommitData', tree); + }) + .catch(() => flash('Error fetching log data.', 'alert', document, null, false, true)); +}; + +export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } = {}) => + new Promise((resolve, reject) => { + if (!state.trees[`${projectId}/${branchId}`]) { + const selectedProject = state.projects[projectId]; + commit(types.CREATE_TREE, { treePath: `${projectId}/${branchId}` }); + + service + .getFiles(selectedProject.web_url, branchId) + .then(res => res.json()) + .then(data => { + const worker = new FilesDecoratorWorker(); + worker.addEventListener('message', e => { + const { entries, treeList } = e.data; + const selectedTree = state.trees[`${projectId}/${branchId}`]; + + commit(types.SET_ENTRIES, entries); + commit(types.SET_DIRECTORY_DATA, { + treePath: `${projectId}/${branchId}`, + data: treeList, + }); + commit(types.TOGGLE_LOADING, { + entry: selectedTree, + forceValue: false, + }); + + worker.terminate(); + + resolve(); + }); + + worker.postMessage({ + data, + projectId, + branchId, + }); + }) + .catch(e => { + flash('Error loading tree data. Please try again.', 'alert', document, null, false, true); + reject(e); + }); + } else { + resolve(); + } + }); diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js new file mode 100644 index 0000000000..a77cdbc13c --- /dev/null +++ b/app/assets/javascripts/ide/stores/getters.js @@ -0,0 +1,37 @@ +export const activeFile = state => state.openFiles.find(file => file.active) || null; + +export const addedFiles = state => state.changedFiles.filter(f => f.tempFile); + +export const modifiedFiles = state => state.changedFiles.filter(f => !f.tempFile); + +export const projectsWithTrees = state => + Object.keys(state.projects).map(projectId => { + const project = state.projects[projectId]; + + return { + ...project, + branches: Object.keys(project.branches).map(branchId => { + const branch = project.branches[branchId]; + + return { + ...branch, + tree: state.trees[branch.treeId], + }; + }), + }; + }); + +export const currentMergeRequest = state => { + if (state.projects[state.currentProjectId]) { + return state.projects[state.currentProjectId].mergeRequests[state.currentMergeRequestId]; + } + return null; +}; + +// eslint-disable-next-line no-confusing-arrow +export const currentIcon = state => + state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right'; + +export const hasChanges = state => !!state.changedFiles.length; + +export const hasMergeRequest = state => !!state.currentMergeRequestId; diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js new file mode 100644 index 0000000000..7c82ce7976 --- /dev/null +++ b/app/assets/javascripts/ide/stores/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import state from './state'; +import * as actions from './actions'; +import * as getters from './getters'; +import mutations from './mutations'; +import commitModule from './modules/commit'; + +Vue.use(Vuex); + +export default new Vuex.Store({ + state: state(), + actions, + mutations, + getters, + modules: { + commit: commitModule, + }, +}); diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js new file mode 100644 index 0000000000..367c45f7e2 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -0,0 +1,200 @@ +import $ from 'jquery'; +import { sprintf, __ } from '~/locale'; +import flash from '~/flash'; +import { stripHtml } from '~/lib/utils/text_utility'; +import * as rootTypes from '../../mutation_types'; +import { createCommitPayload, createNewMergeRequestUrl } from '../../utils'; +import router from '../../../ide_router'; +import service from '../../../services'; +import * as types from './mutation_types'; +import * as consts from './constants'; +import eventHub from '../../../eventhub'; + +export const updateCommitMessage = ({ commit }, message) => { + commit(types.UPDATE_COMMIT_MESSAGE, message); +}; + +export const discardDraft = ({ commit }) => { + commit(types.UPDATE_COMMIT_MESSAGE, ''); +}; + +export const updateCommitAction = ({ commit }, commitAction) => { + commit(types.UPDATE_COMMIT_ACTION, commitAction); +}; + +export const updateBranchName = ({ commit }, branchName) => { + commit(types.UPDATE_NEW_BRANCH_NAME, branchName); +}; + +export const setLastCommitMessage = ({ rootState, commit }, data) => { + const currentProject = rootState.projects[rootState.currentProjectId]; + const commitStats = data.stats + ? sprintf(__('with %{additions} additions, %{deletions} deletions.'), { + additions: data.stats.additions, // eslint-disable-line indent + deletions: data.stats.deletions, // eslint-disable-line indent + }) // eslint-disable-line indent + : ''; + const commitMsg = sprintf( + __('Your changes have been committed. Commit %{commitId} %{commitStats}'), + { + commitId: `${ + data.short_id + }`, + commitStats, + }, + false, + ); + + commit(rootTypes.SET_LAST_COMMIT_MSG, commitMsg, { root: true }); +}; + +export const checkCommitStatus = ({ rootState }) => + service + .getBranchData(rootState.currentProjectId, rootState.currentBranchId) + .then(({ data }) => { + const { id } = data.commit; + const selectedBranch = + rootState.projects[rootState.currentProjectId].branches[rootState.currentBranchId]; + + if (selectedBranch.workingReference !== id) { + return true; + } + + return false; + }) + .catch(() => + flash( + __('Error checking branch data. Please try again.'), + 'alert', + document, + null, + false, + true, + ), + ); + +export const updateFilesAfterCommit = ( + { commit, dispatch, state, rootState, rootGetters }, + { data, branch }, +) => { + const selectedProject = rootState.projects[rootState.currentProjectId]; + const lastCommit = { + commit_path: `${selectedProject.web_url}/commit/${data.id}`, + commit: { + id: data.id, + message: data.message, + authored_date: data.committed_date, + author_name: data.committer_name, + }, + }; + + commit( + rootTypes.SET_BRANCH_WORKING_REFERENCE, + { + projectId: rootState.currentProjectId, + branchId: rootState.currentBranchId, + reference: data.id, + }, + { root: true }, + ); + + rootState.changedFiles.forEach(entry => { + commit( + rootTypes.SET_LAST_COMMIT_DATA, + { + entry, + lastCommit, + }, + { root: true }, + ); + + eventHub.$emit(`editor.update.model.content.${entry.path}`, entry.content); + + commit( + rootTypes.SET_FILE_RAW_DATA, + { + file: entry, + raw: entry.content, + }, + { root: true }, + ); + + commit( + rootTypes.TOGGLE_FILE_CHANGED, + { + file: entry, + changed: false, + }, + { root: true }, + ); + }); + + commit(rootTypes.REMOVE_ALL_CHANGES_FILES, null, { root: true }); + + if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH) { + router.push( + `/project/${rootState.currentProjectId}/blob/${branch}/${rootGetters.activeFile.path}`, + ); + } +}; + +export const commitChanges = ({ commit, state, getters, dispatch, rootState }) => { + const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH; + const payload = createCommitPayload(getters.branchName, newBranch, state, rootState); + const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus'); + + commit(types.UPDATE_LOADING, true); + + return getCommitStatus + .then( + branchChanged => + new Promise(resolve => { + if (branchChanged) { + // show the modal with a Bootstrap call + $('#ide-create-branch-modal').modal('show'); + } else { + resolve(); + } + }), + ) + .then(() => service.commit(rootState.currentProjectId, payload)) + .then(({ data }) => { + commit(types.UPDATE_LOADING, false); + + if (!data.short_id) { + flash(data.message, 'alert', document, null, false, true); + return null; + } + + dispatch('setLastCommitMessage', data); + dispatch('updateCommitMessage', ''); + return dispatch('updateFilesAfterCommit', { + data, + branch: getters.branchName, + }) + .then(() => { + if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) { + dispatch( + 'redirectToUrl', + createNewMergeRequestUrl( + rootState.projects[rootState.currentProjectId].web_url, + getters.branchName, + rootState.currentBranchId, + ), + { root: true }, + ); + } + }) + .then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH)); + }) + .catch(err => { + let errMsg = __('Error committing changes. Please try again.'); + if (err.response.data && err.response.data.message) { + errMsg += ` (${stripHtml(err.response.data.message)})`; + } + flash(errMsg, 'alert', document, null, false, true); + window.dispatchEvent(new Event('resize')); + + commit(types.UPDATE_LOADING, false); + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/constants.js b/app/assets/javascripts/ide/stores/modules/commit/constants.js new file mode 100644 index 0000000000..230b0a3d9b --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/constants.js @@ -0,0 +1,3 @@ +export const COMMIT_TO_CURRENT_BRANCH = '1'; +export const COMMIT_TO_NEW_BRANCH = '2'; +export const COMMIT_TO_NEW_BRANCH_MR = '3'; diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js new file mode 100644 index 0000000000..f7cdd6adb0 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js @@ -0,0 +1,24 @@ +import * as consts from './constants'; + +export const discardDraftButtonDisabled = state => state.commitMessage === '' || state.submitCommitLoading; + +export const commitButtonDisabled = (state, getters, rootState) => + getters.discardDraftButtonDisabled || !rootState.changedFiles.length; + +export const newBranchName = (state, _, rootState) => + `${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr(-5)}`; + +export const branchName = (state, getters, rootState) => { + if ( + state.commitAction === consts.COMMIT_TO_NEW_BRANCH || + state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR + ) { + if (state.newBranchName === '') { + return getters.newBranchName; + } + + return state.newBranchName; + } + + return rootState.currentBranchId; +}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/index.js b/app/assets/javascripts/ide/stores/modules/commit/index.js new file mode 100644 index 0000000000..3bf65b0284 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/index.js @@ -0,0 +1,12 @@ +import state from './state'; +import mutations from './mutations'; +import * as actions from './actions'; +import * as getters from './getters'; + +export default { + namespaced: true, + state: state(), + mutations, + actions, + getters, +}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/mutation_types.js b/app/assets/javascripts/ide/stores/modules/commit/mutation_types.js new file mode 100644 index 0000000000..9221f054e9 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/mutation_types.js @@ -0,0 +1,4 @@ +export const UPDATE_COMMIT_MESSAGE = 'UPDATE_COMMIT_MESSAGE'; +export const UPDATE_COMMIT_ACTION = 'UPDATE_COMMIT_ACTION'; +export const UPDATE_NEW_BRANCH_NAME = 'UPDATE_NEW_BRANCH_NAME'; +export const UPDATE_LOADING = 'UPDATE_LOADING'; diff --git a/app/assets/javascripts/ide/stores/modules/commit/mutations.js b/app/assets/javascripts/ide/stores/modules/commit/mutations.js new file mode 100644 index 0000000000..797357e3df --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/mutations.js @@ -0,0 +1,24 @@ +import * as types from './mutation_types'; + +export default { + [types.UPDATE_COMMIT_MESSAGE](state, commitMessage) { + Object.assign(state, { + commitMessage, + }); + }, + [types.UPDATE_COMMIT_ACTION](state, commitAction) { + Object.assign(state, { + commitAction, + }); + }, + [types.UPDATE_NEW_BRANCH_NAME](state, newBranchName) { + Object.assign(state, { + newBranchName, + }); + }, + [types.UPDATE_LOADING](state, submitCommitLoading) { + Object.assign(state, { + submitCommitLoading, + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/state.js b/app/assets/javascripts/ide/stores/modules/commit/state.js new file mode 100644 index 0000000000..8dae50961b --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/commit/state.js @@ -0,0 +1,6 @@ +export default () => ({ + commitMessage: '', + commitAction: '1', + newBranchName: '', + submitCommitLoading: false, +}); diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js new file mode 100644 index 0000000000..e3f504e5ab --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -0,0 +1,55 @@ +export const SET_INITIAL_DATA = 'SET_INITIAL_DATA'; +export const TOGGLE_LOADING = 'TOGGLE_LOADING'; +export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA'; +export const SET_LAST_COMMIT_MSG = 'SET_LAST_COMMIT_MSG'; +export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED'; +export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED'; +export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS'; + +// Project Mutation Types +export const SET_PROJECT = 'SET_PROJECT'; +export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT'; +export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN'; + +// Merge Request Mutation Types +export const SET_MERGE_REQUEST = 'SET_MERGE_REQUEST'; +export const SET_CURRENT_MERGE_REQUEST = 'SET_CURRENT_MERGE_REQUEST'; +export const SET_MERGE_REQUEST_CHANGES = 'SET_MERGE_REQUEST_CHANGES'; +export const SET_MERGE_REQUEST_VERSIONS = 'SET_MERGE_REQUEST_VERSIONS'; + +// Branch Mutation Types +export const SET_BRANCH = 'SET_BRANCH'; +export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE'; +export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; + +// Tree mutation types +export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; +export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; +export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; +export const CREATE_TREE = 'CREATE_TREE'; +export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES'; + +// File mutation types +export const SET_FILE_DATA = 'SET_FILE_DATA'; +export const TOGGLE_FILE_OPEN = 'TOGGLE_FILE_OPEN'; +export const SET_FILE_ACTIVE = 'SET_FILE_ACTIVE'; +export const SET_FILE_RAW_DATA = 'SET_FILE_RAW_DATA'; +export const SET_FILE_BASE_RAW_DATA = 'SET_FILE_BASE_RAW_DATA'; +export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; +export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; +export const SET_FILE_POSITION = 'SET_FILE_POSITION'; +export const SET_FILE_VIEWMODE = 'SET_FILE_VIEWMODE'; +export const SET_FILE_EOL = 'SET_FILE_EOL'; +export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; +export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED'; +export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED'; +export const TOGGLE_FILE_CHANGED = 'TOGGLE_FILE_CHANGED'; +export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH'; +export const SET_ENTRIES = 'SET_ENTRIES'; +export const CREATE_TMP_ENTRY = 'CREATE_TMP_ENTRY'; +export const SET_FILE_MERGE_REQUEST_CHANGE = 'SET_FILE_MERGE_REQUEST_CHANGE'; +export const UPDATE_VIEWER = 'UPDATE_VIEWER'; +export const UPDATE_DELAY_VIEWER_CHANGE = 'UPDATE_DELAY_VIEWER_CHANGE'; + +export const ADD_PENDING_TAB = 'ADD_PENDING_TAB'; +export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js new file mode 100644 index 0000000000..5e5eb83166 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -0,0 +1,103 @@ +import * as types from './mutation_types'; +import projectMutations from './mutations/project'; +import mergeRequestMutation from './mutations/merge_request'; +import fileMutations from './mutations/file'; +import treeMutations from './mutations/tree'; +import branchMutations from './mutations/branch'; + +export default { + [types.SET_INITIAL_DATA](state, data) { + Object.assign(state, data); + }, + [types.TOGGLE_LOADING](state, { entry, forceValue = undefined }) { + if (entry.path) { + Object.assign(state.entries[entry.path], { + loading: forceValue !== undefined ? forceValue : !state.entries[entry.path].loading, + }); + } else { + Object.assign(entry, { + loading: forceValue !== undefined ? forceValue : !entry.loading, + }); + } + }, + [types.SET_LEFT_PANEL_COLLAPSED](state, collapsed) { + Object.assign(state, { + leftPanelCollapsed: collapsed, + }); + }, + [types.SET_RIGHT_PANEL_COLLAPSED](state, collapsed) { + Object.assign(state, { + rightPanelCollapsed: collapsed, + }); + }, + [types.SET_RESIZING_STATUS](state, resizing) { + Object.assign(state, { + panelResizing: resizing, + }); + }, + [types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) { + Object.assign(entry.lastCommit, { + id: lastCommit.commit.id, + url: lastCommit.commit_path, + message: lastCommit.commit.message, + author: lastCommit.commit.author_name, + updatedAt: lastCommit.commit.authored_date, + }); + }, + [types.SET_LAST_COMMIT_MSG](state, lastCommitMsg) { + Object.assign(state, { + lastCommitMsg, + }); + }, + [types.SET_ENTRIES](state, entries) { + Object.assign(state, { + entries, + }); + }, + [types.CREATE_TMP_ENTRY](state, { data, projectId, branchId }) { + Object.keys(data.entries).reduce((acc, key) => { + const entry = data.entries[key]; + const foundEntry = state.entries[key]; + + if (!foundEntry) { + Object.assign(state.entries, { + [key]: entry, + }); + } else { + const tree = entry.tree.filter( + f => foundEntry.tree.find(e => e.path === f.path) === undefined, + ); + Object.assign(foundEntry, { + tree: foundEntry.tree.concat(tree), + }); + } + + return acc.concat(key); + }, []); + + const foundEntry = state.trees[`${projectId}/${branchId}`].tree.find( + e => e.path === data.treeList[0].path, + ); + + if (!foundEntry) { + Object.assign(state.trees[`${projectId}/${branchId}`], { + tree: state.trees[`${projectId}/${branchId}`].tree.concat(data.treeList), + }); + } + }, + [types.UPDATE_VIEWER](state, viewer) { + Object.assign(state, { + viewer, + }); + }, + [types.UPDATE_DELAY_VIEWER_CHANGE](state, delayViewerUpdated) { + Object.assign(state, { + delayViewerUpdated, + }); + }, + ...projectMutations, + ...mergeRequestMutation, + ...fileMutations, + ...treeMutations, + ...branchMutations, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js new file mode 100644 index 0000000000..2972ba5e38 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/branch.js @@ -0,0 +1,26 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_BRANCH](state, currentBranchId) { + Object.assign(state, { + currentBranchId, + }); + }, + [types.SET_BRANCH](state, { projectPath, branchName, branch }) { + Object.assign(state.projects[projectPath], { + branches: { + [branchName]: { + ...branch, + treeId: `${projectPath}/${branchName}`, + active: true, + workingReference: '', + }, + }, + }); + }, + [types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) { + Object.assign(state.projects[projectId].branches[branchId], { + workingReference: reference, + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js new file mode 100644 index 0000000000..eeb14b5490 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -0,0 +1,147 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_FILE_ACTIVE](state, { path, active }) { + Object.assign(state.entries[path], { + active, + }); + + if (active && !state.entries[path].pending) { + Object.assign(state, { + openFiles: state.openFiles.map(f => + Object.assign(f, { active: f.pending ? false : f.active }), + ), + }); + } + }, + [types.TOGGLE_FILE_OPEN](state, path) { + Object.assign(state.entries[path], { + opened: !state.entries[path].opened, + }); + + if (state.entries[path].opened) { + Object.assign(state, { + openFiles: state.openFiles.filter(f => f.path !== path).concat(state.entries[path]), + }); + } else { + const file = state.entries[path]; + + Object.assign(state, { + openFiles: state.openFiles.filter(f => f.key !== file.key), + }); + } + }, + [types.SET_FILE_DATA](state, { data, file }) { + Object.assign(state.entries[file.path], { + id: data.id, + blamePath: data.blame_path, + commitsPath: data.commits_path, + permalink: data.permalink, + rawPath: data.raw_path, + binary: data.binary, + renderError: data.render_error, + raw: null, + baseRaw: null, + html: data.html, + size: data.size, + }); + }, + [types.SET_FILE_RAW_DATA](state, { file, raw }) { + Object.assign(state.entries[file.path], { + raw, + }); + }, + [types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) { + Object.assign(state.entries[file.path], { + baseRaw, + }); + }, + [types.UPDATE_FILE_CONTENT](state, { path, content }) { + const changed = content !== state.entries[path].raw; + + Object.assign(state.entries[path], { + content, + changed, + }); + }, + [types.SET_FILE_LANGUAGE](state, { file, fileLanguage }) { + Object.assign(state.entries[file.path], { + fileLanguage, + }); + }, + [types.SET_FILE_EOL](state, { file, eol }) { + Object.assign(state.entries[file.path], { + eol, + }); + }, + [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { + Object.assign(state.entries[file.path], { + editorRow, + editorColumn, + }); + }, + [types.SET_FILE_MERGE_REQUEST_CHANGE](state, { file, mrChange }) { + Object.assign(state.entries[file.path], { + mrChange, + }); + }, + [types.SET_FILE_VIEWMODE](state, { file, viewMode }) { + Object.assign(state.entries[file.path], { + viewMode, + }); + }, + [types.DISCARD_FILE_CHANGES](state, path) { + Object.assign(state.entries[path], { + content: state.entries[path].raw, + changed: false, + }); + }, + [types.ADD_FILE_TO_CHANGED](state, path) { + Object.assign(state, { + changedFiles: state.changedFiles.concat(state.entries[path]), + }); + }, + [types.REMOVE_FILE_FROM_CHANGED](state, path) { + Object.assign(state, { + changedFiles: state.changedFiles.filter(f => f.path !== path), + }); + }, + [types.TOGGLE_FILE_CHANGED](state, { file, changed }) { + Object.assign(state.entries[file.path], { + changed, + }); + }, + [types.ADD_PENDING_TAB](state, { file, keyPrefix = 'pending' }) { + const pendingTab = state.openFiles.find(f => f.path === file.path && f.pending); + let openFiles = state.openFiles.map(f => + Object.assign(f, { active: f.path === file.path, opened: false }), + ); + + if (!pendingTab) { + const openFile = openFiles.find(f => f.path === file.path); + + openFiles = openFiles.concat(openFile ? null : file).reduce((acc, f) => { + if (!f) return acc; + + if (f.path === file.path) { + return acc.concat({ + ...f, + active: true, + pending: true, + opened: true, + key: `${keyPrefix}-${f.key}`, + }); + } + + return acc.concat(f); + }, []); + } + + Object.assign(state, { openFiles }); + }, + [types.REMOVE_PENDING_TAB](state, file) { + Object.assign(state, { + openFiles: state.openFiles.filter(f => f.key !== file.key), + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/merge_request.js b/app/assets/javascripts/ide/stores/mutations/merge_request.js new file mode 100644 index 0000000000..334819fe70 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/merge_request.js @@ -0,0 +1,33 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_MERGE_REQUEST](state, currentMergeRequestId) { + Object.assign(state, { + currentMergeRequestId, + }); + }, + [types.SET_MERGE_REQUEST](state, { projectPath, mergeRequestId, mergeRequest }) { + Object.assign(state.projects[projectPath], { + mergeRequests: { + [mergeRequestId]: { + ...mergeRequest, + active: true, + changes: [], + versions: [], + baseCommitSha: null, + }, + }, + }); + }, + [types.SET_MERGE_REQUEST_CHANGES](state, { projectPath, mergeRequestId, changes }) { + Object.assign(state.projects[projectPath].mergeRequests[mergeRequestId], { + changes, + }); + }, + [types.SET_MERGE_REQUEST_VERSIONS](state, { projectPath, mergeRequestId, versions }) { + Object.assign(state.projects[projectPath].mergeRequests[mergeRequestId], { + versions, + baseCommitSha: versions.length ? versions[0].base_commit_sha : null, + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/project.js b/app/assets/javascripts/ide/stores/mutations/project.js new file mode 100644 index 0000000000..284b39a2c7 --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/project.js @@ -0,0 +1,24 @@ +import * as types from '../mutation_types'; + +export default { + [types.SET_CURRENT_PROJECT](state, currentProjectId) { + Object.assign(state, { + currentProjectId, + }); + }, + [types.SET_PROJECT](state, { projectPath, project }) { + // Add client side properties + Object.assign(project, { + tree: [], + branches: {}, + mergeRequests: {}, + active: true, + }); + + Object.assign(state, { + projects: Object.assign({}, state.projects, { + [projectPath]: project, + }), + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js new file mode 100644 index 0000000000..1176c040fb --- /dev/null +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -0,0 +1,34 @@ +import * as types from '../mutation_types'; + +export default { + [types.TOGGLE_TREE_OPEN](state, path) { + Object.assign(state.entries[path], { + opened: !state.entries[path].opened, + }); + }, + [types.CREATE_TREE](state, { treePath }) { + Object.assign(state, { + trees: Object.assign({}, state.trees, { + [treePath]: { + tree: [], + loading: true, + }, + }), + }); + }, + [types.SET_DIRECTORY_DATA](state, { data, treePath }) { + Object.assign(state.trees[treePath], { + tree: data, + }); + }, + [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) { + Object.assign(tree, { + lastCommitPath: url, + }); + }, + [types.REMOVE_ALL_CHANGES_FILES](state) { + Object.assign(state, { + changedFiles: [], + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js new file mode 100644 index 0000000000..e5cc881400 --- /dev/null +++ b/app/assets/javascripts/ide/stores/state.js @@ -0,0 +1,20 @@ +export default () => ({ + currentProjectId: '', + currentBranchId: '', + currentMergeRequestId: '', + changedFiles: [], + endpoints: {}, + lastCommitMsg: '', + lastCommitPath: '', + loading: false, + openFiles: [], + parentTreeUrl: '', + trees: {}, + projects: {}, + leftPanelCollapsed: false, + rightPanelCollapsed: false, + panelResizing: false, + entries: {}, + viewer: 'editor', + delayViewerUpdated: false, +}); diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js new file mode 100644 index 0000000000..05a019de54 --- /dev/null +++ b/app/assets/javascripts/ide/stores/utils.js @@ -0,0 +1,134 @@ +export const dataStructure = () => ({ + id: '', + // Key will contain a mixture of ID and path + // it can also contain a prefix `pending-` for files opened in review mode + key: '', + type: '', + projectId: '', + branchId: '', + name: '', + url: '', + path: '', + tempFile: false, + tree: [], + loading: false, + opened: false, + active: false, + changed: false, + lastCommitPath: '', + lastCommit: { + id: '', + url: '', + message: '', + updatedAt: '', + author: '', + }, + blamePath: '', + commitsPath: '', + permalink: '', + rawPath: '', + binary: false, + html: '', + raw: '', + content: '', + parentTreeUrl: '', + renderError: false, + base64: false, + editorRow: 1, + editorColumn: 1, + fileLanguage: '', + eol: '', + viewMode: 'edit', + previewMode: null, + size: 0, +}); + +export const decorateData = entity => { + const { + id, + projectId, + branchId, + type, + url, + name, + path, + renderError, + content = '', + tempFile = false, + active = false, + opened = false, + changed = false, + parentTreeUrl = '', + base64 = false, + previewMode, + file_lock, + html, + } = entity; + + return { + ...dataStructure(), + id, + projectId, + branchId, + key: `${name}-${type}-${id}`, + type, + name, + url, + path, + tempFile, + opened, + active, + parentTreeUrl, + changed, + renderError, + content, + base64, + previewMode, + file_lock, + html, + }; +}; + +export const findEntry = (tree, type, name, prop = 'name') => + tree.find(f => f.type === type && f[prop] === name); + +export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path); + +export const setPageTitle = title => { + document.title = title; +}; + +export const createCommitPayload = (branch, newBranch, state, rootState) => ({ + branch, + commit_message: state.commitMessage, + actions: rootState.changedFiles.map(f => ({ + action: f.tempFile ? 'create' : 'update', + file_path: f.path, + content: f.content, + encoding: f.base64 ? 'base64' : 'text', + })), + start_branch: newBranch ? rootState.currentBranchId : undefined, +}); + +export const createNewMergeRequestUrl = (projectUrl, source, target) => + `${projectUrl}/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}`; + +const sortTreesByTypeAndName = (a, b) => { + if (a.type === 'tree' && b.type === 'blob') { + return -1; + } else if (a.type === 'blob' && b.type === 'tree') { + return 1; + } + if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; + if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; + return 0; +}; + +export const sortTree = sortedTree => + sortedTree + .map(entity => + Object.assign(entity, { + tree: entity.tree.length ? sortTree(entity.tree) : [], + }), + ) + .sort(sortTreesByTypeAndName); diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js new file mode 100644 index 0000000000..a167327690 --- /dev/null +++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js @@ -0,0 +1,90 @@ +import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; +import { decorateData, sortTree } from '../utils'; + +self.addEventListener('message', e => { + const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data; + + const treeList = []; + let file; + const entries = data.reduce((acc, path) => { + const pathSplit = path.split('/'); + const blobName = pathSplit.pop().trim(); + + if (pathSplit.length > 0) { + pathSplit.reduce((pathAcc, folderName) => { + const parentFolder = acc[pathAcc[pathAcc.length - 1]]; + const folderPath = `${parentFolder ? `${parentFolder.path}/` : ''}${folderName}`; + const foundEntry = acc[folderPath]; + + if (!foundEntry) { + const tree = decorateData({ + projectId, + branchId, + id: folderPath, + name: folderName, + path: folderPath, + url: `/${projectId}/tree/${branchId}/${folderPath}/`, + type: 'tree', + parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`, + tempFile, + changed: tempFile, + opened: tempFile, + }); + + Object.assign(acc, { + [folderPath]: tree, + }); + + if (parentFolder) { + parentFolder.tree.push(tree); + } else { + treeList.push(tree); + } + + pathAcc.push(tree.path); + } else { + pathAcc.push(foundEntry.path); + } + + return pathAcc; + }, []); + } + + if (blobName !== '') { + const fileFolder = acc[pathSplit.join('/')]; + file = decorateData({ + projectId, + branchId, + id: path, + name: blobName, + path, + url: `/${projectId}/blob/${branchId}/${path}`, + type: 'blob', + parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`, + tempFile, + changed: tempFile, + content, + base64, + previewMode: viewerInformationForPath(blobName), + }); + + Object.assign(acc, { + [path]: file, + }); + + if (fileFolder) { + fileFolder.tree.push(file); + } else { + treeList.push(file); + } + } + + return acc; + }, {}); + + self.postMessage({ + entries, + treeList: sortTree(treeList), + file, + }); +}); diff --git a/app/assets/javascripts/image_diff/image_diff.js b/app/assets/javascripts/image_diff/image_diff.js index f3af92cf2b..fab0255c37 100644 --- a/app/assets/javascripts/image_diff/image_diff.js +++ b/app/assets/javascripts/image_diff/image_diff.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import imageDiffHelper from './helpers/index'; import ImageBadge from './image_badge'; import { isImageLoaded } from '../lib/utils/image_utility'; diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index 523bd2adb9..b469e1e2ad 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import _ from 'underscore'; import { __, sprintf } from './locale'; import axios from './lib/utils/axios_utils'; diff --git a/app/assets/javascripts/init_changes_dropdown.js b/app/assets/javascripts/init_changes_dropdown.js index 1bab7965c1..09cca1dc7d 100644 --- a/app/assets/javascripts/init_changes_dropdown.js +++ b/app/assets/javascripts/init_changes_dropdown.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import stickyMonitor from './lib/utils/sticky'; export default (stickyTop) => { diff --git a/app/assets/javascripts/init_labels.js b/app/assets/javascripts/init_labels.js index 5f20055510..15da5d5cce 100644 --- a/app/assets/javascripts/init_labels.js +++ b/app/assets/javascripts/init_labels.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import LabelManager from './label_manager'; import GroupLabelSubscription from './group_label_subscription'; import ProjectLabelSubscription from './project_label_subscription'; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 2848fe003c..741894b5e6 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import axios from '../lib/utils/axios_utils'; import flash from '../flash'; diff --git a/app/assets/javascripts/issuable/auto_width_dropdown_select.js b/app/assets/javascripts/issuable/auto_width_dropdown_select.js index 14a2bfbe4e..b2c2de9e5d 100644 --- a/app/assets/javascripts/issuable/auto_width_dropdown_select.js +++ b/app/assets/javascripts/issuable/auto_width_dropdown_select.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + let instanceCount = 0; class AutoWidthDropdownSelect { diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js index 8c1b2e78ca..e003fb1d12 100644 --- a/app/assets/javascripts/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -1,4 +1,6 @@ /* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */ + +import $ from 'jquery'; import _ from 'underscore'; import axios from './lib/utils/axios_utils'; import Flash from './flash'; diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index 2056efe701..2307c8e0d8 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -1,5 +1,6 @@ /* eslint-disable class-methods-use-this, no-new */ +import $ from 'jquery'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import MilestoneSelect from './milestone_select'; import issueStatusSelect from './issue_status_select'; diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index da99394ff9..7470d634b9 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import Cookies from 'js-cookie'; import bp from './breakpoints'; import UsersSelect from './users_select'; diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index fdfad0b6a4..bb8b3d91e4 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -1,6 +1,7 @@ /* eslint-disable func-names, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */ /* global GitLab */ +import $ from 'jquery'; import Pikaday from 'pikaday'; import Autosave from './autosave'; import UsersSelect from './users_select'; diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js index 0683ca82a3..06ec454616 100644 --- a/app/assets/javascripts/issuable_index.js +++ b/app/assets/javascripts/issuable_index.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import axios from './lib/utils/axios_utils'; import flash from './flash'; import { __ } from './locale'; diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 333bbd9e0b..5113ac6775 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -1,4 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */ + +import $ from 'jquery'; import axios from './lib/utils/axios_utils'; import { addDelimiter } from './lib/utils/text_utility'; import flash from './flash'; diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 1338be0ec4..ae577e04a5 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -1,4 +1,5 @@ @@ -28,5 +36,21 @@ {{ title }}: {{ value }} + + + + + +

diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue index 56814a5252..af47056d98 100644 --- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue +++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue @@ -22,6 +22,11 @@ type: Boolean, required: true, }, + runnerHelpUrl: { + type: String, + required: false, + default: '', + }, }, computed: { shouldRenderContent() { @@ -39,6 +44,21 @@ runnerId() { return `#${this.job.runner.id}`; }, + hasTimeout() { + return this.job.metadata != null && this.job.metadata.timeout_human_readable !== null; + }, + timeout() { + if (this.job.metadata == null) { + return ''; + } + + let t = this.job.metadata.timeout_human_readable; + if (this.job.metadata.timeout_source !== '') { + t += ` (from ${this.job.metadata.timeout_source})`; + } + + return t; + }, renderBlock() { return this.job.merge_request || this.job.duration || @@ -114,6 +134,13 @@ title="Queued" :value="queued" /> + { props: { isLoading: this.mediator.state.isLoading, job: this.mediator.store.state.job, + runnerHelpUrl: dataset.runnerHelpUrl, }, }); }, diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index 61b40f79db..e230dbbd4a 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -1,4 +1,6 @@ /* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, max-len */ + +import $ from 'jquery'; import Sortable from 'vendor/Sortable'; import flash from './flash'; diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js index 7aab13ed9c..d85ae85170 100644 --- a/app/assets/javascripts/labels.js +++ b/app/assets/javascripts/labels.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + export default class Labels { constructor() { this.setSuggestedColor = this.setSuggestedColor.bind(this); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 9b46bbf83d..824d3f7ca0 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -1,6 +1,8 @@ /* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */ /* global Issuable */ /* global ListLabel */ + +import $ from 'jquery'; import _ from 'underscore'; import { __ } from './locale'; import axios from './lib/utils/axios_utils'; diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 1b4900827b..e317718877 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import ContextualSidebar from './contextual_sidebar'; import initFlyOutNav from './fly_out_nav'; diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js index 0bf2ba6acc..3873f4528c 100644 --- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js +++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + /** * Linked Tabs * diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index ed90db317d..9ff2042475 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,4 +1,4 @@ -import jQuery from 'jquery'; +import $ from 'jquery'; import Cookies from 'js-cookie'; import axios from './axios_utils'; import { getLocationHash } from './url_utility'; @@ -33,6 +33,7 @@ export const checkPageAndAction = (page, action) => { export const isInIssuePage = () => checkPageAndAction('issues', 'show'); export const isInMRPage = () => checkPageAndAction('merge_requests', 'show'); +export const isInEpicPage = () => checkPageAndAction('epics', 'show'); export const isInNoteablePage = () => isInIssuePage() || isInMRPage(); export const hasVueMRDiscussionsCookie = () => Cookies.get('vue_mr_discussions'); @@ -142,7 +143,7 @@ export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2; export const scrollToElement = (element) => { let $el = element; - if (!(element instanceof jQuery)) { + if (!(element instanceof $)) { $el = $(element); } const top = $el.offset().top; diff --git a/app/assets/javascripts/lib/utils/csrf.js b/app/assets/javascripts/lib/utils/csrf.js index 0bdb547d31..ca9828c468 100644 --- a/app/assets/javascripts/lib/utils/csrf.js +++ b/app/assets/javascripts/lib/utils/csrf.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + /* This module provides easy access to the CSRF token and caches it for re-use. It also exposes some values commonly used in relation diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index d6cccbef42..c3d94d63c1 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import timeago from 'timeago.js'; import dateFormat from 'vendor/date.format'; import { pluralize } from './text_utility'; diff --git a/app/assets/javascripts/lib/utils/dom_utils.js b/app/assets/javascripts/lib/utils/dom_utils.js index de65ea15a6..914de9de94 100644 --- a/app/assets/javascripts/lib/utils/dom_utils.js +++ b/app/assets/javascripts/lib/utils/dom_utils.js @@ -1,7 +1,12 @@ -/* eslint-disable import/prefer-default-export */ +import $ from 'jquery'; +import { isInIssuePage, isInMRPage, isInEpicPage, hasVueMRDiscussionsCookie } from './common_utils'; + +const isVueMRDiscussions = () => isInMRPage() && hasVueMRDiscussionsCookie() && !$('#diffs').is(':visible'); export const addClassIfElementExists = (element, className) => { if (element) { element.classList.add(className); } }; + +export const isInVueNoteablePage = () => isInIssuePage() || isInEpicPage() || isVueMRDiscussions(); diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 5dc98b4a92..5a16adea4d 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -1,26 +1,25 @@ /* eslint-disable import/prefer-default-export, func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len, vars-on-top */ +import $ from 'jquery'; +import { insertText } from '~/lib/utils/common_utils'; -const textUtils = {}; - -textUtils.selectedText = function(text, textarea) { +function selectedText(text, textarea) { return text.substring(textarea.selectionStart, textarea.selectionEnd); -}; +} -textUtils.lineBefore = function(text, textarea) { +function lineBefore(text, textarea) { var split; split = text.substring(0, textarea.selectionStart).trim().split('\n'); return split[split.length - 1]; -}; +} -textUtils.lineAfter = function(text, textarea) { +function lineAfter(text, textarea) { return text.substring(textarea.selectionEnd).trim().split('\n')[0]; -}; +} -textUtils.blockTagText = function(text, textArea, blockTag, selected) { - var lineAfter, lineBefore; - lineBefore = this.lineBefore(text, textArea); - lineAfter = this.lineAfter(text, textArea); - if (lineBefore === blockTag && lineAfter === blockTag) { +function blockTagText(text, textArea, blockTag, selected) { + const before = lineBefore(text, textArea); + const after = lineAfter(text, textArea); + if (before === blockTag && after === blockTag) { // To remove the block tag we have to select the line before & after if (blockTag != null) { textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1); @@ -30,10 +29,30 @@ textUtils.blockTagText = function(text, textArea, blockTag, selected) { } else { return blockTag + "\n" + selected + "\n" + blockTag; } -}; +} -textUtils.insertText = function(textArea, text, tag, blockTag, selected, wrap) { - var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine; +function moveCursor(textArea, tag, wrapped, removedLastNewLine) { + var pos; + if (!textArea.setSelectionRange) { + return; + } + if (textArea.selectionStart === textArea.selectionEnd) { + if (wrapped) { + pos = textArea.selectionStart - tag.length; + } else { + pos = textArea.selectionStart; + } + + if (removedLastNewLine) { + pos -= 1; + } + + return textArea.setSelectionRange(pos, pos); + } +} + +export function insertMarkdownText(textArea, text, tag, blockTag, selected, wrap) { + var textToInsert, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine; removedLastNewLine = false; removedFirstNewLine = false; currentLineEmpty = false; @@ -65,9 +84,9 @@ textUtils.insertText = function(textArea, text, tag, blockTag, selected, wrap) { if (selectedSplit.length > 1 && (!wrap || (blockTag != null && blockTag !== ''))) { if (blockTag != null && blockTag !== '') { - insertText = this.blockTagText(text, textArea, blockTag, selected); + textToInsert = blockTagText(text, textArea, blockTag, selected); } else { - insertText = selectedSplit.map(function(val) { + textToInsert = selectedSplit.map(function(val) { if (val.indexOf(tag) === 0) { return "" + (val.replace(tag, '')); } else { @@ -76,78 +95,42 @@ textUtils.insertText = function(textArea, text, tag, blockTag, selected, wrap) { }).join('\n'); } } else { - insertText = "" + startChar + tag + selected + (wrap ? tag : ' '); + textToInsert = "" + startChar + tag + selected + (wrap ? tag : ' '); } if (removedFirstNewLine) { - insertText = '\n' + insertText; + textToInsert = '\n' + textToInsert; } if (removedLastNewLine) { - insertText += '\n'; + textToInsert += '\n'; } - if (document.queryCommandSupported('insertText')) { - inserted = document.execCommand('insertText', false, insertText); - } - if (!inserted) { - try { - document.execCommand("ms-beginUndoUnit"); - } catch (error) {} - textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText); - try { - document.execCommand("ms-endUndoUnit"); - } catch (error) {} - } - return this.moveCursor(textArea, tag, wrap, removedLastNewLine); -}; + insertText(textArea, textToInsert); + return moveCursor(textArea, tag, wrap, removedLastNewLine); +} -textUtils.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) { - var pos; - if (!textArea.setSelectionRange) { - return; - } - if (textArea.selectionStart === textArea.selectionEnd) { - if (wrapped) { - pos = textArea.selectionStart - tag.length; - } else { - pos = textArea.selectionStart; - } - - if (removedLastNewLine) { - pos -= 1; - } - - return textArea.setSelectionRange(pos, pos); - } -}; - -textUtils.updateText = function(textArea, tag, blockTag, wrap) { +function updateText(textArea, tag, blockTag, wrap) { var $textArea, selected, text; $textArea = $(textArea); textArea = $textArea.get(0); text = $textArea.val(); - selected = this.selectedText(text, textArea); + selected = selectedText(text, textArea); $textArea.focus(); - return this.insertText(textArea, text, tag, blockTag, selected, wrap); -}; + return insertMarkdownText(textArea, text, tag, blockTag, selected, wrap); +} -textUtils.init = function(form) { - var self; - self = this; - return $('.js-md', form).off('click').on('click', function() { - var $this; - $this = $(this); - return self.updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend')); - }); -}; - -textUtils.removeListeners = function(form) { - return $('.js-md', form).off('click'); -}; - -textUtils.replaceRange = function(s, start, end, substitute) { +function replaceRange(s, start, end, substitute) { return s.substring(0, start) + substitute + s.substring(end); -}; +} -export default textUtils; +export function addMarkdownListeners(form) { + return $('.js-md', form).off('click').on('click', function() { + const $this = $(this); + return updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend')); + }); +} + +export function removeMarkdownListeners(form) { + return $('.js-md', form).off('click'); +} diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index c0ce078651..b54ecd2d54 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -7,7 +7,8 @@ * @param {String} text * @returns {String} */ -export const addDelimiter = text => (text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text); +export const addDelimiter = text => + (text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text); /** * Returns '99+' for numbers bigger than 99. @@ -22,7 +23,8 @@ export const highCountTrim = count => (count > 99 ? '99+' : count); * @param {String} string * @requires {String} */ -export const humanize = string => string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); +export const humanize = string => + string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); /** * Adds an 's' to the end of the string when count is bigger than 0 @@ -53,7 +55,7 @@ export const slugify = str => str.trim().toLowerCase(); * @param {Number} maxLength * @returns {String} */ -export const truncate = (string, maxLength) => `${string.substr(0, (maxLength - 3))}...`; +export const truncate = (string, maxLength) => `${string.substr(0, maxLength - 3)}...`; /** * Capitalizes first character @@ -65,20 +67,6 @@ export function capitalizeFirstCharacter(text) { return `${text[0].toUpperCase()}${text.slice(1)}`; } -export function camelCase(str) { - return str.replace(/_+([a-z])/gi, ($1, $2) => $2.toUpperCase()); -} - -export function camelCaseKeys(obj = {}) { - return Object.keys(obj).reduce((acc, key) => { - const camelKey = camelCase(key); - return { - ...acc, - [camelKey]: obj[key], - }; - }, {}); -} - /** * Replaces all html tags from a string with the given replacement. * @@ -94,3 +82,15 @@ export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, re * @param {*} string */ export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase()); + +/** + * Converts a sentence to lower case from the second word onwards + * e.g. Hello World => Hello world + * + * @param {*} string + */ +export const convertToSentenceCase = string => { + const splitWord = string.split(' ').map((word, index) => (index > 0 ? word.toLowerCase() : word)); + + return splitWord.join(' '); +}; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index a266bb6771..dd17544b65 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -51,7 +51,7 @@ export function removeParams(params) { const url = document.createElement('a'); url.href = window.location.href; - params.forEach((param) => { + params.forEach(param => { url.search = removeParamQueryString(url.search, param); }); @@ -83,3 +83,11 @@ export function refreshCurrentPage() { export function redirectTo(url) { return window.location.assign(url); } + +export function webIDEUrl(route = undefined) { + let returnUrl = `${gon.relative_url_root}/-/ide/`; + if (route) { + returnUrl += `project${route}`; + } + return returnUrl; +} diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index e5c1fce3db..f2323f5745 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-underscore-dangle, no-param-reassign, prefer-template, quotes, comma-dangle, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-else-return, max-len */ +import $ from 'jquery'; + // LineHighlighter // // Handles single- and multi-line selection and highlight for blob views. diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js index 3688a57937..403e216e70 100644 --- a/app/assets/javascripts/logo.js +++ b/app/assets/javascripts/logo.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + export default function initLogoAnimation() { window.addEventListener('beforeunload', () => { $('.tanuki-logo').addClass('animate'); diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 53b01cca1d..2c80baba10 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -1,5 +1,5 @@ /* eslint-disable import/first */ -/* global ConfirmDangerModal */ +/* global $ */ import jQuery from 'jquery'; import Cookies from 'js-cookie'; @@ -20,7 +20,6 @@ import './behaviors/'; // everything else import loadAwardsHandler from './awards_handler'; import bp from './breakpoints'; -import './confirm_danger_modal'; import Flash, { removeFlashClickListener } from './flash'; import './gl_dropdown'; import initTodoToggle from './header'; @@ -31,7 +30,6 @@ import LazyLoader from './lazy_loader'; import initLogoAnimation from './logo'; import './milestone_select'; import './projects_dropdown'; -import './render_gfm'; import initBreadcrumbs from './breadcrumb'; import initDispatcher from './dispatcher'; @@ -214,16 +212,6 @@ document.addEventListener('DOMContentLoaded', () => { $(document).trigger('toggle.comments'); }); - $document.on('click', '.js-confirm-danger', (e) => { - const btn = $(e.target); - const form = btn.closest('form'); - const text = btn.data('confirmDangerMessage'); - e.preventDefault(); - - // eslint-disable-next-line no-new - new ConfirmDangerModal(form, text); - }); - $document.on('breakpoint:change', (e, breakpoint) => { if (breakpoint === 'sm' || breakpoint === 'xs') { const $gutterIcon = $sidebarGutterToggle.find('i'); diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js index 84e70e35ba..d27922a209 100644 --- a/app/assets/javascripts/member_expiration_date.js +++ b/app/assets/javascripts/member_expiration_date.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import Pikaday from 'pikaday'; import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index 330ebed5f7..7d0c701fd7 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -1,3 +1,5 @@ +import $ from 'jquery'; + export default class Members { constructor() { this.addListeners(); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js index 8be7314ded..db1d09eb2f 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js @@ -1,5 +1,6 @@ /* eslint-disable comma-dangle, object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue, max-len */ +import $ from 'jquery'; import Vue from 'vue'; import Cookies from 'js-cookie'; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 66b258839a..4abd5433bb 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -1,5 +1,6 @@ /* eslint-disable new-cap, comma-dangle, no-new */ +import $ from 'jquery'; import Vue from 'vue'; import Flash from '../flash'; import initIssuableSidebar from '../init_issuable_sidebar'; diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index a64093afcf..d8222ebec6 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -1,4 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */ + +import $ from 'jquery'; import { __ } from '~/locale'; import TaskList from './task_list'; import MergeRequestTabs from './merge_request_tabs'; diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 46789e324c..3f84f4b949 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -1,16 +1,13 @@ /* eslint-disable no-new, class-methods-use-this */ +import $ from 'jquery'; import Cookies from 'js-cookie'; import axios from './lib/utils/axios_utils'; import flash from './flash'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import initChangesDropdown from './init_changes_dropdown'; import bp from './breakpoints'; -import { - parseUrlPathname, - handleLocationHash, - isMetaClick, -} from './lib/utils/common_utils'; +import { parseUrlPathname, handleLocationHash, isMetaClick } from './lib/utils/common_utils'; import { getLocationHash } from './lib/utils/url_utility'; import initDiscussionTab from './image_diff/init_discussion_tab'; import Diff from './diff'; @@ -68,10 +65,10 @@ import Notes from './notes'; let location = window.location; export default class MergeRequestTabs { - constructor({ action, setUrl, stubLocation } = {}) { const mergeRequestTabs = document.querySelector('.js-tabs-affix'); const navbar = document.querySelector('.navbar-gitlab'); + const peek = document.getElementById('js-peek'); const paddingTop = 16; this.diffsLoaded = false; @@ -85,6 +82,10 @@ export default class MergeRequestTabs { this.showTab = this.showTab.bind(this); this.stickyTop = navbar ? navbar.offsetHeight - paddingTop : 0; + if (peek) { + this.stickyTop += peek.offsetHeight; + } + if (mergeRequestTabs) { this.stickyTop += mergeRequestTabs.offsetHeight; } @@ -103,8 +104,7 @@ export default class MergeRequestTabs { .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) .on('click', '.js-show-tab', this.showTab); - $('.merge-request-tabs a[data-toggle="tab"]') - .on('click', this.clickTab); + $('.merge-request-tabs a[data-toggle="tab"]').on('click', this.clickTab); } // Used in tests @@ -113,8 +113,7 @@ export default class MergeRequestTabs { .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) .off('click', '.js-show-tab', this.showTab); - $('.merge-request-tabs a[data-toggle="tab"]') - .off('click', this.clickTab); + $('.merge-request-tabs a[data-toggle="tab"]').off('click', this.clickTab); } destroyPipelinesView() { @@ -177,10 +176,7 @@ export default class MergeRequestTabs { scrollToElement(container) { if (location.hash) { - const offset = 0 - ( - $('.navbar-gitlab').outerHeight() + - $('.js-tabs-affix').outerHeight() - ); + const offset = 0 - ($('.navbar-gitlab').outerHeight() + $('.js-tabs-affix').outerHeight()); const $el = $(`${container} ${location.hash}:not(.match)`); if ($el.length) { $.scrollTo($el[0], { offset }); @@ -234,9 +230,13 @@ export default class MergeRequestTabs { // Turbolinks' history. // // See https://github.com/rails/turbolinks/issues/363 - window.history.replaceState({ - url: newState, - }, document.title, newState); + window.history.replaceState( + { + url: newState, + }, + document.title, + newState, + ); return newState; } @@ -252,7 +252,8 @@ export default class MergeRequestTabs { this.toggleLoading(true); - axios.get(`${source}.json`) + axios + .get(`${source}.json`) .then(({ data }) => { document.querySelector('div#commits').innerHTML = data.html; localTimeAgo($('.js-timeago', 'div#commits')); @@ -297,7 +298,8 @@ export default class MergeRequestTabs { this.toggleLoading(true); - axios.get(`${urlPathname}.json${location.search}`) + axios + .get(`${urlPathname}.json${location.search}`) .then(({ data }) => { const $container = $('#diffs'); $container.html(data.html); @@ -326,8 +328,7 @@ export default class MergeRequestTabs { cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'), suggestionSections: $(el).find('.js-file-fork-suggestion-section'), actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'), - }) - .init(); + }).init(); }); // Scroll any linked note into view @@ -382,8 +383,7 @@ export default class MergeRequestTabs { resetViewContainer() { if (this.fixedLayoutPref !== null) { - $('.content-wrapper .container-fluid') - .toggleClass('container-limited', this.fixedLayoutPref); + $('.content-wrapper .container-fluid').toggleClass('container-limited', this.fixedLayoutPref); } } @@ -432,12 +432,11 @@ export default class MergeRequestTabs { const $diffTabs = $('#diff-notes-app'); - $tabs.off('affix.bs.affix affix-top.bs.affix') + $tabs + .off('affix.bs.affix affix-top.bs.affix') .affix({ offset: { - top: () => ( - $diffTabs.offset().top - $tabs.height() - $fixedNav.height() - ), + top: () => $diffTabs.offset().top - $tabs.height() - $fixedNav.height(), }, }) .on('affix.bs.affix', () => $diffTabs.css({ marginTop: $tabs.height() })) diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index b1d74250df..e6e3a66aa2 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -1,3 +1,4 @@ +import $ from 'jquery'; import axios from './lib/utils/axios_utils'; import flash from './flash'; diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 7fa3e25a0e..c749042a14 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -1,6 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */ /* global Issuable */ /* global ListMilestone */ + +import $ from 'jquery'; import _ from 'underscore'; import axios from './lib/utils/axios_utils'; import { timeFor } from './lib/utils/datetime_utility'; diff --git a/app/assets/javascripts/mini_pipeline_graph_dropdown.js b/app/assets/javascripts/mini_pipeline_graph_dropdown.js index c7bccd483a..01399de4c6 100644 --- a/app/assets/javascripts/mini_pipeline_graph_dropdown.js +++ b/app/assets/javascripts/mini_pipeline_graph_dropdown.js @@ -1,4 +1,6 @@ /* eslint-disable no-new */ + +import $ from 'jquery'; import flash from './flash'; import axios from './lib/utils/axios_utils'; diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 8ca94ef3e2..f5572be5fb 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -1,158 +1,155 @@ diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index 9517b8ccb6..c77f451c2d 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -1,87 +1,90 @@