diff options
1638 files changed, 12934 insertions, 5545 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 084febe175e..adde3400107 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -538,6 +538,8 @@ lint:javascript:report: <<: *except-docs <<: *pull-cache stage: post-test + dependencies: + - setup-test-env before_script: [] script: - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000000..72906051c5c --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +7.5
\ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 9785e7626f9..f661a29d9d1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -563,7 +563,7 @@ Style/Proc: # branches, and conditions. Metrics/AbcSize: Enabled: true - Max: 57.08 + Max: 56.96 # This cop checks if the length of a block exceeds some maximum value. Metrics/BlockLength: diff --git a/CHANGELOG.md b/CHANGELOG.md index de3b4b0d3e7..412cd86bad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,224 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.4.2 (2017-07-28) + +- Fix job merge request link to a forked source project. !12965 +- Improve redirect route query performance. !13062 +- Allow admin to read_users_list even if it's restricted. !13066 +- Fixes 500 error caused by pending delete projects in admin dashboard. !13067 +- Add instrumentation to MarkupHelper#link_to_gfm. !13069 +- Pending delete projects should not show in deploy keys. !13088 +- Fix sizing of custom header logo in new navigation. +- Fix crash on /help/ui. +- Fix creating merge request diffs when diff contains bytes that are invalid in UTF-8. +- fix vertical alignment of New Project button. +- Add LDAP SSL certificate verification option. +- Fix vertical alignment in firefox and safari for pipeline mini graph. + +## 9.4.1 (2017-07-25) + +- Fix pipeline_schedules pages throwing error 500 (when ref is empty). !12983 +- Fix editing project with container images present. !13028 +- Fix some invalid entries in PO files. !13032 +- Fix cross site request protection when logging in as a regular user when LDAP is enabled. !13049 +- Fix bug causing metrics files to be truncated. !35420 +- Fix anonymous access to public projects in groups with pending invites. +- Fixed issue boards sidebar close icon size. +- Fixed duplicate new milestone buttons when new navigation is turned on. +- Fix margins in the mini graph for pipeline in commits box. + +## 9.4.0 (2017-07-22) + +- Add blame view age mapping. !7198 (Jeff Stubler) +- Add support for image and services configuration in .gitlab-ci.yml. !8578 +- Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients. !9045 (jneen) +- Use fa-chevron-down on dropdown arrows for consistency. !9659 (TM Lee) +- Update the devise mail templates to match the design of the pipeline emails. !10483 (Alexis Reigel) +- Handle renamed submodules in repository browser. !10798 (David Turner) +- Display all current broadcast messages, not just the last one. !11113 (rickettm) +- Fix CI/CD status in case there are only allowed to failed jobs in the pipeline. !11166 +- Omit trailing / leading hyphens in CI_COMMIT_REF_SLUG variable to make it usable as a hostname. !11218 (Stefan Hanreich) +- Moved "Members in a project" menu entry and path locations. !11560 +- Additional Prometheus metrics support. !11712 +- Rename all reserved paths that could have been created. !11713 +- Move uploads from `uploads/system` to `uploads/-/system` to free up `system` as a group name. !11713 +- Fix offline runner detection. !11751 (Alessio Caiazza) +- Use authorize_update_pipeline_schedule in PipelineSchedulesController. !11846 +- Rollback project repo move if there is an error in Projects::TransferService. !11877 +- Help landing page customizations. !11878 (Robin Bobbitt) +- Fixes "sign in / Register" active state underline misalignment. !11890 (Frank Sierra) +- Honor the "Remember me" parameter for OAuth-based login. !11963 +- Instruct user to use personal access token for Git over HTTP. !11986 (Robin Bobbitt) +- Accept image for avatar in project API. !11988 (Ivan Chernov) +- Supplement Simplified Chinese translation of Project Page & Repository Page. !11994 (Huang Tao) +- Supplement Traditional Chinese in Hong Kong translation of Project Page & Repository Page. !11995 (Huang Tao) +- Make the revision on the `/help` page clickable. !12016 +- Display issue state in issue links section of merge request widget. !12021 +- Enable support for webpack code-splitting by dynamically setting publicPath at runtime. !12032 +- Replace PhantomJS with headless Chrome for karma test suite. !12036 +- Prevent description change notes when toggling tasks. !12057 (Jared Deckard <jared.deckard@gmail.com>) +- Update QA Dockerfile to lock Chrome browser version. !12071 +- Fix FIDO U2F for Opera browser. !12082 (Jakub Kramarz and Jonas Kalderstam) +- Supplement Bulgarian translation of Project Page & Repository Page. !12083 (Lyubomir Vasilev) +- Removes deleted_at and pending_delete occurrences in Project related queries. !12091 +- Provide hint to create a personal access token for Git over HTTP. !12105 (Robin Bobbitt) +- Display own user id in account settings page. !12141 (Riccardo Padovani) +- Accept image for avatar in user API. !12143 (Ivan Chernov) +- Disable fork button on project limit. !12145 (Ivan Chernov) +- Added "created_after" and "created_before" params to issuables. !12151 (Kyle Bishop @kybishop) +- Supplement Portuguese Brazil translation of Project Page & Repository Page. !12156 (Huang Tao) +- Add review apps to usage metrics. !12185 +- Adding French translations. !12200 (Erwan "Dremor" Georget) +- Ensures default user limits when external user is unchecked. !12218 +- Provide KUBECONFIG from KubernetesService for runners. !12223 +- Filter archived project in API v3 only if param present. !12245 (Ivan Chernov) +- Add explicit message when no runners on admin. !12266 (Takuya Noguchi) +- Split pipelines as internal and external in the usage data. !12277 +- Fix API Scoping. !12300 +- Remove registry image delete button if user cant delete it. !12317 (Ivan Chernov) +- Allow the feature flags to be enabled/disabled with more granularity. !12357 +- Allow to enable the performance bar per user or Feature group. !12362 +- Rename duplicated variables with the same key for projects. Add environment_scope column to variables and add unique constraint to make sure that no variables could be created with the same key within a project. !12363 +- Add variables to pipelines schedules. !12372 +- Add User#full_private_access? to check if user has access to all private groups & projects. !12373 +- Change milestone endpoint for groups. !12374 (Takuya Noguchi) +- Improve performance of the pipeline charts page. !12378 +- Add option to run Gitaly on a remote server. !12381 +- #20628 Enable implicit grant in GitLab as OAuth Provider. !12384 (Mateusz Pytel) +- Replace 'snippets/snippets.feature' spinach with rspec. !12385 (Alexander Randa @randaalex) +- Add Simplified Chinese translations of Commits Page. !12405 (Huang Tao) +- Add Traditional Chinese in HongKong translations of Commits Page. !12406 (Huang Tao) +- Add Traditional Chinese in Taiwan translations of Commits Page. !12407 (Huang Tao) +- Add Portuguese Brazil translations of Commits Page. !12408 (Huang Tao) +- Add French translations of Commits Page. !12409 (Huang Tao) +- Add Esperanto translations of Commits Page. !12410 (Huang Tao) +- Add Bulgarian translations of Commits Page. !12411 (Huang Tao) +- Remove bin/ci/upgrade.rb as not working all. !12414 (Takuya Noguchi) +- Store merge request ref_fetched status in the database. !12424 +- Replace 'dashboard/merge_requests' spinach with rspec. !12440 (Alexander Randa (@randaalex)) +- Add Esperanto translations for Cycle Analytics, Project, and Repository pages. !12442 (Huang Tao) +- Allow unauthenticated access to the /api/v4/users API. !12445 +- Drop GFM support for the title of Milestone/MergeRequest in template. !12451 (Takuya Noguchi) +- Replace 'dashboard/todos' spinach with rspec. !12453 (Alexander Randa (@randaalex)) +- Cache open issue and merge request counts for project tabs to speed up project pages. !12457 +- Introduce cache policies for CI jobs. !12483 +- Improve support for external issue references. !12485 +- Fix errors caused by attempts to report already blocked or deleted users. !12502 (Horacio Bertorello) +- Allow customize CI config path. !12509 (Keith Pope) +- Supplement Traditional Chinese in Taiwan translation of Project Page & Repository Page. !12514 (Huang Tao) +- Closes any open Autocomplete of the markdown editor when the form is closed. !12521 +- Inserts exact matches of name, username and email to the top of the search list. !12525 +- Use smaller min-width for dropdown-menu-nav only on mobile. !12528 (Takuya Noguchi) +- Hide archived project labels from group issue tracker. !12547 (Horacio Bertorello) +- Replace 'dashboard/new-project.feature' spinach with rspec. !12550 (Alexander Randa (@randaalex)) +- Remove group modal like remove project modal (requires typing + confirmation). !12569 (Diego Souza) +- Add Italian translation of Cycle Analytics Page & Project Page & Repository Page. !12578 (Huang Tao) +- Add Group secret variables. !12582 +- Update jobs page output to have a scrollable page. !12587 +- Add user projects API. !12596 (Ivan Chernov) +- Allow creation of files and directories with spaces through Web UI. !12608 +- Improve members view on mobile. !12619 +- Fixed the chart legend not being set correctly. !12628 +- Add Italian translations of Commits Page. !12645 (Huang Tao) +- Allow admins to disable all restricted visibility levels. !12649 +- Allow admins to retrieve user agent details for an issue or snippet. !12655 +- Update welcome page UX for new users. !12662 +- N+1 problems on milestone page. !12670 (Takuya Noguchi) +- Upgrade GitLab Workhorse to v2.3.0. !12676 +- Remove option to disable Gitaly. !12677 +- Improve the performance of the project list API. !12679 +- Add creation time filters to user search API for admins. !12682 +- Add Japanese translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12693 (Huang Tao) +- Undo adding the /reassign quick action. !12701 +- Fix dashboard labels dropdown. !12708 +- Username and password are no longer stripped from import url on mirror update. !12725 +- Add Russian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12743 (Huang Tao) +- Add Ukrainian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12744 (Huang Tao) +- Prevent bad data being added to application settings when Redis is unavailable. !12750 +- Do not show pipeline schedule button for non-member. !12757 (Takuya Noguchi) +- Return `is_admin` attribute in the GET /user endpoint for admins. !12811 +- Recover from renaming project that has container images. !12840 +- Exact matches of username and email are now on top of the user search. !12868 +- Use Ghost user for last_edited_by and merge_user when original user is deleted. !12933 +- Fix docker tag reference routing constraints. !12961 +- Optimize creation of commit API by using Repository#commit instead of Repository#commits. +- Speed up used languages calculation on charts page. +- Make loading new merge requests (those created after the 9.4 upgrade) faster. +- Ensure participants for issues, merge requests, etc. are calculated correctly when sending notifications. +- Handle nameless legacy jobs. +- Bump Faraday and dependent OAuth2 gem version to support no_proxy variable. +- Renders 404 if given project is not readable by the user on Todos dashboard. +- Render CI statuses with warnings in orange. +- Document the Delete Merged Branches functionality. +- Add wells to admin dashboard overview to fix spacing problems. +- Removes hover style for nodes that are either links or buttons in the pipeline graph. +- more visual contrast in pagination widget. +- Deprecate Healthcheck Access Token in favor of IP whitelist. +- Drop GFM support for issuable title on milestone for consistency and performance. (Takuya Noguchi) +- fix left & right padding on sidebar. +- Cleanup minor UX issues in the performance dashboard. +- Remove two columned layout from project member settings. +- Make font size of contextual sub menu items 14px. +- Fix vertical space in job details sidebar. +- Fix alignment of controls in mr issuable list. +- Add wip message to new navigation preference section. +- Add group members counting and plan related data on namespaces API. +- Fix spacing on runner buttons. +- Remove uploads/appearance symlink. A leftover from a previous migration. +- Change order of monospace fonts to fix bug on some linux distros. +- Limit commit & snippets comments width. +- Fixed dashboard milestone tabs not loading. +- Detect if file that appears to be text in the first 1024 bytes is actually binary afer loading all data. +- Fix inconsistent display of the "Browse files" button in the commit list. +- Implement diff viewers. +- Fix 'New merge request' button for users who don't have push access to canonical project. +- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths. +- Show group name instead of path on group page. +- Don't check if MailRoom is running on Omnibus. +- Limit OpenGraph image size to 64x64. +- Don't show auxiliary blob viewer for README when there is no wiki. +- Strip trailing whitespace in relative submodule URL. +- Update /target_branch slash command description to be more consistent. +- Remove unnecessary top padding on group MR index. +- Added printing_merge_requst_link_enabled to the API. (David Turner <dturner@twosigma.com>) +- Re-enable realtime for environments table. +- Create responsive mobile view for pipelines table. +- Adds realtime feature to job show view header and sidebar info. Updates UX. +- Use color inputs for broadcast messages. +- Center dropdown for mini graph. +- Users can subscribe to group labels on the group labels page. +- Add issuable-list class to shared mr/issue lists to fix new responsive layout design. +- Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor of "slash commands". +- Don't mark empty MRs as merged on push to the target branch. +- Improve issue rendering performance with lots of notes from other users. +- Fixed overflow on mobile screens for the slash commands. +- Fix an infinite loop when handling user-supplied regular expressions. +- Fixed sidebar not collapsing on merge requests in mobile screens. +- Speed up project removals by adding foreign keys with cascading deletes to various tables. +- Fix mobile view of files view buttons. +- Fixed dropdown filter input not focusing after transition. +- Fixed GFM references not being included when updating issues inline. +- Remove issues/merge requests drag n drop and sorting from milestone view. +- Add native group milestones. +- Fix API bug accepting wrong parameter to create merge request. +- Clean up UI of issuable lists and make more responsive. +- Improve the overall UX for the new monitoring dashboard. +- Fixed the y_label not setting correctly for each graph on the monitoring dashboard. +- Changed utilities imports from ~ to relative paths. +- Remove unused space in sidebar todo toggle when not signed in. +- Limit the width of the projects README text. +- Add a simple mode to merge request API. +- Make Project#ensure_repository force create a repo. +- Use uploads/system directory for personal snippets. +- Defer project destroys within a namespace in Groups::DestroyService#async_execute. +- Log rescued exceptions to Sentry. +- Remove remaining N+1 queries in merge requests API with emojis and labels. + +## 9.3.9 (2017-07-20) + +- Fix an infinite loop when handling user-supplied regular expressions. + ## 9.3.8 (2017-07-19) - Improve support for external issue references. !12485 @@ -270,6 +488,10 @@ entry. - Remove foreigh key on ci_trigger_schedules only if it exists. - Allow translation of Pipeline Schedules. +## 9.2.9 (2017-07-20) + +- Fix an infinite loop when handling user-supplied regular expressions. + ## 9.2.8 (2017-07-19) - Improve support for external issue references. !12485 @@ -521,6 +743,10 @@ entry. - Fix preemptive scroll bar on user activity calendar. - Pipeline chat notifications convert seconds to minutes and hours. +## 9.1.9 (2017-07-20) + +- Fix an infinite loop when handling user-supplied regular expressions. + ## 9.1.8 (2017-07-19) - Improve support for external issue references. !12485 @@ -840,6 +1066,10 @@ entry. - Only send chat notifications for the default branch. - Don't fill in the default kubernetes namespace. +## 9.0.12 (2017-07-20) + +- Fix an infinite loop when handling user-supplied regular expressions. + ## 9.0.11 (2017-07-19) - Renders 404 if given project is not readable by the user on Todos dashboard. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89e505709a3..12fb34b24be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._ - [Issue tracker guidelines](#issue-tracker-guidelines) - [Issue weight](#issue-weight) - [Regression issues](#regression-issues) - - [Technical debt](#technical-debt) + - [Technical and UX debt](#technical-and-ux-debt) - [Stewardship](#stewardship) - [Merge requests](#merge-requests) - [Merge request guidelines](#merge-request-guidelines) @@ -114,8 +114,8 @@ scheduling into milestones. Labelling is a task for everyone. Most issues will have labels for at least one of the following: - Type: ~"feature proposal", ~bug, ~customer, etc. -- Subject: ~wiki, ~"container registry", ~ldap, ~api, etc. -- Team: ~CI, ~Discussion, ~Edge, ~Frontend, ~Platform, etc. +- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc. +- Team: ~CI, ~Discussion, ~Edge, ~Platform, etc. - Priority: ~Deliverable, ~Stretch All labels, their meaning and priority are defined on the @@ -278,7 +278,7 @@ For feature proposals for EE, open an issue on the In order to help track the feature proposals, we have created a [`feature proposal`][fpl] label. For the time being, users that are not members of the project cannot add labels. You can instead ask one of the [core team] -members to add the label `feature proposal` to the issue or add the following +members to add the label ~"feature proposal" to the issue or add the following code snippet right after your description in a new line: `~"feature proposal"`. Please keep feature proposals as small and simple as possible, complex ones @@ -344,27 +344,29 @@ addressed. [8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127 [update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue -### Technical debt +### Technical and UX debt -In order to track things that can be improved in GitLab's codebase, we created -the ~"technical debt" label in [GitLab's issue tracker][ce-tracker]. +In order to track things that can be improved in GitLab's codebase, +we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker]. +For user experience improvements, we use the ~"UX debt" label. -This label should be added to issues that describe things that can be improved, -shortcuts that have been taken, code that needs refactoring, features that need -additional attention, and all other things that have been left behind due to -high velocity of development. +These labels should be added to issues that describe things that can be improved, +shortcuts that have been taken, features that need additional attention, and all +other things that have been left behind due to high velocity of development. +For example, code that needs refactoring should use the ~"technical debt" label, +user experience refinements should use the ~"UX debt" label. Everyone can create an issue, though you may need to ask for adding a specific label, if you do not have permissions to do it by yourself. Additional labels -can be combined with the `technical debt` label, to make it easier to schedule +can be combined with these labels, to make it easier to schedule the improvements for a release. -Issues tagged with the `technical debt` label have the same priority like issues +Issues tagged with these labels have the same priority like issues that describe a new feature to be introduced in GitLab, and should be scheduled for a release by the appropriate person. -Make sure to mention the merge request that the `technical debt` issue is -associated with in the description of the issue. +Make sure to mention the merge request that the ~"technical debt" issue or +~"UX debt" issue is associated with in the description of the issue. ### Stewardship diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 21574090598..d21d277be51 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.22.0 +0.25.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index c7cb1311a64..8a30e8f94a3 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -5.3.1 +5.4.0 @@ -15,7 +15,8 @@ gem 'default_value_for', '~> 3.0.0' gem 'mysql2', '~> 0.4.5', group: :mysql gem 'pg', '~> 0.18.2', group: :postgres -gem 'rugged', '~> 0.25.1.1' +gem 'rugged', '~> 0.26.0' +gem 'grape-route-helpers', '~> 2.0.0' gem 'faraday', '~> 0.12' @@ -57,10 +58,14 @@ gem 'validates_hostname', '~> 1.0.6' # Browser detection gem 'browser', '~> 2.2' +# GPG +gem 'gpgme' + # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master -gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' +gem 'gitlab_omniauth-ldap', '~> 2.0.3', require: 'omniauth-ldap' +gem 'net-ldap' # Git Wiki # Required manually in config/initializers/gollum.rb to control load order @@ -71,7 +76,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false gem 'github-linguist', '~> 4.7.0', require: 'linguist' # API -gem 'grape', '~> 0.19.0' +gem 'grape', '~> 0.19.2' gem 'grape-entity', '~> 0.6.0' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' @@ -164,7 +169,7 @@ gem 'rainbow', '~> 2.2' gem 'settingslogic', '~> 2.0.9' # Linear-time regex library for untrusted regular expressions -gem 're2', '~> 1.0.0' +gem 're2', '~> 1.1.1' # Misc @@ -284,7 +289,7 @@ group :metrics do gem 'influxdb', '~> 0.2', require: false # Prometheus - gem 'prometheus-client-mmap', '~>0.7.0.beta9' + gem 'prometheus-client-mmap', '~>0.7.0.beta11' gem 'raindrops', '~> 0.18' end @@ -353,7 +358,7 @@ group :development, :test do end group :test do - gem 'shoulda-matchers', '~> 2.8.0', require: false + gem 'shoulda-matchers', '~> 3.1.2', require: false gem 'email_spec', '~> 1.6.0' gem 'json-schema', '~> 2.6.2' gem 'webmock', '~> 2.3.2' @@ -386,7 +391,7 @@ gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' # Gitaly GRPC client -gem 'gitaly', '~> 0.17.0' +gem 'gitaly', '~> 0.21.0' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 0c4fc1fee0c..627750e2c1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -269,7 +269,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly (0.17.0) + gitaly (0.21.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -288,11 +288,11 @@ GEM mime-types (>= 1.16, < 3) posix-spawn (~> 0.3) gitlab-markup (1.5.1) - gitlab_omniauth-ldap (1.2.1) - net-ldap (~> 0.9) - omniauth (~> 1.0) - pyu-ruby-sasl (~> 0.0.3.1) - rubyntlm (~> 0.3) + gitlab_omniauth-ldap (2.0.3) + net-ldap (~> 0.16) + omniauth (~> 1.3) + pyu-ruby-sasl (>= 0.0.3.3, < 0.1) + rubyntlm (~> 0.5) globalid (0.3.7) activesupport (>= 4.1.0) gollum-grit_adapter (1.0.1) @@ -332,19 +332,25 @@ GEM multi_json (~> 1.11) os (~> 0.9) signet (~> 0.7) - grape (0.19.1) + gpgme (2.0.13) + mini_portile2 (~> 2.1) + grape (0.19.2) activesupport builder hashie (>= 2.1.0) multi_json (>= 1.3.2) multi_xml (>= 0.5.2) - mustermann-grape (~> 0.4.0) + mustermann-grape (~> 1.0.0) rack (>= 1.3.0) rack-accept virtus (>= 1.0.0) grape-entity (0.6.0) activesupport multi_json (>= 1.3.2) + grape-route-helpers (2.0.0) + activesupport + grape (~> 0.16, >= 0.16.0) + rake grpc (1.4.0) google-protobuf (~> 3.1) googleauth (~> 0.5.1) @@ -463,12 +469,11 @@ GEM multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) - mustermann (0.4.0) - tool (~> 0.2) - mustermann-grape (0.4.0) - mustermann (= 0.4.0) + mustermann (1.0.0) + mustermann-grape (1.0.0) + mustermann (~> 1.0.0) mysql2 (0.4.5) - net-ldap (0.12.1) + net-ldap (0.16.0) netrc (0.11.0) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) @@ -592,7 +597,7 @@ GEM premailer-rails (1.9.7) actionmailer (>= 3, < 6) premailer (~> 1.7, >= 1.7.9) - prometheus-client-mmap (0.7.0.beta9) + prometheus-client-mmap (0.7.0.beta11) mmap2 (~> 2.2, >= 2.2.7) pry (0.10.4) coderay (~> 1.1.0) @@ -657,7 +662,7 @@ GEM debugger-ruby_core_source (~> 1.3) rdoc (4.2.2) json (~> 1.4) - re2 (1.0.0) + re2 (1.1.1) recaptcha (3.0.0) json recursive-open-struct (1.0.0) @@ -741,12 +746,12 @@ GEM nokogiri (>= 1.5.10) ruby_parser (3.9.0) sexp_processor (~> 4.1) - rubyntlm (0.5.2) + rubyntlm (0.6.2) rubypants (0.2.0) rubyzip (1.2.1) rufus-scheduler (3.4.0) et-orbi (~> 1.0) - rugged (0.25.1.1) + rugged (0.26.0) safe_yaml (1.0.4) sanitize (2.1.0) nokogiri (>= 1.4.4) @@ -775,8 +780,8 @@ GEM sexp_processor (4.9.0) sham_rack (1.3.6) rack - shoulda-matchers (2.8.0) - activesupport (>= 3.0.0) + shoulda-matchers (3.1.2) + activesupport (>= 4.0.0) sidekiq (5.0.4) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) @@ -850,7 +855,6 @@ GEM timfel-krb5-auth (0.8.3) toml-rb (0.3.15) citrus (~> 3.0, > 3.0) - tool (0.2.3) truncato (0.7.8) htmlentities (~> 4.3.1) nokogiri (~> 1.6.1) @@ -972,17 +976,19 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly (~> 0.17.0) + gitaly (~> 0.21.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.5.1) - gitlab_omniauth-ldap (~> 1.2.1) + gitlab_omniauth-ldap (~> 2.0.3) gollum-lib (~> 4.2) gollum-rugged_adapter (~> 0.4.4) gon (~> 6.1.0) google-api-client (~> 0.8.6) - grape (~> 0.19.0) + gpgme + grape (~> 0.19.2) grape-entity (~> 0.6.0) + grape-route-helpers (~> 2.0.0) haml_lint (~> 0.21.0) hamlit (~> 2.6.1) hashie-forbidden_attributes @@ -1010,6 +1016,7 @@ DEPENDENCIES minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.4.5) + net-ldap nokogiri (~> 1.6.7, >= 1.6.7.2) oauth2 (~> 1.4) octokit (~> 4.6.2) @@ -1043,7 +1050,7 @@ DEPENDENCIES pg (~> 0.18.2) poltergeist (~> 1.9.0) premailer-rails (~> 1.9.7) - prometheus-client-mmap (~> 0.7.0.beta9) + prometheus-client-mmap (~> 0.7.0.beta11) pry-byebug (~> 3.4.1) pry-rails (~> 0.3.4) rack-attack (~> 4.4.1) @@ -1057,7 +1064,7 @@ DEPENDENCIES raindrops (~> 0.18) rblineprof (~> 0.3.6) rdoc (~> 4.2) - re2 (~> 1.0.0) + re2 (~> 1.1.1) recaptcha (~> 3.0) redcarpet (~> 3.4) redis (~> 3.2) @@ -1077,7 +1084,7 @@ DEPENDENCIES ruby-prof (~> 0.16.2) ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) - rugged (~> 0.25.1.1) + rugged (~> 0.26.0) sanitize (~> 2.0) sass-rails (~> 5.0.6) scss_lint (~> 0.54.0) @@ -1086,7 +1093,7 @@ DEPENDENCIES sentry-raven (~> 2.5.3) settingslogic (~> 2.0.9) sham_rack (~> 1.3.6) - shoulda-matchers (~> 2.8.0) + shoulda-matchers (~> 3.1.2) sidekiq (~> 5.0) sidekiq-cron (~> 0.6.0) sidekiq-limit_fetch (~> 3.4) @@ -1 +1 @@ -9.4.0-pre +9.5.0-pre diff --git a/app/assets/images/new_repo.png b/app/assets/images/new_repo.png Binary files differnew file mode 100644 index 00000000000..ed3af06ab1d --- /dev/null +++ b/app/assets/images/new_repo.png diff --git a/app/assets/images/old_repo.png b/app/assets/images/old_repo.png Binary files differnew file mode 100644 index 00000000000..c3c3b791ad9 --- /dev/null +++ b/app/assets/images/old_repo.png diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index c77e4dba098..76b724e1bcb 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -96,15 +96,12 @@ const Api = { .done(projects => callback(projects)); }, - commitMultiple(id, data, callback, token) { + commitMultiple(id, data, callback) { // 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', id); return $.ajax({ url, - headers: { - PRIVATE_TOKEN: token, - }, type: 'POST', contentType: 'application/json; charset=utf-8', data: JSON.stringify(data), diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js index 36bfe457be9..510bedbf641 100644 --- a/app/assets/javascripts/commons/bootstrap.js +++ b/app/assets/javascripts/commons/bootstrap.js @@ -8,6 +8,7 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/modal'; import 'bootstrap-sass/assets/javascripts/bootstrap/tab'; import 'bootstrap-sass/assets/javascripts/bootstrap/transition'; import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; +import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; // custom jQuery functions $.fn.extend({ diff --git a/app/assets/javascripts/copy_as_gfm.js b/app/assets/javascripts/copy_as_gfm.js index ba9d9a3e1f7..54257531284 100644 --- a/app/assets/javascripts/copy_as_gfm.js +++ b/app/assets/javascripts/copy_as_gfm.js @@ -1,6 +1,7 @@ /* 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 './lib/utils/common_utils'; +import { placeholderImage } from './lazy_loader'; const gfmRules = { // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert @@ -56,6 +57,11 @@ const gfmRules = { return text; }, }, + ImageLazyLoadFilter: { + 'img'(el, text) { + return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`; + }, + }, VideoLinkFilter: { '.video-container'(el) { const videoEl = el.querySelector('video'); @@ -163,7 +169,9 @@ const gfmRules = { return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n'); }, 'img'(el) { - return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`; + const imageSrc = el.src; + const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : (el.dataset.src || ''); + return `![${el.getAttribute('alt')}](${imageUrl})`; }, 'a.anchor'(el, text) { // Don't render a Markdown link for the anchor link inside a heading diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 58bd223754f..799b9643212 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -20,6 +20,8 @@ /* global NamespaceSelects */ /* global Project */ /* global ProjectAvatar */ +/* global MergeRequest */ +/* global Compare */ /* global CompareAutocomplete */ /* global ProjectNew */ /* global ProjectShow */ @@ -41,7 +43,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; import Landing from './landing'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; -import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; import Pipelines from './pipelines'; import BlobViewer from './blob/viewer/index'; @@ -53,6 +54,7 @@ import ShortcutsBlob from './shortcuts_blob'; import SigninTabsMemoizer from './signin_tabs_memoizer'; import Star from './star'; import Todos from './todos'; +import TreeView from './tree'; import UsagePing from './usage_ping'; import UsernameValidator from './username_validator'; import VersionCheckImage from './version_check_image'; @@ -63,6 +65,11 @@ import ScrollHelper from './helpers/scroll_helper'; import initExperimentalFlags from './experimental_flags'; import OAuthRememberMe from './oauth_remember_me'; import PerformanceBar from './performance_bar'; +import initNotes from './init_notes'; +import initLegacyFilters from './init_legacy_filters'; +import initIssuableSidebar from './init_issuable_sidebar'; +import GpgBadges from './gpg_badges'; +import FeatureHelper from './helpers/feature_helper'; (function() { var Dispatcher; @@ -160,6 +167,8 @@ import PerformanceBar from './performance_bar'; new Issue(); shortcut_handler = new ShortcutsIssuable(); new ZenMode(); + initIssuableSidebar(); + initNotes(); break; case 'dashboard:milestones:index': new ProjectSelect(); @@ -170,10 +179,12 @@ import PerformanceBar from './performance_bar'; new Milestone(); new Sidebar(); break; + case 'dashboard:issues': + case 'dashboard:merge_requests': case 'groups:issues': case 'groups:merge_requests': - new UsersSelect(); new ProjectSelect(); + initLegacyFilters(); break; case 'dashboard:todos:index': new Todos(); @@ -225,6 +236,19 @@ import PerformanceBar from './performance_bar'; new gl.IssuableTemplateSelectors(); break; case 'projects:merge_requests:creations:new': + const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); + if (mrNewCompareNode) { + new Compare({ + targetProjectUrl: mrNewCompareNode.dataset.targetProjectUrl, + sourceBranchUrl: mrNewCompareNode.dataset.sourceBranchUrl, + targetBranchUrl: mrNewCompareNode.dataset.targetBranchUrl, + }); + } else { + const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit'); + new MergeRequest({ + action: mrNewSubmitNode.dataset.mrSubmitAction, + }); + } case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:edit': new gl.Diff(); @@ -241,6 +265,9 @@ import PerformanceBar from './performance_bar'; new gl.GLForm($('.tag-form'), true); new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs); break; + case 'projects:snippets:show': + initNotes(); + break; case 'projects:snippets:new': case 'projects:snippets:edit': case 'projects:snippets:create': @@ -261,15 +288,18 @@ import PerformanceBar from './performance_bar'; new gl.Diff(); shortcut_handler = new ShortcutsIssuable(true); new ZenMode(); + + initIssuableSidebar(); + initNotes(); + + const mrShowNode = document.querySelector('.merge-request'); + window.mergeRequest = new MergeRequest({ + action: mrShowNode.dataset.mrAction, + }); break; case 'dashboard:activity': new gl.Activities(); break; - case 'dashboard:issues': - case 'dashboard:merge_requests': - new ProjectSelect(); - new UsersSelect(); - break; case 'projects:commit:show': new Commit(); new gl.Diff(); @@ -278,6 +308,7 @@ import PerformanceBar from './performance_bar'; new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); + initNotes(); break; case 'projects:commit:pipelines': new MiniPipelineGraph({ @@ -285,12 +316,20 @@ import PerformanceBar from './performance_bar'; }).bindEvents(); break; case 'projects:commits:show': + shortcut_handler = new ShortcutsNavigation(); + GpgBadges.fetch(); + break; case 'projects:activity': shortcut_handler = new ShortcutsNavigation(); break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); + + if (FeatureHelper.isNewRepo()) break; + + if ($('#tree-slider').length) new TreeView(); + if ($('#blob-viewer').length) new BlobViewer(); break; case 'projects:edit': setupProjectEdit(); @@ -344,9 +383,22 @@ import PerformanceBar from './performance_bar'; case 'admin:groups:edit': new GroupAvatar(); break; + case 'projects:tree:show': + shortcut_handler = new ShortcutsNavigation(); + + if (FeatureHelper.isNewRepo()) break; + + new TreeView(); + new BlobViewer(); + break; case 'projects:find_file:show': shortcut_handler = true; break; + case 'projects:blob:show': + if (FeatureHelper.isNewRepo()) break; + new BlobViewer(); + initBlob(); + break; case 'projects:blame:show': initBlob(); break; @@ -356,10 +408,20 @@ import PerformanceBar from './performance_bar'; case 'projects:labels:edit': new Labels(); break; + case 'groups:labels:index': case 'projects:labels:index': if ($('.prioritized-labels').length) { new gl.LabelManager(); } + $('.label-subscription').each((i, el) => { + const $el = $(el); + + if ($el.find('.dropdown-group-label').length) { + new gl.GroupLabelSubscription($el); + } else { + new gl.ProjectLabelSubscription($el); + } + }); break; case 'projects:network:show': // Ensure we don't create a particular shortcut handler here. This is @@ -384,12 +446,6 @@ import PerformanceBar from './performance_bar'; new Search(); break; case 'projects:settings:repository:show': - // Initialize Protected Branch Settings - new gl.ProtectedBranchCreate(); - new gl.ProtectedBranchEditList(); - // Initialize Protected Tag Settings - new ProtectedTagCreate(); - new ProtectedTagEditList(); // Initialize expandable settings panels initSettingsPanels(); break; @@ -410,10 +466,15 @@ import PerformanceBar from './performance_bar'; case 'snippets:show': new LineHighlighter(); new BlobViewer(); + initNotes(); break; case 'import:fogbugz:new_user_map': new UsersSelect(); break; + case 'profiles:personal_access_tokens:index': + case 'admin:impersonation_tokens:index': + new gl.DueDateSelectors(); + break; } switch (path.first()) { case 'sessions': @@ -510,6 +571,13 @@ import PerformanceBar from './performance_bar'; case 'protected_branches': shortcut_handler = new ShortcutsNavigation(); } + break; + case 'users': + const action = path[1]; + import(/* webpackChunkName: 'user_profile' */ './users') + .then(user => user.default(action)) + .catch(() => {}); + break; } // If we haven't installed a custom shortcut handler, install the default one if (!shortcut_handler) { diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 5838b1bdbb7..a81389ab088 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter'; import './filtered_search_dropdown'; class DropdownHint extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { input, tokenKeys } = options; + super(options); this.config = { Filter: { template: 'hint', diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js index 34a9e34070c..2615d626c4c 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js @@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter'; import './filtered_search_dropdown'; class DropdownNonUser extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { input, endpoint, symbol } = options; + super(options); this.symbol = symbol; this.config = { Ajax: { diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index 19fed771197..7246ccbb281 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -5,8 +5,9 @@ import './filtered_search_dropdown'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; class DropdownUser extends gl.FilteredSearchDropdown { - constructor(droplab, dropdown, input, tokenKeys, filter) { - super(droplab, dropdown, input, filter); + constructor(options = {}) { + const { tokenKeys } = options; + super(options); this.config = { AjaxFilter: { endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`, diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js index 4209ca0d6e2..9e9a9ef74be 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js @@ -1,7 +1,7 @@ const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; class FilteredSearchDropdown { - constructor(droplab, dropdown, input, filter) { + constructor({ droplab, dropdown, input, filter }) { this.droplab = droplab; this.hookId = input && input.id; this.input = input; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index 6bc6bc43f51..61cef435209 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -42,13 +42,19 @@ class FilteredSearchDropdownManager { milestone: { reference: null, gl: 'DropdownNonUser', - extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'], + extraArguments: { + endpoint: `${this.baseEndpoint}/milestones.json`, + symbol: '%', + }, element: this.container.querySelector('#js-dropdown-milestone'), }, label: { reference: null, gl: 'DropdownNonUser', - extraArguments: [`${this.baseEndpoint}/labels.json`, '~'], + extraArguments: { + endpoint: `${this.baseEndpoint}/labels.json`, + symbol: '~', + }, element: this.container.querySelector('#js-dropdown-label'), }, hint: { @@ -97,13 +103,19 @@ class FilteredSearchDropdownManager { let forceShowList = false; if (!mappingKey.reference) { - const dl = this.droplab; - const defaultArguments = - [null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key]; - const glArguments = defaultArguments.concat(mappingKey.extraArguments || []); + const defaultArguments = { + droplab: this.droplab, + dropdown: element, + input: this.filteredSearchInput, + tokenKeys: this.filteredSearchTokenKeys, + filter: key, + }; + const extraArguments = mappingKey.extraArguments || {}; + const glArguments = Object.assign({}, defaultArguments, extraArguments); // Passing glArguments to `new gl[glClass](<arguments>)` - mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))(); + mappingKey.reference = + new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))(); } if (firstLoad) { diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js new file mode 100644 index 00000000000..1c379e9bb67 --- /dev/null +++ b/app/assets/javascripts/gpg_badges.js @@ -0,0 +1,15 @@ +export default class GpgBadges { + static fetch() { + const form = $('.commits-search-form'); + + $.get({ + url: form.data('signatures-path'), + data: form.serialize(), + }).done((response) => { + const badges = $('.js-loading-gpg-badge'); + response.signatures.forEach((signature) => { + badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); + }); + }); + } +} diff --git a/app/assets/javascripts/helpers/feature_helper.js b/app/assets/javascripts/helpers/feature_helper.js new file mode 100644 index 00000000000..469b65ec4d8 --- /dev/null +++ b/app/assets/javascripts/helpers/feature_helper.js @@ -0,0 +1,11 @@ +import Cookies from 'js-cookie'; + +function isNewRepo() { + return Cookies.get('new_repo') === 'true'; +} + +const FeatureHelper = { + isNewRepo, +}; + +export default FeatureHelper; diff --git a/app/assets/javascripts/how_to_merge.js b/app/assets/javascripts/how_to_merge.js new file mode 100644 index 00000000000..19f4a946f73 --- /dev/null +++ b/app/assets/javascripts/how_to_merge.js @@ -0,0 +1,12 @@ +document.addEventListener('DOMContentLoaded', () => { + const modal = $('#modal_merge_info').modal({ + modal: true, + show: false, + }); + $('.how_to_merge_link').on('click', () => { + modal.show(); + }); + $('.modal-header .close').on('click', () => { + modal.hide(); + }); +}); diff --git a/app/assets/javascripts/init_issuable_sidebar.js b/app/assets/javascripts/init_issuable_sidebar.js new file mode 100644 index 00000000000..29e3d2ea94e --- /dev/null +++ b/app/assets/javascripts/init_issuable_sidebar.js @@ -0,0 +1,18 @@ +/* eslint-disable no-new */ +/* global MilestoneSelect */ +/* global LabelsSelect */ +/* global IssuableContext */ +/* global Sidebar */ + +export default () => { + const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); + + new MilestoneSelect({ + full_path: sidebarOptions.fullPath, + }); + new LabelsSelect(); + new IssuableContext(sidebarOptions.currentUser); + gl.Subscription.bindAll('.subscription'); + new gl.DueDateSelectors(); + window.sidebar = new Sidebar(); +}; diff --git a/app/assets/javascripts/init_legacy_filters.js b/app/assets/javascripts/init_legacy_filters.js new file mode 100644 index 00000000000..1211c2c802c --- /dev/null +++ b/app/assets/javascripts/init_legacy_filters.js @@ -0,0 +1,15 @@ +/* eslint-disable no-new */ +/* global LabelsSelect */ +/* global MilestoneSelect */ +/* global IssueStatusSelect */ +/* global SubscriptionSelect */ + +import UsersSelect from './users_select'; + +export default () => { + new UsersSelect(); + new LabelsSelect(); + new MilestoneSelect(); + new IssueStatusSelect(); + new SubscriptionSelect(); +}; diff --git a/app/assets/javascripts/init_notes.js b/app/assets/javascripts/init_notes.js new file mode 100644 index 00000000000..3a8b4360cb6 --- /dev/null +++ b/app/assets/javascripts/init_notes.js @@ -0,0 +1,14 @@ +/* global Notes */ + +export default () => { + const dataEl = document.querySelector('.js-notes-data'); + const { + notesUrl, + notesIds, + now, + diffView, + autocomplete, + } = JSON.parse(dataEl.innerHTML); + + window.notes = new Notes(notesUrl, notesIds, now, diffView, autocomplete); +}; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index ddd3a6aab99..cf1e6a14725 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -102,7 +102,7 @@ export default class IntegrationSettingsForm { }) .done((res) => { if (res.error) { - new Flash(res.message, null, null, { + new Flash(`${res.message} ${res.service_response}`, null, null, { title: 'Save anyway', clickHandler: (e) => { e.preventDefault(); diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index 4f376599ba9..d314f3c4d43 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar { this.toggleCheckboxDisplay(enable); if (enable) { + this.initAffix(); SidebarHeightManager.init(); } } + initAffix() { + if (!this.$sidebar.hasClass('affix-top')) { + const offsetTop = $('.scrolling-tabs-container').outerHeight() + $('.sub-nav-scroll').outerHeight(); + + this.$sidebar.affix({ + offset: { + top: offsetTop, + }, + }); + } + } + updateSelectedIssuableIds() { this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); } diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index a4d7bf096ef..26392db4b5b 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -4,6 +4,8 @@ import Cookies from 'js-cookie'; import UsersSelect from './users_select'; +const PARTICIPANTS_ROW_COUNT = 7; + (function() { this.IssuableContext = (function() { function IssuableContext(currentUser) { @@ -50,11 +52,9 @@ import UsersSelect from './users_select'; } IssuableContext.prototype.initParticipants = function() { - var _this; - _this = this; $(document).on("click", ".js-participants-more", this.toggleHiddenParticipants); return $(".js-participants-author").each(function(i) { - if (i >= _this.PARTICIPANTS_ROW_COUNT) { + if (i >= PARTICIPANTS_ROW_COUNT) { return $(this).addClass("js-participants-hidden").hide(); } }); diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js new file mode 100644 index 00000000000..3d64b121fa7 --- /dev/null +++ b/app/assets/javascripts/lazy_loader.js @@ -0,0 +1,76 @@ +/* eslint-disable one-export, one-var, one-var-declaration-per-line */ + +import _ from 'underscore'; + +export const placeholderImage = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; +const SCROLL_THRESHOLD = 300; + +export default class LazyLoader { + constructor(options = {}) { + this.lazyImages = []; + this.observerNode = options.observerNode || '#content-body'; + + const throttledScrollCheck = _.throttle(() => this.scrollCheck(), 300); + const debouncedElementsInView = _.debounce(() => this.checkElementsInView(), 300); + + window.addEventListener('scroll', throttledScrollCheck); + window.addEventListener('resize', debouncedElementsInView); + + const scrollContainer = options.scrollContainer || window; + scrollContainer.addEventListener('load', () => this.loadCheck()); + } + searchLazyImages() { + this.lazyImages = [].slice.call(document.querySelectorAll('.lazy')); + this.checkElementsInView(); + } + startContentObserver() { + const contentNode = document.querySelector(this.observerNode) || document.querySelector('body'); + + if (contentNode) { + const observer = new MutationObserver(() => this.searchLazyImages()); + + observer.observe(contentNode, { + childList: true, + subtree: true, + }); + } + } + loadCheck() { + this.searchLazyImages(); + this.startContentObserver(); + } + scrollCheck() { + requestAnimationFrame(() => this.checkElementsInView()); + } + checkElementsInView() { + const scrollTop = pageYOffset; + const visHeight = scrollTop + innerHeight + SCROLL_THRESHOLD; + let imgBoundRect, imgTop, imgBound; + + // Loading Images which are in the current viewport or close to them + this.lazyImages = this.lazyImages.filter((selectedImage) => { + if (selectedImage.getAttribute('data-src')) { + imgBoundRect = selectedImage.getBoundingClientRect(); + + imgTop = scrollTop + imgBoundRect.top; + imgBound = imgTop + imgBoundRect.height; + + if (scrollTop < imgBound && visHeight > imgTop) { + LazyLoader.loadImage(selectedImage); + return false; + } + + return true; + } + return false; + }); + } + static loadImage(img) { + if (img.getAttribute('data-src')) { + img.setAttribute('src', img.getAttribute('data-src')); + img.removeAttribute('data-src'); + img.classList.remove('lazy'); + img.classList.add('js-lazy-loaded'); + } + } +} diff --git a/app/assets/javascripts/locale/bg/app.js b/app/assets/javascripts/locale/bg/app.js deleted file mode 100644 index 1991d5b1407..00000000000 --- a/app/assets/javascripts/locale/bg/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['bg'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-05 09:40-0400","Last-Translator":"Lyubomir Vasilev <lyubomirv@abv.bg>","Language-Team":"Bulgarian","Language":"bg","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=2; plural=(n != 1)","lang":"bg","domain":"app","plural_forms":"nplurals=2; plural=(n != 1)"},"ByAuthor|by":["от"],"Commit":["Подаване","ПодаваниÑ"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Ðнализът на циклите дава общ поглед върху това колко време е нужно на една Ð¸Ð´ÐµÑ Ð´Ð° Ñе превърне в завършена функционалноÑÑ‚ в проекта."],"CycleAnalyticsStage|Code":["Програмиране"],"CycleAnalyticsStage|Issue":["Проблем"],"CycleAnalyticsStage|Plan":["Планиране"],"CycleAnalyticsStage|Production":["Издаване"],"CycleAnalyticsStage|Review":["Преглед и одобрение"],"CycleAnalyticsStage|Staging":["Подготовка за издаване"],"CycleAnalyticsStage|Test":["ТеÑтване"],"Deploy":["ВнедрÑване","ВнедрÑваниÑ"],"FirstPushedBy|First":["Първо"],"FirstPushedBy|pushed by":["изпращане на промени от"],"From issue creation until deploy to production":["От Ñъздаването на проблема до внедрÑването в крайната верÑиÑ"],"From merge request merge until deploy to production":["От прилагането на заÑвката за Ñливане до внедрÑването в крайната верÑиÑ"],"Introducing Cycle Analytics":["ПредÑтавÑме Ви анализът на циклите"],"Last %d day":["ПоÑÐ»ÐµÐ´Ð½Ð¸Ñ %d ден","ПоÑледните %d дни"],"Limited to showing %d event at most":["Ограничено до показване на поÑледното %d Ñъбитие","Ограничено до показване на поÑледните %d ÑъбитиÑ"],"Median":["Медиана"],"New Issue":["Ðов проблем","Ðови проблема"],"Not available":["Ðе е налично"],"Not enough data":["ÐÑма доÑтатъчно данни"],"OpenedNDaysAgo|Opened":["Отворен"],"Pipeline Health":["СъÑтоÑние"],"ProjectLifecycle|Stage":["Етап"],"Read more":["Прочетете повече"],"Related Commits":["Свързани подаваниÑ"],"Related Deployed Jobs":["Свързани задачи за внедрÑване"],"Related Issues":["Свързани проблеми"],"Related Jobs":["Свързани задачи"],"Related Merge Requests":["Свързани заÑвки за Ñливане"],"Related Merged Requests":["Свързани приложени заÑвки за Ñливане"],"Showing %d event":["Показване на %d Ñъбитие","Показване на %d ÑъбитиÑ"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["Етапът на програмиране показва времето от първото подаване до Ñъздаването на заÑвката за Ñливане. Данните ще бъдат добавени тук автоматично Ñлед като бъде Ñъздадена първата заÑвка за Ñливане."],"The collection of events added to the data gathered for that stage.":["СъвкупноÑтта от ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð¸ към данните Ñъбрани за този етап."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Етапът на проблемите показва колко е времето от Ñъздаването на проблем до определÑнето на целеви етап на проекта за него, или до добавÑнето му в ÑпиÑък на дъÑката за проблеми. Започнете да добавÑте проблеми, за да видите данните за този етап."],"The phase of the development lifecycle.":["Етапът от цикъла на разработка"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Етапът на планиране показва колко е времето от преходната Ñтъпка до изпращането на първото подаване. Това време ще бъде добавено автоматично Ñлед като изпратите първото Ñи подаване."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Етапът на издаване показва общото време, което е нужно от Ñъздаването на проблем до внедрÑването на кода в крайната верÑиÑ."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Етапът на преглед и одобрение показва времето от Ñъздаването на заÑвката за Ñливане до прилагането Ñ. Данните ще бъдат добавени автоматично Ñлед като приложите първата Ñи заÑвка за Ñливане."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Етапът на подготовка за издаване показва времето между прилагането на заÑвката за Ñливане и внедрÑването на кода в Ñредата на работещата крайна верÑиÑ. Данните ще бъдат добавени автоматично Ñлед като направите първото Ñи внедрÑване в крайната верÑиÑ."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Етапът на теÑтване показва времето, което е нужно на „Gitlab CI“ да изпълни вÑички задачи за Ñвързаната заÑвка за Ñливане. Данните ще бъдат добавени автоматично Ñлед като приключи изпълнените на първата Ви такава задача."],"The time taken by each data entry gathered by that stage.":["Времето, което отнема вÑеки Ð·Ð°Ð¿Ð¸Ñ Ð¾Ñ‚ данни за ÑÑŠÐ¾Ñ‚Ð²ÐµÑ‚Ð½Ð¸Ñ ÐµÑ‚Ð°Ð¿."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["СтойноÑтта, коÑто Ñе намира в Ñредата на поÑледователноÑтта от наблюдавани данни. Ðапример: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е (5+7)/2 = 6."],"Time before an issue gets scheduled":["Време преди един проблем да бъде планиран за работа"],"Time before an issue starts implementation":["Време преди работата по проблем да започне"],"Time between merge request creation and merge/close":["Време между Ñъздаване на заÑвка за Ñливане и прилагането/отхвърлÑнето Ñ"],"Time until first merge request":["Време преди първата заÑвка за Ñливане"],"Time|hr":["чаÑ","чаÑа"],"Time|min":["мин","мин"],"Time|s":["Ñек"],"Total Time":["Общо време"],"Total test time for all commits/merges":["Общо време за теÑтване на вÑички подаваниÑ/ÑливаниÑ"],"Want to see the data? Please ask an administrator for access.":["ИÑкате ли да видите данните? Помолете админиÑтратор за доÑтъп."],"We don't have enough data to show this stage.":["ÐÑма доÑтатъчно данни за този етап."],"You have reached your project limit":[""],"You need permission.":["Ðуждаете Ñе от разрешение."],"day":["ден","дни"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/de/app.js b/app/assets/javascripts/locale/de/app.js deleted file mode 100644 index 820b1db1879..00000000000 --- a/app/assets/javascripts/locale/de/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['de'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-09 13:44+0200","Language-Team":"German","Language":"de","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"","X-Generator":"Poedit 2.0.1","lang":"de","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["Von"],"Cancel":[""],"Commit":["Commit","Commits"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics liefern einen Überblick darüber, wie viel Zeit in Ihrem Projekt von einer Idee bis zum Produktivdeployment vergeht."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Issue"],"CycleAnalyticsStage|Plan":["Planung"],"CycleAnalyticsStage|Production":["Produktiv"],"CycleAnalyticsStage|Review":["Review"],"CycleAnalyticsStage|Staging":["Staging"],"CycleAnalyticsStage|Test":["Test"],"Delete":[""],"Deploy":["Deployment","Deployments"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["Erster"],"FirstPushedBy|pushed by":["gepusht von"],"From issue creation until deploy to production":["Vom Anlegen des Issues bis zum Produktivdeployment"],"From merge request merge until deploy to production":["Vom Merge Request bis zum Produktivdeployment"],"Interval Pattern":[""],"Introducing Cycle Analytics":["Was sind Cycle Analytics?"],"Last %d day":["Letzter %d Tag","Letzten %d Tage"],"Last Pipeline":[""],"Limited to showing %d event at most":["Eingeschränkt auf maximal %d Ereignis","Eingeschränkt auf maximal %d Ereignisse"],"Median":["Median"],"New Issue":["Neues Issue","Neue Issues"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["Nicht verfügbar"],"Not enough data":["Nicht genügend Daten"],"OpenedNDaysAgo|Opened":["Erstellt"],"Owner":[""],"Pipeline Health":["Pipeline Kennzahlen"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["Phase"],"Read more":["Mehr"],"Related Commits":["Zugehörige Commits"],"Related Deployed Jobs":["Zugehörige Deploymentjobs"],"Related Issues":["Zugehörige Issues"],"Related Jobs":["Zugehörige Jobs"],"Related Merge Requests":["Zugehörige Merge Requests"],"Related Merged Requests":["Zugehörige abgeschlossene Merge Requests"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["Zeige %d Ereignis","Zeige %d Ereignisse"],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["Die Code-Phase stellt die Zeit vom ersten Commit bis zum Erstellen eines Merge Requests dar. Sobald Sie Ihren ersten Merge Request anlegen, werden dessen Daten automatisch ergänzt."],"The collection of events added to the data gathered for that stage.":["Ereignisse, die für diese Phase ausgewertet wurden."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Die Issue-Phase stellt die Zeit vom Anlegen eines Issues bis zum Zuweisen eines Meilensteins oder Hinzufügen zum Issue Board dar. Erstellen Sie einen Issue, damit dessen Daten hier erscheinen."],"The phase of the development lifecycle.":["Die Phase im Entwicklungsprozess."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Pushen des ersten Commits dar. Sobald Sie den ersten Commit pushen, werden dessen Daten hier erscheinen."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Die Produktiv-Phase stellt die Gesamtzeit vom Anlegen eines Issues bis zum Deployment auf dem Produktivsystem dar. Sobald Sie den vollständigen Entwicklungszyklus von einer Idee bis zum Produktivdeployment durchlaufen haben, erscheinen die zugehörigen Daten hier."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Die Review-Phase stellt die Zeit vom Anlegen eines Merge Requests bis zum Mergen dar. Sobald Sie Ihren ersten Merge Request abschließen, werden dessen Daten hier automatisch angezeigt."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Die Staging-Phase stellt die Zeit zwischen Mergen eines Merge Requests und dem Produktivdeployment dar. Sobald Sie das erste Produktivdeployment durchgeführt haben, werden dessen Daten hier automatisch angezeigt."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Die Test-Phase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von Merge Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."],"The time taken by each data entry gathered by that stage.":["Zeit die für das jeweilige Ereignis in der Phase ermittelt wurde."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."],"Time before an issue gets scheduled":["Zeit bis ein Issue geplant wird"],"Time before an issue starts implementation":["Zeit bis die Implementierung für ein Issue beginnt"],"Time between merge request creation and merge/close":["Zeit zwischen Anlegen und Mergen/Schließen eines Merge Requests"],"Time until first merge request":["Zeit bis zum ersten Merge Request"],"Time|hr":["h","h"],"Time|min":["min","min"],"Time|s":["s"],"Total Time":["Gesamtzeit"],"Total test time for all commits/merges":["Gesamte Testlaufzeit für alle Commits/Merges"],"Want to see the data? Please ask an administrator for access.":["Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Administrator."],"We don't have enough data to show this stage.":["Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."],"You have reached your project limit":[""],"You need permission.":["Sie benötigen Zugriffsrechte."],"day":["Tag","Tage"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/en/app.js b/app/assets/javascripts/locale/en/app.js deleted file mode 100644 index 490e8e8cd3f..00000000000 --- a/app/assets/javascripts/locale/en/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":[""],"About auto deploy":[""],"Active":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Are you sure you want to delete this pipeline schedule?":[""],"Attach a file by drag & drop or %{upload_link}":[""],"Branch":["",""],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":[""],"Branches":[""],"Browse files":[""],"ByAuthor|by":[""],"CI configuration":[""],"Cancel":[""],"ChangeTypeActionLabel|Pick into branch":[""],"ChangeTypeActionLabel|Revert in branch":[""],"ChangeTypeAction|Cherry-pick":[""],"ChangeTypeAction|Revert":[""],"Changelog":[""],"Charts":[""],"Cherry-pick this commit":[""],"Cherry-pick this merge request":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["",""],"Commit message":[""],"CommitBoxTitle|Commit":[""],"CommitMessage|Add %{file_name}":[""],"Commits":[""],"Commits|History":[""],"Committed by":[""],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"Create new...":[""],"CreateNewFork|Fork":[""],"CreateTag|Tag":[""],"Cron Timezone":[""],"Cron syntax":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Define a custom pattern with cron syntax":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Directory name":[""],"Don't show again":[""],"Download":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadCommit|Email Patches":[""],"DownloadCommit|Plain Diff":[""],"DownloadSource|Download":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Every day (at 4:00am)":[""],"Every month (on the 1st at 4:00am)":[""],"Every week (Sundays at 4:00am)":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"Fork":["",""],"ForkedFromProjectPath|Forked from":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["",""],"Last Pipeline":[""],"Last Update":[""],"Last commit":[""],"Learn more in the":[""],"Learn more in the|pipeline schedules documentation":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["",""],"Median":[""],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"New branch":[""],"New directory":[""],"New file":[""],"New issue":[""],"New merge request":[""],"New schedule":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OfSearchInADropdown|Filter":[""],"OpenedNDaysAgo|Opened":[""],"Options":[""],"Owner":[""],"Pipeline":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"PipelineSheduleIntervalPattern|Custom":[""],"Pipeline|with stage":[""],"Pipeline|with stages":[""],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":[""],"ProjectNetworkGraph|Graph":[""],"Read more":[""],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Revert this commit":[""],"Revert this merge request":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Scheduling Pipelines":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Select a timezone":[""],"Select target branch":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["",""],"Source code":[""],"StarProject|Star":[""],"Start a %{new_merge_request} with these changes":[""],"Start a <strong>new merge request</strong> with these changes":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":[""],"The collection of events added to the data gathered for that stage.":[""],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You have reached your project limit":[""],"You must sign in to star a project":[""],"You need permission.":[""],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"day":["",""],"new merge request":[""],"notification emails":[""],"parent":["",""]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/es/app.js b/app/assets/javascripts/locale/es/app.js deleted file mode 100644 index 727db6d217e..00000000000 --- a/app/assets/javascripts/locale/es/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-19 15:22-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} cambió %{commit_timeago}"],"About auto deploy":["Acerca del auto despliegue"],"Active":["Activo"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guÃa de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de solo lectura"],"Are you sure you want to delete this pipeline schedule?":["¿Estás seguro que deseas eliminar esta programación del pipeline?"],"Attach a file by drag & drop or %{upload_link}":["Adjunte un archivo arrastrando & soltando o %{upload_link}"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envÃa tus cambios. %{link_to_autodeploy_doc}"],"BranchSwitcherPlaceholder|Search branches":["Buscar ramas"],"BranchSwitcherTitle|Switch branch":["Cambiar rama"],"Branches":["Ramas"],"Browse files":["Examinar los archivos"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Cancel":["Cancelar"],"ChangeTypeActionLabel|Pick into branch":["Escoger en la rama"],"ChangeTypeActionLabel|Revert in branch":["Revertir en la rama"],"ChangeTypeAction|Cherry-pick":["Cherry-pick"],"ChangeTypeAction|Revert":["Revertir"],"Changelog":["Changelog"],"Charts":["Gráficos"],"Cherry-pick this commit":["Escoger este cambio"],"Cherry-pick this merge request":["Escoger esta solicitud de fusión"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallido"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"Commit message":["Mensaje del cambio"],"CommitBoxTitle|Commit":["Cambio"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Committed by":["Enviado por"],"Compare":["Comparar"],"Contribution guide":["GuÃa de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacÃo"],"Create merge request":["Crear solicitud de fusión"],"Create new...":["Crear nuevo..."],"CreateNewFork|Fork":["Bifurcar"],"CreateTag|Tag":["Etiqueta"],"Cron Timezone":["Zona horaria del Cron"],"Cron syntax":["Sintaxis de Cron"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Define a custom pattern with cron syntax":["Definir un patrón personalizado con la sintaxis de cron"],"Delete":["Eliminar"],"Deploy":["Despliegue","Despliegues"],"Description":["Descripción"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download":["Descargar"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadCommit|Email Patches":["Parches por correo electrónico"],"DownloadCommit|Plain Diff":["Diferencias en texto plano"],"DownloadSource|Download":["Descargar"],"Edit":["Editar"],"Edit Pipeline Schedule %{id}":["Editar Programación del Pipeline %{id}"],"Every day (at 4:00am)":["Todos los dÃas (a las 4:00 am)"],"Every month (on the 1st at 4:00am)":["Todos los meses (el dÃa 1 a las 4:00 am)"],"Every week (Sundays at 4:00am)":["Todas las semanas (domingos a las 4:00 am)"],"Failed to change the owner":["Error al cambiar el propietario"],"Failed to remove the pipeline schedule":["Error al eliminar la programación del pipeline"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"Fork":["Bifurcación","Bifurcaciones"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Interval Pattern":["Patrón de intervalo"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d dÃa","Últimos %d dÃas"],"Last Pipeline":["Último Pipeline"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Learn more in the":["Más información en la"],"Learn more in the|pipeline schedules documentation":["documentación sobre la programación de pipelines"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New Pipeline Schedule":["Nueva Programación del Pipeline"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New schedule":["Nueva programación"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"No schedules":["No hay programaciones"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OfSearchInADropdown|Filter":["Filtrar"],"OpenedNDaysAgo|Opened":["Abierto"],"Options":["Opciones"],"Owner":["Propietario"],"Pipeline":["Pipeline"],"Pipeline Health":["Estado del Pipeline"],"Pipeline Schedule":["Programación del Pipeline"],"Pipeline Schedules":["Programaciones de los Pipelines"],"PipelineSchedules|Activated":["Activado"],"PipelineSchedules|Active":["Activos"],"PipelineSchedules|All":["Todos"],"PipelineSchedules|Inactive":["Inactivos"],"PipelineSchedules|Next Run":["Próxima Ejecución"],"PipelineSchedules|None":["Ninguno"],"PipelineSchedules|Provide a short description for this pipeline":["Proporcione una breve descripción para este pipeline"],"PipelineSchedules|Take ownership":["Tomar posesión"],"PipelineSchedules|Target":["Destino"],"PipelineSheduleIntervalPattern|Custom":["Personalizado"],"Pipeline|with stage":["con etapa"],"Pipeline|with stages":["con etapas"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explÃcitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Léeme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Revert this commit":["Revertir este cambio"],"Revert this merge request":["Revertir esta solicitud de fusión"],"Save pipeline schedule":["Guardar programación del pipeline"],"Schedule a new pipeline":["Programar un nuevo pipeline"],"Scheduling Pipelines":["Programación de Pipelines"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Select a timezone":["Selecciona una zona horaria"],"Select target branch":["Selecciona una rama de destino"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Start a %{new_merge_request} with these changes":["Iniciar una %{new_merge_request} con estos cambios"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"Target Branch":["Rama de destino"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquà una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas especÃficas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envÃo de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envÃe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacÃo o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s dÃas"],"Timeago|%s days remaining":["%s dÃas restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 dÃa restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un dÃa"],"Timeago|a month ago":["hace un mes"],"Timeago|a week ago":["hace una semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace un año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s dÃas"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 dÃa"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"UploadLink|click to upload":["Hacer clic para subir"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Solo puedes agregar archivos cuando estás en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You have reached your project limit":[""],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones por cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones solo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"day":["dÃa","dÃas"],"new merge request":["nueva solicitud de fusión"],"notification emails":["correos electrónicos de notificación"],"parent":["padre","padres"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/fr/app.js b/app/assets/javascripts/locale/fr/app.js deleted file mode 100644 index 392fcf06416..00000000000 --- a/app/assets/javascripts/locale/fr/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['fr'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-14 04:21-0400","Last-Translator":"Dremor <egeorget@opmbx.org>","Language-Team":"French (https://www.transifex.com/gitlab-fr/teams/75145/fr/)","Language":"fr","Plural-Forms":"nplurals=2; plural=(n > 1);","X-Generator":"Zanata 3.9.6","lang":"fr","domain":"app","plural_forms":"nplurals=2; plural=(n > 1);"},"ByAuthor|by":["par"],"Commit":["Validation","Validations"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire pour aller d’une idée à sa mise en production pour votre projet."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Incident"],"CycleAnalyticsStage|Plan":["Planification"],"CycleAnalyticsStage|Production":["Production"],"CycleAnalyticsStage|Review":["Examen"],"CycleAnalyticsStage|Staging":["Pré-production"],"CycleAnalyticsStage|Test":["Test"],"Deploy":["Déploiement","Déploiements"],"FirstPushedBy|First":["En premier"],"FirstPushedBy|pushed by":["poussé par"],"From issue creation until deploy to production":["Depuis la création de l'incident jusqu'au déploiement en production"],"From merge request merge until deploy to production":["Depuis la fusion de la demande de fusion jusqu'au déploiement en production"],"Introducing Cycle Analytics":["Introduction à l'analyseur de cycle"],"Last %d day":["Le dernier %d jour","Les derniers %d jours"],"Limited to showing %d event at most":["Limiter l'affichage au plus à %d évènement","Limiter l'affichage au plus à %d évènements"],"Median":["Médian"],"New Issue":["Nouvel incident","Nouveaux incidents"],"Not available":["Indisponible"],"Not enough data":["Données insuffisantes"],"OpenedNDaysAgo|Opened":["Ouvert"],"Pipeline Health":["Santé du Pipeline"],"ProjectLifecycle|Stage":["Étape"],"Read more":["Lire plus"],"Related Commits":["Validations liés"],"Related Deployed Jobs":["Tâches de déploiement liés"],"Related Issues":["Incidents liés"],"Related Jobs":["Tâches liées"],"Related Merge Requests":["Demandes de fusion liées"],"Related Merged Requests":["Demandes fusionnées liées"],"Showing %d event":["Affichage de %d évènement","Affichage de %d évènements"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."],"The collection of events added to the data gathered for that stage.":["L’ensemble d’évènements ajoutés aux données récupérées pour cette étape."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["L'étape des incidents montre le temps nécessaire entre la création d'un incident et son assignation à un jalon, ou son ajout à une liste d'un tableau d'incident. Débutez à créer des incidents pour voir des données pour cette étape."],"The phase of the development lifecycle.":["Les étapes du cycle de développement."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre première validation. Ce temps sera automatiquement ajouté quand vous pousserez votre première validation."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["L’étape de mise en production montre le temps nécessaire entre la création d’un incident et le déploiement du code en production. Les données seront automatiquement ajoutées une fois que vous aurez complété le cycle complet, depuis l’idée jusqu’à la mise en production."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["L’étape d’évaluation montre le temps entre la création de la demande de fusion et la fusion effective de celle-ci. Ces données seront automatiquement ajoutées après que vous ayez fusionné votre première demande de fusion."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["L’étape de pré-production indique le temps entre la fusion de la RF et le déploiement du code dans l’environnent de production. Les données seront automatiquement ajoutées une fois que vous déploierez en production pour la première fois."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["L’étape de test montre le temps que le CI de GitLab met pour exécuter chaque pipeline liés à la demande de fusion. Les données seront automatiquement ajoutées après que votre premier pipeline s’achèvera."],"The time taken by each data entry gathered by that stage.":["Le temps pris par chaque entrée récoltée durant cette étape."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["La valeur située au point médian d’une série de valeur observée. C.à .d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."],"Time before an issue gets scheduled":["Temps avant qu’un incident ne soit planifié"],"Time before an issue starts implementation":["Temps avant que résolution ne débute"],"Time between merge request creation and merge/close":["Temps entre la création d'une demande de fusion et sa fusion/clôture"],"Time until first merge request":["Temps jusqu’à la première demande de fusion"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Temps total"],"Total test time for all commits/merges":["Temps total de test pour toutes les validations/fusions"],"Want to see the data? Please ask an administrator for access.":["Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."],"We don't have enough data to show this stage.":["Nous n'avons pas suffisamment de données pour afficher cette étape."],"You need permission.":["Vous avez besoin d’une autorisation."],"day":["jour","jours"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/pt_BR/app.js b/app/assets/javascripts/locale/pt_BR/app.js deleted file mode 100644 index b9791b7a74b..00000000000 --- a/app/assets/javascripts/locale/pt_BR/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['pt_BR'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-05 03:29-0400","Last-Translator":"Alexandre Alencar <alexandre.alencar@gmail.com>","Language-Team":"Portuguese (Brazil)","Language":"pt-BR","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=2; plural=(n != 1)","lang":"pt_BR","domain":"app","plural_forms":"nplurals=2; plural=(n != 1)"},"ByAuthor|by":["por"],"Commit":["Commit","Commits"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["A Análise de Ciclo fornece uma visão geral de quanto tempo uma ideia demora para ir para produção em seu projeto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Tarefa"],"CycleAnalyticsStage|Plan":["Plano"],"CycleAnalyticsStage|Production":["Produção"],"CycleAnalyticsStage|Review":["Revisão"],"CycleAnalyticsStage|Staging":["Homologação"],"CycleAnalyticsStage|Test":["Teste"],"Deploy":["Implantação","Implantações"],"FirstPushedBy|First":["Primeiro"],"FirstPushedBy|pushed by":["publicado por"],"From issue creation until deploy to production":["Da criação de tarefas até a implantação para a produção"],"From merge request merge until deploy to production":["Da incorporação do merge request até a implantação em produção"],"Introducing Cycle Analytics":["Apresentando a Análise de Ciclo"],"Last %d day":["Último %d dia","Últimos %d dias"],"Limited to showing %d event at most":["Limitado a mostrar %d evento no máximo","Limitado a mostrar %d eventos no máximo"],"Median":["Mediana"],"New Issue":["Nova Tarefa","Novas Tarefas"],"Not available":["Não disponÃvel"],"Not enough data":["Dados insuficientes"],"OpenedNDaysAgo|Opened":["Aberto"],"Pipeline Health":["Saúde da Pipeline"],"ProjectLifecycle|Stage":["Etapa"],"Read more":["Ler mais"],"Related Commits":["Commits Relacionados"],"Related Deployed Jobs":["Jobs Relacionados Incorporados"],"Related Issues":["Tarefas Relacionadas"],"Related Jobs":["Jobs Relacionados"],"Related Merge Requests":["Merge Requests Relacionados"],"Related Merged Requests":["Merge Requests Relacionados"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["O estágio de codificação mostra o tempo desde o primeiro commit até a criação do merge request. \\nOs dados serão automaticamente adicionados aqui uma vez que você tenha criado seu primeiro merge request."],"The collection of events added to the data gathered for that stage.":["A coleção de eventos adicionados aos dados coletados para esse estágio."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["O estágio em questão mostra o tempo que leva desde a criação de uma tarefa até a sua assinatura para um milestone, ou a sua adição para a lista no seu Painel de Tarefas. Comece a criar tarefas para ver dados para esta etapa."],"The phase of the development lifecycle.":["A fase do ciclo de vida do desenvolvimento."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["A fase de planejamento mostra o tempo do passo anterior até empurrar o seu primeiro commit. Este tempo será adicionado automaticamente assim que você realizar seu primeiro commit."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["O estágio de produção mostra o tempo total que leva entre criar uma tarefa e implantar o código na produção. Os dados serão adicionados automaticamente até que você complete todo o ciclo de produção."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["A etapa de revisão mostra o tempo de criação de um merge request até que o merge seja feito. Os dados serão automaticamente adicionados depois que você fizer seu primeiro merge request."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["O estágio de estágio mostra o tempo entre a fusão do MR e o código de implantação para o ambiente de produção. Os dados serão automaticamente adicionados depois de implantar na produção pela primeira vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["A fase de teste mostra o tempo que o GitLab CI leva para executar cada pipeline para o merge request relacionado. Os dados serão automaticamente adicionados após a conclusão do primeiro pipeline."],"The time taken by each data entry gathered by that stage.":["O tempo necessário para cada entrada de dados reunida por essa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["O valor situado no ponto médio de uma série de valores observados. Ex., entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5 + 7) / 2 = 6."],"Time before an issue gets scheduled":["Tempo até que uma tarefa seja planejada"],"Time before an issue starts implementation":["Tempo até que uma tarefa comece a ser implementada"],"Time between merge request creation and merge/close":["Tempo entre a criação do merge request e o merge/fechamento"],"Time until first merge request":["Tempo até o primeiro merge request"],"Time|hr":["h","hs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tempo Total"],"Total test time for all commits/merges":["Tempo de teste total para todos os commits/merges"],"Want to see the data? Please ask an administrator for access.":["Precisa visualizar os dados? Solicite acesso ao administrador."],"We don't have enough data to show this stage.":["Não temos dados suficientes para mostrar esta fase."],"You have reached your project limit":[""],"You need permission.":["Você precisa de permissão."],"day":["dia","dias"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/zh_HK/app.js b/app/assets/javascripts/locale/zh_HK/app.js deleted file mode 100644 index bb0798bfb62..00000000000 --- a/app/assets/javascripts/locale/zh_HK/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['zh_HK'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Hong Kong) (https://www.transifex.com/gitlab-zh/teams/75177/zh_HK/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_HK","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_HK","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["æ交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分æžæ¦‚è¿°äº†é …ç›®å¾žæƒ³æ³•åˆ°ç”¢å“實ç¾çš„å„階段所需的時間。"],"CycleAnalyticsStage|Code":["編碼"],"CycleAnalyticsStage|Issue":["è°é¡Œ"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["生產"],"CycleAnalyticsStage|Review":["評審"],"CycleAnalyticsStage|Staging":["é 發布"],"CycleAnalyticsStage|Test":["測試"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推é€"],"FirstPushedBy|pushed by":["推é€è€…:"],"From issue creation until deploy to production":["從創建è°é¡Œåˆ°éƒ¨ç½²åˆ°ç”Ÿç”¢ç’°å¢ƒ"],"From merge request merge until deploy to production":["從åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²è‡³ç”Ÿç”¢ç’°å¢ƒ"],"Interval Pattern":[""],"Introducing Cycle Analytics":["週期分æžç°¡ä»‹"],"Last %d day":["最後 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["ä¸ä½æ•¸"],"New Issue":["æ–°è°é¡Œ"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["ä¸å¯ç”¨"],"Not enough data":["數據ä¸è¶³"],"OpenedNDaysAgo|Opened":["開始於"],"Owner":[""],"Pipeline Health":["æµæ°´ç·šå¥åº·æŒ‡æ¨™"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["é …ç›®ç”Ÿå‘½é€±æœŸ"],"Read more":["了解更多"],"Related Commits":["相關的æ交"],"Related Deployed Jobs":["相關的部署作æ¥"],"Related Issues":["相關的è°é¡Œ"],"Related Jobs":["相關的作æ¥"],"Related Merge Requests":["相關的åˆä½µè«‹æ±‚"],"Related Merged Requests":["相關已åˆä½µçš„åˆä¸¦è«‹æ±‚"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["顯示 %d 個事件"],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["編碼階段概述了從第一次æ交到創建åˆä½µè«‹æ±‚的時間。創建第壹個åˆä¸¦è«‹æ±‚å¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ¤è™•ã€‚"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["è°é¡ŒéšŽæ®µæ¦‚述了從創建è°é¡Œåˆ°å°‡è°é¡Œè¨ç½®è£ç¨‹ç¢‘或將è°é¡Œæ·»åŠ 到è°é¡Œçœ‹æ¿çš„時間。創建一個è°é¡Œå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ 到æ¤è™•ã€‚"],"The phase of the development lifecycle.":["é …ç›®ç”Ÿå‘½é€±æœŸä¸çš„å„個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段概述了從è°é¡Œæ·»åŠ 到日程後到推é€é¦–次æ交的時間。當首次推é€æäº¤å¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ¤è™•ã€‚"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生產階段概述了從創建è°é¡Œåˆ°å°‡ä»£ç¢¼éƒ¨ç½²åˆ°ç”Ÿç”¢ç’°å¢ƒçš„時間。當完æˆå®Œæ•´çš„æƒ³æ³•åˆ°éƒ¨ç½²ç”Ÿç”¢ï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ¤è™•ã€‚"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["評審階段概述了從創建åˆä¸¦è«‹æ±‚到åˆä½µçš„時間。當創建第壹個åˆä¸¦è«‹æ±‚å¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ¤è™•ã€‚"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["é 發布階段概述了åˆä¸¦è«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²ä»£ç¢¼åˆ°ç”Ÿç”¢ç’°å¢ƒçš„ç¸½æ™‚é–“ã€‚ç•¶é¦–æ¬¡éƒ¨ç½²åˆ°ç”Ÿç”¢ç’°å¢ƒå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ åˆ°æ¤è™•ã€‚"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段概述了GitLab CI為相關åˆä½µè«‹æ±‚é‹è¡Œæ¯å€‹æµæ°´ç·šæ‰€éœ€çš„時間。當第壹個æµæ°´ç·šé‹è¡Œå®Œæˆå¾Œï¼Œæ•¸æ“šå°‡è‡ªå‹•æ·»åŠ 到æ¤è™•ã€‚"],"The time taken by each data entry gathered by that stage.":["該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["ä¸ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間,ä¸ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,ä¸ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["è°é¡Œè¢«åˆ—入日程表的時間"],"Time before an issue starts implementation":["開始進行編碼å‰çš„時間"],"Time between merge request creation and merge/close":["從創建åˆä½µè«‹æ±‚到被åˆä¸¦æˆ–關閉的時間"],"Time until first merge request":["創建第壹個åˆä½µè«‹æ±‚之å‰çš„時間"],"Time|hr":["å°æ™‚"],"Time|min":["分é˜"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有æ交和åˆä½µçš„總測試時間"],"Want to see the data? Please ask an administrator for access.":["權é™ä¸è¶³ã€‚如需查看相關數據,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"],"We don't have enough data to show this stage.":["該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"],"You have reached your project limit":[""],"You need permission.":["您需è¦ç›¸é—œçš„權é™ã€‚"],"day":["天"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/locale/zh_TW/app.js b/app/assets/javascripts/locale/zh_TW/app.js deleted file mode 100644 index b65e0cd1c92..00000000000 --- a/app/assets/javascripts/locale/zh_TW/app.js +++ /dev/null @@ -1 +0,0 @@ -var locales = locales || {}; locales['zh_TW'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Taiwan) (https://www.transifex.com/gitlab-zh/teams/75177/zh_TW/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_TW","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_TW","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["é€äº¤"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分æžæ¦‚è¿°äº†ä½ çš„å°ˆæ¡ˆå¾žæƒ³æ³•åˆ°ç”¢å“實ç¾ï¼Œå„階段所需的時間。"],"CycleAnalyticsStage|Code":["程å¼é–‹ç™¼"],"CycleAnalyticsStage|Issue":["è°é¡Œ"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["上線"],"CycleAnalyticsStage|Review":["複閱"],"CycleAnalyticsStage|Staging":["é å‚™"],"CycleAnalyticsStage|Test":["測試"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推é€"],"FirstPushedBy|pushed by":["推é€è€…:"],"From issue creation until deploy to production":["從è°é¡Œå»ºç«‹è‡³ç·šä¸Šéƒ¨ç½²"],"From merge request merge until deploy to production":["從請求被åˆä½µå¾Œè‡³ç·šä¸Šéƒ¨ç½²"],"Interval Pattern":[""],"Introducing Cycle Analytics":["週期分æžç°¡ä»‹"],"Last %d day":["最後 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["ä¸ä½æ•¸"],"New Issue":["æ–°è°é¡Œ"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["無法使用"],"Not enough data":["資料ä¸è¶³"],"OpenedNDaysAgo|Opened":["開始於"],"Owner":[""],"Pipeline Health":["æµæ°´ç·šå¥åº·æŒ‡æ¨™"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["專案生命週期"],"Read more":["了解更多"],"Related Commits":["相關的é€äº¤"],"Related Deployed Jobs":["相關的部署作æ¥"],"Related Issues":["相關的è°é¡Œ"],"Related Jobs":["相關的作æ¥"],"Related Merge Requests":["相關的åˆä½µè«‹æ±‚"],"Related Merged Requests":["相關已åˆä½µçš„請求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["顯示 %d 個事件"],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["程å¼é–‹ç™¼éšŽæ®µé¡¯ç¤ºå¾žç¬¬ä¸€æ¬¡é€äº¤åˆ°å»ºç«‹åˆä½µè«‹æ±‚的時間。建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["è°é¡ŒéšŽæ®µé¡¯ç¤ºå¾žè°é¡Œå»ºç«‹åˆ°è¨ç½®é‡Œç¨‹ç¢‘ã€æˆ–將該è°é¡ŒåŠ 至è°é¡Œçœ‹æ¿çš„時間。建立第一個è°é¡Œå¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"],"The phase of the development lifecycle.":["專案開發生命週期的å„個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段所顯示的是è°é¡Œè¢«æŽ’程後至第一個é€äº¤è¢«æŽ¨é€çš„時間。一旦完æˆï¼ˆæˆ–執行)首次的推é€ï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["上線階段顯示從建立一個è°é¡Œåˆ°éƒ¨ç½²ç¨‹å¼è‡³ç·šä¸Šçš„總時間。當完æˆå¾žæƒ³æ³•åˆ°ç”¢å“實ç¾çš„循環後,資料將自動填入。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["複閱階段顯示從åˆä½µè«‹æ±‚建立後至被åˆä½µçš„時間。當建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["é 備階段顯示從åˆä½µè«‹æ±‚被åˆä½µå¾Œè‡³éƒ¨ç½²ä¸Šç·šçš„時間。當第一次部署上線後,資料將自動填入。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段顯示相關åˆä½µè«‹æ±‚çš„æµæ°´ç·šæ‰€èŠ±çš„時間。當第一個æµæ°´ç·šé‹ä½œå®Œç•¢å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"],"The time taken by each data entry gathered by that stage.":["æ¯ç†è©²éšŽæ®µç›¸é—œè³‡æ–™æ‰€èŠ±çš„時間。"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["ä¸ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間,ä¸ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,ä¸ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["è°é¡Œç‰å¾…排程的時間"],"Time before an issue starts implementation":["è°é¡Œç‰å¾…開始實作的時間"],"Time between merge request creation and merge/close":["åˆä½µè«‹æ±‚被åˆä½µæˆ–是關閉的時間"],"Time until first merge request":["第一個åˆä½µè«‹æ±‚被建立å‰çš„時間"],"Time|hr":["å°æ™‚"],"Time|min":["分é˜"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有é€äº¤å’Œåˆä½µçš„總測試時間"],"Want to see the data? Please ask an administrator for access.":["權é™ä¸è¶³ã€‚如需查看相關資料,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"],"We don't have enough data to show this stage.":["å› è©²éšŽæ®µçš„è³‡æ–™ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š"],"You have reached your project limit":[""],"You need permission.":["您需è¦ç›¸é—œçš„權é™ã€‚"],"day":["天"]}}};
\ No newline at end of file diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 26c67fb721c..cd45091c211 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -109,6 +109,7 @@ import './label_manager'; import './labels'; import './labels_select'; import './layout_nav'; +import LazyLoader from './lazy_loader'; import './line_highlighter'; import './logo'; import './member_expiration_date'; @@ -144,7 +145,6 @@ import './right_sidebar'; import './search'; import './search_autocomplete'; import './smart_interval'; -import './snippets_list'; import './star'; import './subscription'; import './subscription_select'; @@ -158,6 +158,8 @@ document.addEventListener('beforeunload', function () { $(document).off('scroll'); // Close any open tooltips $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); + // Close any open popover + $('[data-toggle="popover"]').popover('destroy'); }); window.addEventListener('hashchange', gl.utils.handleLocationHash); @@ -166,6 +168,11 @@ window.addEventListener('load', function onLoad() { gl.utils.handleLocationHash(); }, false); +gl.lazyLoader = new LazyLoader({ + scrollContainer: window, + observerNode: '#content-body' +}); + $(function () { var $body = $('body'); var $document = $(document); @@ -241,6 +248,11 @@ $(function () { return $(el).data('placement') || 'bottom'; } }); + // Initialize popovers + $body.popover({ + selector: '[data-toggle="popover"]', + trigger: 'focus' + }); $('.trigger-submit').on('change', function () { return $(this).parents('form').submit(); // Form submitter @@ -284,13 +296,7 @@ $(function () { return $container.remove(); // Commit show suppressed diff }); - $('.navbar-toggle').on('click', function () { - $('.header-content .title, .header-content .navbar-sub-nav').toggle(); - $('.header-content .header-logo').toggle(); - $('.header-content .navbar-collapse').toggle(); - $('.js-navbar-toggle-left, .js-navbar-toggle-right, .title-container').toggle(); - return $('.navbar-toggle').toggleClass('active'); - }); + $('.navbar-toggle').on('click', () => $('.header-content').toggleClass('menu-expanded')); // Show/hide comments on diff $body.on('click', '.js-toggle-diff-comments', function (e) { var $this = $(this); @@ -347,4 +353,14 @@ $(function () { gl.utils.renderTimeago(); $(document).trigger('init.scrolling-tabs'); + + $('form.filter-form').on('submit', function (event) { + const link = document.createElement('a'); + link.href = this.action; + + const action = `${this.action}${link.search === '' ? '?' : '&'}`; + + event.preventDefault(); + gl.utils.visitUrl(`${action}${$(this).serialize()}`); + }); }); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js index c4e379a4a0b..8be7314ded8 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js @@ -175,7 +175,7 @@ import Cookies from 'js-cookie'; getConflictsCountText() { const count = this.getConflictsCount(); - const text = count ? 'conflicts' : 'conflict'; + const text = count > 1 ? 'conflicts' : 'conflict'; return `${count} ${text}`; }, diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 17030c3e4d3..d74cf5328ad 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -2,6 +2,7 @@ /* global Flash */ import Vue from 'vue'; +import initIssuableSidebar from '../init_issuable_sidebar'; import './merge_conflict_store'; import './merge_conflict_service'; import './mixins/line_conflict_utils'; @@ -19,6 +20,8 @@ $(() => { resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath }); + initIssuableSidebar(); + gl.MergeConflictsResolverApp = new Vue({ el: '#conflicts', data: mergeConflictsStore.state, diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 9d481d7c003..6756ab0b3aa 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -8,7 +8,7 @@ var _this, $els; if (currentProject != null) { _this = this; - this.currentProject = JSON.parse(currentProject); + this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject; } $els = $(els); diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 401df308316..bae613aab90 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -6,21 +6,22 @@ import Cookies from 'js-cookie'; (function() { this.Project = (function() { function Project() { - $('ul.clone-options-dropdown a').click(function() { - var url; - if ($(this).hasClass('active')) { - return; - } - $('.active').not($(this)).removeClass('active'); - $(this).toggleClass('active'); - url = $("#project_clone").val(); - $('#project_clone').val(url); + const $cloneOptions = $('ul.clone-options-dropdown'); + const $projectCloneField = $('#project_clone'); + const $cloneBtnText = $('a.clone-dropdown-btn span'); + + $('a', $cloneOptions).on('click', (e) => { + const $this = $(e.currentTarget); + const url = $this.attr('href'); + + e.preventDefault(); + + $('.active', $cloneOptions).not($this).removeClass('active'); + $this.toggleClass('active'); + $projectCloneField.val(url); + $cloneBtnText.text($this.text()); + return $('.clone').text(url); - // Git protocol switcher - // Remove the active class for all buttons (ssh, http, kerberos if shown) - // Add the active class for the clicked button - // Update the input field - // Update the command line instructions }); // Ref switcher this.initRefSwitcher(); diff --git a/app/assets/javascripts/protected_branches/index.js b/app/assets/javascripts/protected_branches/index.js new file mode 100644 index 00000000000..c9e7af127d2 --- /dev/null +++ b/app/assets/javascripts/protected_branches/index.js @@ -0,0 +1,9 @@ +/* eslint-disable no-unused-vars */ + +import ProtectedBranchCreate from './protected_branch_create'; +import ProtectedBranchEditList from './protected_branch_edit_list'; + +$(() => { + const protectedBranchCreate = new ProtectedBranchCreate(); + const protectedBranchEditList = new ProtectedBranchEditList(); +}); diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js index 42993a252c3..38b1406a99f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js @@ -1,31 +1,26 @@ -/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */ +export default class ProtectedBranchAccessDropdown { + constructor(options) { + this.options = options; + this.initDropdown(); + } -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchAccessDropdown = class { - constructor(options) { - const { $dropdown, data, onSelect } = options; - - $dropdown.glDropdown({ - data: data, - selectable: true, - inputId: $dropdown.data('input-id'), - fieldName: $dropdown.data('field-name'), - toggleLabel(item, el) { - if (el.is('.is-active')) { - return item.text; - } else { - return 'Select'; - } - }, - clicked(opts) { - const { e } = opts; - - e.preventDefault(); - onSelect(); + initDropdown() { + const { $dropdown, data, onSelect } = this.options; + $dropdown.glDropdown({ + data, + selectable: true, + inputId: $dropdown.data('input-id'), + fieldName: $dropdown.data('field-name'), + toggleLabel(item, $el) { + if ($el.is('.is-active')) { + return item.text; } - }); - } - }; -})(window); + return 'Select'; + }, + clicked(options) { + options.e.preventDefault(); + onSelect(); + }, + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 57ea2f52814..10da3783123 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -1,55 +1,51 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ -/* global ProtectedBranchDropdown */ - -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchCreate = class { - constructor() { - this.$wrap = this.$form = $('#new_protected_branch'); - this.buildDropdowns(); - } - - buildDropdowns() { - const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); - - // Cache callback - this.onSelectCallback = this.onSelect.bind(this); - - // Allowed to Merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelectCallback - }); - - // Allowed to Push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelectCallback - }); - - // Select default - $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); - $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); - - // Protected branch dropdown - new ProtectedBranchDropdown({ - $dropdown: this.$wrap.find('.js-protected-branch-select'), - onSelect: this.onSelectCallback - }); - } - - // This will run after clicked callback - onSelect() { - // Enable submit button - const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]'); - const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); - const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); - - this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); - } - }; -})(window); +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; +import ProtectedBranchDropdown from './protected_branch_dropdown'; + +export default class ProtectedBranchCreate { + constructor() { + this.$form = $('.js-new-protected-branch'); + this.buildDropdowns(); + } + + buildDropdowns() { + const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge'); + const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push'); + + // Cache callback + this.onSelectCallback = this.onSelect.bind(this); + + // Allowed to Merge dropdown + this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); + + // Allowed to Push dropdown + this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + + // Select default + $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); + $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); + + // Protected branch dropdown + this.protectedBranchDropdown = new ProtectedBranchDropdown({ + $dropdown: this.$form.find('.js-protected-branch-select'), + onSelect: this.onSelectCallback, + }); + } + + // This will run after clicked callback + onSelect() { + // Enable submit button + const $branchInput = this.$form.find('input[name="protected_branch[name]"]'); + const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); + const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); + + this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js index bc6110fcd4e..cc0b2ebe071 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js @@ -1,6 +1,10 @@ -/* eslint-disable comma-dangle, no-unused-vars */ - -class ProtectedBranchDropdown { +export default class ProtectedBranchDropdown { + /** + * @param {Object} options containing + * `$dropdown` target element + * `onSelect` event callback + * $dropdown must be an element created using `dropdown_branch()` rails helper + */ constructor(options) { this.onSelect = options.onSelect; this.$dropdown = options.$dropdown; @@ -12,7 +16,7 @@ class ProtectedBranchDropdown { this.bindEvents(); // Hide footer - this.$dropdownFooter.addClass('hidden'); + this.toggleFooter(true); } buildDropdown() { @@ -21,7 +25,7 @@ class ProtectedBranchDropdown { filterable: true, remote: false, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, toggleLabel(selected) { @@ -36,10 +40,9 @@ class ProtectedBranchDropdown { }, onFilter: this.toggleCreateNewButton.bind(this), clicked: (options) => { - const { $el, e } = options; - e.preventDefault(); + options.e.preventDefault(); this.onSelect(); - } + }, }); } @@ -64,20 +67,22 @@ class ProtectedBranchDropdown { } toggleCreateNewButton(branchName) { - this.selectedBranch = { - title: branchName, - id: branchName, - text: branchName - }; - if (branchName) { + this.selectedBranch = { + title: branchName, + id: branchName, + text: branchName, + }; + this.$dropdownContainer .find('.js-create-new-protected-branch code') .text(branchName); } - this.$dropdownFooter.toggleClass('hidden', !branchName); + this.toggleFooter(!branchName); } -} -window.ProtectedBranchDropdown = ProtectedBranchDropdown; + toggleFooter(toggleState) { + this.$dropdownFooter.toggleClass('hidden', toggleState); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index 6ef59e94384..3b920942a3f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -1,69 +1,67 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ +/* eslint-disable no-new */ /* global Flash */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; - gl.ProtectedBranchEdit = class { - constructor(options) { - this.$wrap = options.$wrap; - this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); +export default class ProtectedBranchEdit { + constructor(options) { + this.$wrap = options.$wrap; + this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); + this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); + this.onSelectCallback = this.onSelect.bind(this); - this.buildDropdowns(); - } + this.buildDropdowns(); + } - buildDropdowns() { - // Allowed to merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelect.bind(this) - }); + buildDropdowns() { + // Allowed to merge dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); - // Allowed to push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelect.bind(this) - }); - } + // Allowed to push dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + } - onSelect() { - const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); - const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); + onSelect() { + const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); + const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); - // Do not update if one dropdown has not selected any option - if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; + // Do not update if one dropdown has not selected any option + if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; - this.$allowedToMergeDropdown.disable(); - this.$allowedToPushDropdown.disable(); + this.$allowedToMergeDropdown.disable(); + this.$allowedToPushDropdown.disable(); - $.ajax({ - type: 'POST', - url: this.$wrap.data('url'), - dataType: 'json', - data: { - _method: 'PATCH', - protected_branch: { - merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('access-level-id'), - access_level: $allowedToMergeInput.val() - }], - push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('access-level-id'), - access_level: $allowedToPushInput.val() - }] - } + $.ajax({ + type: 'POST', + url: this.$wrap.data('url'), + dataType: 'json', + data: { + _method: 'PATCH', + protected_branch: { + merge_access_levels_attributes: [{ + id: this.$allowedToMergeDropdown.data('access-level-id'), + access_level: $allowedToMergeInput.val(), + }], + push_access_levels_attributes: [{ + id: this.$allowedToPushDropdown.data('access-level-id'), + access_level: $allowedToPushInput.val(), + }], }, - error() { - $.scrollTo(0); - new Flash('Failed to update branch!'); - } - }).always(() => { - this.$allowedToMergeDropdown.enable(); - this.$allowedToPushDropdown.enable(); - }); - } - }; -})(window); + }, + error() { + new Flash('Failed to update branch!', null, $('.js-protected-branches-list')); + }, + }).always(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js index 336fa6c57a7..b40d3827c30 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js @@ -1,18 +1,18 @@ -/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */ +/* eslint-disable no-new */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchEdit from './protected_branch_edit'; - gl.ProtectedBranchEditList = class { - constructor() { - this.$wrap = $('.protected-branches-list'); +export default class ProtectedBranchEditList { + constructor() { + this.$wrap = $('.protected-branches-list'); + this.initEditForm(); + } - // Build edit forms - this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { - new gl.ProtectedBranchEdit({ - $wrap: $(el) - }); + initEditForm() { + this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { + new ProtectedBranchEdit({ + $wrap: $(el), }); - } - }; -})(window); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js deleted file mode 100644 index 874d70a1431..00000000000 --- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js +++ /dev/null @@ -1,5 +0,0 @@ -import './protected_branch_access_dropdown'; -import './protected_branch_create'; -import './protected_branch_dropdown'; -import './protected_branch_edit'; -import './protected_branch_edit_list'; diff --git a/app/assets/javascripts/protected_tags/index.js b/app/assets/javascripts/protected_tags/index.js index 61e7ba53862..b1618e24e49 100644 --- a/app/assets/javascripts/protected_tags/index.js +++ b/app/assets/javascripts/protected_tags/index.js @@ -1,2 +1,9 @@ -export { default as ProtectedTagCreate } from './protected_tag_create'; -export { default as ProtectedTagEditList } from './protected_tag_edit_list'; +/* eslint-disable no-unused-vars */ + +import ProtectedTagCreate from './protected_tag_create'; +import ProtectedTagEditList from './protected_tag_edit_list'; + +$(() => { + const protectedtTagCreate = new ProtectedTagCreate(); + const protectedtTagEditList = new ProtectedTagEditList(); +}); diff --git a/app/assets/javascripts/repo/index.js b/app/assets/javascripts/repo/index.js index f0684706c72..f9f9c31f1ef 100644 --- a/app/assets/javascripts/repo/index.js +++ b/app/assets/javascripts/repo/index.js @@ -18,13 +18,12 @@ function initRepo() { Store.service = Service; Store.service.url = repo.dataset.url; - Store.projectName = repo.dataset.projectName; Store.service.refsUrl = repo.dataset.refsUrl; - Store.currentBranch = $('button.dropdown-menu-toggle').attr('data-ref'); - Store.checkIsCommitable(); Store.projectId = repo.dataset.projectId; - Store.tempPrivateToken = repo.dataset.tempToken; + Store.projectName = repo.dataset.projectName; Store.projectUrl = repo.dataset.projectUrl; + Store.currentBranch = $('button.dropdown-menu-toggle').attr('data-ref'); + Store.checkIsCommitable(); this.vm = new Vue({ el: repo, diff --git a/app/assets/javascripts/repo/repo_commit_section.vue b/app/assets/javascripts/repo/repo_commit_section.vue index c03ed7eefb4..49d328b5a4b 100644 --- a/app/assets/javascripts/repo/repo_commit_section.vue +++ b/app/assets/javascripts/repo/repo_commit_section.vue @@ -31,14 +31,12 @@ const RepoCommitSection = { Api.commitMultiple(Store.projectId, payload, (data) => { Store.submitCommitsLoading = false; Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); - console.log('this.changedFiles', this.changedFiles); - console.log('this.files', this.files); this.changedFiles = []; this.openedFiles = []; this.commitMessage = ''; this.editMode = false; $('html, body').animate({ scrollTop: 0 }, 'fast'); - }, Store.tempPrivateToken); + }); }, }, }; @@ -51,13 +49,11 @@ export default RepoCommitSection; <form class="form-horizontal"> <fieldset> <div class="form-group"> - <label class="col-md-4 control-label">Staged files ({{changedFiles.length}})</label> + <label class="col-md-4 control-label staged-files">Staged files ({{changedFiles.length}})</label> <div class="col-md-4"> - <ul class="list-unstyled"> + <ul class="list-unstyled changed-files"> <li v-for="file in changedFiles" :key="file.id"> - <span class="help-block"> - {{file.url}} - </span> + <span class="help-block">{{file.url}}</span> </li> </ul> </div> @@ -76,7 +72,7 @@ export default RepoCommitSection; <label class="col-md-4 control-label" for="target-branch">Target branch</label> <div class="col-md-4"> <div class="input-group"> - <div class="input-group-btn"> + <div class="input-group-btn branch-dropdown"> <button class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button"> Action <i class="fa fa-caret-down"></i> @@ -97,7 +93,7 @@ export default RepoCommitSection; <div class="form-group"> <label class="col-md-4 control-label" for="checkboxes"></label> <div class="col-md-4"> - <div class="checkbox"> + <div class="checkbox new-merge-request"> <label for="checkboxes-0"> <input id="checkboxes-0" name="checkboxes" type="checkbox" value="1"></input> Start a new merge request with these changes @@ -106,9 +102,9 @@ export default RepoCommitSection; </div> </div> <div class="col-md-offset-4 col-md-4"> - <button type="submit" :disabled="!commitMessage || submitCommitsLoading" class="btn btn-success" @click.prevent="makeCommit"> + <button type="submit" :disabled="!commitMessage || submitCommitsLoading" class="btn btn-success submit-commit" @click.prevent="makeCommit"> <i class="fa fa-spinner fa-spin" v-if="submitCommitsLoading"></i> - <span>Commit {{changedFiles.length}} Files</span> + <span class="commit-summary">Commit {{changedFiles.length}} Files</span> </button> </div> </fieldset> diff --git a/app/assets/javascripts/repo/repo_file_buttons.vue b/app/assets/javascripts/repo/repo_file_buttons.vue index 2ef07675c28..f7810e2fdb1 100644 --- a/app/assets/javascripts/repo/repo_file_buttons.vue +++ b/app/assets/javascripts/repo/repo_file_buttons.vue @@ -50,9 +50,5 @@ export default RepoFileButtons; </div> <a href="#" v-if="canPreview" @click.prevent="rawPreviewToggle" class="btn btn-default preview">{{activeFileLabel}}</a> - - <button type="button" class="btn btn-default replace" data-target="#modal-upload-blob" data-toggle="modal">Replace</button> - - <a href="#" class="btn btn-danger delete">Delete</a> </div> </template> diff --git a/app/assets/javascripts/repo/repo_helper.js b/app/assets/javascripts/repo/repo_helper.js index 3d04dbb658f..ff83caa58a1 100644 --- a/app/assets/javascripts/repo/repo_helper.js +++ b/app/assets/javascripts/repo/repo_helper.js @@ -19,7 +19,7 @@ const RepoHelper = { newContent: '', changed: false, loading: false, - } + }; }, key: '', @@ -173,14 +173,14 @@ const RepoHelper = { } } else { // it's a tree - if(!file) Store.isRoot = RepoHelper.isRoot(Service.url); + if (!file) Store.isRoot = RepoHelper.isRoot(Service.url); file = RepoHelper.setDirectoryOpen(file); const newDirectory = RepoHelper.dataToListOfFiles(data); Store.addFilesToDirectory(file, Store.files, newDirectory); Store.prevURL = Service.blobURLtoParentTree(Service.url); } }) - .catch((e) => { + .catch(() => { // RepoHelper.setLoading(false, loadingData); RepoHelper.loadingError(); }); diff --git a/app/assets/javascripts/repo/repo_store.js b/app/assets/javascripts/repo/repo_store.js index 861a99a8b80..dc89ec54fa2 100644 --- a/app/assets/javascripts/repo/repo_store.js +++ b/app/assets/javascripts/repo/repo_store.js @@ -26,7 +26,6 @@ const RepoStore = { defaultTabSize: 100, minTabSize: 30, tabsOverflow: 41, - tempPrivateToken: '', submitCommitsLoading: false, binaryLoaded:false, dialog: { @@ -42,7 +41,7 @@ const RepoStore = { isCommitable: false, binary: false, currentBranch: '', - commitMessage: 'Update README.md', + commitMessage: '', binaryMimeType: '', // scroll bar space for windows scrollWidth: 0, @@ -142,23 +141,23 @@ const RepoStore = { if (file.type === 'tree') return; let foundIndex; RepoStore.openedFiles = RepoStore.openedFiles.filter((openedFile, i) => { - if(openedFile.url === file.url) foundIndex = i; + if (openedFile.url === file.url) foundIndex = i; return openedFile.url !== file.url; }); // now activate the right tab based on what you closed. - if(RepoStore.openedFiles.length === 0) { + if (RepoStore.openedFiles.length === 0) { RepoStore.activeFile = {}; return; } - - if(RepoStore.openedFiles.length === 1 || foundIndex === 0) { + + if (RepoStore.openedFiles.length === 1 || foundIndex === 0) { RepoStore.setActiveFiles(RepoStore.openedFiles[0]); return; } - if(foundIndex) { - if(foundIndex > 0) { + if (foundIndex) { + if (foundIndex > 0) { RepoStore.setActiveFiles(RepoStore.openedFiles[foundIndex - 1]); } } diff --git a/app/assets/javascripts/sidebar/sidebar_bundle.js b/app/assets/javascripts/sidebar/sidebar_bundle.js index 2b02af87d8a..a9df66748c5 100644 --- a/app/assets/javascripts/sidebar/sidebar_bundle.js +++ b/app/assets/javascripts/sidebar/sidebar_bundle.js @@ -5,7 +5,8 @@ import sidebarAssignees from './components/assignees/sidebar_assignees'; import Mediator from './sidebar_mediator'; function domContentLoaded() { - const mediator = new Mediator(gl.sidebarOptions); + const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); + const mediator = new Mediator(sidebarOptions); mediator.fetch(); const sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees'); diff --git a/app/assets/javascripts/snippets_list.js b/app/assets/javascripts/snippets_list.js deleted file mode 100644 index 3b6d999b1c3..00000000000 --- a/app/assets/javascripts/snippets_list.js +++ /dev/null @@ -1,9 +0,0 @@ -function SnippetsList() { - const $holder = $('.snippets-list-holder'); - - $holder.find('.pagination').on('ajax:success', (e, data) => { - $holder.replaceWith(data.html); - }); -} - -window.gl.SnippetsList = SnippetsList; diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index 6d38124f1c1..3a06b477d7c 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,6 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */ /* global Flash */ +import { __, s__ } from './locale'; + export default class Star { constructor() { $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) { @@ -11,10 +13,10 @@ export default class Star { toggleStar = function(isStarred) { $this.parent().find('.star-count').text(data.star_count); if (isStarred) { - $starSpan.removeClass('starred').text('Star'); + $starSpan.removeClass('starred').text(s__('StarProject|Star')); $starIcon.removeClass('fa-star').addClass('fa-star-o'); } else { - $starSpan.addClass('starred').text('Unstar'); + $starSpan.addClass('starred').text(__('Unstar')); $starIcon.removeClass('fa-star-o').addClass('fa-star'); } }; diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index cd305631c10..bba8b5abbb4 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -37,10 +37,6 @@ export default class Todos { this.initFilterDropdown($('.js-type-search'), 'type'); this.initFilterDropdown($('.js-action-search'), 'action_id'); - $('form.filter-form').on('submit', function applyFilters(event) { - event.preventDefault(); - gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`); - }); return new UsersSelect(); } diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js new file mode 100644 index 00000000000..7777ed1c3dc --- /dev/null +++ b/app/assets/javascripts/tree.js @@ -0,0 +1,64 @@ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */ + +export default class TreeView { + constructor() { + this.initKeyNav(); + // Code browser tree slider + // Make the entire tree-item row clickable, but not if clicking another link (like a commit message) + $(".tree-content-holder .tree-item").on('click', function(e) { + var $clickedEl, path; + $clickedEl = $(e.target); + path = $('.tree-item-file-name a', this).attr('href'); + if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) { + if (e.metaKey || e.which === 2) { + e.preventDefault(); + return window.open(path, '_blank'); + } else { + return gl.utils.visitUrl(path); + } + } + }); + // Show the "Loading commit data" for only the first element + $('span.log_loading:first').removeClass('hide'); + } + + initKeyNav() { + var li, liSelected; + li = $("tr.tree-item"); + liSelected = null; + return $('body').keydown(function(e) { + var next, path; + if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) { + return false; + } + if (e.which === 40) { + if (liSelected) { + next = liSelected.next(); + if (next.length > 0) { + liSelected.removeClass("selected"); + liSelected = next.addClass("selected"); + } + } else { + liSelected = li.eq(0).addClass("selected"); + } + return $(liSelected).focus(); + } else if (e.which === 38) { + if (liSelected) { + next = liSelected.prev(); + if (next.length > 0) { + liSelected.removeClass("selected"); + liSelected = next.addClass("selected"); + } + } else { + liSelected = li.last().addClass("selected"); + } + return $(liSelected).focus(); + } else if (e.which === 13) { + path = $('.tree-item.selected .tree-item-file-name a').attr('href'); + if (path) { + return gl.utils.visitUrl(path); + } + } + }); + } +} diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index b7f50cfd083..f091e319f44 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,10 +1,28 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ - import d3 from 'd3'; +const LOADING_HTML = ` + <div class="text-center"> + <i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i> + </div> +`; + +function formatTooltipText({ date, count }) { + const dateObject = new Date(date); + const dateDayName = gl.utils.getDayName(dateObject); + const dateText = dateObject.format('mmm d, yyyy'); + + let contribText = 'No contributions'; + if (count > 0) { + contribText = `${count} contribution${count > 1 ? 's' : ''}`; + } + return `${contribText}<br />${dateDayName} ${dateText}`; +} + +const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); + export default class ActivityCalendar { - constructor(timestamps, calendar_activities_path) { - this.calendar_activities_path = calendar_activities_path; + constructor(container, timestamps, calendarActivitiesPath) { + this.calendarActivitiesPath = calendarActivitiesPath; this.clickDay = this.clickDay.bind(this); this.currentSelectedDate = ''; this.daySpace = 1; @@ -12,25 +30,26 @@ export default class ActivityCalendar { this.daySizeWithSpace = this.daySize + (this.daySpace * 2); this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; this.months = []; + // Loop through the timestamps to create a group of objects // The group of objects will be grouped based on the day of the week they are this.timestampsTmp = []; - var group = 0; + let group = 0; - var today = new Date(); + const today = new Date(); today.setHours(0, 0, 0, 0, 0); - var oneYearAgo = new Date(today); + const oneYearAgo = new Date(today); oneYearAgo.setFullYear(today.getFullYear() - 1); - var days = gl.utils.getDayDifference(oneYearAgo, today); + const days = gl.utils.getDayDifference(oneYearAgo, today); - for (var i = 0; i <= days; i += 1) { - var date = new Date(oneYearAgo); + for (let i = 0; i <= days; i += 1) { + const date = new Date(oneYearAgo); date.setDate(date.getDate() + i); - var day = date.getDay(); - var count = timestamps[date.format('yyyy-mm-dd')]; + const day = date.getDay(); + const count = timestamps[date.format('yyyy-mm-dd')] || 0; // Create a new group array if this is the first day of the week // or if is first object @@ -39,129 +58,119 @@ export default class ActivityCalendar { group += 1; } - var innerArray = this.timestampsTmp[group - 1]; // Push to the inner array the values that will be used to render map - innerArray.push({ - count: count || 0, - date: date, - day: day - }); + const innerArray = this.timestampsTmp[group - 1]; + innerArray.push({ count, date, day }); } // Init color functions - this.colorKey = this.initColorKey(); + this.colorKey = initColorKey(); this.color = this.initColor(); + // Init the svg element - this.renderSvg(group); + this.svg = this.renderSvg(container, group); this.renderDays(); this.renderMonths(); this.renderDayTitles(); this.renderKey(); - this.initTooltips(); + + // Init tooltips + $(`${container} .js-tooltip`).tooltip({ html: true }); } // Add extra padding for the last month label if it is also the last column getExtraWidthPadding(group) { - var extraWidthPadding = 0; - var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); - var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); + let extraWidthPadding = 0; + const lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); + const secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); - if (lastColMonth != secondLastColMonth) { + if (lastColMonth !== secondLastColMonth) { extraWidthPadding = 3; } return extraWidthPadding; } - renderSvg(group) { - var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); - return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); + renderSvg(container, group) { + const width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); + return d3.select(container) + .append('svg') + .attr('width', width) + .attr('height', 167) + .attr('class', 'contrib-calendar'); } renderDays() { - return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { - return function(group, i) { - _.each(group, function(stamp, a) { - var lastMonth, lastMonthX, month, x; + this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g') + .attr('transform', (group, i) => { + _.each(group, (stamp, a) => { if (a === 0 && stamp.day === 0) { - month = stamp.date.getMonth(); - x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; - lastMonth = _.last(_this.months); - if (lastMonth != null) { - lastMonthX = lastMonth.x; - } - if (lastMonth == null) { - return _this.months.push({ - month: month, - x: x - }); - } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { - return _this.months.push({ - month: month, - x: x - }); + const month = stamp.date.getMonth(); + const x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; + const lastMonth = _.last(this.months); + if ( + lastMonth == null || + (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonth.x) + ) { + this.months.push({ month, x }); } } }); - return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; - }; - })(this)).selectAll('rect').data(function(stamp) { - return stamp; - }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { - return function(stamp, i) { - return _this.daySizeWithSpace * stamp.day; - }; - })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { - return function(stamp) { - var contribText, date, dateText; - date = new Date(stamp.date); - contribText = 'No contributions'; - if (stamp.count > 0) { - contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); - } - dateText = date.format('mmm d, yyyy'); - return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText; - }; - })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { - return function(stamp) { - if (stamp.count !== 0) { - return _this.color(Math.min(stamp.count, 40)); - } else { - return '#ededed'; - } - }; - })(this)).attr('data-container', 'body').on('click', this.clickDay); + return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`; + }) + .selectAll('rect') + .data(stamp => stamp) + .enter() + .append('rect') + .attr('x', '0') + .attr('y', stamp => this.daySizeWithSpace * stamp.day) + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('fill', stamp => ( + stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' + )) + .attr('title', stamp => formatTooltipText(stamp)) + .attr('class', 'user-contrib-cell js-tooltip') + .attr('data-container', 'body') + .on('click', this.clickDay); } renderDayTitles() { - var days; - days = [ + const days = [ { text: 'M', - y: 29 + (this.daySizeWithSpace * 1) + y: 29 + (this.daySizeWithSpace * 1), }, { text: 'W', - y: 29 + (this.daySizeWithSpace * 3) + y: 29 + (this.daySizeWithSpace * 3), }, { text: 'F', - y: 29 + (this.daySizeWithSpace * 5) - } + y: 29 + (this.daySizeWithSpace * 5), + }, ]; - return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { - return day.y; - }).text(function(day) { - return day.text; - }).attr('class', 'user-contrib-text'); + this.svg.append('g') + .selectAll('text') + .data(days) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('x', 8) + .attr('y', day => day.y) + .text(day => day.text) + .attr('class', 'user-contrib-text'); } renderMonths() { - return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { - return date.x; - }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { - return function(date) { - return _this.monthNames[date.month]; - }; - })(this)); + this.svg.append('g') + .attr('direction', 'ltr') + .selectAll('text') + .data(this.months) + .enter() + .append('text') + .attr('x', date => date.x) + .attr('y', 10) + .attr('class', 'user-contrib-text') + .text(date => this.monthNames[date.month]); } renderKey() { @@ -169,7 +178,7 @@ export default class ActivityCalendar { const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; this.svg.append('g') - .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) + .attr('transform', `translate(18, ${(this.daySizeWithSpace * 8) + 16})`) .selectAll('rect') .data(keyColors) .enter() @@ -185,43 +194,31 @@ export default class ActivityCalendar { } initColor() { - var colorRange; - colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); } - initColorKey() { - return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); - } - clickDay(stamp) { - var formatted_date; if (this.currentSelectedDate !== stamp.date) { this.currentSelectedDate = stamp.date; - formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); - return $.ajax({ - url: this.calendar_activities_path, - data: { - date: formatted_date - }, + + const date = [ + this.currentSelectedDate.getFullYear(), + this.currentSelectedDate.getMonth() + 1, + this.currentSelectedDate.getDate(), + ].join('-'); + + $.ajax({ + url: this.calendarActivitiesPath, + data: { date }, cache: false, dataType: 'html', - beforeSend: function() { - return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>'); - }, - success: function(data) { - return $('.user-calendar-activities').html(data); - } + beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML), + success: data => $('.user-calendar-activities').html(data), }); } else { this.currentSelectedDate = ''; - return $('.user-calendar-activities').html(''); + $('.user-calendar-activities').html(''); } } - - initTooltips() { - return $('.js-contrib-calendar .js-tooltip').tooltip({ - html: true - }); - } } diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js index ecd8e09161e..33a83f8dae5 100644 --- a/app/assets/javascripts/users/index.js +++ b/app/assets/javascripts/users/index.js @@ -1,7 +1,19 @@ -import ActivityCalendar from './activity_calendar'; -import User from './user'; +import Cookies from 'js-cookie'; +import UserTabs from './user_tabs'; -// use legacy exports until embedded javascript is refactored -window.Calendar = ActivityCalendar; -window.gl = window.gl || {}; -window.gl.User = User; +export default function initUserProfile(action) { + // place profile avatars to top + $('.profile-groups-avatars').tooltip({ + placement: 'top', + }); + + // eslint-disable-next-line no-new + new UserTabs({ parentEl: '.user-profile', action }); + + // hide project limit message + $('.hide-project-limit-message').on('click', (e) => { + e.preventDefault(); + Cookies.set('hide_project_limit_message', 'false'); + $(this).parents('.project-limit-message').remove(); + }); +} diff --git a/app/assets/javascripts/users/user.js b/app/assets/javascripts/users/user.js deleted file mode 100644 index 0b0a3e1afb4..00000000000 --- a/app/assets/javascripts/users/user.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable class-methods-use-this */ - -import Cookies from 'js-cookie'; -import UserTabs from './user_tabs'; - -export default class User { - constructor({ action }) { - this.action = action; - this.placeProfileAvatarsToTop(); - this.initTabs(); - this.hideProjectLimitMessage(); - } - - placeProfileAvatarsToTop() { - $('.profile-groups-avatars').tooltip({ - placement: 'top', - }); - } - - initTabs() { - return new UserTabs({ - parentEl: '.user-profile', - action: this.action, - }); - } - - hideProjectLimitMessage() { - $('.hide-project-limit-message').on('click', (e) => { - e.preventDefault(); - Cookies.set('hide_project_limit_message', 'false'); - $(this).parents('.project-limit-message').remove(); - }); - } -} diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index f8e23c8624d..5fe6603ce7b 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -1,72 +1,76 @@ -/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ - -/* -UserTabs - -Handles persisting and restoring the current tab selection and lazily-loading -content on the Users#show page. - -### Example Markup - - <ul class="nav-links"> - <li class="activity-tab active"> - <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> - Activity - </a> - </li> - <li class="groups-tab"> - <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> - Groups - </a> - </li> - <li class="contributed-tab"> - <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed"> - Contributed projects - </a> - </li> - <li class="projects-tab"> - <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects"> - Personal projects - </a> - </li> - <li class="snippets-tab"> - <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets"> - </a> - </li> - </ul> - - <div class="tab-content"> - <div class="tab-pane" id="activity"> - Activity Content - </div> - <div class="tab-pane" id="groups"> - Groups Content - </div> - <div class="tab-pane" id="contributed"> - Contributed projects content - </div> - <div class="tab-pane" id="projects"> - Projects content - </div> - <div class="tab-pane" id="snippets"> - Snippets content - </div> +import ActivityCalendar from './activity_calendar'; + +/** + * UserTabs + * + * Handles persisting and restoring the current tab selection and lazily-loading + * content on the Users#show page. + * + * ### Example Markup + * + * <ul class="nav-links"> + * <li class="activity-tab active"> + * <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username"> + * Activity + * </a> + * </li> + * <li class="groups-tab"> + * <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups"> + * Groups + * </a> + * </li> + * <li class="contributed-tab"> + * ... + * </li> + * <li class="projects-tab"> + * ... + * </li> + * <li class="snippets-tab"> + * ... + * </li> + * </ul> + * + * <div class="tab-content"> + * <div class="tab-pane" id="activity"> + * Activity Content + * </div> + * <div class="tab-pane" id="groups"> + * Groups Content + * </div> + * <div class="tab-pane" id="contributed"> + * Contributed projects content + * </div> + * <div class="tab-pane" id="projects"> + * Projects content + * </div> + * <div class="tab-pane" id="snippets"> + * Snippets content + * </div> + * </div> + * + * <div class="loading-status"> + * <div class="loading"> + * Loading Animation + * </div> + * </div> + */ + +const CALENDAR_TEMPLATE = ` + <div class="clearfix calendar"> + <div class="js-contrib-calendar"></div> + <div class="calendar-hint"> + Summary of issues, merge requests, push events, and comments + </div> </div> - - <div class="loading-status"> - <div class="loading"> - Loading Animation - </div> - </div> -*/ +`; export default class UserTabs { - constructor ({ defaultAction, action, parentEl }) { + constructor({ defaultAction, action, parentEl }) { this.loaded = {}; this.defaultAction = defaultAction || 'activity'; this.action = action || this.defaultAction; this.$parentEl = $(parentEl) || $(document); - this._location = window.location; + this.windowLocation = window.location; this.$parentEl.find('.nav-links a') .each((i, navLink) => { this.loaded[$(navLink).attr('data-action')] = false; @@ -82,12 +86,10 @@ export default class UserTabs { } bindEvents() { - this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this); - - this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') - .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)); - - this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper); + this.$parentEl + .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') + .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)) + .on('click', '.gl-pagination a', event => this.changeProjectsPage(event)); } changeProjectsPage(e) { @@ -122,7 +124,7 @@ export default class UserTabs { const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; if (loadableActions.indexOf(action) > -1) { - return this.loadTab(action, endpoint); + this.loadTab(action, endpoint); } } @@ -131,25 +133,38 @@ export default class UserTabs { beforeSend: () => this.toggleLoading(true), complete: () => this.toggleLoading(false), dataType: 'json', - type: 'GET', url: endpoint, success: (data) => { const tabSelector = `div#${action}`; this.$parentEl.find(tabSelector).html(data.html); this.loaded[action] = true; - return gl.utils.localTimeAgo($('.js-timeago', tabSelector)); - } + gl.utils.localTimeAgo($('.js-timeago', tabSelector)); + }, }); } loadActivities() { - if (this.loaded['activity']) { + if (this.loaded.activity) { return; } const $calendarWrap = this.$parentEl.find('.user-calendar'); - $calendarWrap.load($calendarWrap.data('href')); + const calendarPath = $calendarWrap.data('calendarPath'); + const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); + + $.ajax({ + dataType: 'json', + url: calendarPath, + success: (activityData) => { + $calendarWrap.html(CALENDAR_TEMPLATE); + + // eslint-disable-next-line no-new + new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath); + }, + }); + + // eslint-disable-next-line no-new new gl.Activities(); - return this.loaded['activity'] = true; + this.loaded.activity = true; } toggleLoading(status) { @@ -158,13 +173,13 @@ export default class UserTabs { } setCurrentAction(source) { - let new_state = source; - new_state = new_state.replace(/\/+$/, ''); - new_state += this._location.search + this._location.hash; + let newState = source; + newState = newState.replace(/\/+$/, ''); + newState += this.windowLocation.search + this.windowLocation.hash; history.replaceState({ - url: new_state - }, document.title, new_state); - return new_state; + url: newState, + }, document.title, newState); + return newState; } getCurrentAction() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js index e8e22ad93a5..744a1cd24fa 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js @@ -108,7 +108,8 @@ export default { </div> <mr-widget-memory-usage v-if="deployment.metrics_url" - :metricsUrl="deployment.metrics_url" + :metrics-url="deployment.metrics_url" + :metrics-monitoring-url="deployment.metrics_monitoring_url" /> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js index 76cb71b6c12..534e2a88eff 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js @@ -7,7 +7,14 @@ import MRWidgetService from '../services/mr_widget_service'; export default { name: 'MemoryUsage', props: { - metricsUrl: { type: String, required: true }, + metricsUrl: { + type: String, + required: true, + }, + metricsMonitoringUrl: { + type: String, + required: true, + }, }, data() { return { @@ -124,7 +131,7 @@ export default { <p v-if="shouldShowMemoryGraph" class="usage-info js-usage-info"> - Memory usage <b>{{memoryChangeType}}</b> from {{memoryFrom}}MB to {{memoryTo}}MB + <a :href="metricsMonitoringUrl">Memory</a> usage <b>{{memoryChangeType}}</b> from {{memoryFrom}}MB to {{memoryTo}}MB </p> <p v-if="shouldShowLoadFailure" diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index 06f7af33f94..0dfa7a31d31 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -35,6 +35,8 @@ width: 40px; height: 40px; padding: 0; + background: $avatar-background; + overflow: hidden; &.avatar-inline { float: none; diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 759401a7806..0ac095f7d8f 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -93,7 +93,7 @@ .is-selected .pika-day, .pika-day:hover, - .is-today .pika-day:hover { + .is-today .pika-day { background: $gl-primary; color: $white-light; box-shadow: none; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 5e410cbf563..3f934403147 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -148,7 +148,6 @@ padding: 5px 8px; color: $gl-text-color; line-height: initial; - text-overflow: ellipsis; border-radius: 2px; white-space: nowrap; overflow: hidden; @@ -203,11 +202,6 @@ border-radius: $border-radius-base; box-shadow: 0 2px 4px $dropdown-shadow-color; - @media (max-width: $screen-sm-min) { - width: 100%; - min-width: 180px; - } - &.dropdown-open-left { right: 0; left: auto; @@ -289,6 +283,11 @@ padding: 5px 8px; color: $gl-text-color-secondary; } + + .badge + span:not(.badge) { + // Expects up to 3 digits on the badge + margin-right: 40px; + } } .droplab-dropdown { @@ -373,7 +372,6 @@ .dropdown-menu, .dropdown-menu-nav { max-width: 280px; - width: auto; } } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index c7c2684d548..8ad082f7a65 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -163,8 +163,18 @@ td.blame-commit { padding: 5px 10px; min-width: 400px; + max-width: 400px; background: $gray-light; border-left: 3px solid; + + .commit-row-title { + display: flex; + } + + .item-title { + flex: 1; + margin-right: 0.5em; + } } @for $i from 0 through 5 { diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 41184907abb..ab2abaca33a 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -393,7 +393,8 @@ @media (max-width: $screen-xs) { .filter-dropdown-container { .dropdown-toggle, - .dropdown { + .dropdown, + .dropdown-menu { width: 100%; } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 20fb10c28d4..605f4284bb5 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -132,6 +132,22 @@ header { } } + &.navbar-gitlab-new { + .fa-times { + display: none; + } + + .menu-expanded { + .fa-ellipsis-v { + display: none; + } + + .fa-times { + display: block; + } + } + } + .global-dropdown { position: absolute; left: -10px; @@ -171,6 +187,19 @@ header { min-height: $header-height; padding-left: 30px; + &.menu-expanded { + @media (max-width: $screen-xs-max) { + .header-logo, + .title-container { + display: none; + } + + .navbar-collapse { + display: block; + } + } + } + .dropdown-menu { margin-top: -5px; } diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 1b8eed59a56..261642f4174 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -125,3 +125,29 @@ overflow: hidden; text-overflow: ellipsis; } + +/* + * Mixin for status badges, as used for pipelines and commit signatures + */ +@mixin status-color($color-light, $color-main, $color-dark) { + color: $color-main; + border-color: $color-main; + + &:not(span):hover { + background-color: $color-light; + color: $color-dark; + border-color: $color-dark; + + svg { + fill: $color-dark; + } + } + + svg { + fill: $color-main; + } +} + +@mixin green-status-color { + @include status-color($green-50, $green-500, $green-700); +} diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index e71bf04aec7..35b4d77a5ab 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -182,6 +182,12 @@ } } + &.nav-controls-new-nav { + > .dropdown { + margin-right: 0; + } + } + > .btn-grouped { float: none; } @@ -190,14 +196,6 @@ display: none; } - .btn, - .dropdown, - .dropdown-toggle, - input, - form { - height: 35px; - } - input { display: inline-block; position: relative; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 542b641e3dd..49b2f0e43a4 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -92,7 +92,6 @@ @mixin maintain-sidebar-dimensions { display: block; width: $gutter-width; - padding: 10px 0; } .issues-bulk-update.right-sidebar { @@ -104,6 +103,15 @@ &.right-sidebar-expanded { @include maintain-sidebar-dimensions; width: $gutter-width; + + .issuable-sidebar-header { + // matches `.top-area .nav-controls` for issuable index pages + padding: 11px 0; + } + + .block:last-of-type { + border: none; + } } &.right-sidebar-collapsed { diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 8a58c1ed567..bf5f124d142 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -11,8 +11,17 @@ } img { - max-width: 100%; + /*max-width: 100%;*/ margin: 0 0 8px; + min-width: 200px; + min-height: 100px; + background-color: $gray-lightest; + } + + img.js-lazy-loaded { + min-width: inherit; + min-height: inherit; + background-color: inherit; } p a:not(.no-attachment-icon) img { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 6b96a88e7ac..80d634487ff 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -380,7 +380,9 @@ $issue-boards-card-shadow: rgba(186, 186, 186, 0.5); * Avatar */ $avatar_radius: 50%; -$avatar-border: $border-color; +$avatar-border: $gray-normal; +$avatar-border-hover: $gray-darker; +$avatar-background: $gray-lightest; $gl-avatar-size: 40px; /* diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index e1873506bec..360ffda8d71 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -21,6 +21,11 @@ header.navbar-gitlab-new { padding-right: 0; color: currentColor; + img { + height: 28px; + margin-right: 10px; + } + > a { display: flex; align-items: center; @@ -41,10 +46,22 @@ header.navbar-gitlab-new { } } + .logo-text { + line-height: initial; + + svg { + width: 55px; + height: 15px; + margin: 0; + fill: $white-light; + } + } + &:hover, &:focus { - color: $tanuki-yellow; - text-decoration: none; + .logo-text svg { + fill: $tanuki-yellow; + } } } } @@ -274,7 +291,7 @@ header.navbar-gitlab-new { .breadcrumbs { display: flex; - min-height: 60px; + min-height: 61px; color: $gl-text-color; border-bottom: 1px solid $border-color; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index ce8f4c41cb5..ae43197a1a6 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -23,6 +23,10 @@ $new-sidebar-width: 220px; position: fixed; height: 100%; } + + .issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header { + padding: 10px 0 15px; + } } .context-header { @@ -165,7 +169,6 @@ $new-sidebar-width: 220px; > li { a { - font-size: 12px; padding: 8px 16px 8px 24px; &:hover, @@ -262,7 +265,7 @@ $new-sidebar-width: 220px; @media (min-width: $screen-sm-min) { height: 475px; // Needed for PhantomJS // scss-lint:disable DuplicateProperty - height: calc(100vh - 120px); + height: calc(100vh - 180px); // scss-lint:enable DuplicateProperty } } diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index df858cffe09..6039cda96d8 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -431,7 +431,10 @@ margin: 5px; } -.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar { +.page-with-layout-nav.page-with-sub-nav .issue-boards-sidebar, +.page-with-new-sidebar.page-with-sidebar .issue-boards-sidebar { + position: absolute; + &.right-sidebar { top: 0; bottom: 0; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index a5e4c3311f8..cd9f2d787c5 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -54,7 +54,11 @@ .mr-widget-pipeline-graph { display: inline-block; vertical-align: middle; - margin: 0 -6px 0 0; + margin-right: 4px; + + .stage-cell .stage-container { + margin: 3px 3px 3px 0; + } .dropdown-menu { margin-top: 11px; @@ -279,3 +283,63 @@ color: $gl-text-color; } } + + +.gpg-status-box { + &.valid { + @include green-status-color; + } + + &.invalid { + @include status-color($gray-dark, $gray, $common-gray-dark); + border-color: $common-gray-light; + } +} + +.gpg-popover-status { + display: flex; + align-items: center; + font-weight: normal; + line-height: 1.5; +} + +.gpg-popover-icon { + // same margin as .s32.avatar + margin-right: $btn-side-margin; + + &.valid { + svg { + border: 1px solid $brand-success; + + fill: $brand-success; + } + } + + &.invalid { + svg { + border: 1px solid $common-gray-light; + + fill: $common-gray-light; + } + } + + svg { + width: 32px; + height: 32px; + border-radius: 50%; + vertical-align: middle; + } +} + +.gpg-popover-user-link { + display: flex; + align-items: center; + margin-bottom: $gl-padding / 2; + text-decoration: none; + color: $gl-text-color; +} + +.commit .gpg-popover-help-link { + display: block; + color: $link-color; +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index aa04e490649..eb269df46fe 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -211,6 +211,10 @@ -webkit-overflow-scrolling: touch; } + &.affix-top .issuable-sidebar { + height: 100%; + } + &.right-sidebar-expanded { width: $gutter_width; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 9637d26e56d..d3862df20d3 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -597,7 +597,7 @@ } // Dropdown button in mini pipeline graph -.mini-pipeline-graph-dropdown-toggle { +button.mini-pipeline-graph-dropdown-toggle { border-radius: 100px; background-color: $white-light; border-width: 1px; @@ -608,6 +608,7 @@ padding: 0; transition: all 0.2s linear; position: relative; + vertical-align: middle; > .fa.fa-caret-down { position: absolute; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 22672614e0d..14ad06b0ac2 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -391,3 +391,26 @@ table.u2f-registrations { margin-bottom: 0; } } + +.gpg-email-badge { + display: inline; + margin-right: $gl-padding / 2; + + .gpg-email-badge-email { + display: inline; + margin-right: $gl-padding / 4; + } + + .label-verification-status { + border-width: 1px; + border-style: solid; + + &.verified { + @include green-status-color; + } + + &.unverified { + @include status-color($gray-dark, $gray, $common-gray-dark); + } + } +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c1423965d0a..b3a90dff89a 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -707,6 +707,7 @@ pre.light-well { background-color: transparent; border: 0; text-align: left; + text-overflow: ellipsis; } .protected-branches-list, @@ -742,7 +743,8 @@ pre.light-well { } } -.protected-tags-list { +.protected-tags-list, +.protected-branches-list { .dropdown-menu-toggle { width: 100%; max-width: 300px; diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 67ad1ae60af..36f622db136 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,22 +1,3 @@ -@mixin status-color($color-light, $color-main, $color-dark) { - color: $color-main; - border-color: $color-main; - - &:not(span):hover { - background-color: $color-light; - color: $color-dark; - border-color: $color-dark; - - svg { - fill: $color-dark; - } - } - - svg { - fill: $color-main; - } -} - .ci-status { padding: 2px 7px 4px; border: 1px solid $gray-darker; @@ -41,7 +22,7 @@ } &.ci-success { - @include status-color($green-50, $green-500, $green-700); + @include green-status-color; } &.ci-canceled, diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index c1bc4c0d675..8367c22d1ca 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -76,88 +76,13 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.require(:application_setting).permit( - application_setting_params_ce + visible_application_setting_attributes ) end - def application_setting_params_ce - [ - :admin_notification_email, - :after_sign_out_path, - :after_sign_up_text, - :akismet_api_key, - :akismet_enabled, - :container_registry_token_expire_delay, - :default_artifacts_expire_in, - :default_branch_protection, - :default_group_visibility, - :default_project_visibility, - :default_projects_limit, - :default_snippet_visibility, - :domain_blacklist_enabled, + def visible_application_setting_attributes + ApplicationSettingsHelper.visible_attributes + [ :domain_blacklist_file, - :domain_blacklist_raw, - :domain_whitelist_raw, - :email_author_in_body, - :enabled_git_access_protocol, - :gravatar_enabled, - :help_page_text, - :help_page_hide_commercial_content, - :help_page_support_url, - :home_page_url, - :housekeeping_bitmaps_enabled, - :housekeeping_enabled, - :housekeeping_full_repack_period, - :housekeeping_gc_period, - :housekeeping_incremental_repack_period, - :html_emails_enabled, - :koding_enabled, - :koding_url, - :password_authentication_enabled, - :plantuml_enabled, - :plantuml_url, - :max_artifacts_size, - :max_attachment_size, - :max_pages_size, - :metrics_enabled, - :metrics_host, - :metrics_method_call_threshold, - :metrics_packet_size, - :metrics_pool_size, - :metrics_port, - :metrics_sample_interval, - :metrics_timeout, - :performance_bar_allowed_group_id, - :performance_bar_enabled, - :recaptcha_enabled, - :recaptcha_private_key, - :recaptcha_site_key, - :repository_checks_enabled, - :require_two_factor_authentication, - :session_expire_delay, - :sign_in_text, - :signup_enabled, - :sentry_dsn, - :sentry_enabled, - :clientside_sentry_dsn, - :clientside_sentry_enabled, - :send_user_confirmation_email, - :shared_runners_enabled, - :shared_runners_text, - :sidekiq_throttling_enabled, - :sidekiq_throttling_factor, - :two_factor_grace_period, - :user_default_external, - :user_oauth_applications, - :unique_ips_limit_per_user, - :unique_ips_limit_time_window, - :unique_ips_limit_enabled, - :version_check_enabled, - :terminal_max_session_time, - :polling_interval_multiplier, - :prometheus_metrics_enabled, - :usage_ping_enabled, - disabled_oauth_sign_in_sources: [], import_sources: [], repository_storages: [], diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 434ff6b2a62..16590e66d61 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -50,6 +50,6 @@ class Admin::ApplicationsController < Admin::ApplicationController # Only allow a trusted parameter "white list" through. def application_params - params[:doorkeeper_application].permit(:name, :redirect_uri, :scopes) + params.require(:doorkeeper_application).permit(:name, :redirect_uri, :trusted, :scopes) end end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 8360ce08bdc..05e749c00c0 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -1,6 +1,6 @@ class Admin::DashboardController < Admin::ApplicationController def index - @projects = Project.with_route.limit(10) + @projects = Project.without_deleted.with_route.limit(10) @users = User.limit(10) @groups = Group.with_route.limit(10) end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 984d5398708..0b6cd71e651 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -3,18 +3,9 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :group, only: [:show, :transfer] def index - params[:sort] ||= 'latest_activity_desc' - @projects = Project.with_statistics - @projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present? - @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? - @projects = @projects.with_push if params[:with_push].present? - @projects = @projects.abandoned if params[:abandoned].present? - @projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present? - @projects = @projects.non_archived unless params[:archived].present? - @projects = @projects.personal(current_user) if params[:personal].present? - @projects = @projects.search(params[:name]) if params[:name].present? - @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) + finder = Admin::ProjectsFinder.new(params: params, current_user: current_user) + @projects = finder.execute + @sort = finder.sort respond_to do |format| format.html diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 43462b13903..d14b1dbecf6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -70,6 +70,16 @@ class ApplicationController < ActionController::Base protected + def append_info_to_payload(payload) + super + payload[:remote_ip] = request.remote_ip + + if current_user.present? + payload[:user_id] = current_user.id + payload[:username] = current_user.username + end + end + # This filter handles both private tokens and personal access tokens def authenticate_user_from_private_token! token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb new file mode 100644 index 00000000000..6779cc6ddac --- /dev/null +++ b/app/controllers/profiles/gpg_keys_controller.rb @@ -0,0 +1,47 @@ +class Profiles::GpgKeysController < Profiles::ApplicationController + before_action :set_gpg_key, only: [:destroy, :revoke] + + def index + @gpg_keys = current_user.gpg_keys + @gpg_key = GpgKey.new + end + + def create + @gpg_key = current_user.gpg_keys.new(gpg_key_params) + + if @gpg_key.save + redirect_to profile_gpg_keys_path + else + @gpg_keys = current_user.gpg_keys.select(&:persisted?) + render :index + end + end + + def destroy + @gpg_key.destroy + + respond_to do |format| + format.html { redirect_to profile_gpg_keys_url, status: 302 } + format.js { head :ok } + end + end + + def revoke + @gpg_key.revoke + + respond_to do |format| + format.html { redirect_to profile_gpg_keys_url, status: 302 } + format.js { head :ok } + end + end + + private + + def gpg_key_params + params.require(:gpg_key).permit(:key) + end + + def set_gpg_key + @gpg_key = current_user.gpg_keys.find(params[:id]) + end +end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 95de3a44641..221e01b415a 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -22,6 +22,7 @@ class Projects::ApplicationController < ApplicationController def project return @project if @project + return nil unless params[:project_id] || params[:id] path = File.join(params[:namespace_id], params[:project_id] || params[:id]) auth_proc = ->(project) { !project.pending_delete? } diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index 6c25cd83a24..06ba73d8e8d 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:index] before_action :no_cache_headers, except: [:index] - def build - build_status = Gitlab::Badge::Build::Status + def pipeline + pipeline_status = Gitlab::Badge::Pipeline::Status .new(project, params[:ref]) - render_badge build_status + render_badge pipeline_status end def coverage diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 4346ef8de02..fee74725d6c 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -4,6 +4,7 @@ class Projects::BlobController < Projects::ApplicationController include CreatesCommit include RendersBlob include ActionView::Helpers::SanitizeHelper + include ApplicationHelper # Raised when given an invalid file path InvalidPathError = Class.new(StandardError) @@ -40,7 +41,7 @@ class Projects::BlobController < Projects::ApplicationController assign_ref_vars @last_commit = @repository.last_commit_for_path(@commit.id, tree.path) || @commit - render 'projects/tree/show' + show_new_repo? ? render('projects/tree/show') : render('show') end format.json do diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 37b5a6e9d48..2de9900d449 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -6,18 +6,9 @@ class Projects::CommitsController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_download_code! + before_action :set_commits def show - @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i - search = params[:search] - - @commits = - if search.present? - @repository.find_commits_by_message(search, @ref, @path, @limit, @offset) - else - @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) - end - @note_counts = project.notes.where(commit_id: @commits.map(&:id)) .group(:commit_id).count @@ -37,4 +28,33 @@ class Projects::CommitsController < Projects::ApplicationController end end end + + def signatures + respond_to do |format| + format.json do + render json: { + signatures: @commits.select(&:has_signature?).map do |commit| + { + commit_sha: commit.sha, + html: view_to_html_string('projects/commit/_signature', signature: commit.signature) + } + end + } + end + end + end + + private + + def set_commits + @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i + search = params[:search] + + @commits = + if search.present? + @repository.find_commits_by_message(search, @ref, @path, @limit, @offset) + else + @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) + end + end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 0ac9da2ff0f..e2ccabb22db 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -8,7 +8,6 @@ class Projects::IssuesController < Projects::ApplicationController prepend_before_action :authenticate_user!, only: [:new] - before_action :redirect_to_external_issue_tracker, only: [:index, :new] before_action :check_issues_available! before_action :issue, except: [:index, :new, :create, :bulk_update] @@ -243,19 +242,19 @@ class Projects::IssuesController < Projects::ApplicationController end def authorize_update_issue! - return render_404 unless can?(current_user, :update_issue, @issue) + render_404 unless can?(current_user, :update_issue, @issue) end def authorize_admin_issues! - return render_404 unless can?(current_user, :admin_issue, @project) + render_404 unless can?(current_user, :admin_issue, @project) end def authorize_create_merge_request! - return render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user) + render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user) end def check_issues_available! - return render_404 unless @project.feature_available?(:issues, current_user) && @project.default_issues_tracker? + return render_404 unless @project.feature_available?(:issues, current_user) end def redirect_to_external_issue_tracker diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 70c41da4de5..d361e661d0e 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -223,12 +223,18 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo if can?(current_user, :read_environment, environment) && environment.has_metrics? metrics_project_environment_deployment_path(environment.project, environment, deployment) end + + metrics_monitoring_url = + if can?(current_user, :read_environment, environment) + environment_metrics_path(environment) + end { id: environment.id, name: environment.name, url: project_environment_path(project, environment), metrics_url: metrics_url, + metrics_monitoring_url: metrics_monitoring_url, stop_url: stop_url, external_url: environment.external_url, external_url_formatted: environment.formatted_external_url, diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index ea7ceb3eaa5..15a2ff56b92 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -35,7 +35,7 @@ module Projects def define_badges_variables @ref = params[:ref] || @project.default_branch || 'master' - @badges = [Gitlab::Badge::Build::Status, + @badges = [Gitlab::Badge::Pipeline::Status, Gitlab::Badge::Coverage::Report] @badges.map! do |badge| diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index ac98470c2b1..968d880886c 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -55,6 +55,9 @@ class Projects::WikisController < Projects::ApplicationController else render 'edit' end + rescue WikiPage::PageChangedError + @conflict = true + render 'edit' end def create @@ -119,6 +122,6 @@ class Projects::WikisController < Projects::ApplicationController end def wiki_params - params.require(:wiki).permit(:title, :content, :format, :message) + params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha) end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8149dee59bd..4f99845a5d9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -312,10 +312,10 @@ class ProjectsController < Projects::ApplicationController def project_params params.require(:project) - .permit(project_params_ce) + .permit(project_params_attributes) end - def project_params_ce + def project_params_attributes [ :avatar, :build_allow_git_fetch, diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 0e8a57f8e03..9e743685d60 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -5,6 +5,14 @@ class SessionsController < Devise::SessionsController skip_before_action :check_two_factor_requirement, only: [:destroy] + # Explicitly call protect from forgery before anything else. Otherwise the + # CSFR-token might be cleared before authentication is done. This was the case + # when LDAP was enabled and the `OmniauthCallbacksController` is loaded + # + # *Note:* `prepend: true` is the default for rails4, but this will be changed + # to `prepend: false` in rails5. + protect_from_forgery prepend: true, with: :exception + prepend_before_action :check_initial_setup, only: [:new] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] @@ -15,12 +23,7 @@ class SessionsController < Devise::SessionsController def new set_minimum_password_length - @ldap_servers = - if Gitlab.config.ldap.enabled - Gitlab::LDAP::Config.servers - else - [] - end + @ldap_servers = Gitlab::LDAP::Config.available_servers super end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8131eba6a2f..4ee855806ab 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -73,10 +73,7 @@ class UsersController < ApplicationController end def calendar - calendar = contributions_calendar - @activity_dates = calendar.activity_dates - - render 'calendar', layout: false + render json: contributions_calendar.activity_dates end def calendar_activities diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb new file mode 100644 index 00000000000..a5ba791a513 --- /dev/null +++ b/app/finders/admin/projects_finder.rb @@ -0,0 +1,33 @@ +class Admin::ProjectsFinder + attr_reader :sort, :namespace_id, :visibility_level, :with_push, + :abandoned, :last_repository_check_failed, :archived, + :personal, :name, :page, :current_user + + def initialize(params:, current_user:) + @current_user = current_user + @sort = params.fetch(:sort) { 'latest_activity_desc' } + @namespace_id = params[:namespace_id] + @visibility_level = params[:visibility_level] + @with_push = params[:with_push] + @abandoned = params[:abandoned] + @last_repository_check_failed = params[:last_repository_check_failed] + @archived = params[:archived] + @personal = params[:personal] + @name = params[:name] + @page = params[:page] + end + + def execute + items = Project.with_statistics + items = items.in_namespace(namespace_id) if namespace_id.present? + items = items.where(visibility_level: visibility_level) if visibility_level.present? + items = items.with_push if with_push.present? + items = items.abandoned if abandoned.present? + items = items.where(last_repository_check_failed: true) if last_repository_check_failed.present? + items = items.non_archived unless archived.present? + items = items.personal(current_user) if personal.present? + items = items.search(name) if name.present? + items = items.sort(sort) + items.includes(:namespace).order("namespaces.path, projects.name ASC").page(page) + end +end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index fc63e30c8fb..6fe17a2b99d 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -11,6 +11,7 @@ # group_id: integer # project_id: integer # milestone_title: string +# author_id: integer # assignee_id: integer # search: string # label_name: string diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 2fc34f186ad..771da3d441d 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -10,6 +10,7 @@ # group_id: integer # project_id: integer # milestone_title: string +# author_id: integer # assignee_id: integer # search: string # label_name: string diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1c165700b19..2e31e0ead21 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -301,4 +301,8 @@ module ApplicationHelper def show_new_nav? cookies["new_nav"] == "true" end + + def show_new_repo? + cookies["new_repo"] == "true" + end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 29b88c60dab..6825adcb39f 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -1,4 +1,5 @@ module ApplicationSettingsHelper + extend self delegate :gravatar_enabled?, :signup_enabled?, :password_authentication_enabled?, @@ -91,4 +92,88 @@ module ApplicationSettingsHelper def sidekiq_queue_options_for_select options_for_select(Sidekiq::Queue.all.map(&:name), @application_setting.sidekiq_throttling_queues) end + + def visible_attributes + [ + :admin_notification_email, + :after_sign_out_path, + :after_sign_up_text, + :akismet_api_key, + :akismet_enabled, + :clientside_sentry_dsn, + :clientside_sentry_enabled, + :container_registry_token_expire_delay, + :default_artifacts_expire_in, + :default_branch_protection, + :default_group_visibility, + :default_project_visibility, + :default_projects_limit, + :default_snippet_visibility, + :disabled_oauth_sign_in_sources, + :domain_blacklist_enabled, + :domain_blacklist_raw, + :domain_whitelist_raw, + :email_author_in_body, + :enabled_git_access_protocol, + :gravatar_enabled, + :help_page_hide_commercial_content, + :help_page_support_url, + :help_page_text, + :home_page_url, + :housekeeping_bitmaps_enabled, + :housekeeping_enabled, + :housekeeping_full_repack_period, + :housekeeping_gc_period, + :housekeeping_incremental_repack_period, + :html_emails_enabled, + :import_sources, + :koding_enabled, + :koding_url, + :max_artifacts_size, + :max_attachment_size, + :max_pages_size, + :metrics_enabled, + :metrics_host, + :metrics_method_call_threshold, + :metrics_packet_size, + :metrics_pool_size, + :metrics_port, + :metrics_sample_interval, + :metrics_timeout, + :password_authentication_enabled, + :performance_bar_allowed_group_id, + :performance_bar_enabled, + :plantuml_enabled, + :plantuml_url, + :polling_interval_multiplier, + :prometheus_metrics_enabled, + :recaptcha_enabled, + :recaptcha_private_key, + :recaptcha_site_key, + :repository_checks_enabled, + :repository_storages, + :require_two_factor_authentication, + :restricted_visibility_levels, + :send_user_confirmation_email, + :sentry_dsn, + :sentry_enabled, + :session_expire_delay, + :shared_runners_enabled, + :shared_runners_text, + :sidekiq_throttling_enabled, + :sidekiq_throttling_factor, + :sidekiq_throttling_queues, + :sign_in_text, + :signup_enabled, + :terminal_max_session_time, + :two_factor_grace_period, + :unique_ips_limit_enabled, + :unique_ips_limit_per_user, + :unique_ips_limit_time_window, + :usage_ping_enabled, + :user_default_external, + :user_oauth_applications, + :version_check_enabled + ] + end end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index bbe7f3c8fb4..0e068d4b51c 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -11,17 +11,12 @@ module AvatarsHelper def user_avatar_without_link(options = {}) avatar_size = options[:size] || 16 user_name = options[:user].try(:name) || options[:user_name] - css_class = options[:css_class] || '' avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size) data_attributes = { container: 'body' } - if options[:lazy] - data_attributes[:src] = avatar_url - end - image_tag( - options[:lazy] ? '' : avatar_url, - class: "avatar has-tooltip s#{avatar_size} #{css_class}", + avatar_url, + class: %W[avatar has-tooltip s#{avatar_size}].push(*options[:css_class]), alt: "#{user_name}'s avatar", title: user_name, data: data_attributes diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index d08e346d605..69220a1c0f6 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -113,6 +113,10 @@ module CommitsHelper commit_action_link('cherry-pick', commit, continue_to_path, btn_class: btn_class, has_tooltip: has_tooltip) end + def commit_signature_badge_classes(additional_classes) + %w(btn status-box gpg-status-box) + Array(additional_classes) + end + protected # Private: Returns a link to a person. If the person has a matching user and diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index fdbca789d21..5f11fe62030 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -61,8 +61,8 @@ module EmailsHelper else image_tag( image_url('mailers/gitlab_header_logo.gif'), - size: "55x50", - alt: "GitLab" + size: '55x50', + alt: 'GitLab' ) end end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 425af547330..f4fad7150e8 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -354,4 +354,14 @@ module IssuablesHelper params[:format] = :json if issuable.is_a?(Issue) end end + + def issuable_sidebar_options(issuable, can_edit_issuable) + { + endpoint: "#{issuable_json_path(issuable)}?basic=true", + editable: can_edit_issuable, + currentUser: current_user.as_json(only: [:username, :id, :name], methods: :avatar_url), + rootPath: root_path, + fullPath: @project.full_path + } + end end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 42b6cfdf02f..7e1ccb23e9e 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -17,10 +17,10 @@ module IssuesHelper return '' if project.nil? url = - if options[:only_path] - project.issues_tracker.issue_path(issue_iid) + if options[:internal] + url_for_internal_issue(issue_iid, project, options) else - project.issues_tracker.issue_url(issue_iid) + url_for_tracker_issue(issue_iid, project, options) end # Ensure we return a valid URL to prevent possible XSS. @@ -29,6 +29,24 @@ module IssuesHelper '' end + def url_for_tracker_issue(issue_iid, project, options) + if options[:only_path] + project.issues_tracker.issue_path(issue_iid) + else + project.issues_tracker.issue_url(issue_iid) + end + end + + def url_for_internal_issue(issue_iid, project = @project, options = {}) + helpers = Gitlab::Routing.url_helpers + + if options[:only_path] + helpers.namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: issue_iid) + else + helpers.namespace_project_issue_url(namespace_id: project.namespace, project_id: project, id: issue_iid) + end + end + def bulk_update_milestone_options milestones = @project.milestones.active.reorder(due_date: :asc, title: :asc).to_a milestones.unshift(Milestone::None) @@ -158,4 +176,6 @@ module IssuesHelper # Required for Banzai::Filter::IssueReferenceFilter module_function :url_for_issue + module_function :url_for_internal_issue + module_function :url_for_tracker_issue end diff --git a/app/helpers/lazy_image_tag_helper.rb b/app/helpers/lazy_image_tag_helper.rb new file mode 100644 index 00000000000..2c5619ac41b --- /dev/null +++ b/app/helpers/lazy_image_tag_helper.rb @@ -0,0 +1,24 @@ +module LazyImageTagHelper + def placeholder_image + "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + end + + # Override the default ActionView `image_tag` helper to support lazy-loading + def image_tag(source, options = {}) + options = options.symbolize_keys + + unless options.delete(:lazy) == false + options[:data] ||= {} + options[:data][:src] = path_to_image(source) + options[:class] ||= "" + options[:class] << " lazy" + + source = placeholder_image + end + + super(source, options) + end + + # Required for Banzai::Filter::ImageLazyLoadFilter + module_function :placeholder_image +end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 0a0881d95cf..505579674c9 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -130,4 +130,14 @@ module NotesHelper can?(current_user, :create_note, @project) end end + + def initial_notes_data(autocomplete) + { + notesUrl: notes_url, + notesIds: @notes.map(&:id), + now: Time.now.to_i, + diffView: diff_view, + autocomplete: autocomplete + } + end end diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb index 209bd56b78a..08fd97cd048 100644 --- a/app/helpers/system_note_helper.rb +++ b/app/helpers/system_note_helper.rb @@ -18,7 +18,8 @@ module SystemNoteHelper 'milestone' => 'icon_clock_o', 'discussion' => 'icon_comment_o', 'moved' => 'icon_arrow_circle_o_right', - 'outdated' => 'icon_edit' + 'outdated' => 'icon_edit', + 'duplicate' => 'icon_clone' }.freeze def icon_for_system_note(note) diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index a48d4475e97..ce435ca2241 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -8,6 +8,6 @@ module TriggersHelper end def service_trigger_url(service) - "#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" + "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger" end end diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb index 456598b4c28..3b175251446 100644 --- a/app/helpers/version_check_helper.rb +++ b/app/helpers/version_check_helper.rb @@ -2,7 +2,7 @@ module VersionCheckHelper def version_status_badge if Rails.env.production? && current_application_settings.version_check_enabled image_url = VersionCheck.new.url - image_tag image_url, class: 'js-version-status-badge' + image_tag image_url, class: 'js-version-status-badge', lazy: false end end end diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb index 256cbcd73a1..c401030e34a 100644 --- a/app/mailers/emails/profile.rb +++ b/app/mailers/emails/profile.rb @@ -14,7 +14,7 @@ module Emails end def new_ssh_key_email(key_id) - @key = Key.find_by_id(key_id) + @key = Key.find_by(id: key_id) return unless @key @@ -22,5 +22,15 @@ module Emails @target_url = user_url(@user) mail(to: @user.notification_email, subject: subject("SSH key was added to your account")) end + + def new_gpg_key_email(gpg_key_id) + @gpg_key = GpgKey.find_by(id: gpg_key_id) + + return unless @gpg_key + + @current_user = @user = @gpg_key.user + @target_url = user_url(@user) + mail(to: @user.notification_email, subject: subject("GPG key was added to your account")) + end end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 898ce45f60e..bd7c4cd45ea 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -315,7 +315,9 @@ class ApplicationSetting < ActiveRecord::Base Array(read_attribute(:repository_storages)) end + # DEPRECATED # repository_storage is still required in the API. Remove in 9.0 + # Still used in API v3 def repository_storage repository_storages.first end diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb index c52b6f15913..25ecf2d5937 100644 --- a/app/models/chat_team.rb +++ b/app/models/chat_team.rb @@ -3,4 +3,13 @@ class ChatTeam < ActiveRecord::Base validates :namespace, uniqueness: true belongs_to :namespace + + def remove_mattermost_team(current_user) + Mattermost::Team.new(current_user).destroy(team_id: team_id) + rescue Mattermost::ClientError => e + # Either the group is not found, or the user doesn't have the proper + # access on the mattermost instance. In the first case, we're done either way + # in the latter case, we can't recover by retrying, so we just log what happened + Rails.logger.error("Mattermost team deletion failed: #{e}") + end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 416a2a33378..8be2dee6479 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -219,6 +219,7 @@ module Ci variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group variables += secret_variables(environment: environment) variables += trigger_request.user_variables if trigger_request + variables += pipeline.variables.map(&:to_runner_variable) variables += pipeline.pipeline_schedule.job_variables if pipeline.pipeline_schedule variables += persisted_environment_variables if environment diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index b646b32fc64..d2abcf30034 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -15,13 +15,14 @@ module Ci has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :builds, foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent + has_many :variables, class_name: 'Ci::PipelineVariable' # Merge requests for which the current pipeline is running against # the merge request's latest commit. has_many :merge_requests, foreign_key: "head_pipeline_id" has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build' - has_many :retryable_builds, -> { latest.failed_or_canceled }, foreign_key: :commit_id, class_name: 'Ci::Build' + has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus' has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :artifacts, -> { latest.with_artifacts_not_expired.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build' diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index e4ae1b35f66..085eeeae157 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -40,10 +40,6 @@ module Ci update_attribute(:active, false) end - def runnable_by_owner? - Ability.allowed?(owner, :create_pipeline, project) - end - def set_next_run_at self.next_run_at = Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now) end diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb new file mode 100644 index 00000000000..00b419c3efa --- /dev/null +++ b/app/models/ci/pipeline_variable.rb @@ -0,0 +1,10 @@ +module Ci + class PipelineVariable < ActiveRecord::Base + extend Ci::Model + include HasVariable + + belongs_to :pipeline + + validates :key, uniqueness: { scope: :pipeline_id } + end +end diff --git a/app/models/commit.rb b/app/models/commit.rb index 1e19f00106a..7940733f557 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -234,6 +234,14 @@ class Commit @statuses[ref] = pipelines.latest_status(ref) end + def signature + return @signature if defined?(@signature) + + @signature = gpg_commit.signature + end + + delegate :has_signature?, to: :gpg_commit + def revert_branch_name "revert-#{short_id}" end @@ -382,4 +390,8 @@ class Commit def merged_merge_request_no_cache(user) MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? end + + def gpg_commit + @gpg_commit ||= Gitlab::Gpg::Commit.new(self) + end end diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 95152dcd68c..48547a938fc 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -11,7 +11,7 @@ module CacheMarkdownField extend ActiveSupport::Concern # Increment this number every time the renderer changes its output - CACHE_VERSION = 1 + CACHE_VERSION = 2 # changes to these attributes cause the cache to be invalidates INVALIDATED_BY = %w[author project].freeze diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index fc6b840f7a8..ef95d6b0f98 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -17,7 +17,13 @@ module ProtectedRef class_methods do def protected_ref_access_levels(*types) types.each do |type| - has_many :"#{type}_access_levels", dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + # We need to set `inverse_of` to make sure the `belongs_to`-object is set + # when creating children using `accepts_nested_attributes_for`. + # + # If we don't `protected_branch` or `protected_tag` would be empty and + # `project` cannot be delegated to it, which in turn would cause validations + # to fail. + has_many :"#{type}_access_levels", dependent: :destroy, inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." } @@ -25,8 +31,8 @@ module ProtectedRef end end - def protected_ref_accessible_to?(ref, user, action:) - access_levels_for_ref(ref, action: action).any? do |access_level| + def protected_ref_accessible_to?(ref, user, action:, protected_refs: nil) + access_levels_for_ref(ref, action: action, protected_refs: protected_refs).any? do |access_level| access_level.check_access(user) end end @@ -37,8 +43,9 @@ module ProtectedRef end end - def access_levels_for_ref(ref, action:) - self.matching(ref).map(&:"#{action}_access_levels").flatten + def access_levels_for_ref(ref, action:, protected_refs: nil) + self.matching(ref, protected_refs: protected_refs) + .map(&:"#{action}_access_levels").flatten end def matching(ref_name, protected_refs: nil) diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb new file mode 100644 index 00000000000..3df60ddc950 --- /dev/null +++ b/app/models/gpg_key.rb @@ -0,0 +1,107 @@ +class GpgKey < ActiveRecord::Base + KEY_PREFIX = '-----BEGIN PGP PUBLIC KEY BLOCK-----'.freeze + KEY_SUFFIX = '-----END PGP PUBLIC KEY BLOCK-----'.freeze + + include ShaAttribute + + sha_attribute :primary_keyid + sha_attribute :fingerprint + + belongs_to :user + has_many :gpg_signatures + + validates :user, presence: true + + validates :key, + presence: true, + uniqueness: true, + format: { + with: /\A#{KEY_PREFIX}((?!#{KEY_PREFIX})(?!#{KEY_SUFFIX}).)+#{KEY_SUFFIX}\Z/m, + message: "is invalid. A valid public GPG key begins with '#{KEY_PREFIX}' and ends with '#{KEY_SUFFIX}'" + } + + validates :fingerprint, + presence: true, + uniqueness: true, + # only validate when the `key` is valid, as we don't want the user to show + # the error about the fingerprint + unless: -> { errors.has_key?(:key) } + + validates :primary_keyid, + presence: true, + uniqueness: true, + # only validate when the `key` is valid, as we don't want the user to show + # the error about the fingerprint + unless: -> { errors.has_key?(:key) } + + before_validation :extract_fingerprint, :extract_primary_keyid + after_commit :update_invalid_gpg_signatures, on: :create + after_commit :notify_user, on: :create + + def primary_keyid + super&.upcase + end + + def fingerprint + super&.upcase + end + + def key=(value) + super(value&.strip) + end + + def user_infos + @user_infos ||= Gitlab::Gpg.user_infos_from_key(key) + end + + def verified_user_infos + user_infos.select do |user_info| + user_info[:email] == user.email + end + end + + def emails_with_verified_status + user_infos.map do |user_info| + [ + user_info[:email], + user_info[:email] == user.email + ] + end.to_h + end + + def verified? + emails_with_verified_status.any? { |_email, verified| verified } + end + + def update_invalid_gpg_signatures + InvalidGpgSignatureUpdateWorker.perform_async(self.id) + end + + def revoke + GpgSignature.where(gpg_key: self, valid_signature: true).update_all( + gpg_key_id: nil, + valid_signature: false, + updated_at: Time.zone.now + ) + + destroy + end + + private + + def extract_fingerprint + # we can assume that the result only contains one item as the validation + # only allows one key + self.fingerprint = Gitlab::Gpg.fingerprints_from_key(key).first + end + + def extract_primary_keyid + # we can assume that the result only contains one item as the validation + # only allows one key + self.primary_keyid = Gitlab::Gpg.primary_keyids_from_key(key).first + end + + def notify_user + NotificationService.new.new_gpg_key(self) + end +end diff --git a/app/models/gpg_signature.rb b/app/models/gpg_signature.rb new file mode 100644 index 00000000000..1ac0e123ff1 --- /dev/null +++ b/app/models/gpg_signature.rb @@ -0,0 +1,21 @@ +class GpgSignature < ActiveRecord::Base + include ShaAttribute + + sha_attribute :commit_sha + sha_attribute :gpg_key_primary_keyid + + belongs_to :project + belongs_to :gpg_key + + validates :commit_sha, presence: true + validates :project_id, presence: true + validates :gpg_key_primary_keyid, presence: true + + def gpg_key_primary_keyid + super&.upcase + end + + def commit + project.commit(commit_sha) + end +end diff --git a/app/models/group.rb b/app/models/group.rb index dfa4e8adedd..bd5735ed82e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -167,10 +167,14 @@ class Group < Namespace end def has_owner?(user) + return false unless user + members_with_parents.owners.where(user_id: user).any? end def has_master?(user) + return false unless user + members_with_parents.masters.where(user_id: user).any? end @@ -212,7 +216,7 @@ class Group < Namespace end def members_with_parents - GroupMember.non_request.where(source_id: ancestors.pluck(:id).push(id)) + GroupMember.active.where(source_id: ancestors.pluck(:id).push(id)).where.not(user_id: nil) end def users_with_parents diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e4e7999d0f2..a910099b4c1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -596,7 +596,7 @@ class MergeRequest < ActiveRecord::Base # running `ReferenceExtractor` on each of them separately. # This optimization does not apply to issues from external sources. def cache_merge_request_closes_issues!(current_user) - return if project.has_external_issue_tracker? + return unless project.issues_enabled? transaction do self.merge_requests_closing_issues.delete_all diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 4b141945ab4..ec87aee9310 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -236,10 +236,21 @@ class MergeRequestDiff < ActiveRecord::Base def create_merge_request_diff_files(diffs) rows = diffs.map.with_index do |diff, index| - diff.to_hash.merge( + diff_hash = diff.to_hash.merge( + binary: false, merge_request_diff_id: self.id, relative_order: index ) + + # Compatibility with old diffs created with Psych. + diff_hash.tap do |hash| + diff_text = hash[:diff] + + if diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only? + hash[:binary] = true + hash[:diff] = [diff_text].pack('m0') + end + end end Gitlab::Database.bulk_insert('merge_request_diff_files', rows) @@ -268,9 +279,7 @@ class MergeRequestDiff < ActiveRecord::Base st_diffs end elsif merge_request_diff_files.present? - merge_request_diff_files - .as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS) - .map(&:with_indifferent_access) + merge_request_diff_files.map(&:to_hash) end end diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb index 598ebd4d829..1199ff5af22 100644 --- a/app/models/merge_request_diff_file.rb +++ b/app/models/merge_request_diff_file.rb @@ -8,4 +8,14 @@ class MergeRequestDiffFile < ActiveRecord::Base encode_utf8(diff) if diff.respond_to?(:encoding) end + + def diff + binary? ? super.unpack('m0').first : super + end + + def to_hash + keys = Gitlab::Git::Diff::SERIALIZE_KEYS - [:diff] + + as_json(only: keys).merge(diff: diff).with_indifferent_access + end end diff --git a/app/models/project.rb b/app/models/project.rb index 0b357d5d003..d827bfaa806 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -734,9 +734,11 @@ class Project < ActiveRecord::Base end def get_issue(issue_id, current_user) - if default_issues_tracker? - IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id) - else + issue = IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id) if issues_enabled? + + if issue + issue + elsif external_issue_tracker ExternalIssue.new(issue_id, self) end end @@ -758,7 +760,7 @@ class Project < ActiveRecord::Base end def external_issue_reference_pattern - external_issue_tracker.class.reference_pattern + external_issue_tracker.class.reference_pattern(only_long: issues_enabled?) end def default_issues_tracker? diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 6d6a3ae3647..31984c5d7ed 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -8,8 +8,12 @@ class IssueTrackerService < Service # This pattern does not support cross-project references # The other code assumes that this pattern is a superset of all # overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN - def self.reference_pattern - @reference_pattern ||= %r{(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)} + def self.reference_pattern(only_long: false) + if only_long + %r{(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)} + else + %r{(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)} + end end def default? diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 5498a2e17b2..2aa19443198 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -3,10 +3,8 @@ class JiraService < IssueTrackerService validates :url, url: true, presence: true, if: :activated? validates :api_url, url: true, allow_blank: true - validates :project_key, presence: true, if: :activated? - prop_accessor :username, :password, :url, :api_url, :project_key, - :jira_issue_transition_id, :title, :description + prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id, :title, :description before_update :reset_password @@ -18,7 +16,7 @@ class JiraService < IssueTrackerService end # {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1 - def self.reference_pattern + def self.reference_pattern(only_long: true) @reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} end @@ -54,10 +52,6 @@ class JiraService < IssueTrackerService @client ||= JIRA::Client.new(options) end - def jira_project - @jira_project ||= jira_request { client.Project.find(project_key) } - end - def help "You need to configure JIRA before enabling this service. For more details read the @@ -88,18 +82,12 @@ class JiraService < IssueTrackerService [ { type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true }, { type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' }, - { type: 'text', name: 'project_key', placeholder: 'Project Key', required: true }, { type: 'text', name: 'username', placeholder: '', required: true }, { type: 'password', name: 'password', placeholder: '', required: true }, - { type: 'text', name: 'jira_issue_transition_id', placeholder: '' } + { type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID', placeholder: '' } ] end - # URLs to redirect from Gitlab issues pages to jira issue tracker - def project_url - "#{url}/issues/?jql=project=#{project_key}" - end - def issues_url "#{url}/browse/:id" end @@ -172,7 +160,10 @@ class JiraService < IssueTrackerService def test(_) result = test_settings - { success: result.present?, result: result } + success = result.present? + result = @error if @error && !success + + { success: success, result: result } end # JIRA does not need test data. @@ -184,7 +175,7 @@ class JiraService < IssueTrackerService def test_settings return unless client_url.present? # Test settings by getting the project - jira_request { jira_project.present? } + jira_request { client.ServerInfo.all.attrs } end private @@ -300,7 +291,8 @@ class JiraService < IssueTrackerService yield rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError, OpenSSL::SSL::SSLError => e - Rails.logger.info "#{self.class.name} Send message ERROR: #{client_url} - #{e.message}" + @error = e.message + Rails.logger.info "#{self.class.name} Send message ERROR: #{client_url} - #{@error}" nil end diff --git a/app/models/repository.rb b/app/models/repository.rb index 8663cf5e602..50b7a477904 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -457,10 +457,6 @@ class Repository nil end - def blob_by_oid(oid) - Gitlab::Git::Blob.raw(self, oid) - end - def root_ref if raw_repository raw_repository.root_ref @@ -471,8 +467,17 @@ class Repository end cache_method :root_ref + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314 def exists? - refs_directory_exists? + return false unless path_with_namespace + + Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| + if enabled + raw_repository.exists? + else + refs_directory_exists? + end + end end cache_method :exists? @@ -1095,8 +1100,6 @@ class Repository end def refs_directory_exists? - return false unless path_with_namespace - File.exist?(File.join(path_to_repo, 'refs')) end diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb index 414c95f7705..0b33e45473b 100644 --- a/app/models/system_note_metadata.rb +++ b/app/models/system_note_metadata.rb @@ -1,7 +1,8 @@ class SystemNoteMetadata < ActiveRecord::Base ICON_TYPES = %w[ commit description merge confidential visible label assignee cross_reference - title time_tracking branch milestone discussion task moved opened closed merged + title time_tracking branch milestone discussion task moved + opened closed merged duplicate outdated ].freeze diff --git a/app/models/user.rb b/app/models/user.rb index c26be6d05a2..6e66c587a1f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -76,6 +76,7 @@ class User < ActiveRecord::Base where(type.not_eq('DeployKey').or(type.eq(nil))) end, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :gpg_keys has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -157,6 +158,7 @@ class User < ActiveRecord::Base before_save :ensure_authentication_token, :ensure_incoming_email_token before_save :ensure_user_rights_and_limits, if: :external_changed? after_save :ensure_namespace_correct + after_commit :update_invalid_gpg_signatures, on: :update, if: -> { previous_changes.key?('email') } after_initialize :set_projects_limit after_destroy :post_destroy_hook @@ -512,6 +514,10 @@ class User < ActiveRecord::Base end end + def update_invalid_gpg_signatures + gpg_keys.each(&:update_invalid_gpg_signatures) + end + # Returns the groups a user has access to def authorized_groups union = Gitlab::SQL::Union diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 224eb3cd4d0..148998bc9be 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,4 +1,6 @@ class WikiPage + PageChangedError = Class.new(StandardError) + include ActiveModel::Validations include ActiveModel::Conversion include StaticModel @@ -136,6 +138,10 @@ class WikiPage versions.first end + def last_commit_sha + commit&.sha + end + # Returns the Date that this latest version was # created on. def created_at @@ -182,17 +188,22 @@ class WikiPage # Updates an existing Wiki Page, creating a new version. # - # new_content - The raw markup content to replace the existing. - # format - Optional symbol representing the content format. - # See ProjectWiki::MARKUPS Hash for available formats. - # message - Optional commit message to set on the new version. + # new_content - The raw markup content to replace the existing. + # format - Optional symbol representing the content format. + # See ProjectWiki::MARKUPS Hash for available formats. + # message - Optional commit message to set on the new version. + # last_commit_sha - Optional last commit sha to validate the page unchanged. # # Returns the String SHA1 of the newly created page # or False if the save was unsuccessful. - def update(new_content = "", format = :markdown, message = nil) + def update(new_content, format: :markdown, message: nil, last_commit_sha: nil) @attributes[:content] = new_content @attributes[:format] = format + if last_commit_sha && last_commit_sha != self.last_commit_sha + raise PageChangedError.new("You are attempting to update a page that has changed since you started editing it.") + end + save :update_page, @page, content, format, message end diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 386822d3ff6..984e5482288 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -1,17 +1,15 @@ module Ci class BuildPolicy < CommitStatusPolicy - condition(:protected_action) do - next false unless @subject.action? - + condition(:protected_ref) do access = ::Gitlab::UserAccess.new(@user, project: @subject.project) if @subject.tag? !access.can_create_tag?(@subject.ref) else - !access.can_merge_to_branch?(@subject.ref) + !access.can_update_branch?(@subject.ref) end end - rule { protected_action }.prevent :update_build + rule { protected_ref }.prevent :update_build end end diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index a2dde95dbc8..4e689a9efd5 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -1,5 +1,17 @@ module Ci class PipelinePolicy < BasePolicy delegate { @subject.project } + + condition(:protected_ref) do + access = ::Gitlab::UserAccess.new(@user, project: @subject.project) + + if @subject.tag? + !access.can_create_tag?(@subject.ref) + else + !access.can_update_branch?(@subject.ref) + end + end + + rule { protected_ref }.prevent :update_pipeline end end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 55eefa76d3f..1c91425f589 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy prevent :log_in end - rule { ~restricted_public_level }.policy do + rule { admin | ~restricted_public_level }.policy do enable :read_users_list end end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 323131c0f7e..0133091db57 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -10,7 +10,8 @@ class ProjectPolicy < BasePolicy desc "User is a project owner" condition :owner do - @user && project.owner == @user || (project.group && project.group.has_owner?(@user)) + (project.owner.present? && project.owner == @user) || + project.group&.has_owner?(@user) end desc "Project has public builds enabled" @@ -287,9 +288,6 @@ class ProjectPolicy < BasePolicy prevent :create_issue prevent :update_issue prevent :admin_issue - end - - rule { issues_disabled & default_issues_tracker }.policy do prevent :read_issue end diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 20f9938f038..743a08acefe 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -16,7 +16,8 @@ class BuildDetailsEntity < JobEntity end expose :path do |build| - project_merge_request_path(project, build.merge_request) + project_merge_request_path(build.merge_request.project, + build.merge_request) end end diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb index 068013c8829..c75431a79ae 100644 --- a/app/serializers/deploy_key_entity.rb +++ b/app/serializers/deploy_key_entity.rb @@ -9,7 +9,7 @@ class DeployKeyEntity < Grape::Entity expose :created_at expose :updated_at expose :projects, using: ProjectEntity do |deploy_key| - deploy_key.projects.select { |project| options[:user].can?(:read_project, project) } + deploy_key.projects.without_deleted.select { |project| options[:user].can?(:read_project, project) } end expose :can_edit diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 273386776fa..884b681ff81 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -15,12 +15,48 @@ module Ci pipeline_schedule: schedule ) + result = validate(current_user || trigger_request.trigger.owner, + ignore_skip_ci: ignore_skip_ci, + save_on_errors: save_on_errors) + + return result if result + + begin + Ci::Pipeline.transaction do + pipeline.save! + + yield(pipeline) if block_given? + + Ci::CreatePipelineStagesService + .new(project, current_user) + .execute(pipeline) + end + rescue ActiveRecord::RecordInvalid => e + return error("Failed to persist the pipeline: #{e}") + end + + update_merge_requests_head_pipeline + + cancel_pending_pipelines if project.auto_cancel_pending_pipelines? + + pipeline_created_counter.increment(source: source) + + pipeline.tap(&:process!) + end + + private + + def validate(triggering_user, ignore_skip_ci:, save_on_errors:) unless project.builds_enabled? return error('Pipeline is disabled') end - unless trigger_request || can?(current_user, :create_pipeline, project) - return error('Insufficient permissions to create a new pipeline') + unless allowed_to_trigger_pipeline?(triggering_user) + if can?(triggering_user, :create_pipeline, project) + return error("Insufficient permissions for protected ref '#{ref}'") + else + return error('Insufficient permissions to create a new pipeline') + end end unless branch? || tag? @@ -46,24 +82,29 @@ module Ci unless pipeline.has_stage_seeds? return error('No stages / jobs for this pipeline.') end + end - Ci::Pipeline.transaction do - update_merge_requests_head_pipeline if pipeline.save - - Ci::CreatePipelineStagesService - .new(project, current_user) - .execute(pipeline) + def allowed_to_trigger_pipeline?(triggering_user) + if triggering_user + allowed_to_create?(triggering_user) + else # legacy triggers don't have a corresponding user + !project.protected_for?(ref) end + end - cancel_pending_pipelines if project.auto_cancel_pending_pipelines? + def allowed_to_create?(triggering_user) + access = Gitlab::UserAccess.new(triggering_user, project: project) - pipeline_created_counter.increment(source: source) - - pipeline.tap(&:process!) + can?(triggering_user, :create_pipeline, project) && + if branch? + access.can_update_branch?(ref) + elsif tag? + access.can_create_tag?(ref) + else + true # Allow it for now and we'll reject when we check ref existence + end end - private - def update_merge_requests_head_pipeline return unless pipeline.latest? @@ -113,15 +154,21 @@ module Ci end def branch? - project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref) + return @is_branch if defined?(@is_branch) + + @is_branch = + project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref) end def tag? - project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref) + return @is_tag if defined?(@is_tag) + + @is_tag = + project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref) end def ref - Gitlab::Git.ref_name(origin_ref) + @ref ||= Gitlab::Git.ref_name(origin_ref) end def valid_sha? diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb index cf3d4aee2bc..b2aa457bbd5 100644 --- a/app/services/ci/create_trigger_request_service.rb +++ b/app/services/ci/create_trigger_request_service.rb @@ -1,12 +1,19 @@ +# This class is deprecated because we're closing Ci::TriggerRequest. +# New class is PipelineTriggerService (app/services/ci/pipeline_trigger_service.rb) +# which is integrated with Ci::PipelineVariable instaed of Ci::TriggerRequest. +# We remove this class after we removed v1 and v3 API. This class is still being +# referred by such legacy code. module Ci - class CreateTriggerRequestService - def execute(project, trigger, ref, variables = nil) + module CreateTriggerRequestService + Result = Struct.new(:trigger_request, :pipeline) + + def self.execute(project, trigger, ref, variables = nil) trigger_request = trigger.trigger_requests.create(variables: variables) pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref) .execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request) - trigger_request if pipeline.persisted? + Result.new(trigger_request, pipeline) end end end diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb new file mode 100644 index 00000000000..1e5ad28ba57 --- /dev/null +++ b/app/services/ci/pipeline_trigger_service.rb @@ -0,0 +1,44 @@ +module Ci + class PipelineTriggerService < BaseService + def execute + if trigger_from_token + create_pipeline_from_trigger(trigger_from_token) + end + end + + private + + def create_pipeline_from_trigger(trigger) + # this check is to not leak the presence of the project if user cannot read it + return unless trigger.project == project + + pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref]) + .execute(:trigger, ignore_skip_ci: true) do |pipeline| + trigger.trigger_requests.create!(pipeline: pipeline) + create_pipeline_variables!(pipeline) + end + + if pipeline.persisted? + success(pipeline: pipeline) + else + error(pipeline.errors.messages, 400) + end + end + + def trigger_from_token + return @trigger if defined?(@trigger) + + @trigger = Ci::Trigger.find_by_token(params[:token].to_s) + end + + def create_pipeline_variables!(pipeline) + return unless params[:variables] + + variables = params[:variables].map do |key, value| + { key: key, value: value } + end + + pipeline.variables.create!(variables) + end + end +end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 20d1fb29289..bb7680c5054 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -56,6 +56,8 @@ class GitPushService < BaseService perform_housekeeping update_caches + + update_signatures end def update_gitattributes @@ -80,6 +82,12 @@ class GitPushService < BaseService ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size]) end + def update_signatures + @push_commits.each do |commit| + CreateGpgSignatureWorker.perform_async(commit.sha, @project.id) + end + end + # Schedules processing of commit messages. def process_commit_messages default = is_default_branch? diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index 80c51cb5a72..f565612a89d 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -21,6 +21,8 @@ module Groups DestroyService.new(group, current_user).execute end + group.chat_team&.remove_mattermost_team(current_user) + group.really_destroy! end end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 9078b1f0983..ea497729115 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -58,6 +58,7 @@ class IssuableBaseService < BaseService params.delete(:assignee_ids) params.delete(:assignee_id) params.delete(:due_date) + params.delete(:canonical_issue_id) end filter_assignee(issuable) diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 34199eb5d13..4c198fc96ea 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -7,6 +7,14 @@ module Issues issue_data end + def reopen_service + Issues::ReopenService + end + + def close_service + Issues::CloseService + end + private def create_assignee_note(issue, old_assignees) diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb index ddef5281498..74459c3342c 100644 --- a/app/services/issues/close_service.rb +++ b/app/services/issues/close_service.rb @@ -16,13 +16,13 @@ module Issues # The code calling this method is responsible for ensuring that a user is # allowed to close the given issue. def close_issue(issue, commit: nil, notifications: true, system_note: true) - if project.jira_tracker? && project.jira_service.active + if project.jira_tracker? && project.jira_service.active && issue.is_a?(ExternalIssue) project.jira_service.close_issue(commit, issue) todo_service.close_issue(issue, current_user) return issue end - if project.default_issues_tracker? && issue.close + if project.issues_enabled? && issue.close event_service.close_issue(issue, current_user) create_note(issue, commit) if system_note notification_service.close_issue(issue, current_user) if notifications diff --git a/app/services/issues/duplicate_service.rb b/app/services/issues/duplicate_service.rb new file mode 100644 index 00000000000..5c0854e664d --- /dev/null +++ b/app/services/issues/duplicate_service.rb @@ -0,0 +1,24 @@ +module Issues + class DuplicateService < Issues::BaseService + def execute(duplicate_issue, canonical_issue) + return if canonical_issue == duplicate_issue + return unless can?(current_user, :update_issue, duplicate_issue) + return unless can?(current_user, :create_note, canonical_issue) + + create_issue_duplicate_note(duplicate_issue, canonical_issue) + create_issue_canonical_note(canonical_issue, duplicate_issue) + + close_service.new(project, current_user, {}).execute(duplicate_issue) + end + + private + + def create_issue_duplicate_note(duplicate_issue, canonical_issue) + SystemNoteService.mark_duplicate_issue(duplicate_issue, duplicate_issue.project, current_user, canonical_issue) + end + + def create_issue_canonical_note(canonical_issue, duplicate_issue) + SystemNoteService.mark_canonical_issue_of_duplicate(canonical_issue, canonical_issue.project, current_user, duplicate_issue) + end + end +end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index cd9f9a4a16e..8d918ccc635 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -5,6 +5,7 @@ module Issues def execute(issue) handle_move_between_iids(issue) filter_spam_check_params + change_issue_duplicate(issue) update(issue) end @@ -53,14 +54,6 @@ module Issues end end - def reopen_service - Issues::ReopenService - end - - def close_service - Issues::CloseService - end - def handle_move_between_iids(issue) return unless params[:move_between_iids] @@ -72,6 +65,15 @@ module Issues issue.move_between(issue_before, issue_after) end + def change_issue_duplicate(issue) + canonical_issue_id = params.delete(:canonical_issue_id) + canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id) + + if canonical_issue + Issues::DuplicateService.new(project, current_user).execute(issue, canonical_issue) + end + end + private def get_issue_if_allowed(project, iid) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 3a98a5f6b64..b94921d2a08 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -17,6 +17,16 @@ class NotificationService end end + # Always notify the user about gpg key added + # + # This is a security email so it will be sent even if the user user disabled + # notifications + def new_gpg_key(gpg_key) + if gpg_key.user + mailer.new_gpg_key_email(gpg_key.id).deliver_later + end + end + # Always notify user about email added to profile def new_email(email) if email.user diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index e2b2660ea71..f6e8b6655f2 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -15,40 +15,48 @@ module Projects def execute return false unless can?(current_user, :remove_project, project) - repo_path = project.path_with_namespace - wiki_path = repo_path + '.wiki' - # Flush the cache for both repositories. This has to be done _before_ # removing the physical repositories as some expiration code depends on # Git data (e.g. a list of branch names). - flush_caches(project, wiki_path) + flush_caches(project) Projects::UnlinkForkService.new(project, current_user).execute - Project.transaction do - project.team.truncate - project.destroy! - - unless remove_legacy_registry_tags - raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.') - end - - unless remove_repository(repo_path) - raise_error('Failed to remove project repository. Please try again or contact administrator.') - end + attempt_destroy_transaction(project) - unless remove_repository(wiki_path) - raise_error('Failed to remove wiki repository. Please try again or contact administrator.') - end - end - - log_info("Project \"#{project.path_with_namespace}\" was removed") system_hook_service.execute_hooks_for(project, :destroy) + log_info("Project \"#{project.full_path}\" was removed") + true + rescue => error + attempt_rollback(project, error.message) + false + rescue Exception => error # rubocop:disable Lint/RescueException + # Project.transaction can raise Exception + attempt_rollback(project, error.message) + raise end private + def repo_path + project.path_with_namespace + end + + def wiki_path + repo_path + '.wiki' + end + + def trash_repositories! + unless remove_repository(repo_path) + raise_error('Failed to remove project repository. Please try again or contact administrator.') + end + + unless remove_repository(wiki_path) + raise_error('Failed to remove wiki repository. Please try again or contact administrator.') + end + end + def remove_repository(path) # Skip repository removal. We use this flag when remove user or group return true if params[:skip_repo] == true @@ -70,6 +78,26 @@ module Projects end end + def attempt_rollback(project, message) + return unless project + + project.update_attributes(delete_error: message, pending_delete: false) + log_error("Deletion failed on #{project.full_path} with the following message: #{message}") + end + + def attempt_destroy_transaction(project) + Project.transaction do + unless remove_legacy_registry_tags + raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.') + end + + trash_repositories! + + project.team.truncate + project.destroy! + end + end + ## # This method makes sure that we correctly remove registry tags # for legacy image repository (when repository path equals project path). @@ -96,7 +124,7 @@ module Projects "#{path}+#{project.id}#{DELETED_FLAG}" end - def flush_caches(project, wiki_path) + def flush_caches(project) project.repository.before_delete Repository.new(wiki_path, project).before_delete diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index e60b854f916..749a1cc56d8 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -130,7 +130,11 @@ module Projects end def max_size - current_application_settings.max_pages_size.megabytes || MAX_SIZE + max_pages_size = current_application_settings.max_pages_size.megabytes + + return MAX_SIZE if max_pages_size.zero? + + [max_pages_size, MAX_SIZE].min end def tmp_path diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 30ca95eef7a..d81035e4eba 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -5,7 +5,7 @@ module Projects return error('New visibility level not allowed!') end - if project.has_container_registry_tags? + if renaming_project_with_container_registry_tags? return error('Cannot rename project because it contains container registry tags!') end @@ -44,6 +44,13 @@ module Projects true end + def renaming_project_with_container_registry_tags? + new_path = params[:path] + + new_path && new_path != project.path && + project.has_container_registry_tags? + end + def changing_default_branch? new_branch = params[:default_branch] diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb index 6f82159e6c7..5dc1b91d2c0 100644 --- a/app/services/quick_actions/interpret_service.rb +++ b/app/services/quick_actions/interpret_service.rb @@ -471,6 +471,24 @@ module QuickActions end end + desc 'Mark this issue as a duplicate of another issue' + explanation do |duplicate_reference| + "Marks this issue as a duplicate of #{duplicate_reference}." + end + params '#issue' + condition do + issuable.is_a?(Issue) && + issuable.persisted? && + current_user.can?(:"update_#{issuable.to_ability_name}", issuable) + end + command :duplicate do |duplicate_param| + canonical_issue = extract_references(duplicate_param, :issue).first + + if canonical_issue.present? + @updates[:canonical_issue_id] = canonical_issue.id + end + end + def extract_users(params) return [] if params.nil? diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index da0f21d449a..2dbee9c246e 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -552,6 +552,44 @@ module SystemNoteService create_note(NoteSummary.new(noteable, project, author, body, action: 'moved')) end + # Called when a Noteable has been marked as a duplicate of another Issue + # + # noteable - Noteable object + # project - Project owning noteable + # author - User performing the change + # canonical_issue - Issue that this is a duplicate of + # + # Example Note text: + # + # "marked this issue as a duplicate of #1234" + # + # "marked this issue as a duplicate of other_project#5678" + # + # Returns the created Note object + def mark_duplicate_issue(noteable, project, author, canonical_issue) + body = "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}" + create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate')) + end + + # Called when a Noteable has been marked as the canonical Issue of a duplicate + # + # noteable - Noteable object + # project - Project owning noteable + # author - User performing the change + # duplicate_issue - Issue that was a duplicate of this + # + # Example Note text: + # + # "marked #1234 as a duplicate of this issue" + # + # "marked other_project#5678 as a duplicate of this issue" + # + # Returns the created Note object + def mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue) + body = "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue" + create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate')) + end + private def notes_for_mentioner(mentioner, noteable, notes) diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb index 8f6a50da838..c628e6781af 100644 --- a/app/services/wiki_pages/update_service.rb +++ b/app/services/wiki_pages/update_service.rb @@ -1,7 +1,7 @@ module WikiPages class UpdateService < WikiPages::BaseService def execute(page) - if page.update(@params[:content], @params[:format], @params[:message]) + if page.update(@params[:content], format: @params[:format], message: @params[:message], last_commit_sha: @params[:last_commit_sha]) execute_hooks(page, 'update') end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 26f7c1a473a..8bb2a563990 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -315,7 +315,9 @@ %fieldset %legend Metrics - Prometheus %p - Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available + Enable a Prometheus metrics endpoint at + %code= metrics_path + to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available = link_to 'here', admin_health_check_path \. This setting requires a = link_to 'restart', help_page_path('administration/restart_gitlab') @@ -327,10 +329,13 @@ = f.label :prometheus_metrics_enabled do = f.check_box :prometheus_metrics_enabled Enable Prometheus Metrics - - unless Gitlab::Metrics.metrics_folder_present? - .help-block - %strong.cred WARNING: - Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory. + - unless Gitlab::Metrics.metrics_folder_present? + .help-block + %strong.cred WARNING: + Environment variable + %code prometheus_multiproc_dir + does not exist or is not pointing to a valid directory. + = link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory') %fieldset %legend Profiling - Performance Bar diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index 061f8991b11..93827d6a1ab 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -6,6 +6,7 @@ .col-sm-10 = f.text_field :name, class: 'form-control' = doorkeeper_errors_for application, :name + = content_tag :div, class: 'form-group' do = f.label :redirect_uri, class: 'col-sm-2 control-label' .col-sm-10 @@ -19,6 +20,13 @@ %code= Doorkeeper.configuration.native_redirect_uri for local tests + = content_tag :div, class: 'form-group' do + = f.label :trusted, class: 'col-sm-2 control-label' + .col-sm-10 + = f.check_box :trusted + %span.help-block + Trusted applications are automatically authorized on GitLab OAuth flow. + .form-group = f.label :scopes, class: 'col-sm-2 control-label' .col-sm-10 diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index eb4293c7e37..94d33fa6489 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -11,6 +11,7 @@ %th Name %th Callback URL %th Clients + %th Trusted %th %th %tbody.oauth-applications @@ -19,5 +20,6 @@ %td= link_to application.name, admin_application_path(application) %td= application.redirect_uri %td= application.access_tokens.map(&:resource_owner_id).uniq.count + %td= application.trusted? ? 'Y': 'N' %td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link' %td= render 'delete_form', application: application diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 14683cc66e9..5125aa21b06 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -23,6 +23,12 @@ %div %span.monospace= uri + %tr + %td + Trusted + %td + = @application.trusted? ? 'Y' : 'N' + = render "shared/tokens/scopes_list", token: @application .form-actions diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 128b5dc01ab..8e94e68bc11 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -150,7 +150,7 @@ .well-segment.well-centered = link_to admin_groups_path do %h3.text-center - Groups + Groups: = number_with_delimiter(Group.count) %hr = link_to 'New group', new_admin_group_path, class: "btn btn-new" diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 2da8f615470..126550ee10e 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -2,26 +2,6 @@ = render "admin/dashboard/head" %div{ class: container_class } - - %p.prepend-top-default - %span - To register a new Runner you should enter the following registration - token. - With this token the Runner will request a unique Runner token and use - that for future communication. - %br - Registration token is - %code#runners-token= current_application_settings.runners_registration_token - - .bs-callout.clearfix - .pull-left - %p - You can reset runners registration token by pressing a button below. - .prepend-top-10 - = button_to "Reset runners registration token", reset_runners_token_admin_application_settings_path, - method: :put, class: 'btn btn-default', - data: { confirm: 'Are you sure you want to reset registration token?' } - .bs-callout %p A 'Runner' is a process which runs a job. @@ -46,6 +26,19 @@ %span.label.label-danger paused \- Runner will not receive any new jobs + .bs-callout.clearfix + .pull-left + %p + You can reset runners registration token by pressing a button below. + .prepend-top-10 + = button_to _("Reset runners registration token"), reset_runners_token_admin_application_settings_path, + method: :put, class: 'btn btn-default', + data: { confirm: _("Are you sure you want to reset registration token?") } + + = render partial: 'ci/runner/how_to_setup_runner', + locals: { registration_token: current_application_settings.runners_registration_token, + type: 'shared' } + .append-bottom-20.clearfix .pull-left = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml new file mode 100644 index 00000000000..b75dab0acc5 --- /dev/null +++ b/app/views/ci/runner/_how_to_setup_runner.html.haml @@ -0,0 +1,16 @@ +- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank' +.bs-callout.help-callout + %h4= _("How to setup a #{type} Runner for a new project") + + %ol + %li + = _("Install a Runner compatible with GitLab CI") + = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe + %li + = _("Specify the following URL during the Runner setup:") + %code= root_url(only_path: false) + %li + = _("Use the following registration token during setup:") + %code#registration_token= registration_token + %li + = _("Start the Runner!") diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index e80d10dc8f1..bfd7dd25a7d 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -7,6 +7,6 @@ %span.light - has_icon = provider_has_icon?(provider) = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" - %fieldset + %fieldset.prepend-top-10 = check_box_tag :remember_me - = label_tag :remember_me, 'Remember Me' + = label_tag :remember_me, 'Remember me' diff --git a/app/views/groups/_shared_projects.html.haml b/app/views/groups/_shared_projects.html.haml deleted file mode 100644 index b1694c919d0..00000000000 --- a/app/views/groups/_shared_projects.html.haml +++ /dev/null @@ -1 +0,0 @@ -= render 'shared/projects/list', projects: projects, stars: false, skip_namespace: false diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index bc3293fd100..1927c448c92 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -76,6 +76,7 @@ = link_to "Settings", profile_path %li = link_to "Turn on new navigation", profile_preferences_path(anchor: "new-navigation") + = render 'shared/repo/user_dropdown_link' %li.divider %li = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link" diff --git a/app/views/layouts/header/_new.html.haml b/app/views/layouts/header/_new.html.haml index 4697d91724b..54f0d2872d6 100644 --- a/app/views/layouts/header/_new.html.haml +++ b/app/views/layouts/header/_new.html.haml @@ -6,8 +6,8 @@ %h1.title = link_to root_path, title: 'Dashboard' do = brand_header_logo - %span.hidden-xs - GitLab + %span.logo-text.hidden-xs + = render 'shared/logo_type.svg' - if current_user = render "layouts/nav/new_dashboard" @@ -70,6 +70,7 @@ = link_to "Settings", profile_path %li = link_to "Turn off new navigation", profile_preferences_path(anchor: "new-navigation") + = render 'shared/repo/user_dropdown_link' %li.divider %li = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link" @@ -81,6 +82,6 @@ %button.navbar-toggle.hidden-sm.hidden-md.hidden-lg{ type: 'button' } %span.sr-only Toggle navigation = icon('ellipsis-v', class: 'js-navbar-toggle-right') - = icon('times', class: 'js-navbar-toggle-left', style: 'display: none;') + = icon('times', class: 'js-navbar-toggle-left') = render 'shared/outdated_browser' diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml index 224b24befbe..78927bfffcd 100644 --- a/app/views/layouts/help.html.haml +++ b/app/views/layouts/help.html.haml @@ -1,3 +1,4 @@ +- @breadcrumb_title = "Help" - page_title "Help" - header_title "Help", help_path diff --git a/app/views/layouts/nav/_new_profile_sidebar.html.haml b/app/views/layouts/nav/_new_profile_sidebar.html.haml index 239e6b949e2..6bbd569583e 100644 --- a/app/views/layouts/nav/_new_profile_sidebar.html.haml +++ b/app/views/layouts/nav/_new_profile_sidebar.html.haml @@ -47,6 +47,10 @@ = link_to profile_keys_path, title: 'SSH Keys' do %span SSH Keys + = nav_link(controller: :gpg_keys) do + = link_to profile_gpg_keys_path, title: 'GPG Keys' do + %span + GPG Keys = nav_link(controller: :preferences) do = link_to profile_preferences_path, title: 'Preferences' do %span diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index 21f175291fa..00395b222e4 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -75,10 +75,10 @@ Registry - if project_nav_tab? :issues - = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do + = nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do %span - - if @project.default_issues_tracker? + - if @project.issues_enabled? %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) Issues @@ -113,7 +113,7 @@ Milestones - if project_nav_tab? :merge_requests - = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do + = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 424905ea890..26d9640e98a 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -43,6 +43,10 @@ = link_to profile_keys_path, title: 'SSH Keys' do %span SSH Keys + = nav_link(controller: :gpg_keys) do + = link_to profile_gpg_keys_path, title: 'GPG Keys' do + %span + GPG Keys = nav_link(controller: :preferences) do = link_to profile_preferences_path, title: 'Preferences' do %span diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index fb90bb4b472..924cd2e9681 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -23,16 +23,16 @@ Registry - if project_nav_tab? :issues - = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do + = nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do %span Issues - - if @project.default_issues_tracker? + - if @project.issues_enabled? %span.badge.count.issue_counter= number_with_delimiter(issuables_count_for_state(:issues, :opened, finder: IssuesFinder.new(current_user, project_id: @project.id))) - if project_nav_tab? :merge_requests - controllers = [:merge_requests, 'projects/merge_requests/conflicts'] - - controllers.push(:merge_requests, :labels, :milestones) unless @project.default_issues_tracker? + - controllers.push(:merge_requests, :labels, :milestones) unless @project.issues_enabled? = nav_link(controller: controllers) do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span diff --git a/app/views/notify/new_gpg_key_email.html.haml b/app/views/notify/new_gpg_key_email.html.haml new file mode 100644 index 00000000000..4b9350c4e88 --- /dev/null +++ b/app/views/notify/new_gpg_key_email.html.haml @@ -0,0 +1,10 @@ +%p + Hi #{@user.name}! +%p + A new GPG key was added to your account: +%p + Fingerprint: + %code= @gpg_key.fingerprint +%p + If this key was added in error, you can remove it under + = link_to "GPG Keys", profile_gpg_keys_url diff --git a/app/views/notify/new_gpg_key_email.text.erb b/app/views/notify/new_gpg_key_email.text.erb new file mode 100644 index 00000000000..80b5a1fd7ff --- /dev/null +++ b/app/views/notify/new_gpg_key_email.text.erb @@ -0,0 +1,7 @@ +Hi <%= @user.name %>! + +A new GPG key was added to your account: + +Fingerprint: <%= @gpg_key.fingerprint %> + +If this key was added in error, you can remove it at <%= profile_gpg_keys_url %> diff --git a/app/views/profiles/gpg_keys/_email_with_badge.html.haml b/app/views/profiles/gpg_keys/_email_with_badge.html.haml new file mode 100644 index 00000000000..5f7844584e1 --- /dev/null +++ b/app/views/profiles/gpg_keys/_email_with_badge.html.haml @@ -0,0 +1,8 @@ +- css_classes = %w(label label-verification-status) +- css_classes << (verified ? 'verified': 'unverified') +- text = verified ? 'Verified' : 'Unverified' + +.gpg-email-badge + .gpg-email-badge-email= email + %div{ class: css_classes } + = text diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml new file mode 100644 index 00000000000..3fcf563d970 --- /dev/null +++ b/app/views/profiles/gpg_keys/_form.html.haml @@ -0,0 +1,10 @@ +%div + = form_for [:profile, @gpg_key], html: { class: 'js-requires-input' } do |f| + = form_errors(@gpg_key) + + .form-group + = f.label :key, class: 'label-light' + = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'." + + .prepend-top-default + = f.submit 'Add key', class: "btn btn-create" diff --git a/app/views/profiles/gpg_keys/_key.html.haml b/app/views/profiles/gpg_keys/_key.html.haml new file mode 100644 index 00000000000..b04981f90e3 --- /dev/null +++ b/app/views/profiles/gpg_keys/_key.html.haml @@ -0,0 +1,18 @@ +%li.key-list-item + .pull-left.append-right-10 + = icon 'key', class: "settings-list-icon hidden-xs" + .key-list-item-info + - key.emails_with_verified_status.map do |email, verified| + = render partial: 'email_with_badge', locals: { email: email, verified: verified } + + .description + %code= key.fingerprint + .pull-right + %span.key-created-at + created #{time_ago_with_tooltip(key.created_at)} + = link_to profile_gpg_key_path(key), data: { confirm: 'Are you sure? Removing this GPG key does not affect already signed commits.' }, method: :delete, class: "btn btn-danger prepend-left-10" do + %span.sr-only Remove + = icon('trash') + = link_to revoke_profile_gpg_key_path(key), data: { confirm: 'Are you sure? All commits that were signed with this GPG key will be unverified.' }, method: :put, class: "btn btn-danger prepend-left-10" do + %span.sr-only Revoke + Revoke diff --git a/app/views/profiles/gpg_keys/_key_table.html.haml b/app/views/profiles/gpg_keys/_key_table.html.haml new file mode 100644 index 00000000000..cabb92c5a24 --- /dev/null +++ b/app/views/profiles/gpg_keys/_key_table.html.haml @@ -0,0 +1,11 @@ +- is_admin = local_assigns.fetch(:admin, false) + +- if @gpg_keys.any? + %ul.well-list + = render partial: 'profiles/gpg_keys/key', collection: @gpg_keys, locals: { is_admin: is_admin } +- else + %p.settings-message.text-center + - if is_admin + There are no GPG keys associated with this account. + - else + There are no GPG keys with access to your account. diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml new file mode 100644 index 00000000000..8331daeeb75 --- /dev/null +++ b/app/views/profiles/gpg_keys/index.html.haml @@ -0,0 +1,21 @@ +- page_title "GPG Keys" += render 'profiles/head' + +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + = page_title + %p + GPG keys allow you to verify signed commits. + .col-lg-9 + %h5.prepend-top-0 + Add a GPG key + %p.profile-settings-content + Before you can add a GPG key you need to + = link_to 'generate it.', help_page_path('workflow/gpg_signed_commits/index.md') + = render 'form' + %hr + %h5 + Your GPG keys (#{@gpg_keys.count}) + .append-bottom-default + = render 'key_table' diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 9aed498a8a0..9664eccc834 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -40,6 +40,28 @@ New .col-sm-12 %hr + .col-lg-4.profile-settings-sidebar#new-navigation + %h4.prepend-top-0 + New Repository + %p + This setting allows you to turn on or off the new upcoming repository concept. + .col-lg-8.syntax-theme + .nav-wip + %p + The new repository is currently a work-in-progress concept and only usable on wide-screens. There are a number of improvements that we are working on in order to further refine the repository view. + %p + %a{ href: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/31890', target: 'blank' } Learn more + about the improvements that are coming soon! + = label_tag do + .preview= image_tag "old_repo.png" + %input.js-experiment-feature-toggle{ type: "radio", value: "false", name: "new_repo", checked: !show_new_repo? } + Old + = label_tag do + .preview= image_tag "new_repo.png" + %input.js-experiment-feature-toggle{ type: "radio", value: "true", name: "new_repo", checked: show_new_repo? } + New + .col-sm-12 + %hr .col-lg-4.profile-settings-sidebar %h4.prepend-top-0 Behavior diff --git a/app/views/projects/_deletion_failed.html.haml b/app/views/projects/_deletion_failed.html.haml new file mode 100644 index 00000000000..4f3698f91e6 --- /dev/null +++ b/app/views/projects/_deletion_failed.html.haml @@ -0,0 +1,6 @@ +- project = local_assigns.fetch(:project) +- return unless project.delete_error.present? + +.project-deletion-failed-message.alert.alert-warning + This project was scheduled for deletion, but failed with the following message: + = project.delete_error diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 7b399d31ba7..2f73216ac68 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -1,7 +1,12 @@ - commit = local_assigns.fetch(:commit) { @repository.commit } - ref = local_assigns.fetch(:ref) { current_ref } - project = local_assigns.fetch(:project) { @project } + #tree-holder.tree-holder.clearfix .nav-block - = render 'projects/tree/tree_header' - = render 'projects/tree/tree_content' + = render 'projects/tree/tree_header', tree: @tree + + - if !show_new_repo? && commit + = render 'shared/commit_well', commit: commit, ref: ref, project: project + + = render 'projects/tree/tree_content', tree: @tree diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml new file mode 100644 index 00000000000..f47d84ef755 --- /dev/null +++ b/app/views/projects/_flash_messages.html.haml @@ -0,0 +1,8 @@ +- project = local_assigns.fetch(:project) +- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message + += content_for flash_message_container do + = render partial: 'deletion_failed', locals: { project: project } + - if current_user && can?(current_user, :download_code, project) + = render 'shared/no_ssh' + = render 'shared/no_password' diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index f11afe8fc22..c7359d873d9 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -21,8 +21,8 @@ .commit = author_avatar(commit, size: 36) .commit-row-title - %strong - = link_to_gfm truncate(commit.title, length: 35), project_commit_path(@project, commit.id), class: "cdark" + %span.item-title.str-truncated-100 + = link_to_gfm commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title .pull-right = link_to commit.short_id, project_commit_path(@project, commit), class: "commit-sha" diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index ea72270a82c..facadddcd9e 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -8,3 +8,7 @@ = render "projects/blob/auxiliary_viewer", blob: blob #blob-content-holder.blob-content-holder + - if !show_new_repo? + %article.file-holder + = render 'projects/blob/header', blob: blob + = render 'projects/blob/content', blob: blob diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 7428bb5d3ac..ae3fbc93633 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -7,7 +7,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'blob' = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' = render 'projects/last_push' diff --git a/app/views/projects/blob/viewers/_image.html.haml b/app/views/projects/blob/viewers/_image.html.haml index 640d59b3174..5fd22a59217 100644 --- a/app/views/projects/blob/viewers/_image.html.haml +++ b/app/views/projects/blob/viewers/_image.html.haml @@ -1,2 +1,2 @@ .file-content.image_file - %img{ src: blob_raw_url, alt: viewer.blob.name } + = image_tag(blob_raw_url, alt: viewer.blob.name) diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index e248676be0d..c82ae35a685 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -2,7 +2,7 @@ = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do - if current_user.starred?(@project) = icon('star') - %span.starred= _('Unstar') + %span.starred= _('Unstar') - else = icon('star-o') %span= s_('StarProject|Star') diff --git a/app/views/projects/commit/_ajax_signature.html.haml b/app/views/projects/commit/_ajax_signature.html.haml new file mode 100644 index 00000000000..22674b671c9 --- /dev/null +++ b/app/views/projects/commit/_ajax_signature.html.haml @@ -0,0 +1,3 @@ +- if commit.has_signature? + %button{ class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'auto top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } } + %i.fa.fa-spinner.fa-spin diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 45109f2c58b..419fbe99af8 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,5 +1,6 @@ .page-content-header .header-main-content + = render partial: 'signature', object: @commit.signature %strong #{ s_('CommitBoxTitle|Commit') } %span.commit-sha= @commit.short_id diff --git a/app/views/projects/commit/_invalid_signature_badge.html.haml b/app/views/projects/commit/_invalid_signature_badge.html.haml new file mode 100644 index 00000000000..3a73aae9d95 --- /dev/null +++ b/app/views/projects/commit/_invalid_signature_badge.html.haml @@ -0,0 +1,9 @@ +- title = capture do + .gpg-popover-icon.invalid + = render 'shared/icons/icon_status_notfound_borderless.svg' + %div + This commit was signed with an <strong>unverified</strong> signature. + +- locals = { signature: signature, title: title, label: 'Unverified', css_classes: ['invalid'] } + += render partial: 'projects/commit/signature_badge', locals: locals diff --git a/app/views/projects/commit/_signature.html.haml b/app/views/projects/commit/_signature.html.haml new file mode 100644 index 00000000000..60fa52557ef --- /dev/null +++ b/app/views/projects/commit/_signature.html.haml @@ -0,0 +1,5 @@ +- if signature + - if signature.valid_signature? + = render partial: 'projects/commit/valid_signature_badge', locals: { signature: signature } + - else + = render partial: 'projects/commit/invalid_signature_badge', locals: { signature: signature } diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml new file mode 100644 index 00000000000..66f00eb5507 --- /dev/null +++ b/app/views/projects/commit/_signature_badge.html.haml @@ -0,0 +1,18 @@ +- css_classes = commit_signature_badge_classes(css_classes) + +- title = capture do + .gpg-popover-status + = title + +- content = capture do + .clearfix + = content + + GPG Key ID: + %span.monospace= signature.gpg_key_primary_keyid + + + = link_to('Learn more about signing commits', help_page_path('workflow/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link') + +%button{ class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'auto top', title: title, content: content } } + = label diff --git a/app/views/projects/commit/_valid_signature_badge.html.haml b/app/views/projects/commit/_valid_signature_badge.html.haml new file mode 100644 index 00000000000..db1a41bbf64 --- /dev/null +++ b/app/views/projects/commit/_valid_signature_badge.html.haml @@ -0,0 +1,32 @@ +- title = capture do + .gpg-popover-icon.valid + = render 'shared/icons/icon_status_success_borderless.svg' + %div + This commit was signed with a <strong>verified</strong> signature. + +- content = capture do + - gpg_key = signature.gpg_key + - user = gpg_key&.user + - user_name = signature.gpg_key_user_name + - user_email = signature.gpg_key_user_email + + - if user + = link_to user_path(user), class: 'gpg-popover-user-link' do + %div + = user_avatar_without_link(user: user, size: 32) + + %div + %strong= gpg_key.user.name + %div @#{gpg_key.user.username} + - else + = mail_to user_email do + %div + = user_avatar_without_link(user_name: user_name, user_email: user_email, size: 32) + + %div + %strong= user_name + %div= user_email + +- locals = { signature: signature, title: title, content: content, label: 'Verified', css_classes: ['valid'] } + += render partial: 'projects/commit/signature_badge', locals: locals diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 1033bad0d49..12b73ecdf13 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -9,7 +9,7 @@ - cache_key.push(commit.status(ref)) if commit.status(ref) = cache(cache_key, expires_in: 1.day) do - %li.commit.flex-list.js-toggle-container{ id: "commit-#{commit.short_id}" } + %li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" } .avatar-cell.hidden-xs = author_avatar(commit, size: 36) @@ -36,9 +36,15 @@ #{ commit_text.html_safe } - .commit-actions.flex-row.hidden-xs + .commit-actions.hidden-xs - if commit.status(ref) = render_commit_status(commit, ref: ref) + + - if request.xhr? + = render partial: 'projects/commit/signature', object: commit.signature + - else + = render partial: 'projects/commit/ajax_signature', locals: { commit: commit } + = link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent" = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = link_to_browse_code(project, commit) diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index c764e35dd2a..d14897428d0 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -7,7 +7,7 @@ %span.commits-count= n_("%d commit", "%d commits", commits.count) % commits.count %li.commits-row{ data: { day: day } } - %ul.content-list.commit-list + %ul.content-list.commit-list.flex-list = render partial: 'projects/commits/commit', collection: commits, locals: { project: project, ref: ref } - if hidden > 0 diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 844ebb65148..bd2d900997e 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -29,7 +29,7 @@ = link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' .control - = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form') do + = form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do = search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false } .control = link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do diff --git a/app/views/projects/diffs/viewers/_image.html.haml b/app/views/projects/diffs/viewers/_image.html.haml index 33d3dcbeafa..aa004a739d7 100644 --- a/app/views/projects/diffs/viewers/_image.html.haml +++ b/app/views/projects/diffs/viewers/_image.html.haml @@ -8,7 +8,7 @@ .image %span.wrap .frame{ class: (diff_file.deleted_file? ? 'deleted' : 'added') } - %img{ src: blob_raw_path, alt: diff_file.file_path } + = image_tag(blob_raw_path, alt: diff_file.file_path) %p.image-info= number_to_human_size(blob.size) - else .image @@ -16,7 +16,7 @@ %span.wrap .frame.deleted %a{ href: project_blob_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path)) } - %img{ src: old_blob_raw_path, alt: diff_file.old_path } + = image_tag(old_blob_raw_path, alt: diff_file.old_path) %p.image-info.hide %span.meta-filesize= number_to_human_size(old_blob.size) | @@ -28,7 +28,7 @@ %span.wrap .frame.added %a{ href: project_blob_path(@project, tree_join(diff_file.content_sha, diff_file.new_path)) } - %img{ src: blob_raw_path, alt: diff_file.new_path } + = image_tag(blob_raw_path, alt: diff_file.new_path) %p.image-info.hide %span.meta-filesize= number_to_human_size(blob.size) | @@ -41,10 +41,10 @@ .swipe.view.hide .swipe-frame .frame.deleted - %img{ src: old_blob_raw_path, alt: diff_file.old_path } + = image_tag(old_blob_raw_path, alt: diff_file.old_path) .swipe-wrap .frame.added - %img{ src: blob_raw_path, alt: diff_file.new_path } + = image_tag(blob_raw_path, alt: diff_file.new_path) %span.swipe-bar %span.top-handle %span.bottom-handle @@ -52,9 +52,9 @@ .onion-skin.view.hide .onion-skin-frame .frame.deleted - %img{ src: old_blob_raw_path, alt: diff_file.old_path } + = image_tag(old_blob_raw_path, alt: diff_file.old_path) .frame.added - %img{ src: blob_raw_path, alt: diff_file.new_path } + = image_tag(blob_raw_path, alt: diff_file.new_path) .controls .transparent .drag-track diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 0f132a68ce1..d17709380d5 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,10 +1,6 @@ - @no_container = true -- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message -= content_for flash_message_container do - - if current_user && can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - = render 'shared/no_password' += render partial: 'flash_messages', locals: { project: @project } = render "projects/head" = render "home_panel" diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index d02ea5cccc3..4b9da02c6b8 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,6 +1,7 @@ - @no_container = true - page_title "Labels" - hide_class = '' +- can_admin_label = can?(current_user, :admin_label, @project) - if show_new_nav? && can?(current_user, :admin_label, @project) - content_for :breadcrumbs_extra do @@ -12,15 +13,17 @@ %div{ class: container_class } .top-area.adjust .nav-text - Labels can be applied to issues and merge requests. Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. + Labels can be applied to issues and merge requests. + - if can_admin_label + Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging. - .nav-controls{ class: ("visible-xs" if show_new_nav?) } - - if can?(current_user, :admin_label, @project) + - if can_admin_label + .nav-controls{ class: ("visible-xs" if show_new_nav?) } = link_to new_project_label_path(@project), class: "btn btn-new" do New label .labels - - if can?(current_user, :admin_label, @project) + - if can_admin_label -# Only show it in the first page - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') .prioritized-labels{ class: ('hide' if hide) } @@ -33,7 +36,7 @@ - if @labels.present? .other-labels - - if can?(current_user, :admin_label, @project) + - if can_admin_label %h5{ class: ('hide' if hide) } Other Labels %ul.content-list.manage-labels-list.js-other-labels = render partial: 'shared/label', subject: @project, collection: @labels, as: :label diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index 766cb272bec..917ec7fdbda 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -1,3 +1,6 @@ +- content_for :page_specific_javascripts do + = webpack_bundle_tag('how_to_merge') + #modal_merge_info.modal .modal-dialog .modal-content @@ -50,14 +53,3 @@ = succeed '.' do You can also checkout merge requests locally by = link_to 'following these guidelines', help_page_path('user/project/merge_requests/index.md', anchor: "checkout-merge-requests-locally"), target: '_blank', rel: 'noopener noreferrer' - -:javascript - $(function(){ - var modal = $('#modal_merge_info').modal({modal: true, show:false}); - $('.how_to_merge_link').bind("click", function(){ - modal.show(); - }); - $('.modal-header .close').bind("click", function(){ - modal.hide(); - }) - }) diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index 4e5aae496b1..8958b2cf5e1 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -3,7 +3,7 @@ = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: project_new_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors - .merge-request-branches.row + .merge-request-branches.js-merge-request-new-compare.row{ 'data-target-project-url': project_new_merge_request_update_branches_path(@source_project), 'data-source-branch-url': project_new_merge_request_branch_from_path(@source_project), 'data-target-branch-url': project_new_merge_request_branch_to_path(@source_project) } .col-md-6 .panel.panel-default.panel-new-merge-request .panel-heading @@ -66,10 +66,3 @@ - if @merge_request.errors.any? = form_errors(@merge_request) = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" - -:javascript - new Compare({ - targetProjectUrl: "#{project_new_merge_request_update_branches_path(@source_project)}", - sourceBranchUrl: "#{project_new_merge_request_branch_from_path(@source_project)}", - targetBranchUrl: "#{project_new_merge_request_branch_to_path(@source_project)}" - }); diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml index c72dd1d8e29..4b5fa28078a 100644 --- a/app/views/projects/merge_requests/creations/_new_submit.html.haml +++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml @@ -17,7 +17,7 @@ = f.hidden_field :target_project_id = f.hidden_field :target_branch -.mr-compare.merge-request +.mr-compare.merge-request.js-merge-request-new-submit{ 'data-mr-submit-action': "#{j params[:tab].presence || 'new'}" } - if @commits.empty? .commits-empty %h4 @@ -50,8 +50,3 @@ .mr-loading-status = spinner - -:javascript - var merge_request = new MergeRequest({ - action: "#{j params[:tab].presence || 'new'}", - }); diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index bfeb746ee83..c020e7db380 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -4,7 +4,7 @@ - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project - page_title "Merge Requests" -- unless @project.default_issues_tracker? +- unless @project.issues_enabled? = content_for :sub_nav do = render "projects/merge_requests/head" diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 2efc1d68190..ea6cd16c7ad 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -3,10 +3,10 @@ - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('diff_notes') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('diff_notes') -.merge-request{ 'data-url' => merge_request_path(@merge_request, format: :json), 'data-project-path' => project_path(@merge_request.project) } +.merge-request{ 'data-mr-action': "#{j params[:tab].presence || 'show'}", 'data-url' => merge_request_path(@merge_request, format: :json), 'data-project-path' => project_path(@merge_request.project) } = render "projects/merge_requests/mr_title" .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } } @@ -15,13 +15,13 @@ - if @merge_request.source_branch_exists? = render "projects/merge_requests/how_to_merge" + -# haml-lint:disable InlineJavaScript :javascript window.gl.mrWidgetData = #{serialize_issuable(@merge_request)} #js-vue-mr-widget.mr-widget - content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'vue_merge_request_widget' .content-block.content-block-small.emoji-list-container @@ -88,10 +88,3 @@ = render "projects/commit/change", type: 'revert', commit: @merge_request.merge_commit, title: @merge_request.title - if @merge_request.can_be_cherry_picked? = render "projects/commit/change", type: 'cherry-pick', commit: @merge_request.merge_commit, title: @merge_request.title - -:javascript - $(function () { - window.mergeRequest = new MergeRequest({ - action: "#{j params[:tab].presence || 'show'}", - }); - }); diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index a89387bc8f1..e0b29b0c2e1 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,7 +1,7 @@ - @no_container = true - page_title 'Milestones' -- if show_new_nav? +- if show_new_nav? && can?(current_user, :admin_milestone, @project) - content_for :breadcrumbs_extra do = link_to "New milestone", new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New milestone' @@ -11,10 +11,10 @@ .top-area = render 'shared/milestones_filter', counts: milestone_counts(@project.milestones) - .nav-controls + .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) } = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @project) - = link_to new_project_milestone_path(@project), class: 'btn btn-new', title: 'New milestone' do + = link_to new_project_milestone_path(@project), class: "btn btn-new #{("visible-xs" if show_new_nav?)}", title: 'New milestone' do New milestone .milestones diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index a2d7a21d5f6..87cc23fc649 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -72,7 +72,7 @@ %div - if fogbugz_import_enabled? = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do - = icon('bug', text: 'Fogbugz') + = icon('bug', text: 'FogBugz') %div - if gitea_import_enabled? = link_to new_import_gitea_url, class: 'btn import_gitea' do diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index 97c0407a01d..7343d6e039c 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -4,7 +4,7 @@ = pipeline_schedule.description %td.branch-name-cell = icon('code-fork') - - if pipeline_schedule.ref + - if pipeline_schedule.ref.present? = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" %td - if pipeline_schedule.last_pipeline diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml index 5c00bb6883c..2a0704bc7af 100644 --- a/app/views/projects/protected_branches/shared/_branches_list.html.haml +++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-branches-list +.panel.panel-default.protected-branches-list.js-protected-branches-list - if @protected_branches.empty? .panel-heading %h3.panel-title @@ -23,6 +23,8 @@ - if can_admin_project %th %tbody + %tr + %td.flash-container{ colspan: 5 } = yield = paginate @protected_branches, theme: 'gitlab' diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml index b619fa57e05..9f0c4f3b3a8 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| += form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f| .panel.panel-default .panel-heading %h3.panel-title diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml index 6e3cd4ada71..3f42ae58438 100644 --- a/app/views/projects/protected_tags/shared/_tags_list.html.haml +++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-tags-list +.panel.panel-default.protected-tags-list.js-protected-tags-list - if @protected_tags.empty? .panel-heading %h3.panel-title diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index f8835454140..28ccbf7eb15 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -1,21 +1,8 @@ %h3 Specific Runners -.bs-callout.help-callout - %h4 How to setup a specific Runner for a new project - - %ol - %li - Install a Runner compatible with GitLab CI - (checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it). - %li - Specify the following URL during the Runner setup: - %code= root_url(only_path: false) - %li - Use the following registration token during setup: - %code= @project.runners_token - %li - Start the Runner! - += render partial: 'ci/runner/how_to_setup_runner', + locals: { registration_token: @project.runners_token, + type: 'specific' } - if @project_runners.any? %h4.underlined-title Runners activated for this project diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index ac98a5a5b50..370516ca55e 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,19 +1,16 @@ - @no_container = true - breadcrumb_title "Project" - @content_class = "limit-container-width" unless fluid_layout -- flash_message_container = show_new_nav? ? :new_global_flash : :flash_message = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' +- if show_new_repo? + - content_for :page_specific_javascripts do + = webpack_bundle_tag 'common_vue' + = webpack_bundle_tag 'repo' -= content_for flash_message_container do - - if current_user && can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - = render 'shared/no_password' += render partial: 'flash_messages', locals: { project: @project } = render "projects/head" = render "projects/last_push" diff --git a/app/views/projects/tree/_old_tree_content.html.haml b/app/views/projects/tree/_old_tree_content.html.haml new file mode 100644 index 00000000000..7ff7257caf7 --- /dev/null +++ b/app/views/projects/tree/_old_tree_content.html.haml @@ -0,0 +1,24 @@ +.tree-content-holder + .table-holder + %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } + %thead + %tr + %th= s_('ProjectFileTree|Name') + %th.hidden-xs + .pull-left= _('Last commit') + %th.text-right= _('Last Update') + - if @path.present? + %tr.tree-item + %td.tree-item-file-name + = link_to "..", project_tree_path(@project, up_dir_path), class: 'prepend-left-10' + %td + %td.hidden-xs + + = render_tree(tree) + + - if tree.readme + = render "projects/tree/readme", readme: tree.readme + +- if can_edit_tree? + = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post + = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_old_tree_header.html.haml b/app/views/projects/tree/_old_tree_header.html.haml new file mode 100644 index 00000000000..13705ca303b --- /dev/null +++ b/app/views/projects/tree/_old_tree_header.html.haml @@ -0,0 +1,70 @@ +%ul.breadcrumb.repo-breadcrumb + %li + = link_to project_tree_path(@project, @ref) do + = @project.path + - path_breadcrumbs do |title, path| + %li + = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) + + - if current_user + %li + - if !on_top_of_branch? + %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } } + = icon('plus') + - else + %span.dropdown + %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown", "data-target" => ".add-to-tree-dropdown" } + = icon('plus') + .add-to-tree-dropdown + %ul.dropdown-menu + - if can_edit_tree? + %li + = link_to project_new_blob_path(@project, @id) do + = icon('pencil fw') + #{ _('New file') } + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do + = icon('file fw') + #{ _('Upload file') } + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do + = icon('folder fw') + #{ _('New directory') } + - elsif can?(current_user, :fork_project, @project) + %li + - continue_params = { to: project_new_blob_path(@project, @id), + notice: edit_in_new_fork_notice, + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + = icon('pencil fw') + #{ _('New file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to upload a file again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + = icon('file fw') + #{ _('Upload file') } + %li + - continue_params = { to: request.fullpath, + notice: edit_in_new_fork_notice + " Try to create a new directory again.", + notice_now: edit_in_new_fork_notice_now } + - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, + continue: continue_params) + = link_to fork_path, method: :post do + = icon('folder fw') + #{ _('New directory') } + + %li.divider + %li + = link_to new_project_branch_path(@project) do + = icon('code-fork fw') + #{ _('New branch') } + %li + = link_to new_project_tag_path(@project) do + = icon('tags fw') + #{ _('New tag') } diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index ede1ae8198a..c51a8fdcec1 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,8 +1,8 @@ -#repo{ data: { url: repo_url(@project), 'project-name' => @project.name, refs_url: refs_namespace_project_path(@project.namespace, @project, format: "json"), project_url: namespace_project_path(@project.namespace, @project), project_id: @project.id, temp_token: @current_user.private_token } } +- if show_new_repo? + = render 'shared/repo/repo', project: @project +- else + = render 'projects/tree/old_tree_content', tree: tree -- if can_edit_tree? - = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post - = render 'projects/blob/new_dir' :javascript // Load last commit log for each file in tree diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index fd34140d051..5df3d10d4eb 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -4,10 +4,14 @@ =icon('long-arrow-right', title: 'to target branch') = render 'shared/target_switcher', destination: 'tree', path: @path + - if !show_new_repo? + = render 'projects/tree/old_tree_header' + .tree-controls - %a.btn.btn-default#editable-mode{ "href"=>"#", "@click.prevent" => "editClicked", "v-cloak" => 1, "v-if" => "isCommitable" } - %i{ ":class" => "buttonIcon" } - %span {{buttonLabel}} + - if show_new_repo? + = render 'shared/repo/editable_mode' + - else + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/find_file_link' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 7b173a869a5..c5a73e4c53e 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -6,9 +6,10 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'repo' +- if show_new_repo? + - content_for :page_specific_javascripts do + = webpack_bundle_tag 'common_vue' + = webpack_bundle_tag 'repo' = render "projects/commits/head" diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index fc6b7a33943..adb8d5aaecb 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -4,6 +4,8 @@ = form_errors(@page) = f.hidden_field :title, value: @page.title + - if @page.persisted? + = f.hidden_field :last_commit_sha, value: @page.last_commit_sha .form-group .col-sm-12= f.label :format, class: 'control-label-full-width' .col-sm-12 diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index df0ec14eb3b..8fd60216536 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,6 +1,12 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout - page_title "Edit", @page.title.capitalize, "Wiki" +- if @conflict + .alert.alert-danger + Someone edited the page the same time you did. Please check out + = link_to "the page", project_wiki_path(@project, @page), target: "_blank" + and make sure your changes will not unintentionally remove theirs. + .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } = icon('angle-double-left') diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 75704eda361..b4843eafdb7 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -20,11 +20,3 @@ = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' } .input-group-btn = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"), class: "btn-default btn-clipboard") - -:javascript - $('ul.clone-options-dropdown a').on('click',function(e){ - e.preventDefault(); - var $this = $(this); - $('a.clone-dropdown-btn span').text($this.text()); - $('#project_clone').val($this.attr('href')); - }); diff --git a/app/views/shared/_commit_well.html.haml b/app/views/shared/_commit_well.html.haml new file mode 100644 index 00000000000..50e3d80a84d --- /dev/null +++ b/app/views/shared/_commit_well.html.haml @@ -0,0 +1,4 @@ +.info-well.hidden-xs.project-last-commit.append-bottom-default + .well-segment + %ul.blob-commit-info + = render 'projects/commits/commit', commit: commit, ref: ref, project: project diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 2f776a17f45..8ded7440de3 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -76,11 +76,3 @@ = link_to destroy_label_path(label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, data: {confirm: label_deletion_confirm_text(label), toggle: "tooltip"} do %span.sr-only Delete = icon('trash-o') - - - if current_user - - if can_subscribe_to_label_in_different_levels?(label) - :javascript - new gl.GroupLabelSubscription('##{dom_id(label)} .label-subscription'); - - else - :javascript - new gl.ProjectLabelSubscription('##{dom_id(label)} .label-subscription'); diff --git a/app/views/shared/_logo_type.svg b/app/views/shared/_logo_type.svg new file mode 100644 index 00000000000..cb07e2634a9 --- /dev/null +++ b/app/views/shared/_logo_type.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 617 169"><path d="M315.26 2.97h-21.8l.1 162.5h88.3v-20.1h-66.5l-.1-142.4M465.89 136.95c-5.5 5.7-14.6 11.4-27 11.4-16.6 0-23.3-8.2-23.3-18.9 0-16.1 11.2-23.8 35-23.8 4.5 0 11.7.5 15.4 1.2v30.1h-.1m-22.6-98.5c-17.6 0-33.8 6.2-46.4 16.7l7.7 13.4c8.9-5.2 19.8-10.4 35.5-10.4 17.9 0 25.8 9.2 25.8 24.6v7.9c-3.5-.7-10.7-1.2-15.1-1.2-38.2 0-57.6 13.4-57.6 41.4 0 25.1 15.4 37.7 38.7 37.7 15.7 0 30.8-7.2 36-18.9l4 15.9h15.4v-83.2c-.1-26.3-11.5-43.9-44-43.9M557.63 149.1c-8.2 0-15.4-1-20.8-3.5V70.5c7.4-6.2 16.6-10.7 28.3-10.7 21.1 0 29.2 14.9 29.2 39 0 34.2-13.1 50.3-36.7 50.3m9.2-110.6c-19.5 0-30 13.3-30 13.3v-21l-.1-27.8h-21.3l.1 158.5c10.7 4.5 25.3 6.9 41.2 6.9 40.7 0 60.3-26 60.3-70.9-.1-35.5-18.2-59-50.2-59M77.9 20.6c19.3 0 31.8 6.4 39.9 12.9l9.4-16.3C114.5 6 97.3 0 78.9 0 32.5 0 0 28.3 0 85.4c0 59.8 35.1 83.1 75.2 83.1 20.1 0 37.2-4.7 48.4-9.4l-.5-63.9V75.1H63.6v20.1h38l.5 48.5c-5 2.5-13.6 4.5-25.3 4.5-32.2 0-53.8-20.3-53.8-63-.1-43.5 22.2-64.6 54.9-64.6M231.43 2.95h-21.3l.1 27.3v94.3c0 26.3 11.4 43.9 43.9 43.9 4.5 0 8.9-.4 13.1-1.2v-19.1c-3.1.5-6.4.7-9.9.7-17.9 0-25.8-9.2-25.8-24.6v-65h35.7v-17.8h-35.7l-.1-38.5M155.96 165.47h21.3v-124h-21.3v124M155.96 24.37h21.3V3.07h-21.3v21.3"/></svg> diff --git a/app/views/shared/_mr_head.html.haml b/app/views/shared/_mr_head.html.haml index 4211ec6351d..e7355ae2eea 100644 --- a/app/views/shared/_mr_head.html.haml +++ b/app/views/shared/_mr_head.html.haml @@ -1,4 +1,4 @@ -- if @project.default_issues_tracker? +- if @project.issues_enabled? = render "projects/issues/head" - else = render "projects/merge_requests/head" diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml index c1acee1a211..5f3cdaefd54 100644 --- a/app/views/shared/_new_project_item_select.html.haml +++ b/app/views/shared/_new_project_item_select.html.haml @@ -1,6 +1,6 @@ - if @projects.any? .project-item-select-holder - = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }, with_feature_enabled: local_assigns[:with_feature_enabled] - %a.btn.btn-new.new-project-item-select-button{ data: { relative_path: local_assigns[:path] } } + = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path] }, with_feature_enabled: local_assigns[:with_feature_enabled] + %a.btn.btn-new.new-project-item-select-button = local_assigns[:label] = icon('caret-down') diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml index b20055a564e..e415ec64c38 100644 --- a/app/views/shared/_personal_access_tokens_form.html.haml +++ b/app/views/shared/_personal_access_tokens_form.html.haml @@ -23,18 +23,3 @@ .prepend-top-default = f.submit "Create #{type} token", class: "btn btn-create" - -:javascript - var $dateField = $('.datepicker'); - var date = $dateField.val(); - - new Pikaday({ - field: $dateField.get(0), - theme: 'gitlab-theme animate-picker', - format: 'yyyy-mm-dd', - minDate: new Date(), - container: $dateField.parent().get(0), - onSelect: function(dateText) { - $dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); - } - }); diff --git a/app/views/shared/icons/_icon_clone.svg b/app/views/shared/icons/_icon_clone.svg new file mode 100644 index 00000000000..ccc897aa98f --- /dev/null +++ b/app/views/shared/icons/_icon_clone.svg @@ -0,0 +1,3 @@ +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14"> +<path d="M13 12.75v-8.5q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h8.5q0.102 0 0.176-0.074t0.074-0.176zM14 4.25v8.5q0 0.516-0.367 0.883t-0.883 0.367h-8.5q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883zM11 1.25v1.25h-1v-1.25q0-0.102-0.074-0.176t-0.176-0.074h-8.5q-0.102 0-0.176 0.074t-0.074 0.176v8.5q0 0.102 0.074 0.176t0.176 0.074h1.25v1h-1.25q-0.516 0-0.883-0.367t-0.367-0.883v-8.5q0-0.516 0.367-0.883t0.883-0.367h8.5q0.516 0 0.883 0.367t0.367 0.883z"></path> +</svg> diff --git a/app/views/shared/icons/_icon_status_notfound_borderless.svg b/app/views/shared/icons/_icon_status_notfound_borderless.svg new file mode 100644 index 00000000000..e58bd264ef8 --- /dev/null +++ b/app/views/shared/icons/_icon_status_notfound_borderless.svg @@ -0,0 +1 @@ +<svg width="22" height="22" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"><path d="M12.822 11.29c.816-.581 1.421-1.348 1.683-2.322.603-2.243-.973-4.553-3.53-4.553-1.15 0-2.085.41-2.775 1.089-.42.413-.672.835-.8 1.167a1.179 1.179 0 0 0 2.2.847c.016-.043.1-.184.252-.334.264-.259.613-.412 1.123-.412.938 0 1.47.78 1.254 1.584-.105.39-.37.726-.773 1.012a3.25 3.25 0 0 1-.945.47 1.179 1.179 0 0 0-.874 1.138v2.234a1.179 1.179 0 1 0 2.358 0V11.78a5.9 5.9 0 0 0 .827-.492z" fill-rule="nonzero"/><ellipse cx="10.825" cy="16.711" rx="1.275" ry="1.322"/></svg> diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml index 964fe5220f7..0d507cc7a6e 100644 --- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml +++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml @@ -1,9 +1,9 @@ - type = local_assigns.fetch(:type) -%aside.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top{ data: { "offset-top" => "50", "spy" => "affix" }, "aria-live" => "polite" } +%aside.issues-bulk-update.js-right-sidebar.right-sidebar{ "aria-live" => "polite", data: { 'signed-in': current_user.present? } } .issuable-sidebar.hidden = form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update" do - .block + .block.issuable-sidebar-header .filter-item.inline.update-issues-btn.pull-left = button_tag "Update all", class: "btn update-selected-issues btn-info", disabled: true = button_tag "Cancel", class: "btn btn-default js-bulk-update-menu-hide pull-right" diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 2cabbc8c560..c4ed7f6e750 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -36,13 +36,3 @@ .row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) } - if has_labels = render 'shared/labels_row', labels: @labels - -:javascript - new LabelsSelect(); - new MilestoneSelect(); - new IssueStatusSelect(); - new SubscriptionSelect(); - $('form.filter-form').on('submit', function (event) { - event.preventDefault(); - gl.utils.visitUrl(this.action + '&' + $(this).serialize()); - }); diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index db407363a09..8a71819aa8e 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -16,5 +16,3 @@ .hide-collapsed.participants-more %a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } } + #{participants_extra} more -:javascript - IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row}; diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 6f0b7600698..3428d6e0445 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -108,12 +108,3 @@ #js-add-issues-btn.prepend-left-10 - elsif type != :boards_modal = render 'shared/sort_dropdown' - -- unless type === :boards_modal - :javascript - $(document).off('page:restore').on('page:restore', function (event) { - if (gl.FilteredSearchManager) { - const filteredSearchManager = new gl.FilteredSearchManager(); - filteredSearchManager.setup(); - } - }); diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index ecbaa901792..b08267357e5 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -138,17 +138,4 @@ = project_ref = clipboard_button(text: project_ref, title: "Copy reference to clipboard", placement: "left") - :javascript - gl.sidebarOptions = { - endpoint: "#{issuable_json_path(issuable)}?basic=true", - editable: #{can_edit_issuable ? true : false}, - currentUser: #{current_user.to_json(only: [:username, :id, :name], methods: :avatar_url)}, - rootPath: "#{root_path}" - }; - - new MilestoneSelect('{"full_path":"#{@project.full_path}"}'); - new LabelsSelect(); - new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}'); - gl.Subscription.bindAll('.subscription'); - new gl.DueDateSelectors(); - window.sidebar = new Sidebar(); + %script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable, can_edit_issuable).to_json.html_safe diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml index 549d2e2f61e..1615871385e 100644 --- a/app/views/shared/milestones/_participants_tab.html.haml +++ b/app/views/shared/milestones/_participants_tab.html.haml @@ -4,5 +4,5 @@ = link_to user, title: user.name, class: "darken" do = image_tag avatar_icon(user, 32), class: "avatar s32" %strong= truncate(user.name, length: 40) - %br - %small.cgray= user.username + %div + %small.cgray= user.username diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml index f0fcc414756..eae04c9bbb8 100644 --- a/app/views/shared/notes/_notes_with_form.html.haml +++ b/app/views/shared/notes/_notes_with_form.html.haml @@ -22,5 +22,4 @@ = link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes') to comment -:javascript - var notes = new Notes("#{notes_url}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}", #{autocomplete}) +%script.js-notes-data{ type: "application/json" }= initial_notes_data(autocomplete).to_json.html_safe diff --git a/app/views/shared/repo/_editable_mode.html.haml b/app/views/shared/repo/_editable_mode.html.haml new file mode 100644 index 00000000000..7db6431e1de --- /dev/null +++ b/app/views/shared/repo/_editable_mode.html.haml @@ -0,0 +1,3 @@ +%a.btn.btn-default#editable-mode{ "href"=>"#", "@click.prevent" => "editClicked", "v-cloak" => 1, "v-if" => "isCommitable" } + %i{ ":class" => "buttonIcon" } + %span {{buttonLabel}} diff --git a/app/views/shared/repo/_repo.html.haml b/app/views/shared/repo/_repo.html.haml new file mode 100644 index 00000000000..919904a81a7 --- /dev/null +++ b/app/views/shared/repo/_repo.html.haml @@ -0,0 +1 @@ +#repo{ data: { url: repo_url(project), project_name: project.name, refs_url: refs_project_path(project, format: :json), project_url: project_path(project), project_id: project.id } } diff --git a/app/views/shared/repo/_user_dropdown_link.html.haml b/app/views/shared/repo/_user_dropdown_link.html.haml new file mode 100644 index 00000000000..bddd83a123d --- /dev/null +++ b/app/views/shared/repo/_user_dropdown_link.html.haml @@ -0,0 +1 @@ +%li= link_to "Turn #{show_new_repo? ? 'off' : 'on'} new repository", profile_preferences_path(anchor: "new-repository") diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml index ac3701233ad..dfea8b40bd8 100644 --- a/app/views/snippets/_snippets.html.haml +++ b/app/views/snippets/_snippets.html.haml @@ -1,4 +1,3 @@ -- remote = local_assigns.fetch(:remote, false) - link_project = local_assigns.fetch(:link_project, false) .snippets-list-holder @@ -8,7 +7,4 @@ %li .nothing-here-block Nothing here. - = paginate @snippets, theme: 'gitlab', remote: remote - -:javascript - gl.SnippetsList(); + = paginate @snippets, theme: 'gitlab' diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml deleted file mode 100644 index 57b8845c55d..00000000000 --- a/app/views/users/calendar.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.clearfix.calendar - .js-contrib-calendar - .calendar-hint - Summary of issues, merge requests, push events, and comments -:javascript - new Calendar( - #{@activity_dates.to_json}, - '#{user_calendar_activities_path}' - ); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 919ba5d15d3..a449706c567 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -2,9 +2,6 @@ - @hide_breadcrumbs = true - page_title @user.name - page_description @user.bio -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('users') - header_title @user.name, user_path(@user) - @no_container = true @@ -107,7 +104,7 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - .user-calendar{ data: { href: user_calendar_path } } + .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path } } %h4.center.light %i.fa.fa-spinner.fa-spin .user-calendar-activities @@ -131,10 +128,3 @@ .loading-status = spinner - -:javascript - var userProfile; - - userProfile = new gl.User({ - action: "#{controller.action_name}" - }); diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb new file mode 100644 index 00000000000..4f47717ff69 --- /dev/null +++ b/app/workers/create_gpg_signature_worker.rb @@ -0,0 +1,16 @@ +class CreateGpgSignatureWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + def perform(commit_sha, project_id) + project = Project.find_by(id: project_id) + + return unless project + + commit = project.commit(commit_sha) + + return unless commit + + commit.signature + end +end diff --git a/app/workers/invalid_gpg_signature_update_worker.rb b/app/workers/invalid_gpg_signature_update_worker.rb new file mode 100644 index 00000000000..db6b1ea8e8d --- /dev/null +++ b/app/workers/invalid_gpg_signature_update_worker.rb @@ -0,0 +1,12 @@ +class InvalidGpgSignatureUpdateWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + def perform(gpg_key_id) + gpg_key = GpgKey.find_by(id: gpg_key_id) + + return unless gpg_key + + Gitlab::Gpg::InvalidGpgSignatureUpdater.new(gpg_key).run + end +end diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb index 7b485b3363c..d7087f20dfc 100644 --- a/app/workers/pipeline_schedule_worker.rb +++ b/app/workers/pipeline_schedule_worker.rb @@ -6,15 +6,12 @@ class PipelineScheduleWorker Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now) .preload(:owner, :project).find_each do |schedule| begin - unless schedule.runnable_by_owner? - schedule.deactivate! - next - end - - Ci::CreatePipelineService.new(schedule.project, - schedule.owner, - ref: schedule.ref) + pipeline = Ci::CreatePipelineService.new(schedule.project, + schedule.owner, + ref: schedule.ref) .execute(:schedule, save_on_errors: false, schedule: schedule) + + schedule.deactivate! unless pipeline.persisted? rescue => e Rails.logger.error "#{schedule.id}: Failed to create a scheduled pipeline: #{e.message}" ensure diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb index b462327490e..a9188b78460 100644 --- a/app/workers/project_destroy_worker.rb +++ b/app/workers/project_destroy_worker.rb @@ -3,14 +3,11 @@ class ProjectDestroyWorker include DedicatedSidekiqQueue def perform(project_id, user_id, params) - begin - project = Project.unscoped.find(project_id) - rescue ActiveRecord::RecordNotFound - return - end - + project = Project.find(project_id) user = User.find(user_id) ::Projects::DestroyService.new(project, user, params.symbolize_keys).execute + rescue ActiveRecord::RecordNotFound => error + logger.error("Failed to delete project (#{project_id}): #{error.message}") end end diff --git a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml deleted file mode 100644 index 2c915e62357..00000000000 --- a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added "created_after" and "created_before" params to issuables -merge_request: 12151 -author: Kyle Bishop @kybishop diff --git a/changelogs/unreleased/12200-add-french-translation.yml b/changelogs/unreleased/12200-add-french-translation.yml deleted file mode 100644 index f31d982e0b9..00000000000 --- a/changelogs/unreleased/12200-add-french-translation.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Adding French translations" -merge_request: 12200 -author : Erwan "Dremor" Georget diff --git a/changelogs/unreleased/13336-multiple-broadcast-messages.yml b/changelogs/unreleased/13336-multiple-broadcast-messages.yml deleted file mode 100644 index 7dc73e1c6ea..00000000000 --- a/changelogs/unreleased/13336-multiple-broadcast-messages.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display all current broadcast messages, not just the last one -merge_request: 11113 -author: rickettm diff --git a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml b/changelogs/unreleased/18000-remember-me-for-oauth-login.yml deleted file mode 100644 index 1ef92756a76..00000000000 --- a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Honor the "Remember me" parameter for OAuth-based login -merge_request: 11963 -author: diff --git a/changelogs/unreleased/1827-prevent-concurrent-editing-wiki.yml b/changelogs/unreleased/1827-prevent-concurrent-editing-wiki.yml new file mode 100644 index 00000000000..c8c2bb3eb4c --- /dev/null +++ b/changelogs/unreleased/1827-prevent-concurrent-editing-wiki.yml @@ -0,0 +1,4 @@ +--- +title: Alert the user if a Wiki page changed while they were editing it in order to prevent overwriting changes. +merge_request: 9707 +author: Hiroyuki Sato diff --git a/changelogs/unreleased/20628-add-oauth-implicit-grant.yml b/changelogs/unreleased/20628-add-oauth-implicit-grant.yml deleted file mode 100644 index 58a28142feb..00000000000 --- a/changelogs/unreleased/20628-add-oauth-implicit-grant.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "#20628 Enable implicit grant in GitLab as OAuth Provider" -merge_request: 12384 -author: Mateusz Pytel diff --git a/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml b/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml new file mode 100644 index 00000000000..c4c3fc7ceb2 --- /dev/null +++ b/changelogs/unreleased/20817-please-add-coordinator-url-to-admin-area-runner-page.yml @@ -0,0 +1,4 @@ +--- +title: Add coordinator url to admin area runner page +merge_request: 11603 +author: diff --git a/changelogs/unreleased/22600-related-resources-uris-using-grape-source-helpers.yml b/changelogs/unreleased/22600-related-resources-uris-using-grape-source-helpers.yml new file mode 100644 index 00000000000..837a34bd067 --- /dev/null +++ b/changelogs/unreleased/22600-related-resources-uris-using-grape-source-helpers.yml @@ -0,0 +1,4 @@ +--- +title: Declare related resources into V4 API entities +merge_request: +author: diff --git a/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml deleted file mode 100644 index 07c201de96e..00000000000 --- a/changelogs/unreleased/23036-replace-dashboard-mr-spinach.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'dashboard/merge_requests' spinach with rspec -merge_request: 12440 -author: Alexander Randa (@randaalex) diff --git a/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml deleted file mode 100644 index a5f78202c93..00000000000 --- a/changelogs/unreleased/23036-replace-dashboard-new-project-spinach.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'dashboard/new-project.feature' spinach with rspec -merge_request: 12550 -author: Alexander Randa (@randaalex) diff --git a/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml b/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml deleted file mode 100644 index 65df9a836a5..00000000000 --- a/changelogs/unreleased/23036-replace-dashboard-todo-spinach.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'dashboard/todos' spinach with rspec -merge_request: 12453 -author: Alexander Randa (@randaalex) diff --git a/changelogs/unreleased/23036-replace-snippets-spinach.yml b/changelogs/unreleased/23036-replace-snippets-spinach.yml deleted file mode 100644 index 545805b1302..00000000000 --- a/changelogs/unreleased/23036-replace-snippets-spinach.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'snippets/snippets.feature' spinach with rspec -merge_request: 12385 -author: Alexander Randa @randaalex diff --git a/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml b/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml deleted file mode 100644 index 442406c3c04..00000000000 --- a/changelogs/unreleased/23162-allow-creation-of-files-and-dirs-with-spaces-in-web-ui.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow creation of files and directories with spaces through Web UI -merge_request: 12608 -author: diff --git a/changelogs/unreleased/23998-blame-age-map.yml b/changelogs/unreleased/23998-blame-age-map.yml deleted file mode 100644 index 26a38f0939c..00000000000 --- a/changelogs/unreleased/23998-blame-age-map.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add blame view age mapping -merge_request: 7198 -author: Jeff Stubler diff --git a/changelogs/unreleased/2501-ce-port-update-welcome-page.yml b/changelogs/unreleased/2501-ce-port-update-welcome-page.yml deleted file mode 100644 index cac8a522308..00000000000 --- a/changelogs/unreleased/2501-ce-port-update-welcome-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update welcome page UX for new users -merge_request: 12662 -author: diff --git a/changelogs/unreleased/25102-files-view-button.yml b/changelogs/unreleased/25102-files-view-button.yml deleted file mode 100644 index 4ba815d9464..00000000000 --- a/changelogs/unreleased/25102-files-view-button.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix mobile view of files view buttons -merge_request: -author: diff --git a/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml b/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml deleted file mode 100644 index 6688e79588f..00000000000 --- a/changelogs/unreleased/25103-mobile-members-page-user-avatar-is-misaligned.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve members view on mobile -merge_request: 12619 -author: diff --git a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml deleted file mode 100644 index 9fa824b161d..00000000000 --- a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Disable fork button on project limit -merge_request: 12145 -author: Ivan Chernov diff --git a/changelogs/unreleased/26125-match-username-on-search.yml b/changelogs/unreleased/26125-match-username-on-search.yml deleted file mode 100644 index 74e918bec16..00000000000 --- a/changelogs/unreleased/26125-match-username-on-search.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Inserts exact matches of name, username and email to the top of the search - list -merge_request: 12525 -author: diff --git a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml deleted file mode 100644 index 667454ae95d..00000000000 --- a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Accept image for avatar in user API -merge_request: 12143 -author: Ivan Chernov diff --git a/changelogs/unreleased/26372-duplicate-issue-slash-command.yml b/changelogs/unreleased/26372-duplicate-issue-slash-command.yml new file mode 100644 index 00000000000..3108344e0bf --- /dev/null +++ b/changelogs/unreleased/26372-duplicate-issue-slash-command.yml @@ -0,0 +1,4 @@ +--- +title: Added /duplicate quick action to close a duplicate issue +merge_request: 12845 +author: Ryan Scott diff --git a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml deleted file mode 100644 index 497239db808..00000000000 --- a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor - of "slash commands" -merge_request: -author: diff --git a/changelogs/unreleased/27586-center-dropdown.yml b/changelogs/unreleased/27586-center-dropdown.yml deleted file mode 100644 index 4935f7504f7..00000000000 --- a/changelogs/unreleased/27586-center-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Center dropdown for mini graph -merge_request: -author: diff --git a/changelogs/unreleased/27645-html-email-brackets-bug.yml b/changelogs/unreleased/27645-html-email-brackets-bug.yml deleted file mode 100644 index e8004d03884..00000000000 --- a/changelogs/unreleased/27645-html-email-brackets-bug.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients -merge_request: 9045 -author: jneen diff --git a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml deleted file mode 100644 index 92b5b59f46f..00000000000 --- a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use fa-chevron-down on dropdown arrows for consistency -merge_request: 9659 -author: TM Lee diff --git a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml deleted file mode 100644 index 97ebabaff1c..00000000000 --- a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use color inputs for broadcast messages -merge_request: -author: diff --git a/changelogs/unreleased/28202_decrease_abc_threshold_step2.yml b/changelogs/unreleased/28202_decrease_abc_threshold_step2.yml new file mode 100644 index 00000000000..b8f30b52b18 --- /dev/null +++ b/changelogs/unreleased/28202_decrease_abc_threshold_step2.yml @@ -0,0 +1,4 @@ +--- +title: Decrease ABC threshold to 56.96 +merge_request: 11227 +author: Maxim Rydkin diff --git a/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml b/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml deleted file mode 100644 index 720a79b8e1c..00000000000 --- a/changelogs/unreleased/28717-support-additional-prometheus-metrics.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Additional Prometheus metrics support -merge_request: 11712 -author: diff --git a/changelogs/unreleased/29289-project-destroy-clean-up-after-failure.yml b/changelogs/unreleased/29289-project-destroy-clean-up-after-failure.yml new file mode 100644 index 00000000000..488b37ac37f --- /dev/null +++ b/changelogs/unreleased/29289-project-destroy-clean-up-after-failure.yml @@ -0,0 +1,4 @@ +--- +title: Handle errors while a project is being deleted asynchronously. +merge_request: 11088 +author: diff --git a/changelogs/unreleased/29893-change-menu-locations.yml b/changelogs/unreleased/29893-change-menu-locations.yml deleted file mode 100644 index d348adc2d74..00000000000 --- a/changelogs/unreleased/29893-change-menu-locations.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: Moved "Members in a project" menu entry and path locations -merge_request: 11560 diff --git a/changelogs/unreleased/30213-project-transfer-move-rollback.yml b/changelogs/unreleased/30213-project-transfer-move-rollback.yml deleted file mode 100644 index 3eb1e399c54..00000000000 --- a/changelogs/unreleased/30213-project-transfer-move-rollback.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Rollback project repo move if there is an error in Projects::TransferService -merge_request: 11877 -author: diff --git a/changelogs/unreleased/30634-protected-pipeline.yml b/changelogs/unreleased/30634-protected-pipeline.yml new file mode 100644 index 00000000000..e46538e5b46 --- /dev/null +++ b/changelogs/unreleased/30634-protected-pipeline.yml @@ -0,0 +1,5 @@ +--- +title: Disallow running the pipeline if ref is protected and user cannot merge the + branch or create the tag +merge_request: 11910 +author: diff --git a/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml b/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml deleted file mode 100644 index 83ce3fb4d0a..00000000000 --- a/changelogs/unreleased/30708-stop-using-deleted-at-to-filter-namespaces.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Removes deleted_at and pending_delete occurrences in Project related queries -merge_request: 12091 -author: diff --git a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml deleted file mode 100644 index 3058404b3f8..00000000000 --- a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Ensures default user limits when external user is unchecked -merge_request: 12218 -author: diff --git a/changelogs/unreleased/31129-jira-project-key-elim.yml b/changelogs/unreleased/31129-jira-project-key-elim.yml new file mode 100644 index 00000000000..bfa0e99f250 --- /dev/null +++ b/changelogs/unreleased/31129-jira-project-key-elim.yml @@ -0,0 +1,4 @@ +--- +title: Remove project_key from the Jira configuration +merge_request: 12050 +author: diff --git a/changelogs/unreleased/31397-job-detail-real-time.yml b/changelogs/unreleased/31397-job-detail-real-time.yml deleted file mode 100644 index 90487a1e75a..00000000000 --- a/changelogs/unreleased/31397-job-detail-real-time.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds realtime feature to job show view header and sidebar info. Updates UX. -merge_request: -author: diff --git a/changelogs/unreleased/31415-responsive-pipelines-table-2.yml b/changelogs/unreleased/31415-responsive-pipelines-table-2.yml deleted file mode 100644 index 59402b85871..00000000000 --- a/changelogs/unreleased/31415-responsive-pipelines-table-2.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Create responsive mobile view for pipelines table -merge_request: -author: diff --git a/changelogs/unreleased/31533-usage-data-projects-stats.yml b/changelogs/unreleased/31533-usage-data-projects-stats.yml new file mode 100644 index 00000000000..11bb6118337 --- /dev/null +++ b/changelogs/unreleased/31533-usage-data-projects-stats.yml @@ -0,0 +1,4 @@ +--- +title: Add Slack and JIRA services counts to Usage Data +merge_request: +author: diff --git a/changelogs/unreleased/31982-liberation-mono-linux.yml b/changelogs/unreleased/31982-liberation-mono-linux.yml deleted file mode 100644 index c0f29cf4c47..00000000000 --- a/changelogs/unreleased/31982-liberation-mono-linux.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Change order of monospace fonts to fix bug on some linux distros -merge_request: -author: diff --git a/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml b/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml deleted file mode 100644 index 99e64b9b467..00000000000 --- a/changelogs/unreleased/32048-shared-runners-admin-buttons-have-odd-spacing.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix spacing on runner buttons. -merge_request: !12535 -author: diff --git a/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml b/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml deleted file mode 100644 index 7fc9e0a4f0e..00000000000 --- a/changelogs/unreleased/32054-rails-should-use-timestamptz-database-type-for-postgresql.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add database helpers 'add_timestamps_with_timezone' and 'timestamps_with_timezone' -merge_request: 11229 -author: @blackst0ne diff --git a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml deleted file mode 100644 index d6534ed4e1a..00000000000 --- a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Filter archived project in API v3 only if param present -merge_request: 12245 -author: Ivan Chernov diff --git a/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml b/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml deleted file mode 100644 index ebb27d118d7..00000000000 --- a/changelogs/unreleased/32408-enable-disable-all-restricted-visibility-levels.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow admins to disable all restricted visibility levels -merge_request: 12649 -author: diff --git a/changelogs/unreleased/32470-pag-links.yml b/changelogs/unreleased/32470-pag-links.yml deleted file mode 100644 index d0fd284f3ee..00000000000 --- a/changelogs/unreleased/32470-pag-links.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: more visual contrast in pagination widget -merge_request: -author: diff --git a/changelogs/unreleased/32483-jira-error.yml b/changelogs/unreleased/32483-jira-error.yml new file mode 100644 index 00000000000..1c530ca5e0f --- /dev/null +++ b/changelogs/unreleased/32483-jira-error.yml @@ -0,0 +1,4 @@ +--- +title: Display specific error message when JIRA test fails +merge_request: +author: diff --git a/changelogs/unreleased/32517-disable-hover-state.yml b/changelogs/unreleased/32517-disable-hover-state.yml deleted file mode 100644 index 31b02778963..00000000000 --- a/changelogs/unreleased/32517-disable-hover-state.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Removes hover style for nodes that are either links or buttons in the pipeline - graph -merge_request: -author: diff --git a/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml b/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml deleted file mode 100644 index 7784d7d0ce0..00000000000 --- a/changelogs/unreleased/32815--Add-Custom-CI-Config-Path.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow customize CI config path -merge_request: 12509 -author: Keith Pope diff --git a/changelogs/unreleased/32834-task-note-only.yml b/changelogs/unreleased/32834-task-note-only.yml deleted file mode 100644 index c9ea61ec4ec..00000000000 --- a/changelogs/unreleased/32834-task-note-only.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent description change notes when toggling tasks -merge_request: 12057 -author: Jared Deckard <jared.deckard@gmail.com> diff --git a/changelogs/unreleased/32838-admin-panel-spacing.yml b/changelogs/unreleased/32838-admin-panel-spacing.yml deleted file mode 100644 index ccd703fa43f..00000000000 --- a/changelogs/unreleased/32838-admin-panel-spacing.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add wells to admin dashboard overview to fix spacing problems -merge_request: -author: diff --git a/changelogs/unreleased/33003-avatar-in-project-api.yml b/changelogs/unreleased/33003-avatar-in-project-api.yml deleted file mode 100644 index 41d796ebb32..00000000000 --- a/changelogs/unreleased/33003-avatar-in-project-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Accept image for avatar in project API -merge_request: 11988 -author: Ivan Chernov diff --git a/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml b/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml deleted file mode 100644 index d3172c405c3..00000000000 --- a/changelogs/unreleased/33082-use-update_pipeline_schedule-for-edit-and-take_ownership-in-pipelineschedulescontroller.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use authorize_update_pipeline_schedule in PipelineSchedulesController -merge_request: 11846 -author: diff --git a/changelogs/unreleased/33097-issue-tracker.yml b/changelogs/unreleased/33097-issue-tracker.yml new file mode 100644 index 00000000000..0b13f7165db --- /dev/null +++ b/changelogs/unreleased/33097-issue-tracker.yml @@ -0,0 +1,4 @@ +--- +title: Associate Issues tab only with internal issues tracker +merge_request: +author: diff --git a/changelogs/unreleased/33130-remove-group-modal.yml b/changelogs/unreleased/33130-remove-group-modal.yml deleted file mode 100644 index 4672d41ded5..00000000000 --- a/changelogs/unreleased/33130-remove-group-modal.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Remove group modal like remove project modal (requires typing + confirmation)" -merge_request: 12569 -author: Diego Souza diff --git a/changelogs/unreleased/33132-change-icon-color.yml b/changelogs/unreleased/33132-change-icon-color.yml deleted file mode 100644 index c0e148f985b..00000000000 --- a/changelogs/unreleased/33132-change-icon-color.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Render CI statuses with warnings in orange -merge_request: -author: diff --git a/changelogs/unreleased/33208-singup-active-state-underline.yml b/changelogs/unreleased/33208-singup-active-state-underline.yml deleted file mode 100644 index cddb43214ea..00000000000 --- a/changelogs/unreleased/33208-singup-active-state-underline.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixes "sign in / Register" active state underline misalignment -merge_request: 11890 -author: Frank Sierra diff --git a/changelogs/unreleased/33360-generate-kubeconfig.yml b/changelogs/unreleased/33360-generate-kubeconfig.yml deleted file mode 100644 index 96f0b1bc93f..00000000000 --- a/changelogs/unreleased/33360-generate-kubeconfig.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Provide KUBECONFIG from KubernetesService for runners -merge_request: 12223 -author: diff --git a/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml b/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml deleted file mode 100644 index 4a7b02fec94..00000000000 --- a/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display issue state in issue links section of merge request widget -merge_request: 12021 -author: diff --git a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml deleted file mode 100644 index a7d8ac9054b..00000000000 --- a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Supplement Simplified Chinese translation of Project Page & Repository Page -merge_request: 11994 -author: Huang Tao diff --git a/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml b/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml deleted file mode 100644 index e383bab23d6..00000000000 --- a/changelogs/unreleased/33442-supplement_traditional_chinese_in_hong_kong_translation_of_i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Supplement Traditional Chinese in Hong Kong translation of Project Page & Repository Page -merge_request: 11995 -author: Huang Tao diff --git a/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml b/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml deleted file mode 100644 index d6b1b2524c6..00000000000 --- a/changelogs/unreleased/33443-supplement_traditional_chinese_in_taiwan_translation_of_i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Supplement Traditional Chinese in Taiwan translation of Project Page & Repository Page -merge_request: 12514 -author: Huang Tao diff --git a/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml b/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml deleted file mode 100644 index 385f18e2560..00000000000 --- a/changelogs/unreleased/33445-document-delete-merge-branches-won-t-touch-protected-branches-docs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Document the Delete Merged Branches functionality -merge_request: -author: diff --git a/changelogs/unreleased/33461-display-user-id.yml b/changelogs/unreleased/33461-display-user-id.yml deleted file mode 100644 index cba94625b07..00000000000 --- a/changelogs/unreleased/33461-display-user-id.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Display own user id in account settings page -merge_request: 12141 -author: Riccardo Padovani diff --git a/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml b/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml deleted file mode 100644 index 590472c0990..00000000000 --- a/changelogs/unreleased/33538-update-ci-dockerfile-now-that-chrome-headless-no-longer-in-beta.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update QA Dockerfile to lock Chrome browser version -merge_request: 12071 -author: diff --git a/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml b/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml deleted file mode 100644 index 4f2ba2e1de3..00000000000 --- a/changelogs/unreleased/33561-supplement_bulgarian_translation_of_i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Supplement Bulgarian translation of Project Page & Repository Page -merge_request: 12083 -author: Lyubomir Vasilev diff --git a/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml b/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml new file mode 100644 index 00000000000..88cfb99a73e --- /dev/null +++ b/changelogs/unreleased/33601-add-csrf-token-verification-to-api.yml @@ -0,0 +1,4 @@ +--- +title: Add CSRF token verification to API +merge_request: 12154 +author: Vitaliy @blackst0ne Klachkov diff --git a/changelogs/unreleased/33657-user-projects-api.yml b/changelogs/unreleased/33657-user-projects-api.yml deleted file mode 100644 index a8d485865e9..00000000000 --- a/changelogs/unreleased/33657-user-projects-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add user projects API -merge_request: 12596 -author: Ivan Chernov diff --git a/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml b/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml deleted file mode 100644 index d2bdc631d2a..00000000000 --- a/changelogs/unreleased/33672-supplement_portuguese_brazil_translation_of_i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Supplement Portuguese Brazil translation of Project Page & Repository Page -merge_request: 12156 -author: Huang Tao diff --git a/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml b/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml deleted file mode 100644 index 7402c33c5c6..00000000000 --- a/changelogs/unreleased/33748-fix-n-plus-1-query-in-the-projects-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve the performance of the project list API -merge_request: 12679 -author: diff --git a/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml b/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml deleted file mode 100644 index c2bce368a58..00000000000 --- a/changelogs/unreleased/33772-readonly-gitlab-ci-cache.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Introduce cache policies for CI jobs -merge_request: 12483 -author: diff --git a/changelogs/unreleased/33837-remove-trash-on-registry-image.yml b/changelogs/unreleased/33837-remove-trash-on-registry-image.yml deleted file mode 100644 index 2d337f5e6e4..00000000000 --- a/changelogs/unreleased/33837-remove-trash-on-registry-image.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove registry image delete button if user cant delete it -merge_request: 12317 -author: Ivan Chernov diff --git a/changelogs/unreleased/33846-no-runner-for-admin.yml b/changelogs/unreleased/33846-no-runner-for-admin.yml deleted file mode 100644 index a2d46802c61..00000000000 --- a/changelogs/unreleased/33846-no-runner-for-admin.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add explicit message when no runners on admin -merge_request: 12266 -author: Takuya Noguchi diff --git a/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml b/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml deleted file mode 100644 index 810cc8489b5..00000000000 --- a/changelogs/unreleased/33929-allow-to-enable-perf-bar-for-a-group.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow to enable the performance bar per user or Feature group -merge_request: 12362 -author: diff --git a/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml b/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml deleted file mode 100644 index a08795e1a26..00000000000 --- a/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Deprecate Healthcheck Access Token in favor of IP whitelist -merge_request: -author: diff --git a/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml b/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml deleted file mode 100644 index 4bacfca7551..00000000000 --- a/changelogs/unreleased/34052-store-mr-ref-fetched-in-database.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Store merge request ref_fetched status in the database -merge_request: 12424 -author: diff --git a/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml b/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml deleted file mode 100644 index 69d5d34b072..00000000000 --- a/changelogs/unreleased/34078-allow-to-enable-feature-flags-with-more-granularity.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow the feature flags to be enabled/disabled with more granularity -merge_request: 12357 -author: diff --git a/changelogs/unreleased/34110-memory-usage-notice-doesn-t-link-anywhere.yml b/changelogs/unreleased/34110-memory-usage-notice-doesn-t-link-anywhere.yml new file mode 100644 index 00000000000..1911705dd2b --- /dev/null +++ b/changelogs/unreleased/34110-memory-usage-notice-doesn-t-link-anywhere.yml @@ -0,0 +1,4 @@ +--- +title: Added link to the MR widget that directs to the monitoring dashboard +merge_request: +author: diff --git a/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml b/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml deleted file mode 100644 index 8f8b5a96c2b..00000000000 --- a/changelogs/unreleased/34116-milestone-filtering-on-group-issues.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Change milestone endpoint for groups -merge_request: 12374 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml b/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml deleted file mode 100644 index a3ade8db214..00000000000 --- a/changelogs/unreleased/34141-allow-unauthenticated-access-to-the-users-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow unauthenticated access to the /api/v4/users API -merge_request: 12445 -author: diff --git a/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml b/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml deleted file mode 100644 index 1a631c3f0a4..00000000000 --- a/changelogs/unreleased/34169-add-simplified-chinese-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Simplified Chinese translations of Commits Page -merge_request: 12405 -author: Huang Tao diff --git a/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml b/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml deleted file mode 100644 index 3cf7c0b547f..00000000000 --- a/changelogs/unreleased/34171-add-traditional-chinese-in-hongkong-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Traditional Chinese in HongKong translations of Commits Page -merge_request: 12406 -author: Huang Tao diff --git a/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml b/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml deleted file mode 100644 index 224b9e1852f..00000000000 --- a/changelogs/unreleased/34172-add-traditional-chinese-in-taiwan-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Traditional Chinese in Taiwan translations of Commits Page -merge_request: 12407 -author: Huang Tao diff --git a/changelogs/unreleased/34173-add-portuguese-brazil-translations-of-commits-page.yml b/changelogs/unreleased/34173-add-portuguese-brazil-translations-of-commits-page.yml deleted file mode 100644 index 16a9216852d..00000000000 --- a/changelogs/unreleased/34173-add-portuguese-brazil-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Portuguese Brazil translations of Commits Page -merge_request: 12408 -author: Huang Tao diff --git a/changelogs/unreleased/34174-add-french-translations-of-commits-page.yml b/changelogs/unreleased/34174-add-french-translations-of-commits-page.yml deleted file mode 100644 index 2b223d2e8bc..00000000000 --- a/changelogs/unreleased/34174-add-french-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add French translations of Commits Page -merge_request: 12409 -author: Huang Tao diff --git a/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml b/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml deleted file mode 100644 index b43a38f3794..00000000000 --- a/changelogs/unreleased/34175-add-esperanto-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Esperanto translations of Commits Page -merge_request: 12410 -author: Huang Tao diff --git a/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml b/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml deleted file mode 100644 index 9177ae3acd1..00000000000 --- a/changelogs/unreleased/34176-add-bulgarian-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Bulgarian translations of Commits Page -merge_request: 12411 -author: Huang Tao diff --git a/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml b/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml deleted file mode 100644 index 4fa385c3c27..00000000000 --- a/changelogs/unreleased/34207-remove-bin-ci-upgrade-rb.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove bin/ci/upgrade.rb as not working all -merge_request: 12414 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml b/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml deleted file mode 100644 index af743f3e506..00000000000 --- a/changelogs/unreleased/34286-add-esperanto-translations-for-cycle-analytics-and-project-and-repository-pages.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Esperanto translations for Cycle Analytics, Project, and Repository pages -merge_request: 12442 -author: Huang Tao diff --git a/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml b/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml deleted file mode 100644 index 42e906d24c6..00000000000 --- a/changelogs/unreleased/34289-drop-gfm-on-milestone-issuable-title.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Drop GFM support for issuable title on milestone for consistency and performance -merge_request: -author: Takuya Noguchi diff --git a/changelogs/unreleased/34309-drop-gfm-mr-ms.yml b/changelogs/unreleased/34309-drop-gfm-mr-ms.yml deleted file mode 100644 index 07fe79e90ee..00000000000 --- a/changelogs/unreleased/34309-drop-gfm-mr-ms.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Drop GFM support for the title of Milestone/MergeRequest in template -merge_request: 12451 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34361-lazy-load-images-on-the-frontend.yml b/changelogs/unreleased/34361-lazy-load-images-on-the-frontend.yml new file mode 100644 index 00000000000..d188a558d38 --- /dev/null +++ b/changelogs/unreleased/34361-lazy-load-images-on-the-frontend.yml @@ -0,0 +1,4 @@ +--- +title: Lazy load images for better Frontend performance +merge_request: 12503 +author: diff --git a/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml b/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml deleted file mode 100644 index 4911315d018..00000000000 --- a/changelogs/unreleased/34403-issue-dropdown-persists-when-adding-issue-number-to-issue-description.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Closes any open Autocomplete of the markdown editor when the form is closed -merge_request: 12521 -author: diff --git a/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml b/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml deleted file mode 100644 index 99291b4c75a..00000000000 --- a/changelogs/unreleased/34468-remove-extra-blank-on-admin-on-mobile.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use smaller min-width for dropdown-menu-nav only on mobile -merge_request: 12528 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34531-remove-scroll.yml b/changelogs/unreleased/34531-remove-scroll.yml deleted file mode 100644 index c3c5289f66f..00000000000 --- a/changelogs/unreleased/34531-remove-scroll.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update jobs page output to have a scrollable page -merge_request: 12587 -author: diff --git a/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml b/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml deleted file mode 100644 index 31f4262c9f9..00000000000 --- a/changelogs/unreleased/34544-add-italian-translation-of-cycle-analytics-page-&-project-page-&-repository-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Italian translation of Cycle Analytics Page & Project Page & Repository Page -merge_request: 12578 -author: Huang Tao diff --git a/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml new file mode 100644 index 00000000000..e843bbac239 --- /dev/null +++ b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml @@ -0,0 +1,4 @@ +--- +title: Extract "@request.env[devise.mapping] = Devise.mappings[:user]" to a test helper +merge_request: 12742 +author: Jacopo Beschi @jacopo-beschi diff --git a/changelogs/unreleased/34578-sidebar-padding.yml b/changelogs/unreleased/34578-sidebar-padding.yml deleted file mode 100644 index dc4647298e6..00000000000 --- a/changelogs/unreleased/34578-sidebar-padding.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: fix left & right padding on sidebar -merge_request: -author: diff --git a/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml b/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml deleted file mode 100644 index 11c01d28dc2..00000000000 --- a/changelogs/unreleased/34590-fix-dashboard-labels-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix dashboard labels dropdown -merge_request: 12708 -author: diff --git a/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml b/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml deleted file mode 100644 index 736991318d7..00000000000 --- a/changelogs/unreleased/34653-minor-ux-cleanups-for-performance-dashboard.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Cleanup minor UX issues in the performance dashboard -merge_request: -author: diff --git a/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml b/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml deleted file mode 100644 index c7a68935e8c..00000000000 --- a/changelogs/unreleased/34655-label-field-for-setting-a-chart-s-legend-text-is-not-working.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed the chart legend not being set correctly -merge_request: 12628 -author: diff --git a/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml b/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml deleted file mode 100644 index 90a1f8c98fe..00000000000 --- a/changelogs/unreleased/34688-add-italian-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Italian translations of Commits Page -merge_request: 12645 -author: Huang Tao diff --git a/changelogs/unreleased/34727-simplified-member-settings.yml b/changelogs/unreleased/34727-simplified-member-settings.yml deleted file mode 100644 index 8c4844c001b..00000000000 --- a/changelogs/unreleased/34727-simplified-member-settings.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove two columned layout from project member settings -merge_request: -author: diff --git a/changelogs/unreleased/34729-blob.yml b/changelogs/unreleased/34729-blob.yml deleted file mode 100644 index 15a469d3af0..00000000000 --- a/changelogs/unreleased/34729-blob.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix crash on /help/ui -merge_request: -author: diff --git a/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml b/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml deleted file mode 100644 index 8df3a1a6940..00000000000 --- a/changelogs/unreleased/34736-n-1-problem-on-milestone-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: N+1 problems on milestone page -merge_request: 12670 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34789-add-japanese-translations-of-commits-page.yml b/changelogs/unreleased/34789-add-japanese-translations-of-commits-page.yml deleted file mode 100644 index 40a24847580..00000000000 --- a/changelogs/unreleased/34789-add-japanese-translations-of-commits-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Japanese translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. -merge_request: 12693 -author: Huang Tao diff --git a/changelogs/unreleased/34880-add-ukrainian-translations-to-i18n.yml b/changelogs/unreleased/34880-add-ukrainian-translations-to-i18n.yml deleted file mode 100644 index 4e8a042fdb5..00000000000 --- a/changelogs/unreleased/34880-add-ukrainian-translations-to-i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Ukrainian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. -merge_request: 12744 -author: Huang Tao diff --git a/changelogs/unreleased/34881-add-russian-translations-to-i18n.yml b/changelogs/unreleased/34881-add-russian-translations-to-i18n.yml deleted file mode 100644 index aed05dd1031..00000000000 --- a/changelogs/unreleased/34881-add-russian-translations-to-i18n.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Russian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. -merge_request: 12743 -author: Huang Tao diff --git a/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml b/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml deleted file mode 100644 index 22c9c45bc75..00000000000 --- a/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Do not show pipeline schedule button for non-member -merge_request: 12757 -author: Takuya Noguchi diff --git a/changelogs/unreleased/34921-global-dropdown-ui-improvement.yml b/changelogs/unreleased/34921-global-dropdown-ui-improvement.yml new file mode 100644 index 00000000000..6a17353ba3f --- /dev/null +++ b/changelogs/unreleased/34921-global-dropdown-ui-improvement.yml @@ -0,0 +1,4 @@ +--- +title: Improve CSS for global nav dropdown UI +merge_request: 12772 +author: Takuya Noguchi diff --git a/changelogs/unreleased/34930-fix-edited-by.yml b/changelogs/unreleased/34930-fix-edited-by.yml deleted file mode 100644 index f133dfab0c2..00000000000 --- a/changelogs/unreleased/34930-fix-edited-by.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Use Ghost user for last_edited_by and merge_user when original user is deleted -merge_request: 12933 -author: diff --git a/changelogs/unreleased/35035-sidebar-job-spaces.yml b/changelogs/unreleased/35035-sidebar-job-spaces.yml deleted file mode 100644 index a9a0211efd9..00000000000 --- a/changelogs/unreleased/35035-sidebar-job-spaces.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix vertical space in job details sidebar -merge_request: -author: diff --git a/changelogs/unreleased/35087-mr-status-misaligned.yml b/changelogs/unreleased/35087-mr-status-misaligned.yml deleted file mode 100644 index 3be43125a61..00000000000 --- a/changelogs/unreleased/35087-mr-status-misaligned.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix alignment of controls in mr issuable list -merge_request: -author: diff --git a/changelogs/unreleased/35163-url-in-commit-message-can-be-broken-in-blame.yml b/changelogs/unreleased/35163-url-in-commit-message-can-be-broken-in-blame.yml new file mode 100644 index 00000000000..4fd60a79782 --- /dev/null +++ b/changelogs/unreleased/35163-url-in-commit-message-can-be-broken-in-blame.yml @@ -0,0 +1,4 @@ +--- +title: Use only CSS to truncate commit message in blame +merge_request: 12900 +author: Takuya Noguchi diff --git a/changelogs/unreleased/35191-prioritized-labels-for-non-member.yml b/changelogs/unreleased/35191-prioritized-labels-for-non-member.yml new file mode 100644 index 00000000000..fbe55d4c2b0 --- /dev/null +++ b/changelogs/unreleased/35191-prioritized-labels-for-non-member.yml @@ -0,0 +1,4 @@ +--- +title: Remove help message about prioritized labels for non-members +merge_request: 12912 +author: Takuya Noguchi diff --git a/changelogs/unreleased/35204-doc-api-ci-lint-typo.yml b/changelogs/unreleased/35204-doc-api-ci-lint-typo.yml new file mode 100644 index 00000000000..45b6c57579b --- /dev/null +++ b/changelogs/unreleased/35204-doc-api-ci-lint-typo.yml @@ -0,0 +1,4 @@ +--- +title: Add link to doc/api/ci/lint.md +merge_request: 12914 +author: Takuya Noguchi diff --git a/changelogs/unreleased/35209-add-wip-info-new-nav-pref.yml b/changelogs/unreleased/35209-add-wip-info-new-nav-pref.yml deleted file mode 100644 index 680e1cd8222..00000000000 --- a/changelogs/unreleased/35209-add-wip-info-new-nav-pref.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add wip message to new navigation preference section -merge_request: -author: diff --git a/changelogs/unreleased/35391-fix-star-i18n-in-js.yml b/changelogs/unreleased/35391-fix-star-i18n-in-js.yml new file mode 100644 index 00000000000..a6fd4dc89fd --- /dev/null +++ b/changelogs/unreleased/35391-fix-star-i18n-in-js.yml @@ -0,0 +1,4 @@ +--- +title: Fix translations for Star/Unstar in JS file +merge_request: +author: diff --git a/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml b/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml deleted file mode 100644 index 4948d415bed..00000000000 --- a/changelogs/unreleased/add-ci_variables-environment_scope-mysql.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Rename duplicated variables with the same key for projects. Add environment_scope - column to variables and add unique constraint to make sure that no variables could - be created with the same key within a project -merge_request: 12363 -author: diff --git a/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml b/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml deleted file mode 100644 index f2591042e98..00000000000 --- a/changelogs/unreleased/add-group-members-counting-and-plan-related-data-on-namespaces-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add group members counting and plan related data on namespaces API -merge_request: -author: diff --git a/changelogs/unreleased/bump-omniauth-ldap-gem-version.yml b/changelogs/unreleased/bump-omniauth-ldap-gem-version.yml new file mode 100644 index 00000000000..42e1c9e8f83 --- /dev/null +++ b/changelogs/unreleased/bump-omniauth-ldap-gem-version.yml @@ -0,0 +1,4 @@ +--- +title: Prevent LDAP login callback from being called with a GET request +merge_request: 13059 +author: diff --git a/changelogs/unreleased/bvl-add-all-settings-to-api.yml b/changelogs/unreleased/bvl-add-all-settings-to-api.yml new file mode 100644 index 00000000000..bfaf237a21c --- /dev/null +++ b/changelogs/unreleased/bvl-add-all-settings-to-api.yml @@ -0,0 +1,4 @@ +--- +title: Make all application-settings accessible through the API +merge_request: 12851 +author: diff --git a/changelogs/unreleased/bvl-free-system-namespace.yml b/changelogs/unreleased/bvl-free-system-namespace.yml deleted file mode 100644 index 6c2d1e0e61f..00000000000 --- a/changelogs/unreleased/bvl-free-system-namespace.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Move uploads from `uploads/system` to `uploads/-/system` to free up `system` as a group name" -merge_request: 11713 -author: diff --git a/changelogs/unreleased/bvl-free-unused-names.yml b/changelogs/unreleased/bvl-free-unused-names.yml new file mode 100644 index 00000000000..53acb95e5bb --- /dev/null +++ b/changelogs/unreleased/bvl-free-unused-names.yml @@ -0,0 +1,5 @@ +--- +title: Free up some top level words, reject top level groups named like files in the + public folder +merge_request: 12932 +author: diff --git a/changelogs/unreleased/bvl-rename-all-reserved-paths.yml b/changelogs/unreleased/bvl-rename-all-reserved-paths.yml deleted file mode 100644 index f37f2fa94ae..00000000000 --- a/changelogs/unreleased/bvl-rename-all-reserved-paths.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Rename all reserved paths that could have been created -merge_request: 11713 -author: diff --git a/changelogs/unreleased/commit-comments-limited-width.yml b/changelogs/unreleased/commit-comments-limited-width.yml deleted file mode 100644 index 97f50105495..00000000000 --- a/changelogs/unreleased/commit-comments-limited-width.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Limit commit & snippets comments width -merge_request: -author: diff --git a/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml b/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml deleted file mode 100644 index 357a623e0e8..00000000000 --- a/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed dashboard milestone tabs not loading -merge_request: -author: diff --git a/changelogs/unreleased/dm-blob-binaryness-change.yml b/changelogs/unreleased/dm-blob-binaryness-change.yml deleted file mode 100644 index f3e3af26f12..00000000000 --- a/changelogs/unreleased/dm-blob-binaryness-change.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Detect if file that appears to be text in the first 1024 bytes is actually - binary afer loading all data -merge_request: -author: diff --git a/changelogs/unreleased/dm-commit-row-browse-button.yml b/changelogs/unreleased/dm-commit-row-browse-button.yml deleted file mode 100644 index 4240a7de5de..00000000000 --- a/changelogs/unreleased/dm-commit-row-browse-button.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix inconsistent display of the "Browse files" button in the commit list -merge_request: -author: diff --git a/changelogs/unreleased/dm-diff-viewers.yml b/changelogs/unreleased/dm-diff-viewers.yml deleted file mode 100644 index e5b1352c8f1..00000000000 --- a/changelogs/unreleased/dm-diff-viewers.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Implement diff viewers -merge_request: -author: diff --git a/changelogs/unreleased/dm-empty-state-new-merge-request.yml b/changelogs/unreleased/dm-empty-state-new-merge-request.yml deleted file mode 100644 index 5fad7a0f883..00000000000 --- a/changelogs/unreleased/dm-empty-state-new-merge-request.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix 'New merge request' button for users who don't have push access to canonical - project -merge_request: -author: diff --git a/changelogs/unreleased/dm-group-page-name.yml b/changelogs/unreleased/dm-group-page-name.yml deleted file mode 100644 index 233879364e3..00000000000 --- a/changelogs/unreleased/dm-group-page-name.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Show group name instead of path on group page -merge_request: -author: diff --git a/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml b/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml deleted file mode 100644 index 7fd252e9b8b..00000000000 --- a/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't check if MailRoom is running on Omnibus -merge_request: -author: diff --git a/changelogs/unreleased/dm-page-image-size.yml b/changelogs/unreleased/dm-page-image-size.yml deleted file mode 100644 index b18c00470fc..00000000000 --- a/changelogs/unreleased/dm-page-image-size.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Limit OpenGraph image size to 64x64 -merge_request: -author: diff --git a/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml b/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml deleted file mode 100644 index 8b026a4c289..00000000000 --- a/changelogs/unreleased/dm-readme-auxiliary-blob-viewer-without-wiki.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't show auxiliary blob viewer for README when there is no wiki -merge_request: -author: diff --git a/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml b/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml deleted file mode 100644 index 616241dd941..00000000000 --- a/changelogs/unreleased/dm-relative-submodule-url-trailing-whitespace.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Strip trailing whitespace in relative submodule URL -merge_request: -author: diff --git a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml b/changelogs/unreleased/dm-target-branch-slash-command-desc.yml deleted file mode 100644 index 768ddf0416e..00000000000 --- a/changelogs/unreleased/dm-target-branch-slash-command-desc.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update /target_branch slash command description to be more consistent -merge_request: -author: diff --git a/changelogs/unreleased/dm-unnecessary-top-padding.yml b/changelogs/unreleased/dm-unnecessary-top-padding.yml deleted file mode 100644 index 4557c06f8e7..00000000000 --- a/changelogs/unreleased/dm-unnecessary-top-padding.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove unnecessary top padding on group MR index -merge_request: -author: diff --git a/changelogs/unreleased/doc-gitaly-network.yml b/changelogs/unreleased/doc-gitaly-network.yml deleted file mode 100644 index 5376d8d5096..00000000000 --- a/changelogs/unreleased/doc-gitaly-network.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add option to run Gitaly on a remote server -merge_request: 12381 -author: diff --git a/changelogs/unreleased/dt-printing-to-api.yml b/changelogs/unreleased/dt-printing-to-api.yml deleted file mode 100644 index 5253b57f21a..00000000000 --- a/changelogs/unreleased/dt-printing-to-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added printing_merge_requst_link_enabled to the API -merge_request: -author: David Turner <dturner@twosigma.com> diff --git a/changelogs/unreleased/dz-fix-calendar-today.yml b/changelogs/unreleased/dz-fix-calendar-today.yml new file mode 100644 index 00000000000..5320d8b26b5 --- /dev/null +++ b/changelogs/unreleased/dz-fix-calendar-today.yml @@ -0,0 +1,4 @@ +--- +title: Fix today day highlight in calendar +merge_request: 13048 +author: diff --git a/changelogs/unreleased/enable-polling-env.yml b/changelogs/unreleased/enable-polling-env.yml deleted file mode 100644 index b3f65f02574..00000000000 --- a/changelogs/unreleased/enable-polling-env.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Re-enable realtime for environments table -merge_request: -author: diff --git a/changelogs/unreleased/enable-webpack-code-splitting.yml b/changelogs/unreleased/enable-webpack-code-splitting.yml deleted file mode 100644 index d61c3b97d11..00000000000 --- a/changelogs/unreleased/enable-webpack-code-splitting.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable support for webpack code-splitting by dynamically setting publicPath - at runtime -merge_request: 12032 -author: diff --git a/changelogs/unreleased/feature-add-support-for-services-configuration.yml b/changelogs/unreleased/feature-add-support-for-services-configuration.yml deleted file mode 100644 index 88a3eacd774..00000000000 --- a/changelogs/unreleased/feature-add-support-for-services-configuration.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add support for image and services configuration in .gitlab-ci.yml -merge_request: 8578 -author: diff --git a/changelogs/unreleased/feature-backup-custom-path.yml b/changelogs/unreleased/feature-backup-custom-path.yml new file mode 100644 index 00000000000..1c5f25b3ee5 --- /dev/null +++ b/changelogs/unreleased/feature-backup-custom-path.yml @@ -0,0 +1,4 @@ +--- +title: Support custom directory in gitlab:backup:create task +merge_request: 12984 +author: Markus Koller diff --git a/changelogs/unreleased/feature-gpg-signed-commits.yml b/changelogs/unreleased/feature-gpg-signed-commits.yml new file mode 100644 index 00000000000..99bc5a309ef --- /dev/null +++ b/changelogs/unreleased/feature-gpg-signed-commits.yml @@ -0,0 +1,4 @@ +--- +title: GPG signed commits integration +merge_request: 9546 +author: Alexis Reigel diff --git a/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml b/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml deleted file mode 100644 index 333895ffba9..00000000000 --- a/changelogs/unreleased/feature-intermediate-12729-group-secret-variables.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add Group secret variables -merge_request: 12582 -author: diff --git a/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml b/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml deleted file mode 100644 index d497575b7f3..00000000000 --- a/changelogs/unreleased/feature-intermediate-32568-adding-variables-to-pipelines-schedules.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add variables to pipelines schedules -merge_request: 12372 -author: diff --git a/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml b/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml deleted file mode 100644 index bbcf2946ea7..00000000000 --- a/changelogs/unreleased/feature-no-hypen-at-end-of-commit-ref-slug.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Omit trailing / leading hyphens in CI_COMMIT_REF_SLUG variable to make it usable as a hostname -merge_request: 11218 -author: Stefan Hanreich diff --git a/changelogs/unreleased/feature-unify-email-layouts.yml b/changelogs/unreleased/feature-unify-email-layouts.yml deleted file mode 100644 index 7a2e3f20b6b..00000000000 --- a/changelogs/unreleased/feature-unify-email-layouts.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Update the devise mail templates to match the design of the pipeline emails -merge_request: 10483 -author: Alexis Reigel diff --git a/changelogs/unreleased/feature-user-agent-details-api.yml b/changelogs/unreleased/feature-user-agent-details-api.yml deleted file mode 100644 index 839ec7d21cd..00000000000 --- a/changelogs/unreleased/feature-user-agent-details-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Allow admins to retrieve user agent details for an issue or snippet -merge_request: 12655 -author: diff --git a/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml b/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml deleted file mode 100644 index 27ac50c6cc2..00000000000 --- a/changelogs/unreleased/feature-user-datetime-search-api-mysql.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add creation time filters to user search API for admins -merge_request: 12682 -author: diff --git a/changelogs/unreleased/fix-33991.yml b/changelogs/unreleased/fix-33991.yml deleted file mode 100644 index 39732611b6e..00000000000 --- a/changelogs/unreleased/fix-33991.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Users can subscribe to group labels on the group labels page -merge_request: -author: diff --git a/changelogs/unreleased/fix-assigned-issuable-lists.yml b/changelogs/unreleased/fix-assigned-issuable-lists.yml deleted file mode 100644 index fc2cd18ddb6..00000000000 --- a/changelogs/unreleased/fix-assigned-issuable-lists.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add issuable-list class to shared mr/issue lists to fix new responsive layout - design -merge_request: -author: diff --git a/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml b/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml deleted file mode 100644 index 2e0573beab6..00000000000 --- a/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Exact matches of username and email are now on top of the user search -merge_request: 12868 -author: diff --git a/changelogs/unreleased/fix-gb-fix-container-registry-tag-routing.yml b/changelogs/unreleased/fix-gb-fix-container-registry-tag-routing.yml deleted file mode 100644 index 5a2644d14a7..00000000000 --- a/changelogs/unreleased/fix-gb-fix-container-registry-tag-routing.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix docker tag reference routing constraints -merge_request: 12961 -author: diff --git a/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml b/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml deleted file mode 100644 index f59c6ecd90c..00000000000 --- a/changelogs/unreleased/fix-gb-fix-skipped-pipeline-with-allowed-to-fail-jobs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix CI/CD status in case there are only allowed to failed jobs in the pipeline -merge_request: 11166 -author: diff --git a/changelogs/unreleased/fix-gb-handle-max-pages-artifacts-size-correctly.yml b/changelogs/unreleased/fix-gb-handle-max-pages-artifacts-size-correctly.yml new file mode 100644 index 00000000000..3d9592bbf2a --- /dev/null +++ b/changelogs/unreleased/fix-gb-handle-max-pages-artifacts-size-correctly.yml @@ -0,0 +1,4 @@ +--- +title: Handle maximum pages artifacts size correctly +merge_request: 13072 +author: diff --git a/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml b/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml deleted file mode 100644 index 7adc53eb8fa..00000000000 --- a/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Recover from renaming project that has container images -merge_request: 12840 -author: diff --git a/changelogs/unreleased/fix-mrs-merged-immediately.yml b/changelogs/unreleased/fix-mrs-merged-immediately.yml deleted file mode 100644 index 41c06614e6d..00000000000 --- a/changelogs/unreleased/fix-mrs-merged-immediately.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Don't mark empty MRs as merged on push to the target branch -merge_request: -author: diff --git a/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml b/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml deleted file mode 100644 index 5781316cfd9..00000000000 --- a/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve issue rendering performance with lots of notes from other users -merge_request: -author: diff --git a/changelogs/unreleased/fix-overflow-slash-commands.yml b/changelogs/unreleased/fix-overflow-slash-commands.yml deleted file mode 100644 index 98ec399e8cb..00000000000 --- a/changelogs/unreleased/fix-overflow-slash-commands.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed overflow on mobile screens for the slash commands -merge_request: -author: diff --git a/changelogs/unreleased/fix-runner_online_check.yml b/changelogs/unreleased/fix-runner_online_check.yml deleted file mode 100644 index bc0de979b4c..00000000000 --- a/changelogs/unreleased/fix-runner_online_check.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix offline runner detection -merge_request: 11751 -author: Alessio Caiazza diff --git a/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml b/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml deleted file mode 100644 index 856990a6126..00000000000 --- a/changelogs/unreleased/fix-sidebar-showing-mobile-merge-requests.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed sidebar not collapsing on merge requests in mobile screens -merge_request: -author: diff --git a/changelogs/unreleased/fix-u2f-for-opera.yml b/changelogs/unreleased/fix-u2f-for-opera.yml deleted file mode 100644 index 0eafb8eff9a..00000000000 --- a/changelogs/unreleased/fix-u2f-for-opera.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix FIDO U2F for Opera browser -merge_request: 12082 -author: Jakub Kramarz and Jonas Kalderstam diff --git a/changelogs/unreleased/fix_docs_commits_multiple_files.yml b/changelogs/unreleased/fix_docs_commits_multiple_files.yml deleted file mode 100644 index 36567354b28..00000000000 --- a/changelogs/unreleased/fix_docs_commits_multiple_files.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Documentation bugfix of invalid JSON payload example of Create a commit with - multiple files and actions -merge_request: 12117 -author: @blackst0ne diff --git a/changelogs/unreleased/foreign-keys-for-project-model.yml b/changelogs/unreleased/foreign-keys-for-project-model.yml deleted file mode 100644 index 3648b1c3735..00000000000 --- a/changelogs/unreleased/foreign-keys-for-project-model.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Speed up project removals by adding foreign keys with cascading deletes to various tables -merge_request: -author: diff --git a/changelogs/unreleased/gitaly-mandatory.yml b/changelogs/unreleased/gitaly-mandatory.yml deleted file mode 100644 index c060e0add29..00000000000 --- a/changelogs/unreleased/gitaly-mandatory.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove option to disable Gitaly -merge_request: 12677 -author: diff --git a/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml b/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml deleted file mode 100644 index ec2f4f9c3d8..00000000000 --- a/changelogs/unreleased/hb-fix-abuse-report-on-stale-user-profile.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix errors caused by attempts to report already blocked or deleted users -merge_request: 12502 -author: Horacio Bertorello diff --git a/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml b/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml deleted file mode 100644 index 3b465d84126..00000000000 --- a/changelogs/unreleased/hb-hide-archived-labels-from-group-issue-tracker.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Hide archived project labels from group issue tracker -merge_request: 12547 -author: Horacio Bertorello diff --git a/changelogs/unreleased/help-landing-page-customizations.yml b/changelogs/unreleased/help-landing-page-customizations.yml deleted file mode 100644 index 58cab751ded..00000000000 --- a/changelogs/unreleased/help-landing-page-customizations.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Help landing page customizations -merge_request: 11878 -author: Robin Bobbitt diff --git a/changelogs/unreleased/help-page-breadcrumb-title-fix.yml b/changelogs/unreleased/help-page-breadcrumb-title-fix.yml new file mode 100644 index 00000000000..040fe4b9916 --- /dev/null +++ b/changelogs/unreleased/help-page-breadcrumb-title-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed new navigation breadcrumb title on help pages +merge_request: +author: diff --git a/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml b/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml deleted file mode 100644 index 05d52fcad0f..00000000000 --- a/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed dropdown filter input not focusing after transition -merge_request: -author: diff --git a/changelogs/unreleased/issue_20900.yml b/changelogs/unreleased/issue_20900.yml deleted file mode 100644 index e8cef6d2bce..00000000000 --- a/changelogs/unreleased/issue_20900.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove issues/merge requests drag n drop and sorting from milestone view -merge_request: -author: diff --git a/changelogs/unreleased/issue_30126_be.yml b/changelogs/unreleased/issue_30126_be.yml deleted file mode 100644 index 96bb8d9574b..00000000000 --- a/changelogs/unreleased/issue_30126_be.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add native group milestones -merge_request: -author: diff --git a/changelogs/unreleased/issue_33205.yml b/changelogs/unreleased/issue_33205.yml deleted file mode 100644 index 54b442048d8..00000000000 --- a/changelogs/unreleased/issue_33205.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix API bug accepting wrong parameter to create merge request -merge_request: -author: diff --git a/changelogs/unreleased/issueable-list-cleanup.yml b/changelogs/unreleased/issueable-list-cleanup.yml deleted file mode 100644 index d3d67d04574..00000000000 --- a/changelogs/unreleased/issueable-list-cleanup.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Clean up UI of issuable lists and make more responsive -merge_request: -author: diff --git a/changelogs/unreleased/karma-headless-chrome.yml b/changelogs/unreleased/karma-headless-chrome.yml deleted file mode 100644 index af3e9b3b0f9..00000000000 --- a/changelogs/unreleased/karma-headless-chrome.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace PhantomJS with headless Chrome for karma test suite -merge_request: 12036 -author: diff --git a/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml b/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml deleted file mode 100644 index f84d41b7929..00000000000 --- a/changelogs/unreleased/monitoring-dashboard-fine-tuning-ux.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve the overall UX for the new monitoring dashboard -merge_request: -author: diff --git a/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml b/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml deleted file mode 100644 index 8a0e9ca855c..00000000000 --- a/changelogs/unreleased/monitoring-dashboard-fix-y-label.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed the y_label not setting correctly for each graph on the monitoring dashboard -merge_request: -author: diff --git a/changelogs/unreleased/moved-submodules.yml b/changelogs/unreleased/moved-submodules.yml deleted file mode 100644 index eee858717ed..00000000000 --- a/changelogs/unreleased/moved-submodules.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Handle renamed submodules in repository browser' -merge_request: 10798 -author: David Turner diff --git a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml deleted file mode 100644 index 14b5493a246..00000000000 --- a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Changed utilities imports from ~ to relative paths -merge_request: -author: diff --git a/changelogs/unreleased/pat-alert-when-signin-disabled.yml b/changelogs/unreleased/pat-alert-when-signin-disabled.yml deleted file mode 100644 index dca3670aeb7..00000000000 --- a/changelogs/unreleased/pat-alert-when-signin-disabled.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Provide hint to create a personal access token for Git over HTTP -merge_request: 12105 -author: Robin Bobbitt diff --git a/changelogs/unreleased/pat-msg-on-auth-failure.yml b/changelogs/unreleased/pat-msg-on-auth-failure.yml deleted file mode 100644 index c1b1528bb7a..00000000000 --- a/changelogs/unreleased/pat-msg-on-auth-failure.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Instruct user to use personal access token for Git over HTTP -merge_request: 11986 -author: Robin Bobbitt diff --git a/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml b/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml new file mode 100644 index 00000000000..1186dc59dc7 --- /dev/null +++ b/changelogs/unreleased/pawel-ensure_temp_files_are_deleted_in_fs_metrics-35457.yml @@ -0,0 +1,4 @@ +--- +title: Ensure filesystem metrics test files are deleted +merge_request: +author: diff --git a/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml b/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml new file mode 100644 index 00000000000..dfff4c23308 --- /dev/null +++ b/changelogs/unreleased/pawel-prometheus_client_pid_reuse_error.yml @@ -0,0 +1,4 @@ +--- +title: Fix Prometheus client PID reuse bug +merge_request: 13130 +author: diff --git a/changelogs/unreleased/polish-sidebar-toggle.yml b/changelogs/unreleased/polish-sidebar-toggle.yml deleted file mode 100644 index 41ec567fc52..00000000000 --- a/changelogs/unreleased/polish-sidebar-toggle.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove unused space in sidebar todo toggle when not signed in -merge_request: -author: diff --git a/changelogs/unreleased/post-upload-pack-opt-out.yml b/changelogs/unreleased/post-upload-pack-opt-out.yml new file mode 100644 index 00000000000..302a99795a0 --- /dev/null +++ b/changelogs/unreleased/post-upload-pack-opt-out.yml @@ -0,0 +1,4 @@ +--- +title: Enable gitaly_post_upload_pack by default +merge_request: 13078 +author: diff --git a/changelogs/unreleased/project-readme-limited-width.yml b/changelogs/unreleased/project-readme-limited-width.yml deleted file mode 100644 index 17d87a5691e..00000000000 --- a/changelogs/unreleased/project-readme-limited-width.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Limit the width of the projects README text -merge_request: -author: diff --git a/changelogs/unreleased/rc-fix-branches-api-endpoint.yml b/changelogs/unreleased/rc-fix-branches-api-endpoint.yml new file mode 100644 index 00000000000..a8f49298258 --- /dev/null +++ b/changelogs/unreleased/rc-fix-branches-api-endpoint.yml @@ -0,0 +1,5 @@ +--- +title: Fix the /projects/:id/repository/branches endpoint to handle dots in the branch + name when the project full patch contains a `/` +merge_request: 13115 +author: diff --git a/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml b/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml deleted file mode 100644 index 38227ebfa7a..00000000000 --- a/changelogs/unreleased/replace_spinach_spec_profile_notifications-feature.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'profile/notifications.feature' spinach test with an rspec analog -merge_request: 12345 -author: @blackst0ne diff --git a/changelogs/unreleased/replase_spinach_spec_create-feature.yml b/changelogs/unreleased/replase_spinach_spec_create-feature.yml deleted file mode 100644 index 0613d195d56..00000000000 --- a/changelogs/unreleased/replase_spinach_spec_create-feature.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Replace 'create.feature' spinach test with an rspec analog -merge_request: 12343 -author: @blackst0ne diff --git a/changelogs/unreleased/sh-add-mr-simple-mode.yml b/changelogs/unreleased/sh-add-mr-simple-mode.yml deleted file mode 100644 index 0033ca28444..00000000000 --- a/changelogs/unreleased/sh-add-mr-simple-mode.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add a simple mode to merge request API -merge_request: -author: diff --git a/changelogs/unreleased/sh-allow-force-repo-create.yml b/changelogs/unreleased/sh-allow-force-repo-create.yml deleted file mode 100644 index 2a65ba807bb..00000000000 --- a/changelogs/unreleased/sh-allow-force-repo-create.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make Project#ensure_repository force create a repo -merge_request: -author: diff --git a/changelogs/unreleased/sh-bump-oauth2-gem.yml b/changelogs/unreleased/sh-bump-oauth2-gem.yml deleted file mode 100644 index b894a64968b..00000000000 --- a/changelogs/unreleased/sh-bump-oauth2-gem.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Bump Faraday and dependent OAuth2 gem version to support no_proxy variable -merge_request: -author: diff --git a/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml b/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml deleted file mode 100644 index 9309f961345..00000000000 --- a/changelogs/unreleased/sh-fix-project-destroy-in-namespace.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Defer project destroys within a namespace in Groups::DestroyService#async_execute -merge_request: -author: diff --git a/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml b/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml deleted file mode 100644 index ec9ceab3d81..00000000000 --- a/changelogs/unreleased/sh-log-application-controller-exceptions-sentry.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Log rescued exceptions to Sentry -merge_request: -author: diff --git a/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml b/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml deleted file mode 100644 index 9589659cdc2..00000000000 --- a/changelogs/unreleased/sh-optimize-mr-api-emojis-and-labels.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove remaining N+1 queries in merge requests API with emojis and labels -merge_request: -author: diff --git a/changelogs/unreleased/sh-optimize-project-commit-api.yml b/changelogs/unreleased/sh-optimize-project-commit-api.yml deleted file mode 100644 index e6a8a80593c..00000000000 --- a/changelogs/unreleased/sh-optimize-project-commit-api.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Optimize creation of commit API by using Repository#commit instead of Repository#commits -merge_request: -author: diff --git a/changelogs/unreleased/skip-oauth-authorization-for-trusted-applications.yml b/changelogs/unreleased/skip-oauth-authorization-for-trusted-applications.yml new file mode 100644 index 00000000000..7b4ae355978 --- /dev/null +++ b/changelogs/unreleased/skip-oauth-authorization-for-trusted-applications.yml @@ -0,0 +1,4 @@ +--- +title: Skip oAuth authorization for trusted applications +merge_request: +author: diff --git a/changelogs/unreleased/speed-up-graphs.yml b/changelogs/unreleased/speed-up-graphs.yml deleted file mode 100644 index 7cb155af6fd..00000000000 --- a/changelogs/unreleased/speed-up-graphs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Speed up used languages calculation on charts page -merge_request: -author: diff --git a/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml b/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml deleted file mode 100644 index 6bf03d9a382..00000000000 --- a/changelogs/unreleased/speed-up-issue-counting-for-a-project.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Cache open issue and merge request counts for project tabs to speed up project - pages -merge_request: 12457 -author: diff --git a/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml b/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml deleted file mode 100644 index 00f55edc2b7..00000000000 --- a/changelogs/unreleased/speed-up-merge-request-all-commits-shas.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make loading new merge requests (those created after the 9.4 upgrade) faster -merge_request: -author: diff --git a/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml b/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml deleted file mode 100644 index 7e66ea4ca8b..00000000000 --- a/changelogs/unreleased/stop-notification-recipient-service-modifying-participants.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Ensure participants for issues, merge requests, etc. are calculated correctly - when sending notifications -merge_request: -author: diff --git a/changelogs/unreleased/tc-api-root-merge-requests.yml b/changelogs/unreleased/tc-api-root-merge-requests.yml new file mode 100644 index 00000000000..17456f943eb --- /dev/null +++ b/changelogs/unreleased/tc-api-root-merge-requests.yml @@ -0,0 +1,4 @@ +--- +title: Add top-level merge_requests API endpoint +merge_request: 13060 +author: diff --git a/changelogs/unreleased/tc-follow-up-mia.yml b/changelogs/unreleased/tc-follow-up-mia.yml deleted file mode 100644 index 6327f02032e..00000000000 --- a/changelogs/unreleased/tc-follow-up-mia.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Undo adding the /reassign quick action -merge_request: 12701 -author: diff --git a/changelogs/unreleased/tc-issue-api-assignee.yml b/changelogs/unreleased/tc-issue-api-assignee.yml new file mode 100644 index 00000000000..8d6360d5baf --- /dev/null +++ b/changelogs/unreleased/tc-issue-api-assignee.yml @@ -0,0 +1,4 @@ +--- +title: Add author_id & assignee_id param to /issues API +merge_request: 13004 +author: diff --git a/changelogs/unreleased/tc-link-to-commit-on-help-page.yml b/changelogs/unreleased/tc-link-to-commit-on-help-page.yml deleted file mode 100644 index 3d11ba43d1f..00000000000 --- a/changelogs/unreleased/tc-link-to-commit-on-help-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Make the revision on the `/help` page clickable -merge_request: 12016 -author: diff --git a/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml b/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml deleted file mode 100644 index 7bcbd6468c7..00000000000 --- a/changelogs/unreleased/tc-refactor-projects-finder-init-collection.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add User#full_private_access? to check if user has access to all private groups & projects -merge_request: 12373 -author: diff --git a/changelogs/unreleased/workhorse-2-3-0.yml b/changelogs/unreleased/workhorse-2-3-0.yml deleted file mode 100644 index 17992c8b0ff..00000000000 --- a/changelogs/unreleased/workhorse-2-3-0.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Upgrade GitLab Workhorse to v2.3.0 -merge_request: 12676 -author: diff --git a/changelogs/unreleased/zj-commit-status-sortable-name.yml b/changelogs/unreleased/zj-commit-status-sortable-name.yml deleted file mode 100644 index 1be9ac6380f..00000000000 --- a/changelogs/unreleased/zj-commit-status-sortable-name.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Handle nameless legacy jobs -merge_request: -author: diff --git a/changelogs/unreleased/zj-delete-mm-team.yml b/changelogs/unreleased/zj-delete-mm-team.yml new file mode 100644 index 00000000000..f0c782c4566 --- /dev/null +++ b/changelogs/unreleased/zj-delete-mm-team.yml @@ -0,0 +1,4 @@ +--- +title: Remove Mattermost team when deleting a group +merge_request: 11362 +author: diff --git a/changelogs/unreleased/zj-faster-charts-page.yml b/changelogs/unreleased/zj-faster-charts-page.yml deleted file mode 100644 index 9afcf111328..00000000000 --- a/changelogs/unreleased/zj-faster-charts-page.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Improve performance of the pipeline charts page -merge_request: 12378 -author: diff --git a/changelogs/unreleased/zj-pipeline-badge-improvements.yml b/changelogs/unreleased/zj-pipeline-badge-improvements.yml new file mode 100644 index 00000000000..735192ede2d --- /dev/null +++ b/changelogs/unreleased/zj-pipeline-badge-improvements.yml @@ -0,0 +1,4 @@ +--- +title: Update build badges to be pipeline badges and display passing instead of success +merge_request: +author: diff --git a/changelogs/unreleased/zj-review-apps-usage-data.yml b/changelogs/unreleased/zj-review-apps-usage-data.yml deleted file mode 100644 index 7d224d0fc32..00000000000 --- a/changelogs/unreleased/zj-review-apps-usage-data.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Add review apps to usage metrics -merge_request: 12185 -author: diff --git a/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml b/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml deleted file mode 100644 index 0ace7b99657..00000000000 --- a/changelogs/unreleased/zj-usage-ping-only-gl-pipelines.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Split pipelines as internal and external in the usage data -merge_request: 12277 -author: diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index cb007813b65..e9bf2df490f 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -228,7 +228,8 @@ production: &base # ========================== ## LDAP settings - # You can inspect a sample of the LDAP users with login access by running: + # You can test connections and inspect a sample of the LDAP users with login + # access by running: # bundle exec rake gitlab:ldap:check RAILS_ENV=production ldap: enabled: false @@ -251,13 +252,45 @@ production: &base # Example: 'Paris' or 'Acme, Ltd.' label: 'LDAP' + # Example: 'ldap.mydomain.com' host: '_your_ldap_server' - port: 389 - uid: 'sAMAccountName' - method: 'plain' # "tls" or "ssl" or "plain" + # This port is an example, it is sometimes different but it is always an integer and not a string + port: 389 # usually 636 for SSL + uid: 'sAMAccountName' # This should be the attribute, not the value that maps to uid. + + # Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' + # Encryption method. The "method" key is deprecated in favor of + # "encryption". + # + # Examples: "start_tls" or "simple_tls" or "plain" + # + # Deprecated values: "tls" was replaced with "start_tls" and "ssl" was + # replaced with "simple_tls". + # + encryption: 'plain' + + # Enables SSL certificate verification if encryption method is + # "start_tls" or "simple_tls". (Defaults to false for backward- + # compatibility) + verify_certificates: false + + # Specifies the path to a file containing a PEM-format CA certificate, + # e.g. if you need to use an internal CA. + # + # Example: '/etc/ca.pem' + # + ca_cert: '' + + # Specifies the SSL version for OpenSSL to use, if the OpenSSL default + # is not appropriate. + # + # Example: 'TLSv1_1' + # + ssl_version: '' + # Set a timeout, in seconds, for LDAP queries. This helps avoid blocking # a request if the LDAP server becomes unresponsive. # A value of 0 means there is no timeout. @@ -286,17 +319,20 @@ production: &base # Base where we can search for users # - # Ex. ou=People,dc=gitlab,dc=example + # Ex. 'ou=People,dc=gitlab,dc=example' or 'DC=mydomain,DC=com' # base: '' # Filter LDAP users # - # Format: RFC 4515 http://tools.ietf.org/search/rfc4515 + # Format: RFC 4515 https://tools.ietf.org/search/rfc4515 # Ex. (employeeType=developer) # # Note: GitLab does not support omniauth-ldap's custom filter syntax. # + # Example for getting only specific users: + # '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))' + # user_filter: '' # LDAP attributes that GitLab will use to create an account for the LDAP user. @@ -674,7 +710,7 @@ test: host: 127.0.0.1 port: 3890 uid: 'uid' - method: 'plain' # "tls" or "ssl" or "plain" + encryption: 'plain' # "start_tls" or "simple_tls" or "plain" base: 'dc=example,dc=com' user_filter: '' group_base: 'ou=groups,dc=example,dc=com' diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index ec7ce51b542..02d3161f769 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -145,6 +145,24 @@ if Settings.ldap['enabled'] || Rails.env.test? server['attributes'] = {} if server['attributes'].nil? server['provider_name'] ||= "ldap#{key}".downcase server['provider_class'] = OmniAuth::Utils.camelize(server['provider_name']) + + # For backwards compatibility + server['encryption'] ||= server['method'] + server['encryption'] = 'simple_tls' if server['encryption'] == 'ssl' + server['encryption'] = 'start_tls' if server['encryption'] == 'tls' + + # Certificates are not verified for backwards compatibility. + # This default should be flipped to true in 9.5. + if server['verify_certificates'].nil? + server['verify_certificates'] = false + + message = <<-MSG.strip_heredoc + LDAP SSL certificate verification is disabled for backwards-compatibility. + Please add the "verify_certificates" option to gitlab.yml for each LDAP + server. Certificate verification will be enabled by default in GitLab 9.5. + MSG + Rails.logger.warn(message) + end end end @@ -441,10 +459,6 @@ Settings.backup['pg_schema'] = nil Settings.backup['path'] = Settings.absolute(Settings.backup['path'] || "tmp/backups/") Settings.backup['archive_permissions'] ||= 0600 Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) -# Convert upload connection settings to use symbol keys, to make Fog happy -if Settings.backup['upload']['connection'] - Settings.backup['upload']['connection'] = Hash[Settings.backup['upload']['connection'].map { |k, v| [k.to_sym, v] }] -end Settings.backup['upload']['multipart_chunk_size'] ||= 104857600 Settings.backup['upload']['encryption'] ||= nil Settings.backup['upload']['storage_class'] ||= nil diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index 987324a86c9..a2f8421f5d7 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -6,7 +6,7 @@ Prometheus::Client.configure do |config| config.initial_mmap_file_size = 4 * 1024 config.multiprocess_files_dir = ENV['prometheus_multiproc_dir'] - if Rails.env.development? && Rails.env.test? + if Rails.env.development? || Rails.env.test? config.multiprocess_files_dir ||= Rails.root.join('tmp/prometheus_multiproc_dir') end end diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index 25630b298ce..2aeb94d47cd 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -114,6 +114,9 @@ def instrument_classes(instrumentation) # This is a Rails scope so we have to instrument it manually. instrumentation.instrument_method(Project, :visible_to_user) + # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/34509 + instrumentation.instrument_method(MarkupHelper, :link_to_gfm) + # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159 instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits) end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 3b1317030bc..3aed2136f1b 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -192,7 +192,7 @@ Devise.setup do |config| # # The :"*/*" and "*/*" formats below is required to match Internet # Explorer requests. - # config.navigational_formats = [:"*/*", "*/*", :html] + config.navigational_formats = [:"*/*", "*/*", :html, :zip] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete @@ -206,11 +206,11 @@ Devise.setup do |config| # If you want to use other strategies, that are not supported by Devise, or # change the failure app, you can configure them inside the config.warden block. # - config.warden do |manager| - manager.failure_app = Gitlab::DeviseFailure - # manager.intercept_401 = false - # manager.default_strategies(scope: :user).unshift :some_external_strategy - end + # config.warden do |manager| + # manager.failure_app = Gitlab::DeviseFailure + # manager.intercept_401 = false + # manager.default_strategies(scope: :user).unshift :some_external_strategy + # end if Gitlab::LDAP::Config.enabled? Gitlab::LDAP::Config.providers.each do |provider| diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 8e2e639fc41..40e635bf2cf 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -92,9 +92,9 @@ Doorkeeper.configure do # Under some circumstances you might want to have applications auto-approved, # so that the user skips the authorization step. # For example if dealing with trusted a application. - # skip_authorization do |resource_owner, client| - # client.superapp? or resource_owner.admin? - # end + skip_authorization do |resource_owner, client| + client.application.trusted? + end # WWW-Authenticate Realm (default "Doorkeeper"). # realm "Doorkeeper" diff --git a/config/initializers/grape_route_helpers_fix.rb b/config/initializers/grape_route_helpers_fix.rb new file mode 100644 index 00000000000..d3cf9e453d0 --- /dev/null +++ b/config/initializers/grape_route_helpers_fix.rb @@ -0,0 +1,35 @@ +if defined?(GrapeRouteHelpers) + module GrapeRouteHelpers + class DecoratedRoute + # GrapeRouteHelpers gem tries to parse the versions + # from a string, not supporting Grape `version` array definition. + # + # Without the following fix, we get this on route helpers generation: + # + # => undefined method `scan' for ["v3", "v4"] + # + # 2.0.0 implementation of this method: + # + # ``` + # def route_versions + # version_pattern = /[^\[",\]\s]+/ + # if route_version + # route_version.scan(version_pattern) + # else + # [nil] + # end + # end + # ``` + def route_versions + return [nil] if route_version.nil? || route_version.empty? + + if route_version.is_a?(String) + version_pattern = /[^\[",\]\s]+/ + route_version.scan(version_pattern) + else + route_version + end + end + end + end +end diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb index 14902316240..21fe8d72459 100644 --- a/config/initializers/lograge.rb +++ b/config/initializers/lograge.rb @@ -13,8 +13,11 @@ unless Sidekiq.server? # Add request parameters to log output config.lograge.custom_options = lambda do |event| { - time: event.time, - params: event.payload[:params].except(%w(controller action format)) + time: event.time.utc.iso8601(3), + params: event.payload[:params].except(*%w(controller action format)), + remote_ip: event.payload[:remote_ip], + user_id: event.payload[:user_id], + username: event.payload[:username] } end end diff --git a/config/initializers/mysql_set_length_for_binary_indexes.rb b/config/initializers/mysql_set_length_for_binary_indexes.rb new file mode 100644 index 00000000000..de0bc5322aa --- /dev/null +++ b/config/initializers/mysql_set_length_for_binary_indexes.rb @@ -0,0 +1,21 @@ +# This patches ActiveRecord so indexes for binary columns created using the +# MySQL adapter apply a length of 20. Otherwise MySQL can't create an index on +# binary columns. + +module MysqlSetLengthForBinaryIndex + def add_index(table_name, column_names, options = {}) + Array(column_names).each do |column_name| + column = ActiveRecord::Base.connection.columns(table_name).find { |c| c.name == column_name } + + if column&.type == :binary + options[:length] = 20 + end + end + + super(table_name, column_names, options) + end +end + +if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) + ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:prepend, MysqlSetLengthForBinaryIndex) +end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index f7fa6d1c2de..56c279ffcf4 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -1,13 +1,16 @@ if Gitlab::LDAP::Config.enabled? module OmniAuth::Strategies - server = Gitlab.config.ldap.servers.values.first - klass = server['provider_class'] - const_set(klass, Class.new(LDAP)) unless klass == 'LDAP' + Gitlab::LDAP::Config.available_servers.each do |server| + # do not redeclare LDAP + next if server['provider_name'] == 'ldap' + const_set(server['provider_class'], Class.new(LDAP)) + end end OmniauthCallbacksController.class_eval do - server = Gitlab.config.ldap.servers.values.first - alias_method server['provider_name'], :ldap + Gitlab::LDAP::Config.available_servers.each do |server| + alias_method server['provider_name'], :ldap + end end end @@ -16,7 +19,7 @@ OmniAuth.config.allowed_request_methods = [:post] # In case of auto sign-in, the GET method is used (users don't get to click on a button) OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? OmniAuth.config.before_request_phase do |env| - OmniAuth::RequestForgeryProtection.call(env) + Gitlab::RequestForgeryProtection.call(env) end if Gitlab.config.omniauth.enabled diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml index 60355e9140c..5eb01d62924 100644 --- a/config/prometheus/additional_metrics.yml +++ b/config/prometheus/additional_metrics.yml @@ -1,4 +1,25 @@ -- group: AWS Elastic Load Balancer +- group: Response metrics (HA Proxy) + priority: 10 + metrics: + - title: "Throughput" + y_label: "Requests / Sec" + required_metrics: + - haproxy_frontend_http_requests_total + weight: 1 + queries: + - query_range: 'sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m]))' + label: Total + unit: req / sec + - title: "HTTP Error Rate" + y_label: "Error Rate (%)" + required_metrics: + - haproxy_frontend_http_responses_total + weight: 1 + queries: + - query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))' + label: HTTP Errors + unit: "%" +- group: Response metrics (AWS ELB) priority: 10 metrics: - title: "Throughput" @@ -29,7 +50,7 @@ - query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})' label: HTTP Errors unit: "%" -- group: NGINX +- group: Response metrics (NGINX) priority: 10 metrics: - title: "Throughput" @@ -56,10 +77,10 @@ - nginx_responses_total weight: 1 queries: - - query_range: 'sum(nginx_responses_total{status_code="5xx", %{environment_filter}}) / sum(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}})' + - query_range: 'sum(rate(nginx_responses_total{status_code="5xx", %{environment_filter}}[2m])) / sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m]))' label: HTTP Errors unit: "%" -- group: Kubernetes +- group: System metrics (Kubernetes) priority: 5 metrics: - title: "Memory Usage" diff --git a/config/routes/api.rb b/config/routes/api.rb index 69c8efc151c..ce7a7c88900 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -1,2 +1,2 @@ API::API.logger Rails.logger -mount API::API => '/api' +mount API::API => '/' diff --git a/config/routes/profile.rb b/config/routes/profile.rb index 3dc890e5785..3e4e6111ab8 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -23,6 +23,11 @@ resource :profile, only: [:show, :update] do end resource :preferences, only: [:show, :update] resources :keys, only: [:index, :show, :create, :destroy] + resources :gpg_keys, only: [:index, :create, :destroy] do + member do + put :revoke + end + end resources :emails, only: [:index, :create, :destroy] resources :chat_names, only: [:index, :new, :create, :destroy] do collection do diff --git a/config/routes/project.rb b/config/routes/project.rb index 672b5a9a160..06928c7b9ce 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -379,7 +379,9 @@ constraints(ProjectUrlConstrainer.new) do collection do scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do constraints format: /svg/ do - get :build + # Keep around until 10.0, see gitlab-org/gitlab-ce#35307 + get :build, to: "badges#pipeline" + get :pipeline get :coverage end end diff --git a/config/routes/repository.rb b/config/routes/repository.rb index 11911636fa7..edcf3ddf57b 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -76,6 +76,8 @@ scope format: false do get '/tree/*id', to: 'tree#show', as: :tree get '/raw/*id', to: 'raw#show', as: :raw get '/blame/*id', to: 'blame#show', as: :blame + + get '/commits/*id/signatures', to: 'commits#signatures', as: :signatures get '/commits/*id', to: 'commits#show', as: :commits post '/create_dir/*id', to: 'tree#create_dir', as: :create_dir diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 1d9e69a2408..7496bfa4fbb 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -29,6 +29,8 @@ - [email_receiver, 2] - [emails_on_push, 2] - [mailers, 2] + - [invalid_gpg_signature_update, 2] + - [create_gpg_signature, 2] - [upload_checksum, 1] - [use_key, 1] - [repository_fork, 1] diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index 40a16a32359..cc10da2bd88 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -121,6 +121,10 @@ after_fork do |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection + # reset prometheus client, this will cause any opened metrics files to be closed + defined?(::Prometheus::Client.reinitialize_on_pid_change) && + Prometheus::Client.reinitialize_on_pid_change + # if preload_app is true, then you may also want to check and # restart any other shared sockets/descriptors such as Memcached, # and Redis. TokyoCabinet file handles are safe to reuse diff --git a/config/webpack.config.js b/config/webpack.config.js index 6682421742f..9e6fd8aebe6 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -43,6 +43,7 @@ var config = { group: './group.js', groups: './groups/index.js', groups_list: './groups_list.js', + how_to_merge: './how_to_merge.js', issue_show: './issue_show/index.js', integrations: './integrations', job_details: './jobs/job_details_bundle.js', @@ -57,7 +58,7 @@ var config = { pipelines_details: './pipelines/pipeline_details_bundle.js', profile: './profile/profile_bundle.js', prometheus_metrics: './prometheus_metrics', - protected_branches: './protected_branches/protected_branches_bundle.js', + protected_branches: './protected_branches', protected_tags: './protected_tags', repo: './repo/index.js', sidebar: './sidebar/sidebar_bundle.js', @@ -68,7 +69,6 @@ var config = { stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', u2f: ['vendor/u2f'], - users: './users/index.js', raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', @@ -196,7 +196,6 @@ var config = { name: 'common_d3', chunks: [ 'graphs', - 'users', 'monitoring', ], }), diff --git a/db/migrate/20170222111732_create_gpg_keys.rb b/db/migrate/20170222111732_create_gpg_keys.rb new file mode 100644 index 00000000000..541228e8735 --- /dev/null +++ b/db/migrate/20170222111732_create_gpg_keys.rb @@ -0,0 +1,19 @@ +class CreateGpgKeys < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :gpg_keys do |t| + t.timestamps_with_timezone null: false + + t.references :user, index: true, foreign_key: { on_delete: :cascade } + + t.binary :primary_keyid + t.binary :fingerprint + + t.text :key + + t.index :primary_keyid, unique: true, length: Gitlab::Database.mysql? ? 20 : nil + t.index :fingerprint, unique: true, length: Gitlab::Database.mysql? ? 20 : nil + end + end +end diff --git a/db/migrate/20170428064307_add_column_delete_error_to_projects.rb b/db/migrate/20170428064307_add_column_delete_error_to_projects.rb new file mode 100644 index 00000000000..09f9d9b5b7a --- /dev/null +++ b/db/migrate/20170428064307_add_column_delete_error_to_projects.rb @@ -0,0 +1,7 @@ +class AddColumnDeleteErrorToProjects < ActiveRecord::Migration + DOWNTIME = false + + def change + add_column :projects, :delete_error, :text + end +end diff --git a/db/migrate/20170613154149_create_gpg_signatures.rb b/db/migrate/20170613154149_create_gpg_signatures.rb new file mode 100644 index 00000000000..f6b5e7ebb7b --- /dev/null +++ b/db/migrate/20170613154149_create_gpg_signatures.rb @@ -0,0 +1,23 @@ +class CreateGpgSignatures < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :gpg_signatures do |t| + t.timestamps_with_timezone null: false + + t.references :project, index: true, foreign_key: { on_delete: :cascade } + t.references :gpg_key, index: true, foreign_key: { on_delete: :nullify } + + t.boolean :valid_signature + + t.binary :commit_sha + t.binary :gpg_key_primary_keyid + + t.text :gpg_key_user_name + t.text :gpg_key_user_email + + t.index :commit_sha, unique: true, length: Gitlab::Database.mysql? ? 20 : nil + t.index :gpg_key_primary_keyid, length: Gitlab::Database.mysql? ? 20 : nil + end + end +end diff --git a/db/migrate/20170717200542_add_trusted_column_to_oauth_applications.rb b/db/migrate/20170717200542_add_trusted_column_to_oauth_applications.rb new file mode 100644 index 00000000000..1a013e6aefb --- /dev/null +++ b/db/migrate/20170717200542_add_trusted_column_to_oauth_applications.rb @@ -0,0 +1,15 @@ +class AddTrustedColumnToOauthApplications < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_column_with_default(:oauth_applications, :trusted, :boolean, default: false) + end + + def down + remove_column(:oauth_applications, :trusted) + end +end diff --git a/db/migrate/20170720130522_create_ci_pipeline_variables.rb b/db/migrate/20170720130522_create_ci_pipeline_variables.rb new file mode 100644 index 00000000000..a784f5dd142 --- /dev/null +++ b/db/migrate/20170720130522_create_ci_pipeline_variables.rb @@ -0,0 +1,20 @@ +class CreateCiPipelineVariables < ActiveRecord::Migration + DOWNTIME = false + + def up + create_table :ci_pipeline_variables do |t| + t.string :key, null: false + t.text :value + t.text :encrypted_value + t.string :encrypted_value_salt + t.string :encrypted_value_iv + t.integer :pipeline_id, null: false + end + + add_index :ci_pipeline_variables, [:pipeline_id, :key], unique: true + end + + def down + drop_table :ci_pipeline_variables + end +end diff --git a/db/migrate/20170720130749_add_foreign_key_to_ci_pipeline_variables.rb b/db/migrate/20170720130749_add_foreign_key_to_ci_pipeline_variables.rb new file mode 100644 index 00000000000..550b8a88f02 --- /dev/null +++ b/db/migrate/20170720130749_add_foreign_key_to_ci_pipeline_variables.rb @@ -0,0 +1,15 @@ +class AddForeignKeyToCiPipelineVariables < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key(:ci_pipeline_variables, :ci_pipelines, column: :pipeline_id) + end + + def down + remove_foreign_key(:ci_pipeline_variables, column: :pipeline_id) + end +end diff --git a/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb new file mode 100644 index 00000000000..db60c2087b9 --- /dev/null +++ b/db/migrate/20170724214302_add_lower_path_index_to_redirect_routes.rb @@ -0,0 +1,34 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddLowerPathIndexToRedirectRoutes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX_NAME = 'index_on_redirect_routes_lower_path' + + disable_ddl_transaction! + + def up + return unless Gitlab::Database.postgresql? + + execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON redirect_routes (LOWER(path));" + end + + def down + return unless Gitlab::Database.postgresql? + + # Why not use remove_concurrent_index_by_name? + # + # `index_exists?` doesn't work on this index. Perhaps this is related to the + # fact that the index doesn't show up in the schema. And apparently it isn't + # trivial to write a query that checks for an index. BUT there is a + # convenient `IF EXISTS` parameter for `DROP INDEX`. + if supports_drop_index_concurrently? + disable_statement_timeout + execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME};" + else + execute "DROP INDEX IF EXISTS #{INDEX_NAME};" + end + end +end diff --git a/db/migrate/20170725145659_add_binary_to_merge_request_diff_files.rb b/db/migrate/20170725145659_add_binary_to_merge_request_diff_files.rb new file mode 100644 index 00000000000..1f5fa7e3d49 --- /dev/null +++ b/db/migrate/20170725145659_add_binary_to_merge_request_diff_files.rb @@ -0,0 +1,9 @@ +class AddBinaryToMergeRequestDiffFiles < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :merge_request_diff_files, :binary, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 284b2068166..0abdb987b77 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170717150329) do +ActiveRecord::Schema.define(version: 20170725145659) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -298,6 +298,17 @@ ActiveRecord::Schema.define(version: 20170717150329) do add_index "ci_pipeline_schedules", ["next_run_at", "active"], name: "index_ci_pipeline_schedules_on_next_run_at_and_active", using: :btree add_index "ci_pipeline_schedules", ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree + create_table "ci_pipeline_variables", force: :cascade do |t| + t.string "key", null: false + t.text "value" + t.text "encrypted_value" + t.string "encrypted_value_salt" + t.string "encrypted_value_iv" + t.integer "pipeline_id", null: false + end + + add_index "ci_pipeline_variables", ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree + create_table "ci_pipelines", force: :cascade do |t| t.string "ref" t.string "sha" @@ -540,6 +551,36 @@ ActiveRecord::Schema.define(version: 20170717150329) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree + create_table "gpg_keys", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "user_id" + t.binary "primary_keyid" + t.binary "fingerprint" + t.text "key" + end + + add_index "gpg_keys", ["fingerprint"], name: "index_gpg_keys_on_fingerprint", unique: true, using: :btree + add_index "gpg_keys", ["primary_keyid"], name: "index_gpg_keys_on_primary_keyid", unique: true, using: :btree + add_index "gpg_keys", ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree + + create_table "gpg_signatures", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "project_id" + t.integer "gpg_key_id" + t.boolean "valid_signature" + t.binary "commit_sha" + t.binary "gpg_key_primary_keyid" + t.text "gpg_key_user_name" + t.text "gpg_key_user_email" + end + + add_index "gpg_signatures", ["commit_sha"], name: "index_gpg_signatures_on_commit_sha", unique: true, using: :btree + add_index "gpg_signatures", ["gpg_key_id"], name: "index_gpg_signatures_on_gpg_key_id", using: :btree + add_index "gpg_signatures", ["gpg_key_primary_keyid"], name: "index_gpg_signatures_on_gpg_key_primary_keyid", using: :btree + add_index "gpg_signatures", ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree + create_table "identities", force: :cascade do |t| t.string "extern_uid" t.string "provider" @@ -748,6 +789,7 @@ ActiveRecord::Schema.define(version: 20170717150329) do t.text "new_path", null: false t.text "old_path", null: false t.text "diff", null: false + t.boolean "binary" end add_index "merge_request_diff_files", ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree @@ -996,6 +1038,7 @@ ActiveRecord::Schema.define(version: 20170717150329) do t.datetime "updated_at" t.integer "owner_id" t.string "owner_type" + t.boolean "trusted", default: false, null: false end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree @@ -1134,6 +1177,7 @@ ActiveRecord::Schema.define(version: 20170717150329) do t.integer "cached_markdown_version" t.datetime "last_repository_updated_at" t.string "ci_config_path" + t.text "delete_error" end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree @@ -1584,6 +1628,7 @@ ActiveRecord::Schema.define(version: 20170717150329) do add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify + add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade add_foreign_key "ci_pipelines", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_3d34ab2e06", on_delete: :nullify add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade @@ -1600,6 +1645,9 @@ ActiveRecord::Schema.define(version: 20170717150329) do add_foreign_key "environments", "projects", name: "fk_d1c8c1da6a", on_delete: :cascade add_foreign_key "events", "projects", name: "fk_0434b48643", on_delete: :cascade add_foreign_key "forked_project_links", "projects", column: "forked_to_project_id", name: "fk_434510edb0", on_delete: :cascade + add_foreign_key "gpg_keys", "users", on_delete: :cascade + add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify + add_foreign_key "gpg_signatures", "projects", on_delete: :cascade add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade add_foreign_key "issue_metrics", "issues", on_delete: :cascade diff --git a/doc/README.md b/doc/README.md index 1a7638b3d7e..cc63ecb7eab 100644 --- a/doc/README.md +++ b/doc/README.md @@ -11,15 +11,11 @@ self-hosted, free to use. Every feature available in GitLab CE is also available self-hosted, fully featured solution of GitLab, available under distinct [subscriptions](https://about.gitlab.com/products/): **GitLab Enterprise Edition Starter (EES)** and **GitLab Enterprise Edition Premium (EEP)**. - **GitLab.com**: SaaS GitLab solution, with [free and paid subscriptions](https://about.gitlab.com/gitlab-com/). GitLab.com is hosted by GitLab, Inc., and administrated by GitLab (users don't have access to admin settings). -**GitLab EE** contains all features available in **GitLab CE**, +> **GitLab EE** contains all features available in **GitLab CE**, plus premium features available in each version: **Enterprise Edition Starter** (**EES**) and **Enterprise Edition Premium** (**EEP**). Everything available in **EES** is also available in **EEP**. -**Note:** _We are unifying the documentation for CE and EE. To check if certain feature is -available in CE or EE, look for a note right below the page title containing the GitLab -version which introduced that feature._ - ---- Shortcuts to GitLab's most visited docs: @@ -40,8 +36,10 @@ Shortcuts to GitLab's most visited docs: ### User account -- [Authentication](topics/authentication/index.md): Account security with two-factor authentication, setup your ssh keys and deploy keys for secure access to your projects. -- [Profile settings](profile/README.md): Manage your profile settings, two factor authentication and more. +- [User documentation](user/index.md): Learn how to use GitLab and explore its features +- [User account](user/profile/index.md): Manage your account + - [Authentication](topics/authentication/index.md): Account security with two-factor authentication, setup your ssh keys and deploy keys for secure access to your projects. + - [Profile settings](user/profile/index.md#profile-settings): Manage your profile settings, two factor authentication and more. - [User permissions](user/permissions.md): Learn what each role in a project (external/guest/reporter/developer/master/owner) can do. ### Projects and groups @@ -59,7 +57,7 @@ Shortcuts to GitLab's most visited docs: ### Repository -Manage files and branches from the UI (user interface): +Manage your [repositories](user/project/repository/index.md) from the UI (user interface): - Files - [Create a file](user/project/repository/web_editor.md#create-a-file) @@ -92,6 +90,7 @@ Manage files and branches from the UI (user interface): - [Git](topics/git/index.md): Getting started with Git, branching strategies, Git LFS, advanced use. - [Git cheatsheet](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf): Download a PDF describing the most used Git operations. - [GitLab Flow](workflow/gitlab_flow.md): explore the best of Git with the GitLab Flow strategy. +- [Signing commits](workflow/gpg_signed_commits/index.md): use GPG to sign your commits. ### Migrate and import your projects from other platforms @@ -115,6 +114,7 @@ Manage files and branches from the UI (user interface): - [Project Services](user/project/integrations/project_services.md): Integrate a project with external services, such as CI and chat. - [GitLab Integration](integration/README.md): Integrate with multiple third-party services with GitLab to allow external issue trackers and external authentication. +- [Trello Power-Up](integration/trello_power_up.md): Integrate with GitLab's Trello Power-Up ---- diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index c8987dea5e2..a7395e03d1c 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -69,14 +69,42 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # Example: 'ldap.mydomain.com' host: '_your_ldap_server' # This port is an example, it is sometimes different but it is always an integer and not a string - port: 389 + port: 389 # usually 636 for SSL uid: 'sAMAccountName' # This should be the attribute, not the value that maps to uid. - method: 'plain' # "tls" or "ssl" or "plain" # Examples: 'america\\momo' or 'CN=Gitlab Git,CN=Users,DC=mydomain,DC=com' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' password: '_the_password_of_the_bind_user' + # Encryption method. The "method" key is deprecated in favor of + # "encryption". + # + # Examples: "start_tls" or "simple_tls" or "plain" + # + # Deprecated values: "tls" was replaced with "start_tls" and "ssl" was + # replaced with "simple_tls". + # + encryption: 'plain' + + # Enables SSL certificate verification if encryption method is + # "start_tls" or "simple_tls". (Defaults to false for backward- + # compatibility) + verify_certificates: false + + # Specifies the path to a file containing a PEM-format CA certificate, + # e.g. if you need to use an internal CA. + # + # Example: '/etc/ca.pem' + # + ca_cert: '' + + # Specifies the SSL version for OpenSSL to use, if the OpenSSL default + # is not appropriate. + # + # Example: 'TLSv1_1' + # + ssl_version: '' + # Set a timeout, in seconds, for LDAP queries. This helps avoid blocking # a request if the LDAP server becomes unresponsive. # A value of 0 means there is no timeout. @@ -116,8 +144,8 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # # Note: GitLab does not support omniauth-ldap's custom filter syntax. # - # Below an example for get only specific users - # Example: '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))' + # Example for getting only specific users: + # '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))' # user_filter: '' @@ -228,7 +256,7 @@ Tip: If you want to limit access to the nested members of an Active Directory group you can use the following syntax: ``` -(memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com) +(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com) ``` Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at @@ -250,6 +278,19 @@ In other words, if an existing GitLab user wants to enable LDAP sign-in for themselves, they should check that their GitLab email address matches their LDAP email address, and then sign into GitLab via their LDAP credentials. +## Encryption + +### TLS Server Authentication + +There are two encryption methods, `simple_tls` and `start_tls`. + +For either encryption method, if setting `validate_certificates: false`, TLS +encryption is established with the LDAP server before any LDAP-protocol data is +exchanged but no validation of the LDAP server's SSL certificate is performed. + +>**Note**: Before GitLab 9.5, `validate_certificates: false` is the default if +unspecified. + ## Limitations ### TLS Client Authentication @@ -259,14 +300,6 @@ You should disable anonymous LDAP authentication and enable simple or SASL authentication. The TLS client authentication setting in your LDAP server cannot be mandatory and clients cannot be authenticated with the TLS protocol. -### TLS Server Authentication - -Not supported by GitLab's configuration options. -When setting `method: ssl`, the underlying authentication method used by -`omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with -the LDAP server before any LDAP-protocol data is exchanged but no validation of -the LDAP server's SSL certificate is performed. - ## Troubleshooting ### Debug LDAP user filter with ldapsearch @@ -306,9 +339,9 @@ tree and traverse it. ### Connection Refused If you are getting 'Connection Refused' errors when trying to connect to the -LDAP server please double-check the LDAP `port` and `method` settings used by -GitLab. Common combinations are `method: 'plain'` and `port: 389`, OR -`method: 'ssl'` and `port: 636`. +LDAP server please double-check the LDAP `port` and `encryption` settings used by +GitLab. Common combinations are `encryption: 'plain'` and `port: 389`, OR +`encryption: 'simple_tls'` and `port: 636`. ### Troubleshooting diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md index afafb6bf1f5..57e54815b68 100644 --- a/doc/administration/container_registry.md +++ b/doc/administration/container_registry.md @@ -112,7 +112,7 @@ GitLab from source respectively. >**Note:** Be careful to choose a port different than the one that Registry listens to (`5000` by default), -otherwise you will run into conflicts . +otherwise you will run into conflicts. --- @@ -465,23 +465,42 @@ on how to achieve that. ## Disable Container Registry but use GitLab as an auth endpoint -You can disable the embedded Container Registry to use an external one, but -still use GitLab as an auth endpoint. - **Omnibus GitLab** + +You can use GitLab as an auth endpoint and use a non-bundled Container Registry. + 1. Open `/etc/gitlab/gitlab.rb` and set necessary configurations: ```ruby - registry['enable'] = false gitlab_rails['registry_enabled'] = true gitlab_rails['registry_host'] = "registry.gitlab.example.com" gitlab_rails['registry_port'] = "5005" gitlab_rails['registry_api_url'] = "http://localhost:5000" - gitlab_rails['registry_key_path'] = "/var/opt/gitlab/gitlab-rails/certificate.key" gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry" gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer" ``` +1. A certificate keypair is required for GitLab and the Container Registry to + communicate securely. By default omnibus-gitlab will generate one keypair, + which is saved to `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key`. + When using an non-bundled Container Registry, you will need to supply a + custom certificate key. To do that, add the following to + `/etc/gitlab/gitlab.rb` + + ```ruby + gitlab_rails['registry_key_path'] = "/custom/path/to/registry-key.key" + # registry['internal_key'] should contain the contents of the custom key + # file. Line breaks in the key file should be marked using `\n` character + # Example: + registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n" + ``` + + **Note:** The file specified at `registry_key_path` gets populated with the + content specified by `internal_key`, each time reconfigure is executed. If + no file is specified, omnibus-gitlab will default it to + `/var/opt/gitlab/gitlab-rails/etc/gitlab-registry.key` and will populate + it. + 1. Save the file and [reconfigure GitLab][] for the changes to take effect. **Installations from source** diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md index da9687aa849..ca6d8d2de67 100644 --- a/doc/administration/high_availability/database.md +++ b/doc/administration/high_availability/database.md @@ -97,9 +97,12 @@ If you use a cloud-managed service, or provide your own PostgreSQL: Enter new password: Enter it again: ``` - -1. Enable the `pg_trgm` extension: +1. Exit from editing `template1` prompt by typing `\q` and Enter. +1. Enable the `pg_trgm` extension within the `gitlabhq_production` database: + ``` + gitlab-psql -d gitlabhq_production + CREATE EXTENSION pg_trgm; # Output: diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 137fed35a73..42666357faf 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -5,7 +5,7 @@ configure the GitLab application server(s) now. Complete the steps below for each GitLab application server in your environment. > **Note:** There is some additional configuration near the bottom for - secondary GitLab application servers. It's important to read and understand + additional GitLab application servers. It's important to read and understand these additional steps before proceeding with GitLab installation. 1. If necessary, install the NFS client utility packages using the following @@ -70,10 +70,16 @@ for each GitLab application server in your environment. gitlab_rails['redis_host'] = '10.1.0.6' # IP/hostname of Redis server gitlab_rails['redis_password'] = 'Redis Password' ``` + + > **Note:** To maintain uniformity of links across HA clusters, the `external_url` + on the first application server as well as the additional application + servers should point to the external url that users will use to access GitLab. + In a typical HA setup, this will be the url of the load balancer which will + route traffic to all GitLab application servers in the HA cluster. 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration. -## Primary GitLab application server +## First GitLab application server As a final step, run the setup rake task on the first GitLab application server. It is not necessary to run this on additional application servers. @@ -89,10 +95,10 @@ It is not necessary to run this on additional application servers. [Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https) for more information. -## Additional configuration for secondary GitLab application servers +## Extra configuration for additional GitLab application servers -Secondary GitLab servers (servers configured **after** the first GitLab server) -need some additional configuration. +Additional GitLab servers (servers configured **after** the first GitLab server) +need some extra configuration. 1. Configure shared secrets. These values can be obtained from the primary GitLab server in `/etc/gitlab/gitlab-secrets.json`. Add these to diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 7072ab5d02a..6baae20d16a 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -20,7 +20,7 @@ it, the client IP needs to be [included in a whitelist][whitelist]. Currently the embedded Prometheus server is not automatically configured to collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten once every -[reconfigure of GitLab][reconfigure]. In the future this will not be required. +[reconfigure of GitLab][reconfigure]. In the future this will not be required. ## Metrics available @@ -46,6 +46,20 @@ In this experimental phase, only a few metrics are available: | redis_ping_latency_seconds | Gauge | 9.4 | Round trip time of the redis ping | | user_session_logins_total | Counter | 9.4 | Counter of how many users have logged in | +## Metrics shared directory + +GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services. +Those files are shared among all instances running under Unicorn server. +The directory needs to be accessible to all running Unicorn's processes otherwise +metrics will not function correctly. + +For best performance its advisable that this directory will be located in `tmpfs`. + +Its location is configured using environment variable `prometheus_multiproc_dir`. + +If GitLab is installed using Omnibus and `tmpfs` is available then metrics +directory will be automatically configured. + [↠Back to the main Prometheus page](index.md) [29118]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29118 diff --git a/doc/api/README.md b/doc/api/README.md index 95e7a457848..fe29563eaca 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -29,7 +29,8 @@ following locations: - [Keys](keys.md) - [Labels](labels.md) - [Merge Requests](merge_requests.md) -- [Milestones](milestones.md) +- [Project milestones](milestones.md) +- [Group milestones](group_milestones.md) - [Namespaces](namespaces.md) - [Notes](notes.md) (comments) - [Notification settings](notification_settings.md) @@ -339,7 +340,18 @@ URL-encoded. For example, `/` is represented by `%2F`: ``` -/api/v4/projects/diaspora%2Fdiaspora +GET /api/v4/projects/diaspora%2Fdiaspora +``` + +## Branches & tags name encoding + +If your branch or tag contains a `/`, make sure the branch/tag name is +URL-encoded. + +For example, `/` is represented by `%2F`: + +``` +GET /api/v4/projects/1/branches/my%2Fbranch/commits ``` ## `id` vs `iid` diff --git a/doc/api/ci/lint.md b/doc/api/ci/lint.md index 6a4dca92cfe..e4a6dc809b1 100644 --- a/doc/api/ci/lint.md +++ b/doc/api/ci/lint.md @@ -47,3 +47,5 @@ Example responses: "error": "content is missing" } ``` + +[ce-5953]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5953 diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md new file mode 100644 index 00000000000..086fba7e91d --- /dev/null +++ b/doc/api/group_milestones.md @@ -0,0 +1,120 @@ +# Group milestones API + +## List group milestones + +Returns a list of group milestones. + +``` +GET /groups/:id/milestones +GET /groups/:id/milestones?iids=42 +GET /groups/:id/milestones?iids[]=42&iids[]=43 +GET /groups/:id/milestones?state=active +GET /groups/:id/milestones?state=closed +GET /groups/:id/milestones?search=version +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `iids` | Array[integer] | optional | Return only the milestones having the given `iids` | +| `state` | string | optional | Return only `active` or `closed` milestones` | +| `search` | string | optional | Return only milestones with a title or description matching the provided string | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/5/milestones +``` + +Example Response: + +```json +[ + { + "id": 12, + "iid": 3, + "group_id": 16, + "title": "10.0", + "description": "Version", + "due_date": "2013-11-29", + "start_date": "2013-11-10", + "state": "active", + "updated_at": "2013-10-02T09:24:18Z", + "created_at": "2013-10-02T09:24:18Z" + } +] +``` + + +## Get single milestone + +Gets a single group milestone. + +``` +GET /groups/:id/milestones/:milestone_id +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `milestone_id` (required) - The ID of the group milestone + +## Create new milestone + +Creates a new group milestone. + +``` +POST /groups/:id/milestones +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `title` (required) - The title of an milestone +- `description` (optional) - The description of the milestone +- `due_date` (optional) - The due date of the milestone +- `start_date` (optional) - The start date of the milestone + +## Edit milestone + +Updates an existing group milestone. + +``` +PUT /groups/:id/milestones/:milestone_id +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `milestone_id` (required) - The ID of a group milestone +- `title` (optional) - The title of a milestone +- `description` (optional) - The description of a milestone +- `due_date` (optional) - The due date of the milestone +- `start_date` (optional) - The start date of the milestone +- `state_event` (optional) - The state event of the milestone (close|activate) + +## Get all issues assigned to a single milestone + +Gets all issues assigned to a single group milestone. + +``` +GET /groups/:id/milestones/:milestone_id/issues +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `milestone_id` (required) - The ID of a group milestone + +## Get all merge requests assigned to a single milestone + +Gets all merge requests assigned to a single group milestone. + +``` +GET /groups/:id/milestones/:milestone_id/merge_requests +``` + +Parameters: + +- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user +- `milestone_id` (required) - The ID of a group milestone diff --git a/doc/api/issues.md b/doc/api/issues.md index a00a63bad4b..6bac2927339 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -14,7 +14,9 @@ Read more on [pagination](README.md#pagination). ## List issues -Get all issues created by the authenticated user. +Get all issues the authenticated user has access to. By default it +returns only issues created by the current user. To get all issues, +use parameter `scope=all`. ``` GET /issues @@ -26,7 +28,8 @@ GET /issues?labels=foo,bar&state=opened GET /issues?milestone=1.0.0 GET /issues?milestone=1.0.0&state=opened GET /issues?iids[]=42&iids[]=43 -GET /issues?search=issue+title+or+description +GET /issues?author_id=5 +GET /issues?assignee_id=5 ``` | Attribute | Type | Required | Description | @@ -34,9 +37,12 @@ GET /issues?search=issue+title+or+description | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `milestone` | string | no | The milestone title | +| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. _([Introduced][ce-13004] in GitLab 9.5)_ | +| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `iids` | Array[integer] | no | Return only the issues having the given `iid` | -| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | -| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | +| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Search issues against their `title` and `description` | ```bash @@ -117,6 +123,8 @@ GET /groups/:id/issues?milestone=1.0.0 GET /groups/:id/issues?milestone=1.0.0&state=opened GET /groups/:id/issues?iids[]=42&iids[]=43 GET /groups/:id/issues?search=issue+title+or+description +GET /groups/:id/issues?author_id=5 +GET /groups/:id/issues?assignee_id=5 ``` | Attribute | Type | Required | Description | @@ -126,9 +134,12 @@ GET /groups/:id/issues?search=issue+title+or+description | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `iids` | Array[integer] | no | Return only the issues having the given `iid` | | `milestone` | string | no | The milestone title | -| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | -| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Search group issues against their `title` and `description` | +| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | +| `search` | string | no | Search group issues against their `title` and `description` | ```bash @@ -209,6 +220,8 @@ GET /projects/:id/issues?milestone=1.0.0 GET /projects/:id/issues?milestone=1.0.0&state=opened GET /projects/:id/issues?iids[]=42&iids[]=43 GET /projects/:id/issues?search=issue+title+or+description +GET /projects/:id/issues?author_id=5 +GET /projects/:id/issues?assignee_id=5 ``` | Attribute | Type | Required | Description | @@ -218,9 +231,12 @@ GET /projects/:id/issues?search=issue+title+or+description | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `milestone` | string | no | The milestone title | -| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | -| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Search project issues against their `title` and `description` | +| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | +| `search` | string | no | Search project issues against their `title` and `description` | | `created_after` | datetime | no | Return issues created after the given time (inclusive) | | `created_before` | datetime | no | Return issues created before the given time (inclusive) | @@ -356,7 +372,13 @@ Example response: "user_notes_count": 1, "due_date": null, "web_url": "http://example.com/example/example/issues/1", - "confidential": false + "confidential": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + } } ``` @@ -418,7 +440,13 @@ Example response: "user_notes_count": 0, "due_date": null, "web_url": "http://example.com/example/example/issues/14", - "confidential": false + "confidential": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + } } ``` @@ -481,7 +509,13 @@ Example response: "user_notes_count": 0, "due_date": "2016-07-22", "web_url": "http://example.com/example/example/issues/15", - "confidential": false + "confidential": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + } } ``` @@ -567,7 +601,13 @@ Example response: }, "due_date": null, "web_url": "http://example.com/example/example/issues/11", - "confidential": false + "confidential": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + } } ``` @@ -632,7 +672,13 @@ Example response: }, "due_date": null, "web_url": "http://example.com/example/example/issues/11", - "confidential": false + "confidential": false, + "_links": { + "self": "http://example.com/api/v4/projects/1/issues/2", + "notes": "http://example.com/api/v4/projects/1/issues/2/notes", + "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", + "project": "http://example.com/api/v4/projects/1" + } } ``` @@ -991,3 +1037,5 @@ Example response: "akismet_submitted": false } ``` + +[ce-13004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13004 diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index c90d95e4dd0..d0725b5e06e 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -1,7 +1,104 @@ # Merge requests API +Every API call to merge requests must be authenticated. + ## List merge requests +> [Introduced][ce-13060] in GitLab 9.5. + +Get all merge requests the authenticated user has access to. By +default it returns only merge requests created by the current user. To +get all merge requests, use parameter `scope=all`. + +The `state` parameter can be used to get only merge requests with a +given state (`opened`, `closed`, or `merged`) or all of them (`all`). +The pagination parameters `page` and `per_page` can be used to +restrict the list of merge requests. + +``` +GET /merge_requests +GET /merge_requests?state=opened +GET /merge_requests?state=all +GET /merge_requests?milestone=release +GET /merge_requests?labels=bug,reproduced +GET /merge_requests?author_id=5 +GET /merge_requests?scope=assigned-to-me +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged`| +| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | +| `milestone` | string | no | Return merge requests for a specific milestone | +| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request | +| `labels` | string | no | Return merge requests matching a comma separated list of labels | +| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | +| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | +| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` | +| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me` | +| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` | + +```json +[ + { + "id": 1, + "iid": 1, + "target_branch": "master", + "source_branch": "test1", + "project_id": 3, + "title": "test1", + "state": "opened", + "upvotes": 0, + "downvotes": 0, + "author": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + }, + "assignee": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + }, + "source_project_id": 2, + "target_project_id": 3, + "labels": [ ], + "description": "fixed login page css paddings", + "work_in_progress": false, + "milestone": { + "id": 5, + "iid": 1, + "project_id": 3, + "title": "v2.0", + "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", + "state": "closed", + "created_at": "2015-02-02T19:49:26.013Z", + "updated_at": "2015-02-02T19:49:26.013Z", + "due_date": null + }, + "merge_when_pipeline_succeeds": true, + "merge_status": "can_be_merged", + "sha": "8888888888888888888888888888888888888888", + "merge_commit_sha": null, + "user_notes_count": 1, + "should_remove_source_branch": true, + "force_remove_source_branch": false, + "web_url": "http://example.com/example/example/merge_requests/1" + } +] +``` + +## List project merge requests + Get all merge requests for this project. The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. @@ -29,6 +126,9 @@ Parameters: | `labels` | string | no | Return merge requests matching a comma separated list of labels | | `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | | `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | +| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13060] in GitLab 9.5)_ | +| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | +| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | ```json [ @@ -1165,3 +1265,5 @@ Example response: "total_time_spent": 3600 } ``` + +[ce-13060]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13060 diff --git a/doc/api/projects.md b/doc/api/projects.md index 61ae89a64c0..d3f8e509612 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -99,7 +99,16 @@ Parameters: "repository_size": 1038090, "lfs_objects_size": 0, "job_artifacts_size": 0 - } + }, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" + }, }, { "id": 6, @@ -168,6 +177,15 @@ Parameters: "repository_size": 2066080, "lfs_objects_size": 0, "job_artifacts_size": 0 + }, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" } } ] @@ -257,6 +275,15 @@ Parameters: "repository_size": 1038090, "lfs_objects_size": 0, "job_artifacts_size": 0 + }, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" } }, { @@ -326,6 +353,15 @@ Parameters: "repository_size": 2066080, "lfs_objects_size": 0, "job_artifacts_size": 0 + }, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" } } ] @@ -427,6 +463,15 @@ Parameters: "repository_size": 1038090, "lfs_objects_size": 0, "job_artifacts_size": 0 + }, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" } } ``` @@ -659,7 +704,16 @@ Example response: "shared_with_groups": [], "only_allow_merge_if_pipeline_succeeds": false, "only_allow_merge_if_all_discussions_are_resolved": false, - "request_access_enabled": false + "request_access_enabled": false, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" + } } ``` @@ -725,7 +779,16 @@ Example response: "shared_with_groups": [], "only_allow_merge_if_pipeline_succeeds": false, "only_allow_merge_if_all_discussions_are_resolved": false, - "request_access_enabled": false + "request_access_enabled": false, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" + } } ``` @@ -809,7 +872,16 @@ Example response: "shared_with_groups": [], "only_allow_merge_if_pipeline_succeeds": false, "only_allow_merge_if_all_discussions_are_resolved": false, - "request_access_enabled": false + "request_access_enabled": false, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" + } } ``` @@ -893,7 +965,16 @@ Example response: "shared_with_groups": [], "only_allow_merge_if_pipeline_succeeds": false, "only_allow_merge_if_all_discussions_are_resolved": false, - "request_access_enabled": false + "request_access_enabled": false, + "_links": { + "self": "http://example.com/api/v4/projects", + "issues": "http://example.com/api/v4/projects/1/issues", + "merge_requests": "http://example.com/api/v4/projects/1/merge_requests", + "repo_branches": "http://example.com/api/v4/projects/1/repository_branches", + "labels": "http://example.com/api/v4/projects/1/labels", + "events": "http://example.com/api/v4/projects/1/events", + "members": "http://example.com/api/v4/projects/1/members" + } } ``` diff --git a/doc/api/settings.md b/doc/api/settings.md index 0b4cc98cea6..94a9f8265fb 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -42,7 +42,6 @@ Example response: "gravatar_enabled" : true, "sign_in_text" : null, "container_registry_token_expire_delay": 5, - "repository_storage": "default", "repository_storages": ["default"], "koding_enabled": false, "koding_url": null, @@ -81,7 +80,6 @@ PUT /application/settings | `after_sign_out_path` | string | no | Where to redirect users after logout | | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes | | `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. | -| `repository_storage` | string | no | The first entry in `repository_storages`. Deprecated, but retained for compatibility reasons | | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. | | `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. | | `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. | @@ -121,7 +119,7 @@ Example response: "user_oauth_applications": true, "after_sign_out_path": "", "container_registry_token_expire_delay": 5, - "repository_storage": "default", + "repository_storages": ["default"], "koding_enabled": false, "koding_url": null, "plantuml_enabled": false, diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 9db8e0351cf..9835fab7c98 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -2,9 +2,11 @@ Since GitLab 9.0, API V4 is the preferred version to be used. -API V3 will be removed in GitLab 9.5, to be released on August 22, 2017. In the -meantime, we advise you to make any necessary changes to applications that use -V3. The V3 API documentation is still [available](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-16-stable/doc/api/README.md). +API V3 will be unsupported from GitLab 9.5, to be released on August +22, 2017. It will be removed in GitLab 9.5 or later. In the meantime, we advise +you to make any necessary changes to applications that use V3. The V3 API +documentation is still +[available](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-16-stable/doc/api/README.md). Below are the changes made between V3 and V4. diff --git a/doc/articles/how_to_configure_ldap_gitlab_ce/index.md b/doc/articles/how_to_configure_ldap_gitlab_ce/index.md index 6892905dd94..130e8f542b4 100644 --- a/doc/articles/how_to_configure_ldap_gitlab_ce/index.md +++ b/doc/articles/how_to_configure_ldap_gitlab_ce/index.md @@ -120,7 +120,8 @@ gitlab_rails['ldap_servers'] = { 'host' => 'ad.example.org', 'port' => 636, 'uid' => 'sAMAccountName', - 'method' => 'ssl', + 'encryption' => 'simple_tls', + 'verify_certificates' => true, 'bind_dn' => 'CN=GitLabSRV,CN=Users,DC=GitLab,DC=org', 'password' => 'Password1', 'active_directory' => true, @@ -255,7 +256,7 @@ If `allow_username_or_email_login` is enabled in the LDAP configuration, GitLab ## LDAP extended features on GitLab EE -With [GitLab Enterprise Edition (EE)](https://about.gitlab.com/giltab-ee/), besides everything we just described, you'll +With [GitLab Enterprise Edition (EE)](https://about.gitlab.com/gitlab-ee/), besides everything we just described, you'll have extended functionalities with LDAP, such as: - Group sync diff --git a/doc/articles/index.md b/doc/articles/index.md index 342fa88e80f..a4e41517d83 100644 --- a/doc/articles/index.md +++ b/doc/articles/index.md @@ -11,6 +11,7 @@ They are written by members of the GitLab Team and by - **LDAP** - [How to configure LDAP with GitLab CE](how_to_configure_ldap_gitlab_ce/index.md) + - [How to configure LDAP with GitLab EE](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/) ## Git @@ -23,3 +24,23 @@ They are written by members of the GitLab Team and by - [Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md) - [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md) - [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md) +- [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) +- [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) + +## Sofware development + +- [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) +- [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) +- [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) +- [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) +- [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) + +## Build, test, and deploy with GitLab CI/CD + +**Build, test, and deploy** the software you develop with **[GitLab CI/CD](../ci/README.md)** + +- [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/) +- [Automated Debian Package Build with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/) +- [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/) +- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/) +- [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index df5c66a4c85..6a7f694d705 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -607,8 +607,7 @@ exist, you should see something like: - With GitLab 9.2, all deployments to an environment are shown directly on the monitoring dashboard -If your application is deployed on Kubernetes and you have enabled Prometheus -collecting metrics, you can monitor the performance behavior of your app +If you have enabled Prometheus for collecting metrics, you can monitor the performance behavior of your app through the environments. Once configured, GitLab will attempt to retrieve performance metrics for any diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md index 72a34aa7de9..e67db9ff142 100644 --- a/doc/development/background_migrations.md +++ b/doc/development/background_migrations.md @@ -31,6 +31,18 @@ It's also possible for different migrations to be executed at the same time. This means that different background migrations should not migrate data in a way that would cause conflicts. +## Idempotence + +Background migrations are executed in a context of a Sidekiq process. +Usual Sidekiq rules apply, especially the rule that jobs should be small +and idempotent. + +See [Sidekiq best practices guidelines](https://github.com/mperham/sidekiq/wiki/Best-Practices) +for more details. + +Make sure that in case that your migration job is going to be retried data +integrity is guarateed. + ## How It Works Background migrations are simple classes that define a `perform` method. A @@ -212,3 +224,27 @@ end This migration will then process any jobs for the ExtractServicesUrl migration and continue once all jobs have been processed. Once done you can safely remove the `services.properties` column. + +## Testing + +It is required to write tests for background migrations' scheduling migration +(either a regular migration or a post deployment migration), background +migration itself and a cleanup migration. You can use the `:migration` RSpec +tag when testing a regular / post deployment migration. +See [README][migrations-readme]. + +When you do that, keep in mind that `before` and `after` RSpec hooks are going +to migrate you database down and up, which can result in other background +migrations being called. That means that using `spy` test doubles with +`have_received` is encouraged, instead of using regular test doubles, because +your expectations defined in a `it` block can conflict with what is being +called in RSpec hooks. See [gitlab-org/gitlab-ce#35351][issue-rspec-hooks] +for more details. + +## Best practices + +1. Make sure that background migration jobs are idempotent. +1. Make sure that tests you write are not false positives. + +[migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md +[issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351 diff --git a/doc/development/code_review.md b/doc/development/code_review.md index 4ed89146072..e3f37616757 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -133,6 +133,55 @@ reviewee. tomorrow. When you are not able to find the right balance, ask other people about their opinion. +### GitLab-specific concerns + +GitLab is used in a lot of places. Many users use +our [Omnibus packages](https://about.gitlab.com/installation/), but some use +the [Docker images](https://docs.gitlab.com/omnibus/docker/), some are +[installed from source](https://docs.gitlab.com/ce/install/installation.html), +and there are other installation methods available. GitLab.com itself is a large +Enterprise Edition instance. This has some implications: + +1. **Query changes** should be tested to ensure that they don't result in worse + performance at the scale of GitLab.com: + 1. Generating large quantities of data locally can help. + 2. Asking for query plans from GitLab.com is the most reliable way to validate + these. +2. **Database migrations** must be: + 1. Reversible. + 2. Performant at the scale of GitLab.com - ask a maintainer to test the + migration on the staging environment if you aren't sure. + 3. Categorised correctly: + - Regular migrations run before the new code is running on the instance. + - [Post-deployment migrations](post_deployment_migrations.md) run _after_ + the new code is deployed, when the instance is configured to do that. + - [Background migrations](background_migrations.md) run in Sidekiq, and + should only be done for migrations that would take an extreme amount of + time at GitLab.com scale. +3. **Sidekiq workers** + [cannot change in a backwards-incompatible way](sidekiq_style_guide.md#removing-or-renaming-queues): + 1. Sidekiq queues are not drained before a deploy happens, so there will be + workers in the queue from the previous version of GitLab. + 2. If you need to change a method signature, try to do so across two releases, + and accept both the old and new arguments in the first of those. + 3. Similarly, if you need to remove a worker, stop it from being scheduled in + one release, then remove it in the next. This will allow existing jobs to + execute. + 4. Don't forget, not every instance will upgrade to every intermediate version + (some people may go from X.1.0 to X.10.0, or even try bigger upgrades!), so + try to be liberal in accepting the old format if it is cheap to do so. +4. **Cached values** may persist across releases. If you are changing the type a + cached value returns (say, from a string or nil to an array), change the + cache key at the same time. +5. **Settings** should be added as a + [last resort](https://about.gitlab.com/handbook/product/#convention-over-configuration). + If you're adding a new setting in `gitlab.yml`: + 1. Try to avoid that, and add to `ApplicationSetting` instead. + 2. Ensure that it is also + [added to Omnibus](https://docs.gitlab.com/omnibus/settings/gitlab.yml.html#adding-a-new-setting-to-gitlab-yml). +6. **Filesystem access** can be slow, so try to avoid + [shared files](shared_files.md) when an alternative solution is available. + ### Credits Largely based on the [thoughtbot code review guide]. diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 36c55cbaceb..90d1d9657b9 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -105,8 +105,8 @@ merge request. considered beta or experimental, put this info in a note, not in the heading. - When introducing a new document, be careful for the headings to be grammatically and syntactically correct. It is advised to mention one or all - of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`, - `@SeanPackham`. This is to ensure that no document with wrong heading is going + of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`. + This is to ensure that no document with wrong heading is going live without an audit, thus preventing dead links and redirection issues when corrected - Leave exactly one newline after a heading diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md index 2ddcbe13afa..14ac1133cc0 100644 --- a/doc/development/fe_guide/performance.md +++ b/doc/development/fe_guide/performance.md @@ -23,6 +23,19 @@ controlled by the server. 1. The backend code will most likely be using etags. You do not and should not check for status `304 Not Modified`. The browser will transform it for you. +### Lazy Loading + +To improve the time to first render we are using lazy loading for images. This works by setting +the actual image source on the `data-src` attribute. After the HTML is rendered and JavaScript is loaded, +the value of `data-src` will be moved to `src` automatically if the image is in the current viewport. + +* Prepare images in HTML for lazy loading by renaming the `src` attribute to `data-src` AND adding the class `lazy` +* If you are using the Rails `image_tag` helper, all images will be lazy-loaded by default unless `lazy: false` is provided. + +If you are asynchronously adding content which contains lazy images then you need to call the function +`gl.lazyLoader.searchLazyImages()` which will search for lazy images and load them if needed. +But in general it should be handled automatically through a `MutationObserver` in the lazy loading function. + ## Reducing Asset Footprint ### Page-specific JavaScript diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md index ae844fa1051..149a0159680 100644 --- a/doc/development/fe_guide/style_guide_js.md +++ b/doc/development/fe_guide/style_guide_js.md @@ -447,6 +447,7 @@ A forEach will cause side effects, it will be mutating the array being iterated. 1. `name` 1. `props` 1. `mixins` + 1. `directives` 1. `data` 1. `components` 1. `computedProps` diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md index e3a20f29a09..1e9fdbc65e2 100644 --- a/doc/development/sidekiq_style_guide.md +++ b/doc/development/sidekiq_style_guide.md @@ -36,3 +36,10 @@ slow jobs blocking work (even for different jobs) on the shared queue. Each Sidekiq worker must be tested using RSpec, just like any other class. These tests should be placed in `spec/workers`. + +## Removing or renaming queues + +Try to avoid renaming or removing queues in minor and patch releases. +During online update instance can have pending jobs and removing the queue can +lead to those jobs being stuck forever. If you can't write migration for those +Sidekiq jobs, please consider doing rename or remove queue in major release only.
\ No newline at end of file diff --git a/doc/install/installation.md b/doc/install/installation.md index 5e981b0b3e7..8ded607bcab 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -64,7 +64,7 @@ up-to-date and install it. Install the required packages (needed to compile Ruby and native extensions to Ruby gems): - sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake + sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake If you want to use Kerberos for user authentication, then install libkrb5-dev: diff --git a/doc/integration/README.md b/doc/integration/README.md index e56e58498a6..d70b9a7f54b 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -5,19 +5,23 @@ trackers and external authentication. See the documentation below for details on how to configure these services. -- [JIRA](../user/project/integrations/jira.md) Integrate with the JIRA issue tracker +- [Akismet](akismet.md) Configure Akismet to stop spam +- [Auth0 OmniAuth](auth0.md) Enable the Auth0 OmniAuth provider +- [Bitbucket](bitbucket.md) Import projects from Bitbucket.org and login to your GitLab instance with your +Bitbucket.org account +- [CAS](cas.md) Configure GitLab to sign in using CAS - [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. +- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages +- [JIRA](../user/project/integrations/jira.md) Integrate with the JIRA issue tracker +- [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration - [LDAP](ldap.md) Set up sign in via LDAP -- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd, Azure and Authentiq ID -- [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider -- [CAS](cas.md) Configure GitLab to sign in using CAS - [OAuth2 provider](oauth_provider.md) OAuth2 application creation +- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd, Azure and Authentiq ID - [OpenID Connect](openid_connect_provider.md) Use GitLab as an identity provider -- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages -- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users -- [Akismet](akismet.md) Configure Akismet to stop spam -- [Koding](../administration/integration/koding.md) Configure Koding to use IDE integration - [PlantUML](../administration/integration/plantuml.md) Configure PlantUML to use diagrams in AsciiDoc documents. +- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users +- [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider +- [Trello](trello_power_up.md) Integrate Trello with GitLab > GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index 2dd9b33273c..372e1909330 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -4,14 +4,12 @@ GitLab has a great issue tracker but you can also use an external one such as Jira, Redmine, or Bugzilla. Issue trackers are configurable per GitLab project and allow you to do the following: -- the **Issues** link on the GitLab project pages takes you to the appropriate - issue index of the external tracker -- clicking **New issue** on the project dashboard creates a new issue on the - external tracker - you can reference these external issues inside GitLab interface (merge requests, commits, comments) and they will be automatically converted into links +You can have enabled both external and internal GitLab issue trackers in parallel. The **Issues** link always opens the internal issue tracker and in case the internal issue tracker is disabled the link is not visible in the menu. + ## Configuration The configuration is done via a project's **Services**. diff --git a/doc/integration/img/enable_trello_powerup.png b/doc/integration/img/enable_trello_powerup.png Binary files differnew file mode 100644 index 00000000000..65d01f1c38c --- /dev/null +++ b/doc/integration/img/enable_trello_powerup.png diff --git a/doc/integration/img/trello_card_with_gitlab_powerup.png b/doc/integration/img/trello_card_with_gitlab_powerup.png Binary files differnew file mode 100644 index 00000000000..2965dc35855 --- /dev/null +++ b/doc/integration/img/trello_card_with_gitlab_powerup.png diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md index af8a1c4e5ed..8ba2e8731c8 100644 --- a/doc/integration/oauth_provider.md +++ b/doc/integration/oauth_provider.md @@ -63,6 +63,9 @@ it from the admin area. ![OAuth admin_applications](img/oauth_provider_admin_application.png) +You're also able to mark an application as _trusted_ when creating it through the admin area. By doing that, +the user authorization step is automatically skipped for this application. + --- ## Authorized applications diff --git a/doc/integration/trello_power_up.md b/doc/integration/trello_power_up.md new file mode 100644 index 00000000000..d264486a872 --- /dev/null +++ b/doc/integration/trello_power_up.md @@ -0,0 +1,42 @@ +# Trello Power-Up + +GitLab's Trello Power-Up enables you to seamlessly attach +GitLab **merge requests** to Trello cards. + +![GitLab Trello PowerUp - Trello card](img/trello_card_with_gitlab_powerup.png) + +## Configuring the Power-Up + +In order to get started, you will need to configure your Power-Up. + +In Trello: + +1. Go to your Trello board +1. Select `Power-Ups` to see a listing of all the available Power-Ups +1. Look for a row that says `GitLab` and select the `Enable` button +1. Select the `Settings` (gear) icon +1. In the popup menu, select `Authorize Account` + +In this popup, fill in your `API URL` and `Personal Access Token`. After that, you will be able to attach any merge request to any Trello card on your selected Trello board. + +## What is my API URL? + +Your API URL should be your GitLab instance URL with `/api/v4` appended in the end of the URL. +For example, if your GitLab instance URL is `https://gitlab.com`, your API URL would be `https://gitlab.com/api/v4`. +If your instance's URL is `https://example.com`, your API URL will be `https://example.com/api/v4`. + +![configure GitLab Trello PowerUp in Trello](img/enable_trello_powerup.png) + +## What is my Personal Access Token? + +Your GitLab's personal access token will enable your GitLab account to be accessed +from Trello. + +> Find it in GitLab by clicking on your avatar (upright corner), from which you access +your user **Settings** > **Access Tokens**. + +Learn more about generating a personal access token in the +[Personal Access Token Documentation][personal-access-token-documentation]. +Don't forget to check the API scope checkbox! + +[personal-access-token-documentation]: ../user/profile/personal_access_tokens.html diff --git a/doc/profile/README.md b/doc/profile/README.md index aed64ac1228..fda6d85a84c 100644 --- a/doc/profile/README.md +++ b/doc/profile/README.md @@ -1,5 +1 @@ -# Profile settings - -- [Preferences](../user/profile/preferences.md) -- [Two-factor Authentication (2FA)](../user/profile/account/two_factor_authentication.md) -- [Deleting your account](../user/profile/account/delete_account.md) +This document was moved to [user/profile/account](../user/profile/index.md). diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 855f437cd73..10f5ab3370d 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -5,9 +5,9 @@ An application data backup creates an archive file that contains the database, all repositories and all attachments. -You can only restore a backup to **exactly the same version** of GitLab on which -it was created. The best way to migrate your repositories from one server to -another is through backup restore. +You can only restore a backup to **exactly the same version and type (CE/EE)** +of GitLab on which it was created. The best way to migrate your repositories +from one server to another is through backup restore. ## Backup @@ -270,6 +270,15 @@ For installations from source: remote_directory: 'gitlab_backups' ``` +### Specifying a custom directory for backups + +If you want to group your backups you can pass a `DIRECTORY` environment variable: + +``` +sudo gitlab-rake gitlab:backup:create DIRECTORY=daily +sudo gitlab-rake gitlab:backup:create DIRECTORY=weekly +``` + ### Backup archive permissions The backup archives created by GitLab (`1393513186_2014_02_27_gitlab_backup.tar`) @@ -369,8 +378,8 @@ The [restore prerequisites section](#restore-prerequisites) includes crucial information. Make sure to read and test the whole restore process at least once before attempting to perform it in a production environment. -You can only restore a backup to **exactly the same version** of GitLab that -you created it on, for example 9.1.0. +You can only restore a backup to **exactly the same version and type (CE/EE)** of +GitLab that you created it on, for example CE 9.1.0. ### Restore prerequisites @@ -441,8 +450,8 @@ Deleting tmp directories...[DONE] This procedure assumes that: -- You have installed the **exact same version** of GitLab Omnibus with which the - backup was created. +- You have installed the **exact same version and type (CE/EE)** of GitLab + Omnibus with which the backup was created. - You have run `sudo gitlab-ctl reconfigure` at least once. - GitLab is running. If not, start it using `sudo gitlab-ctl start`. diff --git a/doc/university/process/README.md b/doc/university/process/README.md index 7ff53c2cc3f..04f2d52514f 100644 --- a/doc/university/process/README.md +++ b/doc/university/process/README.md @@ -27,4 +27,4 @@ please submit a merge request to add an upcoming class, assign to 1. Please upload any video recordings to our Youtube channel. We prefer them to be public, if needed they can be unlisted but if so they should be linked from this page. -1. Please create a merge request and assign to [SeanPackham](https://gitlab.com/u/SeanPackham). +1. Please create a merge request and assign to [Erica](https://gitlab.com/u/Erica). diff --git a/doc/user/index.md b/doc/user/index.md new file mode 100644 index 00000000000..522cf932349 --- /dev/null +++ b/doc/user/index.md @@ -0,0 +1,180 @@ +# User documentation + +Welcome to GitLab! We're glad to have you here! + +As a GitLab user you'll have access to all the features +your [subscription](https://about.gitlab.com/products/) +includes, except [GitLab administrator](../README.md#administrator-documentation) +settings, unless you have admin privileges to install, configure, +and upgrade your GitLab instance. + +For GitLab.com, admin privileges are restricted to the GitLab team. + +If you run your own GitLab instance and are looking for the administration settings, +please refer to the [administration](../README.md#administrator-documentation) +documentation. + +## Overview + +GitLab is a fully integrated software development platform that enables you +and your team to work cohesively, faster, transparently, and effectively, +since the discussion of a new idea until taking that idea to production all +all the way through, from within the same platform. + +Please check this page for an overview on [GitLab's features](https://about.gitlab.com/features/). + +## Use cases + +GitLab is a git-based platforms that integrates a great number of essential tools for software development and deployment, and project management: + +- Code hosting in repositories with version control +- Track proposals for new implementations, bug reports, and feedback with a +fully featured [Issue Tracker](project/issues/index.md#issue-tracker) +- Organize and prioritize with [Issue Boards](project/issues/index.md#issue-boards) +- Code review in [Merge Requests](project/merge_requests/index.md) with live-preview changes per +branch with [Review Apps](../ci/review_apps/index.md) +- Build, test and deploy with built-in [Continuous Integration](../ci/README.md) +- Deploy your personal and professional static websites with [GitLab Pages](project/pages/index.md) +- Integrate with Docker with [GitLab Container Registry](project/container_registry.md) +- Track the development lifecycle with [GitLab Cycle Analytics](project/cycle_analytics.md) + +With GitLab Enterprise Edition, you can also: + +- Provide support with [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html) +- Improve collaboration with +[Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#merge-request-approvals), +[Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html), +and [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) +- Create formal relashionships between issues with [Related Issues](https://docs.gitlab.com/ee/user/project/issues/related_issues.html) +- Use [Burndown Charts](https://docs.gitlab.com/ee/user/project/milestones/burndown_charts.html) to track progress during a sprint or while working on a new version of their software. +- Leverage [Elasticsearch](https://docs.gitlab.com/ee/integration/elasticsearch.html) with [Advanced Global Search](https://docs.gitlab.com/ee/user/search/advanced_global_search.html) and [Advanced Syntax Search](https://docs.gitlab.com/ee/user/search/advanced_search_syntax.html) for faster, more advanced code search across your entire GitLab instance +- [Authenticate users with Kerberos](https://docs.gitlab.com/ee/integration/kerberos.html) +- [Mirror a repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html) from elsewhere on your local server. +- [Export issues as CSV](https://docs.gitlab.com/ee/user/project/issues/csv_export.html) +- View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html) +- [Lock files](https://docs.gitlab.com/ee/user/project/file_lock.html) to prevent conflicts +- View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html) +- Leverage your continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html) + +You can also [integrate](project/integrations/project_services.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, JIRA, and a lot more. + +### Articles + +For a complete workflow use case please check [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/#gitlab-workflow-use-case-scenario). + +For more use cases please check our [Technical Articles](../articles/index.md). + +## Projects + +In GitLab, you can create projects for numerous reasons, such as, host +your code, use it as an issue tracker, collaborate on code, and continuously +build, test, and deploy your app with built-in GitLab CI/CD. Or, you can do +it all at once, from one single project. + +### Repository + +Host your codebase in [GitLab repositories](project/repository/index.md) with version control +and as part of a fully integrated platform. + +### Issues + +Explore the best of GitLab [Issues](project/issues/index.md). + +### Merge Requests + +Collanorate on code, gather reviews, live preview changes per branch, and +request approvals with [Merge Requests](project/merge_requests/index.md). + +### Milestones + +Work on multiple issues and merge requests towards the same target date +with [Milestones](project/milestones/index.md). + +### GitLab Pages + +Publish your static site directly from GitLab with [GitLab Pages](project/pages/index.md). You +can [build, test, and deploy any Static Site Generator](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) with Pages. + +### Container Registry + +Build and deploy Docker images with [GitLab Container Registry](project/container_registry.md). + +## GitLab CI/CD + +Use built-in [GitLab CI/CD](../ci/README.md) to test, build, and deploy your applications +directly from GitLab. No third-party integrations needed. + +### Auto Deploy + +Deploy your application out-of-the-box with [GitLab Auto Deploy](../ci/autodeploy/index.md). + +### Review Apps + +Live-preview the changes introduced by a merge request with [Review Apps](../ci/review_apps/index.md). + +## Groups + +With GitLab [Groups](group/index.md) you can assemble related projects together +and grant members access to several projects at once. + +### Subgroups + +Groups can also be nested in [subgroups](group/subgroups/index.md). + +## Account + +There is a lot you can customize and configure +to enjoy the best of GitLab. + +[Manage your user settings](profile/index.md) to change your personal info, +personal access tokens, authorized applications, etc. + +### Authentication + +Read through the [authentication](../topics/authentication/index.md) methods available in GitLab. + +### Permissions + +Learn the different set of [permissions](permissions.md) for user type (guest, reporter, developer, master, owner). + +## Integrations + +[Integrate GitLab](../integration/README.md) with your preferred tool, +such as Trello, JIRA, etc. + +## Git and GitLab + +Learn what is [Git](../topics/git/index.md) and its best practices. + +## Discussions + +In GitLab, you can comment and mention collaborators in issues, +merge requests, code snippets, and commits. + +When performing inline reviews to implementations +to your codebase through merge requests you can +gather feedback through [resolvable discussions](discussions/index.md#resolvable-discussions). + +## Todos + +Never forget to reply to your collaborators. [GitLab Todos](../workflow/todos.md) +are a tool for working faster and more effectively with your team, +by listing all user or group mentions, as well as issues and merge +requests you're assigned to. + +## Snippets + +[Snippets](snippets.md) are code blocks that you want to store in GitLab, from which +you have quick access to. You can also gather feedback on them through +[discussions](#discussions). + +## Webhooks + +Configure [webhooks](project/integrations/webhooks.html) to listen for +specific events like pushes, issues or merge requests. GitLab will send a +POST request with data to the webhook URL. + +## API + +Automate GitLab via [API](../api/README.html). + diff --git a/doc/user/profile/account/index.md b/doc/user/profile/account/index.md index 764354e1e96..06667bfc5f1 100644 --- a/doc/user/profile/account/index.md +++ b/doc/user/profile/account/index.md @@ -1,5 +1,2 @@ -# Profile settings -## Account - -Set up [two-factor authentication](two_factor_authentication.md). +This document was moved to [../index.md#profile-settings](../index.md#profile-settings). diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md new file mode 100644 index 00000000000..0e3747817d2 --- /dev/null +++ b/doc/user/profile/index.md @@ -0,0 +1,47 @@ +# User account + +When logged into their GitLab account, users can customize their +experience according to the best approach to their cases. + +## Username + +Your `username` is a unique [`namespace`](../group/index.md#namespaces) +related to your user ID. + +You can change your `username` from your +[profile settings](#profile-settings). To avoid breaking +paths when you change your `username`, we suggest you follow +[this procedure from the GitLab Team Handbook](https://about.gitlab.com/handbook/tools-and-tips/#how-to-change-your-username-at-gitlabcom). + +## User profile + +Your profile is available from the up-right corner menu bar (user's avatar) > **Profile**, +or from `https://example.gitlab.com/username`. + +On your profile page, you will see the following information: + +- Personal information +- Activity stream: see your activity streamline and the history of your contributions +- Groups: [groups](../group/index.md) you're a member of +- Contributed projects: projects you contributed to +- Personal projects: your personal projects (respecting the project's visibility level) +- Snippets: your personal code [snippets](../snippets.md#personal-snippets) + +## Profile settings + +You can edit your account settings by navigating from the up-right corner menu bar +(user's avatar) > **Settings**, or visiting `https://example.gitlab.com/profile`. + +From there, you can: + +- Update your personal information +- Manage [private tokens](../../api/README.md#private-tokens), email tokens, [2FA](account/two_factor_authentication.md) +- Change your username and [delete your account](account/delete_account.md) +- Manage applications that can +[use GitLab as an OAuth provider](../../integration/oauth_provider.md#introduction-to-oauth) +- Manage [personal access tokens](personal_access_tokens.md) to access your account via API and authorized applications +- Add and delete emails linked to your account +- Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH +- Manage your [preferences](preferences.md#syntax-highlighting-theme) +to customize your own GitLab experience +- Acess your audit log, a security log of important events involving your account diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md index 6a040516231..ba2adc1afda 100644 --- a/doc/user/project/integrations/bugzilla.md +++ b/doc/user/project/integrations/bugzilla.md @@ -20,10 +20,12 @@ Once you have configured and enabled Bugzilla: ## Referencing issues in Bugzilla Issues in Bugzilla can be referenced in two alternative ways: -1. `#<ID>` where `<ID>` is a number (example `#143`) +1. `#<ID>` where `<ID>` is a number (example `#143`). 2. `<PROJECT>-<ID>` where `<PROJECT>` starts with a capital letter which is then followed by capital letters, numbers or underscores, and `<ID>` is a number (example `API_32-143`). +We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker the internal issue will be linked. + Please note that `<PROJECT>` part is ignored and links always point to the address specified in `issues_url`. diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index cf03f2a9033..cfa4c8a93f8 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -98,11 +98,11 @@ in the table below. | Field | Description | | ----- | ----------- | | `Web URL` | The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. | -| `JIRA API URL` | The base URL to the JIRA instance API. E.g., `https://jira-api.example.com`. This is optional. If not entered, the Web URL value be used. | +| `JIRA API URL` | The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. | | `Project key` | Put a JIRA project key (in uppercase), e.g. `MARS` in this field. This is only for testing the configuration settings. JIRA integration in GitLab works with _all_ JIRA projects in your JIRA instance. This field will be removed in a future release. | | `Username` | The user name created in [configuring JIRA step](#configuring-jira). | | `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). | -| `JIRA issue transition` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | +| `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | After saving the configuration, your GitLab project will be able to interact with all JIRA projects in your JIRA instance. diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md new file mode 100644 index 00000000000..309da610cc0 --- /dev/null +++ b/doc/user/project/integrations/prometheus_library/haproxy.md @@ -0,0 +1,20 @@ +# Monitoring HA Proxy +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4 + +GitLab has support for automatically detecting and monitoring HA Proxy. This is provided by leveraging the [HA Proxy Exporter](https://github.com/hnlq715/nginx-vts-exporter), which translates HA Proxy statistics into a Prometheus readable form. + +## Metrics supported + +| Name | Query | +| ---- | ----- | +| Throughput (req/sec) | sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) | +| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_requests_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_requests_total{%{environment_filter}}[2m])) | + +## Configuring Prometheus to monitor for HA Proxy metrics + +To get started with NGINX monitoring, you should install and configure the [HA Proxy exporter](https://github.com/prometheus/haproxy_exporter) which parses these statistics and translates them into a Prometheus monitoring endpoint. + +## Specifying the Environment label + +In order to isolate and only display relevant metrics for a given environment +however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](metrics.md#identifying-environments). diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md index 55146e57370..546e1f51df5 100644 --- a/doc/user/project/integrations/prometheus_library/metrics.md +++ b/doc/user/project/integrations/prometheus_library/metrics.md @@ -4,6 +4,7 @@ GitLab offers automatic detection of select [Prometheus exporters](https://prometheus.io/docs/instrumenting/exporters/). Currently supported exporters are: * [Kubernetes](kubernetes.md) * [NGINX](nginx.md) +* [HA Proxy](haproxy.md) * [Amazon Cloud Watch](cloudwatch.md) We have tried to surface the most important metrics for each exporter, and will be continuing to add support for additional exporters in future releases. If you would like to add support for other official exporters, [contributions](#adding-to-the-library) are welcome. diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md index fe238e74e36..b3470773996 100644 --- a/doc/user/project/integrations/prometheus_library/nginx.md +++ b/doc/user/project/integrations/prometheus_library/nginx.md @@ -9,7 +9,7 @@ GitLab has support for automatically detecting and monitoring NGINX. This is pro | ---- | ----- | | Throughput (req/sec) | sum(rate(nginx_requests_total{server_zone!="*", server_zone!="_", %{environment_filter}}[2m])) | | Latency (ms) | avg(nginx_upstream_response_msecs_avg{%{environment_filter}}) * 1000 | -| HTTP Error Rate (%) | sum(nginx_responses_total{status_code="5xx", %{environment_filter}}) / sum(nginx_responses_total{server_zone!="*", server_zone!="_", %{environment_filter}}) | +| HTTP Error Rate (%) | sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m])) | ## Configuring Prometheus to monitor for NGINX metrics diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md index 8026f1f57bc..cf92465da53 100644 --- a/doc/user/project/integrations/redmine.md +++ b/doc/user/project/integrations/redmine.md @@ -30,5 +30,7 @@ Issues in Redmine can be referenced in two alternative ways: then followed by capital letters, numbers or underscores, and `<ID>` is a number (example `API_32-143`). +We suggest using the longer format if you have both internal and external issue trackers enabled. If you use the shorter format and an issue with the same ID exists in the internal issue tracker the internal issue will be linked. + Please note that `<PROJECT>` part is ignored and links always point to the address specified in `issues_url`. diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md index 138276edf07..074b2c19c43 100644 --- a/doc/user/project/issues/issues_functionalities.md +++ b/doc/user/project/issues/issues_functionalities.md @@ -58,17 +58,15 @@ Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/ - Select a [milestone](../milestones/index.md) to attribute that issue to. -#### 5. Time Tracking (EES/EEP) - -This feature is available only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/). +#### 5. Time Tracking - Estimate time: add an estimate time in which the issue will be implemented - Spend: add the time spent on the implementation of that issue > **Note:** -both estimate and spend times are set via [GitLab Quick Actions](../quick_actions.md). +Both estimate and spend times are set via [GitLab Quick Actions](../quick_actions.md). -Learn more on the [Time Tracking documentation](https://docs.gitlab.com/ee/workflow/time_tracking.html). +Learn more on the [Time Tracking documentation](../../../workflow/time_tracking.md). #### 6. Due date diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md index 258b3a2f955..9ad15a12c3c 100644 --- a/doc/user/project/pipelines/schedules.md +++ b/doc/user/project/pipelines/schedules.md @@ -71,9 +71,10 @@ The next time a pipeline is scheduled, your credentials will be used. >**Note:** When the owner of the schedule doesn't have the ability to create pipelines -anymore, due to e.g., being blocked or removed from the project, the schedule -is deactivated. Another user can take ownership and activate it, so the -schedule can be run again. +anymore, due to e.g., being blocked or removed from the project, or lacking +the permission to run on protected branches or tags. When this happened, the +schedule is deactivated. Another user can take ownership and activate it, so +the schedule can be run again. ## Advanced admin configuration diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 19b51c83222..ce4dd4e99d5 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -37,3 +37,4 @@ do. | `/target_branch <Branch Name>` | Set target branch for current merge request | | `/award :emoji:` | Toggle award for :emoji: | | `/board_move ~column` | Move issue to column on the board | +| `/duplicate #issue` | Closes this issue and marks it as a duplicate of another issue | diff --git a/doc/user/project/repository/img/compare_branches.png b/doc/user/project/repository/img/compare_branches.png Binary files differnew file mode 100755 index 00000000000..353bd72ef4e --- /dev/null +++ b/doc/user/project/repository/img/compare_branches.png diff --git a/doc/user/project/repository/img/contributors_graph.png b/doc/user/project/repository/img/contributors_graph.png Binary files differnew file mode 100755 index 00000000000..c31da7aa1ff --- /dev/null +++ b/doc/user/project/repository/img/contributors_graph.png diff --git a/doc/user/project/repository/img/repo_graph.png b/doc/user/project/repository/img/repo_graph.png Binary files differnew file mode 100755 index 00000000000..28da8ad9589 --- /dev/null +++ b/doc/user/project/repository/img/repo_graph.png diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md new file mode 100644 index 00000000000..bb41eb41795 --- /dev/null +++ b/doc/user/project/repository/index.md @@ -0,0 +1,150 @@ +# Repository + +A [repository](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) +is what you use to store your codebase in GitLab and change it with version control. +A repository is part of a project, which has a lot of other features. + +## Create a repository + +To create a new repository, all you need to do is +[create a new project](../../../gitlab-basics/create-project.md). + +Once you create a new project, you can add new files via UI +(read the section below) or via command line. +To add files from the command line, follow the instructions that will +be presented on the screen when you create a new project, or read +through them in the [command line basics](../../../gitlab-basics/start-using-git.md) +documentation. + +> **Important:** +For security reasons, when using the command line, we strongly recommend +you to [connect with GitLab via SSH](../../../ssh/README.md). + +## Create and edit files + +Host your codebase in GitLab repositories by pushing your files to GitLab. +You can either use the user interface (UI), or connect your local computer +with GitLab [through the command line](../../../gitlab-basics/command-line-commands.md#start-working-on-your-project). + +To configure [GitLab CI/CD](../../../ci/README.md) to build, test, and deploy +you code, add a file called [.`gitlab-ci.yml`](../../../ci/quick_start/README.md) +to your repository's root. + +**From the user interface:** + +GitLab's UI allows you to perform lots of Git commands without having to +touch the command line. Even if you use the command line regularly, sometimes +it's easier to do so [via GitLab UI](web_editor.md): + +- [Create a file](web_editor.md#create-a-file) +- [Upload a file](web_editor.md#upload-a-file) +- [File templates](web_editor.md#template-dropdowns) +- [Create a directory](web_editor.md#create-a-directory) +- [Start a merge request](web_editor.md#tips) + +**From the command line:** + +To get started with the command line, please read through the +[command line basics documentation](../../../gitlab-basics/command-line-commands.md). + +## Branches + +When you submit changes in a new branch, you create a new version +of that project's file tree. Your branch contains all the changes +you are presenting, which are detected by Git line by line. + +To continue your workflow, once you pushed your changes to a new branch, +you can create a [merge request](../merge_requests/index.md), perform +inline code review, and [discuss](../../discussions/index.md) +your implementation with your team. +You can live preview changes submitted to a new branch with +[Review Apps](../../../ci/review_apps/index.md). + +With [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) +subscriptions, you can also request +[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#merge-request-approvals) from your managers. + +To create, delete, and branches via GitLab's UI: + +- [Create a branch](web_editor.md#create-a-new-branch) +- [Protected branches](../protected_branches.md#protected-branches) +- [Delete merged branches](branches/index.md#delete-merged-branches) + +Alternatively, you can use the +[command line](../../../gitlab-basics/start-using-git.md#create-a-branch). + +To learn more about branching strategies read through the +[GitLab Flow](../../../university/training/gitlab_flow.md) documentation. + +## Commits + +When you [commit your changes](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository), +you are introducing those changes to your branch. +Via command line, you can commit multiple times before pushing. + +- **Commit message:** +A commit message is important to identity what is being changed and, +more importantly, why. In GitLab, you can add keywords to the commit +message that will perform one of the actions below: + - **Trigger a GitLab CI/CD pipeline:** + If you have your project configured with [GitLab CI/CD](../../../ci/README.md), + you will trigger a pipeline per push, not per commit. + - **Skip pipelines:** + You can add to you commit message the keyword + [`[ci skip]`](../../../ci/yaml/README.html#skipping-jobs) + and GitLab CI will skip that pipeline. + - **Cross-link issues and merge requests:** + [Cross-linking](../issues/crosslinking_issues.md#from-commit-messages) + is great to keep track of what's is somehow related in your workflow. + If you mention an issue or a merge request in a commit message, they will be shown + on their respective thread. +- **Cherry-pick a commit:** +In GitLab, you can +[cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-picking-a-commit) +right from the UI. +- **Revert a commit:** +Easily [revert a commit](../merge_requests/revert_changes.md#reverting-a-commit) +from the UI to a selected branch. + +## Repository size + +In GitLab.com, your repository size limit it 10GB. For other instances, +the repository size is limited by your system administrators. + +You can also [reduce a repository size using Git](reducing_the_repo_size_using_git.md). + +## Contributors + +All the contributors to your codebase are displayed under your project's **Settings > Contributors**. + +They are ordered from the collaborator with the greatest number +of commits to the fewest, and displayed on a nice graph: + +![contributors to code](img/contributors_graph.png) + +## Repository graph + +The repository graph displays visually the Git flow strategy used in that repository: + +![repository Git flow](img/repo_graph.png) + +Find it under your project's **Repository > Graph**. + +## Compare + +Select branches to compare and view the changes inline: + +![compare branches](img/compare_branches.png) + +Find it under your project's **Repository > Compare**. + +## Locked files (EEP) + +Lock your files to prevent any conflicting changes. + +[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in +[GitLab Enterprise Edition Premium](https://about.gitlab.com/gitlab-ee/). + +## Repository's API + +You can access your repos via [repository API](../../../api/repositories.md). diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md new file mode 100644 index 00000000000..08805a4dc99 --- /dev/null +++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md @@ -0,0 +1,81 @@ +# Reducing the repository size using Git + +A GitLab Entrerprise Edition administrator can set a [repository size limit][admin-repo-size] +which will prevent you to exceed it. + +When a project has reached its size limit, you will not be able to push to it, +create a new merge request, or merge existing ones. You will still be able to +create new issues, and clone the project though. Uploading LFS objects will +also be denied. + +In order to lift these restrictions, the administrator of the GitLab instance +needs to increase the limit on the particular project that exceeded it or you +need to instruct Git to rewrite changes. + +If you exceed the repository size limit, your first thought might be to remove +some data, make a new commit and push back to the repository. Unfortunately, +it's not so easy and that workflow won't work. Deleting files in a commit doesn't +actually reduce the size of the repo since the earlier commits and blobs are +still around. What you need to do is rewrite history with Git's +[`filter-branch` option][gitscm]. + +Note that even with that method, until `git gc` runs on the GitLab side, the +"removed" commits and blobs will still be around. And if a commit was ever +included in an MR, or if a build was run for a commit, or if a user commented +on it, it will be kept around too. So, in these cases the size will not decrease. + +The only fool proof way to actually decrease the repository size is to prune all +the unneeded stuff locally, and then create a new project on GitLab and start +using that instead. + +With that being said, you can try reducing your repository size with the +following method. + +## Using `git filter-branch` to purge files + +> +**Warning:** +Make sure to first make a copy of your repository since rewriting history will +purge the files and information you are about to delete. Also make sure to +inform any collaborators to not use `pull` after your changes, but use `rebase`. + +1. Navigate to your repository: + + ``` + cd my_repository/ + ``` + +1. Change to the branch you want to remove the big file from: + + ``` + git checkout master + ``` + +1. Use `filter-branch` to remove the big file: + + ``` + git filter-branch --force --tree-filter 'rm -f path/to/big_file.mpg' HEAD + ``` + +1. Instruct Git to purge the unwanted data: + + ``` + git reflog expire --expire=now --all && git gc --prune=now --aggressive + ``` + +1. Lastly, force push to the repository: + + ``` + git push --force origin master + ``` + +Your repository should now be below the size limit. + +>**Note:** +As an alternative to `filter-branch`, you can use the `bfg` tool with a +command like: `bfg --delete-files path/to/big_file.mpg`. Read the +[BFG Repo-Cleaner][bfg] documentation for more information. + +[admin-repo-size]: https://docs.gitlab.com/ee/user/admin_area/settings/account_and_limit_settings.html#repository-size-limit +[bfg]: https://rtyley.github.io/bfg-repo-cleaner/ +[gitscm]: https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#The-Nuclear-Option:-filter-branch diff --git a/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys.png b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys.png Binary files differnew file mode 100644 index 00000000000..e525083918b --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys.png diff --git a/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png Binary files differnew file mode 100644 index 00000000000..8e26d98f1b0 --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_paste_pub.png diff --git a/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png Binary files differnew file mode 100644 index 00000000000..f715c46adc3 --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/profile_settings_gpg_keys_single_key.png diff --git a/doc/workflow/gpg_signed_commits/img/project_signed_and_unsigned_commits.png b/doc/workflow/gpg_signed_commits/img/project_signed_and_unsigned_commits.png Binary files differnew file mode 100644 index 00000000000..16ec2d031ae --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/project_signed_and_unsigned_commits.png diff --git a/doc/workflow/gpg_signed_commits/img/project_signed_commit_unverified_signature.png b/doc/workflow/gpg_signed_commits/img/project_signed_commit_unverified_signature.png Binary files differnew file mode 100644 index 00000000000..22565cf7c7e --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/project_signed_commit_unverified_signature.png diff --git a/doc/workflow/gpg_signed_commits/img/project_signed_commit_verified_signature.png b/doc/workflow/gpg_signed_commits/img/project_signed_commit_verified_signature.png Binary files differnew file mode 100644 index 00000000000..1778b2ddf2b --- /dev/null +++ b/doc/workflow/gpg_signed_commits/img/project_signed_commit_verified_signature.png diff --git a/doc/workflow/gpg_signed_commits/index.md b/doc/workflow/gpg_signed_commits/index.md new file mode 100644 index 00000000000..7d5762d2b9d --- /dev/null +++ b/doc/workflow/gpg_signed_commits/index.md @@ -0,0 +1,84 @@ +# Signing commits with GPG + +## Getting started + +- [Git Tools - Signing Your Work](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work) +- [Git Tools - Signing Your Work: GPG introduction](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work#_gpg_introduction) +- [Git Tools - Signing Your Work: Signing commits](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work#_signing_commits) + +## How GitLab handles GPG + +GitLab uses its own keyring to verify the GPG signature. It does not access any +public key server. + +In order to have a commit verified on GitLab the corresponding public key needs +to be uploaded to GitLab. + +For a signature to be verified two prerequisites need to be met: + +1. The public key needs to be added to GitLab +1. One of the emails in the GPG key matches your **primary** email + +## Add a GPG key + +1. On the upper right corner, click on your avatar and go to your **Settings**. + + ![Settings dropdown](../../gitlab-basics/img/profile_settings.png) + +1. Navigate to the **GPG keys** tab. + + ![GPG Keys](img/profile_settings_gpg_keys.png) + +1. Paste your **public** key in the 'Key' box. + + ![Paste GPG public key](img/profile_settings_gpg_keys_paste_pub.png) + +1. Finally, click on **Add key** to add it to GitLab. You will be able to see + its fingerprint, the corresponding email address and creation date. + + ![GPG key single page](img/profile_settings_gpg_keys_single_key.png) + +>**Note:** +Once you add a key, you cannot edit it, only remove it. In case the paste +didn't work, you will have to remove the offending key and re-add it. + +## Remove a GPG key + +1. On the upper right corner, click on your avatar and go to your **Settings**. + +1. Navigate to the **GPG keys** tab. + +1. Click on the trash icon besides the GPG key you want to delete. + +>**Note:** +Removing a key **does not unverify** already signed commits. Commits that were +verified by using this key will stay verified. Only unpushed commits will stay +unverified once you remove this key. + +## Revoke a GPG key + +1. On the upper right corner, click on your avatar and go to your **Settings**. + +1. Navigate to the **GPG keys** tab. + +1. Click on **Revoke** besides the GPG key you want to delete. + +>**Note:** +Revoking a key **unverifies** already signed commits. Commits that were +verified by using this key will change to an unverified state. Future commits +will also stay unverified once you revoke this key. This action should be used +in case your key has been compromised. + +## Verifying commits + +1. Within a project navigate to the **Commits** tag. Signed commits will show a + badge containing either "Verified" or "Unverified", depending on the + verification status of the GPG signature. + + ![Signed and unsigned commits](img/project_signed_and_unsigned_commits.png) + +1. By clicking on the GPG badge details of the signature are displayed. + + ![Signed commit with verified signature](img/project_signed_commit_verified_signature.png) + + ![Signed commit with verified signature](img/project_signed_commit_unverified_signature.png) diff --git a/features/project/badges/build.feature b/features/project/badges/build.feature deleted file mode 100644 index bcf80ed620e..00000000000 --- a/features/project/badges/build.feature +++ /dev/null @@ -1,27 +0,0 @@ -Feature: Project Badges Build - Background: - Given I sign in as a user - And I own a project - And project has CI enabled - And project has a recent build - - Scenario: I want to see a badge for successfully built project - Given recent build is successful - When I display builds badge for a master branch - Then I should see a build success badge - - Scenario: I want to see a badge for project with failed builds - Given recent build failed - When I display builds badge for a master branch - Then I should see a build failed badge - - Scenario: I want to see a badge for project with running builds - Given recent build is successful - And project has another build that is running - When I display builds badge for a master branch - Then I should see a build running badge - - Scenario: I want to see a fresh badge on each request - Given recent build is successful - When I display builds badge for a master branch - Then I should see a badge that has not been cached diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb deleted file mode 100644 index 5a9094ee9d3..00000000000 --- a/features/steps/project/badges/build.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps - include SharedAuthentication - include SharedProject - include SharedBuilds - include RepoHelpers - - step 'I display builds badge for a master branch' do - visit build_project_badges_path(@project, ref: :master, format: :svg) - end - - step 'I should see a build success badge' do - expect_badge('success') - end - - step 'I should see a build failed badge' do - expect_badge('failed') - end - - step 'I should see a build running badge' do - expect_badge('running') - end - - step 'I should see a badge that has not been cached' do - expect(page.response_headers['Cache-Control']).to include 'no-cache' - end - - def expect_badge(status) - svg = Nokogiri::XML.parse(page.body) - expect(page.response_headers['Content-Type']).to include('image/svg+xml') - expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy - end -end diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 906a81b29b3..7e2a357f6b2 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -175,7 +175,6 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps fill_in 'JIRA API URL', with: 'http://jira.example/api' fill_in 'Username', with: 'gitlab' fill_in 'Password', with: 'gitlab' - fill_in 'Project Key', with: 'GITLAB' click_button 'Save' end diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index a2f5d2e1515..6a478c50e5e 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -63,7 +63,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'That page has two revisions' do - @page.update("new content", :markdown, "second commit") + @page.update("new content", message: "second commit") end step 'I click the History button' do @@ -114,7 +114,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'Image should be shown on the page' do - expect(page).to have_xpath("//img[@src=\"image.jpg\"]") + expect(page).to have_xpath("//img[@data-src=\"image.jpg\"]") end step 'I click on image link' do diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index c9b5f58c557..cdacf9839e5 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -68,6 +68,7 @@ module API delete ":id/access_requests/:user_id" do source = find_source(source_type, params[:id]) + status 204 ::Members::DestroyService.new(source, current_user, params) .execute(:requesters) end diff --git a/lib/api/api.rb b/lib/api/api.rb index efcf0976a81..045a0db1842 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -3,6 +3,7 @@ module API include APIGuard allow_access_with_scope :api + prefix :api version %w(v3 v4), using: :path @@ -85,6 +86,9 @@ module API helpers ::API::Helpers helpers ::API::Helpers::CommonHelpers + NO_SLASH_URL_PART_REGEX = %r{[^/]+} + PROJECT_ENDPOINT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze + # Keep in alphabetical order mount ::API::AccessRequests mount ::API::AwardEmoji @@ -109,7 +113,8 @@ module API mount ::API::Members mount ::API::MergeRequestDiffs mount ::API::MergeRequests - mount ::API::Milestones + mount ::API::ProjectMilestones + mount ::API::GroupMilestones mount ::API::Namespaces mount ::API::Notes mount ::API::NotificationSettings diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index 56f19f89642..5a028fc9d0b 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -88,6 +88,7 @@ module API unauthorized! unless award.user == current_user || current_user.admin? + status 204 award.destroy end end diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 3d816f8771d..9dd60d1833b 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -4,19 +4,21 @@ module API class Branches < Grape::API include PaginationParams + BRANCH_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX) + before { authorize! :download_code, user_project } params do requires :id, type: String, desc: 'The ID of a project' end - resource :projects, requirements: { id: %r{[^/]+} } do + resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do desc 'Get a project repository branches' do success Entities::RepoBranch end params do use :pagination end - get ":id/repository/branches" do + get ':id/repository/branches' do branches = ::Kaminari.paginate_array(user_project.repository.branches.sort_by(&:name)) present paginate(branches), with: Entities::RepoBranch, project: user_project @@ -28,7 +30,7 @@ module API params do requires :branch, type: String, desc: 'The name of the branch' end - get ':id/repository/branches/:branch', requirements: { branch: /.+/ } do + get ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do branch = user_project.repository.find_branch(params[:branch]) not_found!("Branch") unless branch @@ -46,7 +48,7 @@ module API optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch' optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch' end - put ':id/repository/branches/:branch/protect', requirements: { branch: /.+/ } do + put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -81,7 +83,7 @@ module API params do requires :branch, type: String, desc: 'The name of the branch' end - put ':id/repository/branches/:branch/unprotect', requirements: { branch: /.+/ } do + put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -99,7 +101,7 @@ module API requires :branch, type: String, desc: 'The name of the branch' requires :ref, type: String, desc: 'Create branch from commit sha or existing branch' end - post ":id/repository/branches" do + post ':id/repository/branches' do authorize_push_project result = CreateBranchService.new(user_project, current_user) @@ -118,7 +120,7 @@ module API params do requires :branch, type: String, desc: 'The name of the branch' end - delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do + delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_push_project result = DeleteBranchService.new(user_project, current_user) @@ -130,7 +132,7 @@ module API end desc 'Delete all merged branches' - delete ":id/repository/merged_branches" do + delete ':id/repository/merged_branches' do DeleteMergedBranchesService.new(user_project, current_user).async_execute accepted! diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb index 395c401203c..9980aec4752 100644 --- a/lib/api/broadcast_messages.rb +++ b/lib/api/broadcast_messages.rb @@ -91,6 +91,7 @@ module API delete ':id' do message = find_message + status 204 message.destroy end end diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb index d5c2f3d5094..42e7c1486b0 100644 --- a/lib/api/deploy_keys.rb +++ b/lib/api/deploy_keys.rb @@ -125,6 +125,7 @@ module API key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) not_found!('Deploy Key') unless key + status 204 key.destroy end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 09a88869063..ce25be34ec4 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -82,6 +82,38 @@ module API end class Project < Grape::Entity + include ::API::Helpers::RelatedResourcesHelpers + + expose :_links do + expose :self do |project| + expose_url(api_v4_projects_path(id: project.id)) + end + + expose :issues, if: -> (*args) { issues_available?(*args) } do |project| + expose_url(api_v4_projects_issues_path(id: project.id)) + end + + expose :merge_requests, if: -> (*args) { mrs_available?(*args) } do |project| + expose_url(api_v4_projects_merge_requests_path(id: project.id)) + end + + expose :repo_branches do |project| + expose_url(api_v4_projects_repository_branches_path(id: project.id)) + end + + expose :labels do |project| + expose_url(api_v4_projects_labels_path(id: project.id)) + end + + expose :events do |project| + expose_url(api_v4_projects_events_path(id: project.id)) + end + + expose :members do |project| + expose_url(api_v4_projects_members_path(id: project.id)) + end + end + expose :id, :description, :default_branch, :tag_list expose :archived?, as: :archived expose :visibility, :ssh_url_to_repo, :http_url_to_repo, :web_url @@ -109,7 +141,7 @@ module API user.avatar_url(only_path: false) end expose :star_count, :forks_count - expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } + expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :public_builds, as: :public_jobs expose :ci_config_path @@ -269,8 +301,8 @@ module API class Milestone < Grape::Entity expose :id, :iid - expose(:project_id) { |entity| entity&.project_id } - expose(:group_id) { |entity| entity&.group_id } + expose :project_id, if: -> (entity, options) { entity&.project_id } + expose :group_id, if: -> (entity, options) { entity&.group_id } expose :title, :description expose :state, :created_at, :updated_at expose :due_date @@ -297,6 +329,26 @@ module API end class Issue < IssueBasic + include ::API::Helpers::RelatedResourcesHelpers + + expose :_links do + expose :self do |issue| + expose_url(api_v4_project_issue_path(id: issue.project_id, issue_iid: issue.iid)) + end + + expose :notes do |issue| + expose_url(api_v4_projects_issues_notes_path(id: issue.project_id, noteable_id: issue.iid)) + end + + expose :award_emoji do |issue| + expose_url(api_v4_projects_issues_award_emoji_path(id: issue.project_id, issue_iid: issue.iid)) + end + + expose :project do |issue| + expose_url(api_v4_projects_path(id: issue.project_id)) + end + end + expose :subscribed do |issue, options| issue.subscribed?(options[:current_user], options[:project] || issue.project) end @@ -619,43 +671,14 @@ module API class ApplicationSetting < Grape::Entity expose :id - expose :default_projects_limit - expose :signup_enabled - expose :password_authentication_enabled - expose :password_authentication_enabled, as: :signin_enabled - expose :gravatar_enabled - expose :sign_in_text - expose :after_sign_up_text - expose :created_at - expose :updated_at - expose :home_page_url - expose :default_branch_protection + expose(*::ApplicationSettingsHelper.visible_attributes) expose(:restricted_visibility_levels) do |setting, _options| setting.restricted_visibility_levels.map { |level| Gitlab::VisibilityLevel.string_level(level) } end - expose :max_attachment_size - expose :session_expire_delay expose(:default_project_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_project_visibility) } expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) } expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) } - expose :default_artifacts_expire_in - expose :domain_whitelist - expose :domain_blacklist_enabled - expose :domain_blacklist - expose :user_oauth_applications - expose :after_sign_out_path - expose :container_registry_token_expire_delay - expose :repository_storage - expose :repository_storages - expose :koding_enabled - expose :koding_url - expose :plantuml_enabled - expose :plantuml_url - expose :terminal_max_session_time - expose :polling_interval_multiplier - expose :help_page_hide_commercial_content - expose :help_page_text - expose :help_page_support_url + expose :password_authentication_enabled, as: :signin_enabled end class Release < Grape::Entity diff --git a/lib/api/environments.rb b/lib/api/environments.rb index 945771d46f3..c774a5c6685 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -79,6 +79,7 @@ module API environment = user_project.environments.find(params[:environment_id]) + status 204 environment.destroy end diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb new file mode 100644 index 00000000000..b85eb59dc0a --- /dev/null +++ b/lib/api/group_milestones.rb @@ -0,0 +1,85 @@ +module API + class GroupMilestones < Grape::API + include MilestoneResponses + include PaginationParams + + before do + authenticate! + end + + params do + requires :id, type: String, desc: 'The ID of a group' + end + resource :groups, requirements: { id: %r{[^/]+} } do + desc 'Get a list of group milestones' do + success Entities::Milestone + end + params do + use :list_params + end + get ":id/milestones" do + list_milestones_for(user_group) + end + + desc 'Get a single group milestone' do + success Entities::Milestone + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a group milestone' + end + get ":id/milestones/:milestone_id" do + authorize! :read_group, user_group + + get_milestone_for(user_group) + end + + desc 'Create a new group milestone' do + success Entities::Milestone + end + params do + requires :title, type: String, desc: 'The title of the milestone' + use :optional_params + end + post ":id/milestones" do + authorize! :admin_milestones, user_group + + create_milestone_for(user_group) + end + + desc 'Update an existing group milestone' do + success Entities::Milestone + end + params do + use :update_params + end + put ":id/milestones/:milestone_id" do + authorize! :admin_milestones, user_group + + update_milestone_for(user_group) + end + + desc 'Get all issues for a single group milestone' do + success Entities::IssueBasic + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a group milestone' + use :pagination + end + get ":id/milestones/:milestone_id/issues" do + milestone_issuables_for(user_group, :issue) + end + + desc 'Get all merge requests for a single group milestone' do + detail 'This feature was introduced in GitLab 9.' + success Entities::MergeRequestBasic + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a group milestone' + use :pagination + end + get ':id/milestones/:milestone_id/merge_requests' do + milestone_issuables_for(user_group, :merge_request) + end + end + end +end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index ebbaed0cbb7..49c3b2278c7 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -125,6 +125,8 @@ module API delete ":id" do group = find_group!(params[:id]) authorize! :admin_group, group + + status 204 ::Groups::DestroyService.new(group, current_user).execute end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 0f4791841d2..234825480f2 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -25,6 +25,10 @@ module API initial_current_user != current_user end + def user_group + @group ||= find_group!(params[:id]) + end + def user_project @project ||= find_project!(params[:id]) end @@ -332,12 +336,14 @@ module API env['warden'] end + # Check if the request is GET/HEAD, or if CSRF token is valid. + def verified_request? + Gitlab::RequestForgeryProtection.verified?(env) + end + # Check the Rails session for valid authentication details - # - # Until CSRF protection is added to the API, disallow this method for - # state-changing endpoints def find_user_from_warden - warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD']) + warden.try(:authenticate) if verified_request? end def initial_current_user diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb new file mode 100644 index 00000000000..769cc1457fc --- /dev/null +++ b/lib/api/helpers/related_resources_helpers.rb @@ -0,0 +1,28 @@ +module API + module Helpers + module RelatedResourcesHelpers + include GrapeRouteHelpers::NamedRouteMatcher + + def issues_available?(project, options) + available?(:issues, project, options[:current_user]) + end + + def mrs_available?(project, options) + available?(:merge_requests, project, options[:current_user]) + end + + def expose_url(path) + url_options = Rails.application.routes.default_url_options + protocol, host, port = url_options.slice(:protocol, :host, :port).values + + URI::HTTP.build(scheme: protocol, host: host, port: port, path: path).to_s + end + + private + + def available?(feature, project, current_user) + project.feature_available?(feature, current_user) + end + end + end +end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 64be08094ed..4cec1145f3a 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -29,6 +29,10 @@ module API optional :search, type: String, desc: 'Search issues for text present in the title or description' optional :created_after, type: DateTime, desc: 'Return issues created after the specified time' optional :created_before, type: DateTime, desc: 'Return issues created before the specified time' + optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' + optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID' + optional :scope, type: String, values: %w[created-by-me assigned-to-me all], + desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`' use :pagination end @@ -55,9 +59,11 @@ module API optional :state, type: String, values: %w[opened closed all], default: 'all', desc: 'Return opened, closed, or all issues' use :issues_params + optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me', + desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`' end get do - issues = find_issues(scope: 'authored') + issues = find_issues present paginate(issues), with: Entities::IssueBasic, current_user: current_user end @@ -112,7 +118,7 @@ module API params do requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' end - get ":id/issues/:issue_iid" do + get ":id/issues/:issue_iid", as: :api_v4_project_issue do issue = find_project_issue(params[:issue_iid]) present issue, with: Entities::Issue, current_user: current_user, project: user_project end @@ -224,6 +230,7 @@ module API not_found!('Issue') unless issue authorize!(:destroy_issue, issue) + status 204 issue.destroy end diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 20b25529d0c..4520c98d951 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -56,6 +56,7 @@ module API label = user_project.labels.find_by(title: params[:name]) not_found!('Label') unless label + status 204 label.destroy end diff --git a/lib/api/members.rb b/lib/api/members.rb index c200e46a328..bb970b7cd54 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -96,6 +96,7 @@ module API # Ensure that memeber exists source.members.find_by!(user_id: params[:user_id]) + status 204 ::Members::DestroyService.new(source, current_user, declared_params).execute end end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index ac33b2b801c..8810d4e441d 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -4,14 +4,77 @@ module API before { authenticate! } + helpers ::Gitlab::IssuableMetadata + + helpers do + def find_merge_requests(args = {}) + args = params.merge(args) + + args[:milestone_title] = args.delete(:milestone) + args[:label_name] = args.delete(:labels) + + merge_requests = MergeRequestsFinder.new(current_user, args).execute + .reorder(args[:order_by] => args[:sort]) + merge_requests = paginate(merge_requests) + .preload(:target_project) + + return merge_requests if args[:view] == 'simple' + + merge_requests + .preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels) + end + + params :merge_requests_params do + optional :state, type: String, values: %w[opened closed merged all], default: 'all', + desc: 'Return opened, closed, merged, or all merge requests' + optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', + desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return merge requests sorted in `asc` or `desc` order.' + optional :milestone, type: String, desc: 'Return merge requests for a specific milestone' + optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time' + optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time' + optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' + optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID' + optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID' + optional :scope, type: String, values: %w[created-by-me assigned-to-me all], + desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' + use :pagination + end + end + + resource :merge_requests do + desc 'List merge requests' do + success Entities::MergeRequestBasic + end + params do + use :merge_requests_params + optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me', + desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' + end + get do + merge_requests = find_merge_requests + + options = { with: Entities::MergeRequestBasic, + current_user: current_user } + + if params[:view] == 'simple' + options[:with] = Entities::MergeRequestSimple + else + options[:issuable_metadata] = issuable_meta_data(merge_requests, 'MergeRequest') + end + + present merge_requests, options + end + end + params do requires :id, type: String, desc: 'The ID of a project' end resource :projects, requirements: { id: %r{[^/]+} } do include TimeTrackingEndpoints - helpers ::Gitlab::IssuableMetadata - helpers do def handle_merge_request_errors!(errors) if errors[:project_access].any? @@ -29,31 +92,6 @@ module API render_api_error!(errors, 400) end - def issue_entity(project) - if project.has_external_issue_tracker? - Entities::ExternalIssue - else - Entities::IssueBasic - end - end - - def find_merge_requests(args = {}) - args = params.merge(args) - - args[:milestone_title] = args.delete(:milestone) - args[:label_name] = args.delete(:labels) - - merge_requests = MergeRequestsFinder.new(current_user, args).execute - .reorder(args[:order_by] => args[:sort]) - merge_requests = paginate(merge_requests) - .preload(:target_project) - - return merge_requests if args[:view] == 'simple' - - merge_requests - .preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels) - end - params :optional_params_ce do optional :description, type: String, desc: 'The description of the merge request' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' @@ -71,19 +109,8 @@ module API success Entities::MergeRequestBasic end params do - optional :state, type: String, values: %w[opened closed merged all], default: 'all', - desc: 'Return opened, closed, merged, or all merge requests' - optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', - desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.' - optional :sort, type: String, values: %w[asc desc], default: 'desc', - desc: 'Return merge requests sorted in `asc` or `desc` order.' + use :merge_requests_params optional :iids, type: Array[Integer], desc: 'The IID array of merge requests' - optional :milestone, type: String, desc: 'Return merge requests for a specific milestone' - optional :labels, type: String, desc: 'Comma-separated list of label names' - optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time' - optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time' - optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' - use :pagination end get ":id/merge_requests" do authorize! :read_merge_request, user_project @@ -137,6 +164,7 @@ module API merge_request = find_project_merge_request(params[:merge_request_iid]) authorize!(:destroy_merge_request, merge_request) + status 204 merge_request.destroy end @@ -277,7 +305,14 @@ module API get ':id/merge_requests/:merge_request_iid/closes_issues' do merge_request = find_merge_request_with_access(params[:merge_request_iid]) issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user)) - present paginate(issues), with: issue_entity(user_project), current_user: current_user + issues = paginate(issues) + + external_issues, internal_issues = issues.partition { |issue| issue.is_a?(ExternalIssue) } + + data = Entities::IssueBasic.represent(internal_issues, current_user: current_user) + data += Entities::ExternalIssue.represent(external_issues, current_user: current_user) + + data.as_json end end end diff --git a/lib/api/milestone_responses.rb b/lib/api/milestone_responses.rb new file mode 100644 index 00000000000..ef09d9505d2 --- /dev/null +++ b/lib/api/milestone_responses.rb @@ -0,0 +1,98 @@ +module API + module MilestoneResponses + extend ActiveSupport::Concern + + included do + helpers do + params :optional_params do + optional :description, type: String, desc: 'The description of the milestone' + optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)' + optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)' + end + + params :list_params do + optional :state, type: String, values: %w[active closed all], default: 'all', + desc: 'Return "active", "closed", or "all" milestones' + optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones' + optional :search, type: String, desc: 'The search criteria for the title or description of the milestone' + use :pagination + end + + params :update_params do + requires :milestone_id, type: Integer, desc: 'The milestone ID number' + optional :title, type: String, desc: 'The title of the milestone' + optional :state_event, type: String, values: %w[close activate], + desc: 'The state event of the milestone ' + use :optional_params + at_least_one_of :title, :description, :due_date, :state_event + end + + def list_milestones_for(parent) + milestones = parent.milestones + milestones = Milestone.filter_by_state(milestones, params[:state]) + milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present? + milestones = filter_by_search(milestones, params[:search]) if params[:search] + + present paginate(milestones), with: Entities::Milestone + end + + def get_milestone_for(parent) + milestone = parent.milestones.find(params[:milestone_id]) + present milestone, with: Entities::Milestone + end + + def create_milestone_for(parent) + milestone = ::Milestones::CreateService.new(parent, current_user, declared_params).execute + + if milestone.valid? + present milestone, with: Entities::Milestone + else + render_api_error!("Failed to create milestone #{milestone.errors.messages}", 400) + end + end + + def update_milestone_for(parent) + milestone = parent.milestones.find(params.delete(:milestone_id)) + + milestone_params = declared_params(include_missing: false) + milestone = ::Milestones::UpdateService.new(parent, current_user, milestone_params).execute(milestone) + + if milestone.valid? + present milestone, with: Entities::Milestone + else + render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400) + end + end + + def milestone_issuables_for(parent, type) + milestone = parent.milestones.find(params[:milestone_id]) + + finder_klass, entity = get_finder_and_entity(type) + + params = build_finder_params(milestone, parent) + + issuables = finder_klass.new(current_user, params).execute + present paginate(issuables), with: entity, current_user: current_user + end + + def build_finder_params(milestone, parent) + finder_params = { milestone_title: milestone.title, sort: 'label_priority' } + + if parent.is_a?(Group) + finder_params.merge(group_id: parent.id) + else + finder_params.merge(project_id: parent.id) + end + end + + def get_finder_and_entity(type) + if type == :issue + [IssuesFinder, Entities::IssueBasic] + else + [MergeRequestsFinder, Entities::MergeRequestBasic] + end + end + end + end + end +end diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb deleted file mode 100644 index 3541d3c95fb..00000000000 --- a/lib/api/milestones.rb +++ /dev/null @@ -1,154 +0,0 @@ -module API - class Milestones < Grape::API - include PaginationParams - - before { authenticate! } - - helpers do - def filter_milestones_state(milestones, state) - case state - when 'active' then milestones.active - when 'closed' then milestones.closed - else milestones - end - end - - params :optional_params do - optional :description, type: String, desc: 'The description of the milestone' - optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)' - optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)' - end - end - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects, requirements: { id: %r{[^/]+} } do - desc 'Get a list of project milestones' do - success Entities::Milestone - end - params do - optional :state, type: String, values: %w[active closed all], default: 'all', - desc: 'Return "active", "closed", or "all" milestones' - optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones' - optional :search, type: String, desc: 'The search criteria for the title or description of the milestone' - use :pagination - end - get ":id/milestones" do - authorize! :read_milestone, user_project - - milestones = user_project.milestones - milestones = filter_milestones_state(milestones, params[:state]) - milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present? - milestones = filter_by_search(milestones, params[:search]) if params[:search] - - present paginate(milestones), with: Entities::Milestone - end - - desc 'Get a single project milestone' do - success Entities::Milestone - end - params do - requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' - end - get ":id/milestones/:milestone_id" do - authorize! :read_milestone, user_project - - milestone = user_project.milestones.find(params[:milestone_id]) - present milestone, with: Entities::Milestone - end - - desc 'Create a new project milestone' do - success Entities::Milestone - end - params do - requires :title, type: String, desc: 'The title of the milestone' - use :optional_params - end - post ":id/milestones" do - authorize! :admin_milestone, user_project - - milestone = ::Milestones::CreateService.new(user_project, current_user, declared_params).execute - - if milestone.valid? - present milestone, with: Entities::Milestone - else - render_api_error!("Failed to create milestone #{milestone.errors.messages}", 400) - end - end - - desc 'Update an existing project milestone' do - success Entities::Milestone - end - params do - requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' - optional :title, type: String, desc: 'The title of the milestone' - optional :state_event, type: String, values: %w[close activate], - desc: 'The state event of the milestone ' - use :optional_params - at_least_one_of :title, :description, :due_date, :state_event - end - put ":id/milestones/:milestone_id" do - authorize! :admin_milestone, user_project - milestone = user_project.milestones.find(params.delete(:milestone_id)) - - milestone_params = declared_params(include_missing: false) - milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone) - - if milestone.valid? - present milestone, with: Entities::Milestone - else - render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400) - end - end - - desc 'Get all issues for a single project milestone' do - success Entities::IssueBasic - end - params do - requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' - use :pagination - end - get ":id/milestones/:milestone_id/issues" do - authorize! :read_milestone, user_project - - milestone = user_project.milestones.find(params[:milestone_id]) - - finder_params = { - project_id: user_project.id, - milestone_title: milestone.title, - sort: 'label_priority' - } - - issues = IssuesFinder.new(current_user, finder_params).execute - present paginate(issues), with: Entities::IssueBasic, current_user: current_user, project: user_project - end - - desc 'Get all merge requests for a single project milestone' do - detail 'This feature was introduced in GitLab 9.' - success Entities::MergeRequestBasic - end - params do - requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' - use :pagination - end - get ':id/milestones/:milestone_id/merge_requests' do - authorize! :read_milestone, user_project - - milestone = user_project.milestones.find(params[:milestone_id]) - - finder_params = { - project_id: user_project.id, - milestone_title: milestone.title, - sort: 'label_priority' - } - - merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute - present paginate(merge_requests), - with: Entities::MergeRequestBasic, - current_user: current_user, - project: user_project - end - end - end -end diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 01ca62b593f..65ff89edf65 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -131,6 +131,7 @@ module API note = user_project.notes.find(params[:note_id]) authorize! :admin_note, note + status 204 ::Notes::DestroyService.new(user_project, current_user).execute(note) end end diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 7a345289617..649dd891f56 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -96,6 +96,7 @@ module API delete ":id/hooks/:hook_id" do hook = user_project.hooks.find(params.delete(:hook_id)) + status 204 hook.destroy end end diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb new file mode 100644 index 00000000000..451998c726a --- /dev/null +++ b/lib/api/project_milestones.rb @@ -0,0 +1,91 @@ +module API + class ProjectMilestones < Grape::API + include PaginationParams + include MilestoneResponses + + before do + authenticate! + end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects, requirements: { id: %r{[^/]+} } do + desc 'Get a list of project milestones' do + success Entities::Milestone + end + params do + use :list_params + end + get ":id/milestones" do + authorize! :read_milestone, user_project + + list_milestones_for(user_project) + end + + desc 'Get a single project milestone' do + success Entities::Milestone + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + end + get ":id/milestones/:milestone_id" do + authorize! :read_milestone, user_project + + get_milestone_for(user_project) + end + + desc 'Create a new project milestone' do + success Entities::Milestone + end + params do + requires :title, type: String, desc: 'The title of the milestone' + use :optional_params + end + post ":id/milestones" do + authorize! :admin_milestone, user_project + + create_milestone_for(user_project) + end + + desc 'Update an existing project milestone' do + success Entities::Milestone + end + params do + use :update_params + end + put ":id/milestones/:milestone_id" do + authorize! :admin_milestone, user_project + + update_milestone_for(user_project) + end + + desc 'Get all issues for a single project milestone' do + success Entities::IssueBasic + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + use :pagination + end + get ":id/milestones/:milestone_id/issues" do + authorize! :read_milestone, user_project + + milestone_issuables_for(user_project, :issue) + end + + desc 'Get all merge requests for a single project milestone' do + detail 'This feature was introduced in GitLab 9.' + success Entities::MergeRequestBasic + end + params do + requires :milestone_id, type: Integer, desc: 'The ID of a project milestone' + use :pagination + end + get ':id/milestones/:milestone_id/merge_requests' do + authorize! :read_milestone, user_project + + milestone_issuables_for(user_project, :merge_request) + end + end + end +end diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 3320eadff0d..f3d905b0068 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -116,6 +116,7 @@ module API not_found!('Snippet') unless snippet authorize! :admin_project_snippet, snippet + status 204 snippet.destroy end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index c459257158d..89dda88d3f5 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -361,6 +361,7 @@ module API authorize! :remove_fork_project, user_project if user_project.forked? + status 204 user_project.forked_project_link.destroy else not_modified! @@ -405,6 +406,7 @@ module API link = user_project.project_group_links.find_by(group_id: params[:group_id]) not_found!('Group Link') unless link + status 204 link.destroy end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 4552115b3e2..405d25ca3c1 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -45,6 +45,7 @@ module API end delete '/' do authenticate_runner! + status 204 Ci::Runner.find_by_token(params[:token]).destroy end diff --git a/lib/api/runners.rb b/lib/api/runners.rb index db6c7c59092..5bf5a18e42f 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -79,6 +79,7 @@ module API runner = get_runner(params[:id]) authenticate_delete_runner!(runner) + status 204 runner.destroy! end end @@ -134,6 +135,7 @@ module API runner = runner_project.runner forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1 + status 204 runner_project.destroy end end diff --git a/lib/api/services.rb b/lib/api/services.rb index 7488f95a9b7..843c05ae32e 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -313,12 +313,6 @@ module API desc: 'The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., https://jira-api.example.com' }, { - required: true, - name: :project_key, - type: String, - desc: 'The short identifier for your JIRA project, all uppercase, e.g., PROJ' - }, - { required: false, name: :username, type: String, diff --git a/lib/api/settings.rb b/lib/api/settings.rb index b19095d1252..d55a61fa638 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -20,59 +20,6 @@ module API success Entities::ApplicationSetting end params do - # CE - at_least_one_of_ce = [ - :admin_notification_email, - :after_sign_out_path, - :after_sign_up_text, - :akismet_enabled, - :container_registry_token_expire_delay, - :default_artifacts_expire_in, - :default_branch_protection, - :default_group_visibility, - :default_project_visibility, - :default_projects_limit, - :default_snippet_visibility, - :disabled_oauth_sign_in_sources, - :domain_blacklist_enabled, - :domain_whitelist, - :email_author_in_body, - :enabled_git_access_protocol, - :gravatar_enabled, - :help_page_hide_commercial_content, - :help_page_text, - :help_page_support_url, - :home_page_url, - :housekeeping_enabled, - :html_emails_enabled, - :import_sources, - :koding_enabled, - :max_artifacts_size, - :max_attachment_size, - :max_pages_size, - :metrics_enabled, - :plantuml_enabled, - :polling_interval_multiplier, - :recaptcha_enabled, - :repository_checks_enabled, - :repository_storage, - :require_two_factor_authentication, - :restricted_visibility_levels, - :send_user_confirmation_email, - :sentry_enabled, - :clientside_sentry_enabled, - :session_expire_delay, - :shared_runners_enabled, - :sidekiq_throttling_enabled, - :sign_in_text, - :password_authentication_enabled, - :signin_enabled, - :signup_enabled, - :terminal_max_session_time, - :user_default_external, - :user_oauth_applications, - :version_check_enabled - ] optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master' optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility' optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility' @@ -151,7 +98,7 @@ module API given clientside_sentry_enabled: ->(val) { val } do requires :clientside_sentry_dsn, type: String, desc: 'Clientside Sentry Data Source Name' end - optional :repository_storage, type: String, desc: 'Storage paths for new projects' + optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects' optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues." optional :koding_enabled, type: Boolean, desc: 'Enable Koding' given koding_enabled: ->(val) { val } do @@ -174,7 +121,8 @@ module API optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.' - at_least_one_of(*at_least_one_of_ce) + optional(*::ApplicationSettingsHelper.visible_attributes) + at_least_one_of(*::ApplicationSettingsHelper.visible_attributes) end put "application/settings" do attrs = declared_params(include_missing: false) diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index fd634037a77..35ece56c65c 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -123,6 +123,7 @@ module API authorize! :destroy_personal_snippet, snippet + status 204 snippet.destroy end diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index ed7b23b474a..c0179037440 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -66,6 +66,7 @@ module API hook = SystemHook.find_by(id: params[:id]) not_found!('System hook') unless hook + status 204 hook.destroy end end diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index a9f2ca2608e..edfdb63d183 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -15,24 +15,22 @@ module API optional :variables, type: Hash, desc: 'The list of variables to be injected into build' end post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do - project = find_project(params[:id]) - trigger = Ci::Trigger.find_by_token(params[:token].to_s) - not_found! unless project && trigger - unauthorized! unless trigger.project == project - # validate variables - variables = params[:variables].to_h - unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + params[:variables] = params[:variables].to_h + unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) } render_api_error!('variables needs to be a map of key-valued strings', 400) end - # create request and trigger builds - trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) - if trigger_request - present trigger_request.pipeline, with: Entities::Pipeline + project = find_project(params[:id]) + not_found! unless project + + result = Ci::PipelineTriggerService.new(project, nil, params).execute + not_found! unless result + + if result[:http_status] + render_api_error!(result[:message], result[:http_status]) else - errors = 'No pipeline created' - render_api_error!(errors, 400) + present result[:pipeline], with: Entities::Pipeline end end @@ -142,6 +140,7 @@ module API trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger + status 204 trigger.destroy end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 81c68ea2658..a590f2692a2 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -235,6 +235,7 @@ module API key = user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key + status 204 key.destroy end @@ -306,6 +307,7 @@ module API user = User.find_by(id: params[:id]) not_found!('User') unless user + status 204 user.delete_async(deleted_by: current_user, params: params) end @@ -406,6 +408,7 @@ module API requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token' end delete ':impersonation_token_id' do + status 204 find_impersonation_token.revoke! end end @@ -483,6 +486,7 @@ module API key = current_user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key + status 204 key.destroy end @@ -534,6 +538,7 @@ module API email = current_user.emails.find_by(id: params[:email_id]) not_found!('Email') unless email + status 204 Emails::DestroyService.new(current_user, email: email.email).execute end diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb index 3759250f7f6..773f667abe0 100644 --- a/lib/api/v3/entities.rb +++ b/lib/api/v3/entities.rb @@ -259,11 +259,40 @@ module API expose :job_events, as: :build_events end - class Issue < ::API::Entities::Issue + class ProjectEntity < Grape::Entity + expose :id, :iid + expose(:project_id) { |entity| entity&.project.try(:id) } + expose :title, :description + expose :state, :created_at, :updated_at + end + + class IssueBasic < ProjectEntity + expose :label_names, as: :labels + expose :milestone, using: ::API::Entities::Milestone + expose :assignees, :author, using: ::API::Entities::UserBasic + + expose :assignee, using: ::API::Entities::UserBasic do |issue, options| + issue.assignees.first + end + + expose :user_notes_count + expose :upvotes, :downvotes + expose :due_date + expose :confidential + + expose :web_url do |issue, options| + Gitlab::UrlBuilder.build(issue) + end + end + + class Issue < IssueBasic unexpose :assignees expose :assignee do |issue, options| ::API::Entities::UserBasic.represent(issue.assignees.first, options) end + expose :subscribed do |issue, options| + issue.subscribed?(options[:current_user], options[:project] || issue.project) + end end end end diff --git a/lib/api/v3/triggers.rb b/lib/api/v3/triggers.rb index a23d6b6b48c..e9d4c35307b 100644 --- a/lib/api/v3/triggers.rb +++ b/lib/api/v3/triggers.rb @@ -28,12 +28,13 @@ module API end # create request and trigger builds - trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) - if trigger_request - present trigger_request, with: ::API::V3::Entities::TriggerRequest + result = Ci::CreateTriggerRequestService.execute(project, trigger, params[:ref].to_s, variables) + pipeline = result.pipeline + + if pipeline.persisted? + present result.trigger_request, with: ::API::V3::Entities::TriggerRequest else - errors = 'No builds created' - render_api_error!(errors, 400) + render_validation_error!(pipeline) end end diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 7fa528fb2d3..7c0fdd3d1be 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -88,6 +88,7 @@ module API variable = user_project.variables.find_by(key: params[:key]) not_found!('Variable') unless variable + status 204 variable.destroy end end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index f755c99ea4a..ca6d6848d41 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -8,18 +8,9 @@ module Backup # Make sure there is a connection ActiveRecord::Base.connection.reconnect! - # saving additional informations - s = {} - s[:db_version] = "#{ActiveRecord::Migrator.current_version}" - s[:backup_created_at] = Time.now - s[:gitlab_version] = Gitlab::VERSION - s[:tar_version] = tar_version - s[:skipped] = ENV["SKIP"] - tar_file = "#{s[:backup_created_at].strftime('%s_%Y_%m_%d_')}#{s[:gitlab_version]}#{FILE_NAME_SUFFIX}" - Dir.chdir(backup_path) do File.open("#{backup_path}/backup_information.yml", "w+") do |file| - file << s.to_yaml.gsub(/^---\n/, '') + file << backup_information.to_yaml.gsub(/^---\n/, '') end # create archive @@ -33,11 +24,11 @@ module Backup abort 'Backup failed' end - upload(tar_file) + upload end end - def upload(tar_file) + def upload $progress.print "Uploading backup archive to remote storage #{remote_directory} ... " connection_settings = Gitlab.config.backup.upload.connection @@ -48,7 +39,7 @@ module Backup directory = connect_to_remote_directory(connection_settings) - if directory.files.create(key: tar_file, body: File.open(tar_file), public: false, + if directory.files.create(key: remote_target, body: File.open(tar_file), public: false, multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, encryption: Gitlab.config.backup.upload.encryption, storage_class: Gitlab.config.backup.upload.storage_class) @@ -177,7 +168,8 @@ module Backup end def connect_to_remote_directory(connection_settings) - connection = ::Fog::Storage.new(connection_settings) + # our settings use string keys, but Fog expects symbols + connection = ::Fog::Storage.new(connection_settings.symbolize_keys) # We only attempt to create the directory for local backups. For AWS # and other cloud providers, we cannot guarantee the user will have @@ -193,6 +185,14 @@ module Backup Gitlab.config.backup.upload.remote_directory end + def remote_target + if ENV['DIRECTORY'] + File.join(ENV['DIRECTORY'], tar_file) + else + tar_file + end + end + def backup_contents folders_to_backup + archives_to_backup + ["backup_information.yml"] end @@ -214,5 +214,19 @@ module Backup def settings @settings ||= YAML.load_file("backup_information.yml") end + + def tar_file + @tar_file ||= "#{backup_information[:backup_created_at].strftime('%s_%Y_%m_%d_')}#{backup_information[:gitlab_version]}#{FILE_NAME_SUFFIX}" + end + + def backup_information + @backup_information ||= { + db_version: ActiveRecord::Migrator.current_version.to_s, + backup_created_at: Time.now, + gitlab_version: Gitlab::VERSION, + tar_version: tar_version, + skipped: ENV["SKIP"] + } + end end end diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index 0ea4eeaed5b..2e259904673 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -118,7 +118,7 @@ module Banzai end if path - content_tag(:img, nil, src: path, class: 'gfm') + content_tag(:img, nil, data: { src: path }, class: 'gfm') end end diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb new file mode 100644 index 00000000000..7a81d583b82 --- /dev/null +++ b/lib/banzai/filter/image_lazy_load_filter.rb @@ -0,0 +1,16 @@ +module Banzai + module Filter + # HTML filter that moves the value of the src attribute to the data-src attribute so it can be lazy loaded + class ImageLazyLoadFilter < HTML::Pipeline::Filter + def call + doc.xpath('descendant-or-self::img').each do |img| + img['class'] ||= '' << 'lazy' + img['data-src'] = img['src'] + img['src'] = LazyImageTagHelper.placeholder_image + end + + doc + end + end + end +end diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index 123c92fd250..f318c425962 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -10,7 +10,7 @@ module Banzai link = doc.document.create_element( 'a', class: 'no-attachment-icon', - href: img['src'], + href: img['data-src'] || img['src'], target: '_blank', rel: 'noopener noreferrer' ) diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index ba1a5ac84b3..ce1ab977d3b 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -20,7 +20,7 @@ module Banzai end def url_for_object(issue, project) - IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) + IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path], internal: true) end def project_from_ref(ref) diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 9e23c8f8c55..c2fed57a0d8 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -22,6 +22,7 @@ module Banzai doc.css('img, video').each do |el| process_link_attr el.attribute('src') + process_link_attr el.attribute('data-src') end doc diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index bd4d1aa9ff8..3208abfc538 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -16,6 +16,7 @@ module Banzai Filter::MathFilter, Filter::UploadLinkFilter, Filter::VideoLinkFilter, + Filter::ImageLazyLoadFilter, Filter::ImageLinkFilter, Filter::EmojiFilter, Filter::TableOfContentsFilter, diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb index 6307c1b571a..1802cd04854 100644 --- a/lib/banzai/reference_parser/external_issue_parser.rb +++ b/lib/banzai/reference_parser/external_issue_parser.rb @@ -21,10 +21,14 @@ module Banzai gather_attributes_per_project(nodes, self.class.data_attribute) end - private - + # we extract only external issue trackers references here, we don't extract cross-project references, + # so we don't need to do anything here. def can_read_reference?(user, ref_project, node) - can?(user, :read_issue, ref_project) + true + end + + def nodes_visible_to_user(user, nodes) + nodes end end end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 6b82b2b4f13..31f66dd5a58 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -52,7 +52,7 @@ module Ci # when old API will be removed (planned for August 2017). model.options.dup.tap do |options| options[:image] = options[:image][:name] if options[:image].is_a?(Hash) - options[:services].map! do |service| + options[:services]&.map! do |service| if service.is_a?(Hash) service[:name] else diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb index 6e622601680..6225203f223 100644 --- a/lib/ci/api/triggers.rb +++ b/lib/ci/api/triggers.rb @@ -24,12 +24,13 @@ module Ci end # create request and trigger builds - trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref], variables) - if trigger_request - present trigger_request, with: Entities::TriggerRequest + result = Ci::CreateTriggerRequestService.execute(project, trigger, params[:ref], variables) + pipeline = result.pipeline + + if pipeline.persisted? + present result.trigger_request, with: Entities::TriggerRequest else - errors = 'No builds created' - render_api_error!(errors, 400) + render_validation_error!(pipeline) end end end diff --git a/lib/gitlab/badge/build/metadata.rb b/lib/gitlab/badge/pipeline/metadata.rb index 2ee35a0d4c1..db1e9f8cfb8 100644 --- a/lib/gitlab/badge/build/metadata.rb +++ b/lib/gitlab/badge/pipeline/metadata.rb @@ -1,8 +1,8 @@ module Gitlab module Badge - module Build + module Pipeline ## - # Class that describes build badge metadata + # Class that describes pipeline badge metadata # class Metadata < Badge::Metadata def initialize(badge) @@ -11,11 +11,11 @@ module Gitlab end def title - 'build status' + 'pipeline status' end def image_url - build_project_badges_url(@project, @ref, format: :svg) + pipeline_project_badges_url(@project, @ref, format: :svg) end def link_url diff --git a/lib/gitlab/badge/build/status.rb b/lib/gitlab/badge/pipeline/status.rb index b762d85b6e5..5fee7a93475 100644 --- a/lib/gitlab/badge/build/status.rb +++ b/lib/gitlab/badge/pipeline/status.rb @@ -1,8 +1,8 @@ module Gitlab module Badge - module Build + module Pipeline ## - # Build status badge + # Pipeline status badge # class Status < Badge::Base attr_reader :project, :ref @@ -15,7 +15,7 @@ module Gitlab end def entity - 'build' + 'pipeline' end def status @@ -25,11 +25,11 @@ module Gitlab end def metadata - @metadata ||= Build::Metadata.new(self) + @metadata ||= Pipeline::Metadata.new(self) end def template - @template ||= Build::Template.new(self) + @template ||= Pipeline::Template.new(self) end end end diff --git a/lib/gitlab/badge/build/template.rb b/lib/gitlab/badge/pipeline/template.rb index bc0e0cd441d..e09db32262d 100644 --- a/lib/gitlab/badge/build/template.rb +++ b/lib/gitlab/badge/pipeline/template.rb @@ -1,12 +1,13 @@ module Gitlab module Badge - module Build + module Pipeline ## - # Class that represents a build badge template. + # Class that represents a pipeline badge template. # # Template object will be passed to badge.svg.erb template. # class Template < Badge::Template + STATUS_RENAME = { 'success' => 'passed' }.freeze STATUS_COLOR = { success: '#4c1', failed: '#e05d44', @@ -27,11 +28,11 @@ module Gitlab end def value_text - @status.to_s + STATUS_RENAME[@status.to_s] || @status.to_s end def key_width - 38 + 62 end def value_width diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb index 5d6977106d6..8503ecf8700 100644 --- a/lib/gitlab/ci/trace/stream.rb +++ b/lib/gitlab/ci/trace/stream.rb @@ -67,13 +67,14 @@ module Gitlab def extract_coverage(regex) return unless valid? - return unless regex + return unless regex.present? regex = Gitlab::UntrustedRegexp.new(regex) match = "" reverse_line do |line| + line.chomp! matches = regex.scan(line) next unless matches.is_a?(Array) next if matches.empty? diff --git a/lib/gitlab/devise_failure.rb b/lib/gitlab/devise_failure.rb deleted file mode 100644 index a78fde9d782..00000000000 --- a/lib/gitlab/devise_failure.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab - class DeviseFailure < Devise::FailureApp - protected - - # Override `Devise::FailureApp#request_format` to handle a special case - # - # This tells Devise to handle an unauthenticated `.zip` request as an HTML - # request (i.e., redirect to sign in). - # - # Otherwise, Devise would respond with a 401 Unauthorized with - # `Content-Type: application/zip` and a response body in plaintext, and the - # browser would freak out. - # - # See https://gitlab.com/gitlab-org/gitlab-ce/issues/12944 - def request_format - if request.format == :zip - Mime::Type.lookup_by_extension(:html).ref - else - super - end - end - end -end diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index edd7795eef0..85e6db0a689 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -237,6 +237,10 @@ module Gitlab branch_name.parameterize << '.patch' end + def patch_url + "https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/#{ENV['CI_JOB_ID']}/artifacts/raw/ee_compat_check/patches/#{ce_patch_name}" + end + def step(desc, cmd = nil) puts "\n=> #{desc}\n" @@ -303,14 +307,11 @@ module Gitlab 2. Apply your branch's patch to EE - # In the CE repo - $ git fetch origin master - $ git diff --binary origin/master...HEAD -- > #{ce_branch}.patch - # In the EE repo $ git fetch origin master $ git checkout -b #{ee_branch_prefix} origin/master - $ git apply --3way path/to/#{ce_branch}.patch + $ wget #{patch_url} + $ git apply --3way #{ce_patch_name} At this point you might have conflicts such as: @@ -324,7 +325,7 @@ module Gitlab If the patch couldn't be applied cleanly, use the following command: # In the EE repo - $ git apply --reject path/to/#{ce_branch}.patch + $ git apply --reject #{ce_patch_name} This option makes git apply the parts of the patch that are applicable, and leave the rejected hunks in corresponding `.rej` files. diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 4175746be39..b6449f27034 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -10,7 +10,7 @@ module Gitlab include Gitlab::EncodingHelper def ref_name(ref) - encode! ref.sub(/\Arefs\/(tags|heads)\//, '') + encode! ref.sub(/\Arefs\/(tags|heads|remotes)\//, '') end def branch_name(ref) diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index 76a562f356e..ca7e3a7c4be 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -98,17 +98,13 @@ module Gitlab # Commit.between(repo, '29eda46b', 'master') # def between(repo, base, head) - commits = Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled| + Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled| if is_enabled repo.gitaly_commit_client.between(base, head) else - repo.commits_between(base, head) + repo.commits_between(base, head).map { |c| decorate(c) } end end - - commits.map do |commit| - decorate(commit) - end rescue Rugged::ReferenceError [] end @@ -135,6 +131,16 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/326 def find_all(repo, options = {}) + Gitlab::GitalyClient.migrate(:find_all_commits) do |is_enabled| + if is_enabled + find_all_by_gitaly(repo, options) + else + find_all_by_rugged(repo, options) + end + end + end + + def find_all_by_rugged(repo, options = {}) actual_options = options.dup allowed_options = [:ref, :max_count, :skip, :order] @@ -173,6 +179,10 @@ module Gitlab [] end + def find_all_by_gitaly(repo, options = {}) + Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options) + end + def decorate(commit, ref = nil) Gitlab::Git::Commit.new(commit, ref) end @@ -214,11 +224,12 @@ module Gitlab def initialize(raw_commit, head = nil) raise "Nil as raw commit passed" unless raw_commit - if raw_commit.is_a?(Hash) + case raw_commit + when Hash init_from_hash(raw_commit) - elsif raw_commit.is_a?(Rugged::Commit) + when Rugged::Commit init_from_rugged(raw_commit) - elsif raw_commit.is_a?(Gitaly::GitCommit) + when Gitlab::GitalyClient::Commit init_from_gitaly(raw_commit) else raise "Invalid raw commit type: #{raw_commit.class}" @@ -298,7 +309,23 @@ module Gitlab end def parents - raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) } + case raw_commit + when Rugged::Commit + raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) } + when Gitlab::GitalyClient::Commit + parent_ids.map { |oid| self.class.find(raw_commit.repository, oid) }.compact + else + raise NotImplementedError, "commit source doesn't support #parents" + end + end + + # Get the gpg signature of this commit. + # + # Ex. + # commit.signature(repo) + # + def signature(repo) + Rugged::Commit.extract_signature(repo.rugged, sha) end def stats @@ -309,7 +336,7 @@ module Gitlab begin raw_commit.to_mbox(options) rescue Rugged::InvalidError => ex - if ex.message =~ /Commit \w+ is a merge commit/ + if ex.message =~ /commit \w+ is a merge commit/i 'Patch format is not currently supported for merge commits.' end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 63eebadff2e..a3bc79109f8 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -45,6 +45,8 @@ module Gitlab :bare?, to: :rugged + delegate :exists?, to: :gitaly_repository_client + # Default branch in the repository def root_ref @root_ref ||= gitaly_migrate(:root_ref) do |is_enabled| @@ -80,10 +82,14 @@ module Gitlab end # Returns an Array of Branches - # - # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/389 - def branches(sort_by: nil) - branches_filter(sort_by: sort_by) + def branches + gitaly_migrate(:branches) do |is_enabled| + if is_enabled + gitaly_ref_client.branches + else + branches_filter + end + end end def reload_rugged @@ -162,20 +168,13 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390 def tags - rugged.references.each("refs/tags/*").map do |ref| - message = nil - - if ref.target.is_a?(Rugged::Tag::Annotation) - tag_message = ref.target.message - - if tag_message.respond_to?(:chomp) - message = tag_message.chomp - end + gitaly_migrate(:tags) do |is_enabled| + if is_enabled + tags_from_gitaly + else + tags_from_rugged end - - target_commit = Gitlab::Git::Commit.find(self, ref.target) - Gitlab::Git::Tag.new(self, ref.name, ref.target, target_commit, message) - end.sort_by(&:name) + end end # Returns true if the given tag exists @@ -208,10 +207,6 @@ module Gitlab !empty? end - def repo_exists? - !!rugged - end - # Discovers the default branch based on the repository's available branches # # - If no branches are present, returns nil @@ -480,20 +475,6 @@ module Gitlab end end - # Sets HEAD to the commit specified by +ref+; +ref+ can be a branch or - # tag name or a commit SHA. Valid +reset_type+ values are: - # - # [:soft] - # the head will be moved to the commit. - # [:mixed] - # will trigger a +:soft+ reset, plus the index will be replaced - # with the content of the commit tree. - # [:hard] - # will trigger a +:mixed+ reset and the working directory will be - # replaced with the content of the index. (Untracked and ignored files - # will be left alone) - delegate :reset, to: :rugged - # Mimic the `git clean` command and recursively delete untracked files. # Valid keys that can be passed in the +options+ hash are: # @@ -518,154 +499,6 @@ module Gitlab # TODO: implement this method end - # Check out the specified ref. Valid options are: - # - # :b - Create a new branch at +start_point+ and set HEAD to the new - # branch. - # - # * These options are passed to the Rugged::Repository#checkout method: - # - # :progress :: - # A callback that will be executed for checkout progress notifications. - # Up to 3 parameters are passed on each execution: - # - # - The path to the last updated file (or +nil+ on the very first - # invocation). - # - The number of completed checkout steps. - # - The number of total checkout steps to be performed. - # - # :notify :: - # A callback that will be executed for each checkout notification - # types specified with +:notify_flags+. Up to 5 parameters are passed - # on each execution: - # - # - An array containing the +:notify_flags+ that caused the callback - # execution. - # - The path of the current file. - # - A hash describing the baseline blob (or +nil+ if it does not - # exist). - # - A hash describing the target blob (or +nil+ if it does not exist). - # - A hash describing the workdir blob (or +nil+ if it does not - # exist). - # - # :strategy :: - # A single symbol or an array of symbols representing the strategies - # to use when performing the checkout. Possible values are: - # - # :none :: - # Perform a dry run (default). - # - # :safe :: - # Allow safe updates that cannot overwrite uncommitted data. - # - # :safe_create :: - # Allow safe updates plus creation of missing files. - # - # :force :: - # Allow all updates to force working directory to look like index. - # - # :allow_conflicts :: - # Allow checkout to make safe updates even if conflicts are found. - # - # :remove_untracked :: - # Remove untracked files not in index (that are not ignored). - # - # :remove_ignored :: - # Remove ignored files not in index. - # - # :update_only :: - # Only update existing files, don't create new ones. - # - # :dont_update_index :: - # Normally checkout updates index entries as it goes; this stops - # that. - # - # :no_refresh :: - # Don't refresh index/config/etc before doing checkout. - # - # :disable_pathspec_match :: - # Treat pathspec as simple list of exact match file paths. - # - # :skip_locked_directories :: - # Ignore directories in use, they will be left empty. - # - # :skip_unmerged :: - # Allow checkout to skip unmerged files (NOT IMPLEMENTED). - # - # :use_ours :: - # For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED). - # - # :use_theirs :: - # For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED). - # - # :update_submodules :: - # Recursively checkout submodules with same options (NOT - # IMPLEMENTED). - # - # :update_submodules_if_changed :: - # Recursively checkout submodules if HEAD moved in super repo (NOT - # IMPLEMENTED). - # - # :disable_filters :: - # If +true+, filters like CRLF line conversion will be disabled. - # - # :dir_mode :: - # Mode for newly created directories. Default: +0755+. - # - # :file_mode :: - # Mode for newly created files. Default: +0755+ or +0644+. - # - # :file_open_flags :: - # Mode for opening files. Default: - # <code>IO::CREAT | IO::TRUNC | IO::WRONLY</code>. - # - # :notify_flags :: - # A single symbol or an array of symbols representing the cases in - # which the +:notify+ callback should be invoked. Possible values are: - # - # :none :: - # Do not invoke the +:notify+ callback (default). - # - # :conflict :: - # Invoke the callback for conflicting paths. - # - # :dirty :: - # Invoke the callback for "dirty" files, i.e. those that do not need - # an update but no longer match the baseline. - # - # :updated :: - # Invoke the callback for any file that was changed. - # - # :untracked :: - # Invoke the callback for untracked files. - # - # :ignored :: - # Invoke the callback for ignored files. - # - # :all :: - # Invoke the callback for all these cases. - # - # :paths :: - # A glob string or an array of glob strings specifying which paths - # should be taken into account for the checkout operation. +nil+ will - # match all files. Default: +nil+. - # - # :baseline :: - # A Rugged::Tree that represents the current, expected contents of the - # workdir. Default: +HEAD+. - # - # :target_directory :: - # A path to an alternative workdir directory in which the checkout - # should be performed. - def checkout(ref, options = {}, start_point = "HEAD") - if options[:b] - rugged.branches.create(ref, start_point) - options.delete(:b) - end - default_options = { strategy: [:recreate_missing, :safe] } - rugged.checkout(ref, default_options.merge(options)) - end - # Delete the specified branch from the repository def delete_branch(branch_name) rugged.branches.delete(branch_name) @@ -815,6 +648,10 @@ module Gitlab @gitaly_commit_client ||= Gitlab::GitalyClient::CommitService.new(self) end + def gitaly_repository_client + @gitaly_repository_client ||= Gitlab::GitalyClient::RepositoryService.new(self) + end + private # Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'. @@ -1113,6 +950,27 @@ module Gitlab end end + def tags_from_rugged + rugged.references.each("refs/tags/*").map do |ref| + message = nil + + if ref.target.is_a?(Rugged::Tag::Annotation) + tag_message = ref.target.message + + if tag_message.respond_to?(:chomp) + message = tag_message.chomp + end + end + + target_commit = Gitlab::Git::Commit.find(self, ref.target) + Gitlab::Git::Tag.new(self, ref.name, ref.target, target_commit, message) + end.sort_by(&:name) + end + + def tags_from_gitaly + gitaly_ref_client.tags + end + def gitaly_migrate(method, &block) Gitlab::GitalyClient.migrate(method, &block) rescue GRPC::NotFound => e diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb index 8122ff0e81f..8e959c57c7c 100644 --- a/lib/gitlab/git/tree.rb +++ b/lib/gitlab/git/tree.rb @@ -17,30 +17,13 @@ module Gitlab def where(repository, sha, path = nil) path = nil if path == '' || path == '/' - commit = repository.lookup(sha) - root_tree = commit.tree - - tree = if path - id = find_id_by_path(repository, root_tree.oid, path) - if id - repository.lookup(id) - else - [] - end - else - root_tree - end - - tree.map do |entry| - new( - id: entry[:oid], - root_id: root_tree.oid, - name: entry[:name], - type: entry[:type], - mode: entry[:filemode].to_s(8), - path: path ? File.join(path, entry[:name]) : entry[:name], - commit_id: sha - ) + Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled| + if is_enabled + client = Gitlab::GitalyClient::CommitService.new(repository) + client.tree_entries(repository, sha, path) + else + tree_entries_from_rugged(repository, sha, path) + end end end @@ -74,6 +57,34 @@ module Gitlab entry[:oid] end end + + def tree_entries_from_rugged(repository, sha, path) + commit = repository.lookup(sha) + root_tree = commit.tree + + tree = if path + id = find_id_by_path(repository, root_tree.oid, path) + if id + repository.lookup(id) + else + [] + end + else + root_tree + end + + tree.map do |entry| + new( + id: entry[:oid], + root_id: root_tree.oid, + name: entry[:name], + type: entry[:type], + mode: entry[:filemode].to_s(8), + path: path ? File.join(path, entry[:name]) : entry[:name], + commit_id: sha + ) + end + end end def initialize(options) diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 435e41e36fb..c90ef282fdd 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -57,7 +57,7 @@ module Gitlab metadata = yield(metadata) if block_given? stub(service, storage).send(rpc, request, metadata) end - + def self.request_metadata(storage) encoded_token = Base64.strict_encode64(token(storage).to_s) { metadata: { 'authorization' => "Bearer #{encoded_token}" } } diff --git a/lib/gitlab/gitaly_client/commit.rb b/lib/gitlab/gitaly_client/commit.rb new file mode 100644 index 00000000000..61fe462d762 --- /dev/null +++ b/lib/gitlab/gitaly_client/commit.rb @@ -0,0 +1,14 @@ +module Gitlab + module GitalyClient + class Commit + attr_reader :repository, :gitaly_commit + + delegate :id, :subject, :body, :author, :committer, :parent_ids, to: :gitaly_commit + + def initialize(repository, gitaly_commit) + @repository = repository + @gitaly_commit = gitaly_commit + end + end + end +end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index b749955cddc..c6e52b530b3 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -60,6 +60,31 @@ module Gitlab entry end + def tree_entries(repository, revision, path) + request = Gitaly::GetTreeEntriesRequest.new( + repository: @gitaly_repo, + revision: revision, + path: path.presence || '.' + ) + + response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request) + + response.flat_map do |message| + message.entries.map do |gitaly_tree_entry| + entry_path = gitaly_tree_entry.path.dup + Gitlab::Git::Tree.new( + id: gitaly_tree_entry.oid, + root_id: gitaly_tree_entry.root_oid, + type: gitaly_tree_entry.type.downcase, + mode: gitaly_tree_entry.mode.to_s(8), + name: File.basename(entry_path), + path: entry_path, + commit_id: gitaly_tree_entry.commit_oid + ) + end + end + end + def commit_count(ref) request = Gitaly::CountCommitsRequest.new( repository: @gitaly_repo, @@ -80,6 +105,19 @@ module Gitlab consume_commits_response(response) end + def find_all_commits(opts = {}) + request = Gitaly::FindAllCommitsRequest.new( + repository: @gitaly_repo, + revision: opts[:ref].to_s, + max_count: opts[:max_count].to_i, + skip: opts[:skip].to_i + ) + request.order = opts[:order].upcase if opts[:order].present? + + response = GitalyClient.call(@repository.storage, :commit_service, :find_all_commits, request) + consume_commits_response(response) + end + private def commit_diff_request_params(commit, options = {}) @@ -94,7 +132,12 @@ module Gitlab end def consume_commits_response(response) - response.flat_map { |r| r.commits } + response.flat_map do |message| + message.commits.map do |gitaly_commit| + commit = GitalyClient::Commit.new(@repository, gitaly_commit) + Gitlab::Git::Commit.new(commit) + end + end end end end diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index 2c3d53410ac..b0f7548b7dc 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -10,6 +10,19 @@ module Gitlab @storage = repository.storage end + def branches + request = Gitaly::FindAllBranchesRequest.new(repository: @gitaly_repo) + response = GitalyClient.call(@storage, :ref_service, :find_all_branches, request) + + response.flat_map do |message| + message.branches.map do |branch| + gitaly_commit = GitalyClient::Commit.new(@repository, branch.target) + target_commit = Gitlab::Git::Commit.decorate(gitaly_commit) + Gitlab::Git::Branch.new(@repository, branch.name, branch.target.id, target_commit) + end + end + end + def default_branch_name request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo) response = GitalyClient.call(@storage, :ref_service, :find_default_branch_name, request) @@ -52,6 +65,12 @@ module Gitlab consume_branches_response(response) end + def tags + request = Gitaly::FindAllTagsRequest.new(repository: @gitaly_repo) + response = GitalyClient.call(@storage, :ref_service, :find_all_tags, request) + consume_tags_response(response) + end + private def consume_refs_response(response) @@ -79,6 +98,25 @@ module Gitlab end end + def consume_tags_response(response) + response.flat_map do |message| + message.tags.map do |gitaly_tag| + if gitaly_tag.target_commit.present? + commit = GitalyClient::Commit.new(@repository, gitaly_tag.target_commit) + gitaly_commit = Gitlab::Git::Commit.new(commit) + end + + Gitlab::Git::Tag.new( + @repository, + encode!(gitaly_tag.name.dup), + gitaly_tag.id, + gitaly_commit, + encode!(gitaly_tag.message.chomp) + ) + end + end + end + def commit_from_local_branches_response(response) # Git messages have no encoding enforcements. However, in the UI we only # handle UTF-8, so basically we cross our fingers that the message force diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb new file mode 100644 index 00000000000..f5d84ea8762 --- /dev/null +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -0,0 +1,16 @@ +module Gitlab + module GitalyClient + class RepositoryService + def initialize(repository) + @repository = repository + @gitaly_repo = repository.gitaly_repository + end + + def exists? + request = Gitaly::RepositoryExistsRequest.new(repository: @gitaly_repo) + + GitalyClient.call(@repository.storage, :repository_service, :exists, request).exists + end + end + end +end diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb new file mode 100644 index 00000000000..e1d1724295a --- /dev/null +++ b/lib/gitlab/gpg.rb @@ -0,0 +1,62 @@ +module Gitlab + module Gpg + extend self + + module CurrentKeyChain + extend self + + def add(key) + GPGME::Key.import(key) + end + + def fingerprints_from_key(key) + import = GPGME::Key.import(key) + + return [] if import.imported == 0 + + import.imports.map(&:fingerprint) + end + end + + def fingerprints_from_key(key) + using_tmp_keychain do + CurrentKeyChain.fingerprints_from_key(key) + end + end + + def primary_keyids_from_key(key) + using_tmp_keychain do + fingerprints = CurrentKeyChain.fingerprints_from_key(key) + + GPGME::Key.find(:public, fingerprints).map { |raw_key| raw_key.primary_subkey.keyid } + end + end + + def user_infos_from_key(key) + using_tmp_keychain do + fingerprints = CurrentKeyChain.fingerprints_from_key(key) + + GPGME::Key.find(:public, fingerprints).flat_map do |raw_key| + raw_key.uids.map { |uid| { name: uid.name, email: uid.email } } + end + end + end + + def using_tmp_keychain + Dir.mktmpdir do |dir| + @original_dirs ||= [GPGME::Engine.dirinfo('homedir')] + @original_dirs.push(dir) + + GPGME::Engine.home_dir = dir + + return_value = yield + + @original_dirs.pop + + GPGME::Engine.home_dir = @original_dirs[-1] + + return_value + end + end + end +end diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb new file mode 100644 index 00000000000..55428b85207 --- /dev/null +++ b/lib/gitlab/gpg/commit.rb @@ -0,0 +1,85 @@ +module Gitlab + module Gpg + class Commit + attr_reader :commit + + def initialize(commit) + @commit = commit + + @signature_text, @signed_text = commit.raw.signature(commit.project.repository) + end + + def has_signature? + !!(@signature_text && @signed_text) + end + + def signature + return unless has_signature? + + cached_signature = GpgSignature.find_by(commit_sha: commit.sha) + return cached_signature if cached_signature.present? + + using_keychain do |gpg_key| + create_cached_signature!(gpg_key) + end + end + + def update_signature!(cached_signature) + using_keychain do |gpg_key| + cached_signature.update_attributes!(attributes(gpg_key)) + end + end + + private + + def using_keychain + Gitlab::Gpg.using_tmp_keychain do + # first we need to get the keyid from the signature to query the gpg + # key belonging to the keyid. + # This way we can add the key to the temporary keychain and extract + # the proper signature. + gpg_key = GpgKey.find_by(primary_keyid: verified_signature.fingerprint) + + if gpg_key + Gitlab::Gpg::CurrentKeyChain.add(gpg_key.key) + @verified_signature = nil + end + + yield gpg_key + end + end + + def verified_signature + @verified_signature ||= GPGME::Crypto.new.verify(@signature_text, signed_text: @signed_text) do |verified_signature| + break verified_signature + end + end + + def create_cached_signature!(gpg_key) + GpgSignature.create!(attributes(gpg_key)) + end + + def attributes(gpg_key) + user_infos = user_infos(gpg_key) + + { + commit_sha: commit.sha, + project: commit.project, + gpg_key: gpg_key, + gpg_key_primary_keyid: gpg_key&.primary_keyid || verified_signature.fingerprint, + gpg_key_user_name: user_infos[:name], + gpg_key_user_email: user_infos[:email], + valid_signature: gpg_signature_valid_signature_value(gpg_key) + } + end + + def gpg_signature_valid_signature_value(gpg_key) + !!(gpg_key && gpg_key.verified? && verified_signature.valid?) + end + + def user_infos(gpg_key) + gpg_key&.verified_user_infos&.first || gpg_key&.user_infos&.first || {} + end + end + end +end diff --git a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb new file mode 100644 index 00000000000..3bb491120ba --- /dev/null +++ b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb @@ -0,0 +1,19 @@ +module Gitlab + module Gpg + class InvalidGpgSignatureUpdater + def initialize(gpg_key) + @gpg_key = gpg_key + end + + def run + GpgSignature + .select(:id, :commit_sha, :project_id) + .where('gpg_key_id IS NULL OR valid_signature = ?', false) + .where(gpg_key_primary_keyid: @gpg_key.primary_keyid) + .find_each do |gpg_signature| + Gitlab::Gpg::Commit.new(gpg_signature.commit).update_signature!(gpg_signature) + end + end + end + end +end diff --git a/lib/gitlab/health_checks/base_abstract_check.rb b/lib/gitlab/health_checks/base_abstract_check.rb index 7de6d4d9367..8b365dab185 100644 --- a/lib/gitlab/health_checks/base_abstract_check.rb +++ b/lib/gitlab/health_checks/base_abstract_check.rb @@ -27,10 +27,10 @@ module Gitlab Metric.new(name, value, labels) end - def with_timing(proc) + def with_timing start = Time.now - result = proc.call - yield result, Time.now.to_f - start.to_f + result = yield + [result, Time.now.to_f - start.to_f] end def catch_timeout(seconds, &block) diff --git a/lib/gitlab/health_checks/fs_shards_check.rb b/lib/gitlab/health_checks/fs_shards_check.rb index bebde857b16..9e91c135956 100644 --- a/lib/gitlab/health_checks/fs_shards_check.rb +++ b/lib/gitlab/health_checks/fs_shards_check.rb @@ -10,47 +10,45 @@ module Gitlab def readiness repository_storages.map do |storage_name| begin - tmp_file_path = tmp_file_path(storage_name) - if !storage_stat_test(storage_name) HealthChecks::Result.new(false, 'cannot stat storage', shard: storage_name) - elsif !storage_write_test(tmp_file_path) - HealthChecks::Result.new(false, 'cannot write to storage', shard: storage_name) - elsif !storage_read_test(tmp_file_path) - HealthChecks::Result.new(false, 'cannot read from storage', shard: storage_name) else - HealthChecks::Result.new(true, nil, shard: storage_name) + with_temp_file(storage_name) do |tmp_file_path| + if !storage_write_test(tmp_file_path) + HealthChecks::Result.new(false, 'cannot write to storage', shard: storage_name) + elsif !storage_read_test(tmp_file_path) + HealthChecks::Result.new(false, 'cannot read from storage', shard: storage_name) + else + HealthChecks::Result.new(true, nil, shard: storage_name) + end + end end rescue RuntimeError => ex message = "unexpected error #{ex} when checking storage #{storage_name}" Rails.logger.error(message) HealthChecks::Result.new(false, message, shard: storage_name) - ensure - delete_test_file(tmp_file_path) end end end def metrics repository_storages.flat_map do |storage_name| - tmp_file_path = tmp_file_path(storage_name) [ - operation_metrics(:filesystem_accessible, :filesystem_access_latency_seconds, -> { storage_stat_test(storage_name) }, shard: storage_name), - operation_metrics(:filesystem_writable, :filesystem_write_latency_seconds, -> { storage_write_test(tmp_file_path) }, shard: storage_name), - operation_metrics(:filesystem_readable, :filesystem_read_latency_seconds, -> { storage_read_test(tmp_file_path) }, shard: storage_name) + storage_stat_metrics(storage_name), + storage_write_metrics(storage_name), + storage_read_metrics(storage_name) ].flatten end end private - def operation_metrics(ok_metric, latency_metric, operation, **labels) - with_timing operation do |result, elapsed| - [ - metric(latency_metric, elapsed, **labels), - metric(ok_metric, result ? 1 : 0, **labels) - ] - end + def operation_metrics(ok_metric, latency_metric, **labels) + result, elapsed = yield + [ + metric(latency_metric, elapsed, **labels), + metric(ok_metric, result ? 1 : 0, **labels) + ] rescue RuntimeError => ex Rails.logger.error("unexpected error #{ex} when checking #{ok_metric}") [metric(ok_metric, 0, **labels)] @@ -68,19 +66,36 @@ module Gitlab Gitlab::Popen.popen([TIMEOUT_EXECUTABLE, COMMAND_TIMEOUT].concat(cmd_args), *args, &block) end - def tmp_file_path(storage_name) - Dir::Tmpname.create(%w(fs_shards_check +deleted), path(storage_name)) { |path| path } + def with_temp_file(storage_name) + temp_file_path = Dir::Tmpname.create(%w(fs_shards_check +deleted), storage_path(storage_name)) { |path| path } + yield temp_file_path + ensure + delete_test_file(temp_file_path) end - def path(storage_name) + def storage_path(storage_name) storages_paths&.dig(storage_name, 'path') end + # All below test methods use shell commands to perform actions on storage volumes. + # In case a storage volume have connectivity problems causing pure Ruby IO operation to wait indefinitely, + # we can rely on shell commands to be terminated once `timeout` kills them. + # + # However we also fallback to pure Ruby file operations in case a specific shell command is missing + # so we are still able to perform healthchecks and gather metrics from such system. + + def delete_test_file(tmp_path) + _, status = exec_with_timeout(%W{ rm -f #{tmp_path} }) + status.zero? + rescue Errno::ENOENT + File.delete(tmp_path) rescue Errno::ENOENT + end + def storage_stat_test(storage_name) - stat_path = File.join(path(storage_name), '.') + stat_path = File.join(storage_path(storage_name), '.') begin _, status = exec_with_timeout(%W{ stat #{stat_path} }) - status == 0 + status.zero? rescue Errno::ENOENT File.exist?(stat_path) && File::Stat.new(stat_path).readable? end @@ -90,7 +105,7 @@ module Gitlab _, status = exec_with_timeout(%W{ tee #{tmp_path} }) do |stdin| stdin.write(RANDOM_STRING) end - status == 0 + status.zero? rescue Errno::ENOENT written_bytes = File.write(tmp_path, RANDOM_STRING) rescue Errno::ENOENT written_bytes == RANDOM_STRING.length @@ -100,17 +115,33 @@ module Gitlab _, status = exec_with_timeout(%W{ diff #{tmp_path} - }) do |stdin| stdin.write(RANDOM_STRING) end - status == 0 + status.zero? rescue Errno::ENOENT file_contents = File.read(tmp_path) rescue Errno::ENOENT file_contents == RANDOM_STRING end - def delete_test_file(tmp_path) - _, status = exec_with_timeout(%W{ rm -f #{tmp_path} }) - status == 0 - rescue Errno::ENOENT - File.delete(tmp_path) rescue Errno::ENOENT + def storage_stat_metrics(storage_name) + operation_metrics(:filesystem_accessible, :filesystem_access_latency_seconds, shard: storage_name) do + with_timing { storage_stat_test(storage_name) } + end + end + + def storage_write_metrics(storage_name) + operation_metrics(:filesystem_writable, :filesystem_write_latency_seconds, shard: storage_name) do + with_temp_file(storage_name) do |tmp_file_path| + with_timing { storage_write_test(tmp_file_path) } + end + end + end + + def storage_read_metrics(storage_name) + operation_metrics(:filesystem_readable, :filesystem_read_latency_seconds, shard: storage_name) do + with_temp_file(storage_name) do |tmp_file_path| + storage_write_test(tmp_file_path) # writes data used by read test + with_timing { storage_read_test(tmp_file_path) } + end + end end end end diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb index 3dcb28a193c..f5026171ba4 100644 --- a/lib/gitlab/health_checks/simple_abstract_check.rb +++ b/lib/gitlab/health_checks/simple_abstract_check.rb @@ -15,14 +15,13 @@ module Gitlab end def metrics - with_timing method(:check) do |result, elapsed| - Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless is_successful?(result) - [ - metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0), - metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0), - metric("#{metric_prefix}_latency_seconds", elapsed) - ] - end + result, elapsed = with_timing(&method(:check)) + Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless is_successful?(result) + [ + metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0), + metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0), + metric("#{metric_prefix}_latency_seconds", elapsed) + ] end private diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb index a1b896c9511..cc282d1415b 100644 --- a/lib/gitlab/i18n.rb +++ b/lib/gitlab/i18n.rb @@ -7,10 +7,10 @@ module Gitlab 'es' => 'Español', 'de' => 'Deutsch', 'fr' => 'Français', - 'pt_BR' => 'Português(Brasil)', + 'pt_BR' => 'Português (Brasil)', 'zh_CN' => '简体ä¸æ–‡', - 'zh_HK' => 'ç¹é«”ä¸æ–‡(香港)', - 'zh_TW' => 'ç¹é«”ä¸æ–‡(臺ç£)', + 'zh_HK' => 'ç¹é«”ä¸æ–‡ (香港)', + 'zh_TW' => 'ç¹é«”ä¸æ–‡ (臺ç£)', 'bg' => 'българÑки', 'ru' => 'РуÑÑкий', 'eo' => 'Esperanto', diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 7b05290e5cc..8867a91c244 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -101,7 +101,7 @@ module Gitlab end def user_attributes - %W(#{config.uid} cn mail dn) + %W(#{config.uid} cn dn) + config.attributes['username'] + config.attributes['email'] end end end diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb index 6fdf68641e2..c8f19cd52d5 100644 --- a/lib/gitlab/ldap/config.rb +++ b/lib/gitlab/ldap/config.rb @@ -2,6 +2,12 @@ module Gitlab module LDAP class Config + NET_LDAP_ENCRYPTION_METHOD = { + simple_tls: :simple_tls, + start_tls: :start_tls, + plain: nil + }.freeze + attr_accessor :provider, :options def self.enabled? @@ -12,6 +18,12 @@ module Gitlab Gitlab.config.ldap.servers.values end + def self.available_servers + return [] unless enabled? + + Array.wrap(servers.first) + end + def self.providers servers.map { |server| server['provider_name'] } end @@ -39,7 +51,7 @@ module Gitlab def adapter_options opts = base_options.merge( - encryption: encryption + encryption: encryption_options ) opts.merge!(auth_options) if has_auth? @@ -50,9 +62,10 @@ module Gitlab def omniauth_options opts = base_options.merge( base: base, - method: options['method'], + encryption: options['encryption'], filter: omniauth_user_filter, - name_proc: name_proc + name_proc: name_proc, + disable_verify_certificates: !options['verify_certificates'] ) if has_auth? @@ -62,6 +75,9 @@ module Gitlab ) end + opts[:ca_file] = options['ca_file'] if options['ca_file'].present? + opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present? + opts end @@ -157,15 +173,37 @@ module Gitlab base_config.servers.values.find { |server| server['provider_name'] == provider } end - def encryption - case options['method'].to_s - when 'ssl' - :simple_tls - when 'tls' - :start_tls - else - nil - end + def encryption_options + method = translate_method(options['encryption']) + return nil unless method + + { + method: method, + tls_options: tls_options(method) + } + end + + def translate_method(method_from_config) + NET_LDAP_ENCRYPTION_METHOD[method_from_config.to_sym] + end + + def tls_options(method) + return { verify_mode: OpenSSL::SSL::VERIFY_NONE } unless method + + opts = if options['verify_certificates'] + OpenSSL::SSL::SSLContext::DEFAULT_PARAMS + else + # It is important to explicitly set verify_mode for two reasons: + # 1. The behavior of OpenSSL is undefined when verify_mode is not set. + # 2. The net-ldap gem implementation verifies the certificate hostname + # unless verify_mode is set to VERIFY_NONE. + { verify_mode: OpenSSL::SSL::VERIFY_NONE } + end + + opts[:ca_file] = options['ca_file'] if options['ca_file'].present? + opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present? + + opts end def auth_options diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb index 60a32d5d5ea..894bd5efae5 100644 --- a/lib/gitlab/path_regex.rb +++ b/lib/gitlab/path_regex.rb @@ -14,42 +14,42 @@ module Gitlab TOP_LEVEL_ROUTES = %w[ - .well-known + 404.html + 422.html + 500.html + 502.html + 503.html abuse_reports admin - all api + apple-touch-icon-precomposed.png + apple-touch-icon.png assets autocomplete ci dashboard + deploy.html explore + favicon.ico files groups health_check help - hooks import invites - issues jwt koding - member - merge_requests - new - notes notification_settings oauth profile projects public - repository robots.txt s search sent_notifications - services + slash-command-logo.png snippets - teams u unicorn_test unsubscribes diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 7668ecacc4b..f5b757ace77 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -33,7 +33,12 @@ module Gitlab def issues if project && project.jira_tracker? - @references[:external_issue] ||= references(:external_issue) + if project.issues_enabled? + @references[:all_issues] ||= references(:external_issue) + references(:issue) + else + @references[:external_issue] ||= references(:external_issue) + + references(:issue).select { |i| i.project_id != project.id } + end else @references[:issue] ||= references(:issue) end diff --git a/lib/omni_auth/request_forgery_protection.rb b/lib/gitlab/request_forgery_protection.rb index 69155131d8d..48dd0487790 100644 --- a/lib/omni_auth/request_forgery_protection.rb +++ b/lib/gitlab/request_forgery_protection.rb @@ -1,6 +1,8 @@ -# Protects OmniAuth request phase against CSRF. +# A module to check CSRF tokens in requests. +# It's used in API helpers and OmniAuth. +# Usage: GitLab::RequestForgeryProtection.call(env) -module OmniAuth +module Gitlab module RequestForgeryProtection class Controller < ActionController::Base protect_from_forgery with: :exception @@ -17,5 +19,13 @@ module OmniAuth def self.call(env) app.call(env) end + + def self.verified?(env) + call(env) + + true + rescue ActionController::InvalidAuthenticityToken + false + end end end diff --git a/lib/gitlab/slash_commands/issue_command.rb b/lib/gitlab/slash_commands/issue_command.rb index 87ea19b8806..3d96982b820 100644 --- a/lib/gitlab/slash_commands/issue_command.rb +++ b/lib/gitlab/slash_commands/issue_command.rb @@ -2,7 +2,7 @@ module Gitlab module SlashCommands class IssueCommand < BaseCommand def self.available?(project) - project.issues_enabled? && project.default_issues_tracker? + project.issues_enabled? end def collection diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index 8b43f0053d6..7ce2e9d636e 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -22,13 +22,9 @@ module Gitlab end def scan(text) - scan_regexp.scan(text).map do |match| - if regexp.number_of_capturing_groups == 0 - match.first - else - match - end - end + matches = scan_regexp.scan(text).to_a + matches.map!(&:first) if regexp.number_of_capturing_groups.zero? + matches end def replace(text, rewrite) @@ -43,7 +39,7 @@ module Gitlab # groups, so work around it def scan_regexp @scan_regexp ||= - if regexp.number_of_capturing_groups == 0 + if regexp.number_of_capturing_groups.zero? RE2::Regexp.new('(' + regexp.source + ')') else regexp diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index dba071d7e47..e0ac21305a5 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -40,14 +40,13 @@ module Gitlab pages_domains: PagesDomain.count, projects: Project.count, projects_imported_from_github: Project.where(import_type: 'github').count, - projects_prometheus_active: PrometheusService.active.count, protected_branches: ProtectedBranch.count, releases: Release.count, snippets: Snippet.count, todos: Todo.count, uploads: Upload.count, web_hooks: WebHook.count - } + }.merge(services_usage) } end @@ -64,6 +63,18 @@ module Gitlab usage_data end + + def services_usage + types = { + JiraService: :projects_jira_active, + SlackService: :projects_slack_notifications_active, + SlackSlashCommandsService: :projects_slack_slash_active, + PrometheusService: :projects_prometheus_active + } + + results = Service.unscoped.where(type: types.keys, active: true).group(:type).count + results.each_with_object({}) { |(key, value), response| response[types[key.to_sym]] = value } + end end end end diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb index 8e91ee7287c..d9a5af09f08 100644 --- a/lib/gitlab/user_access.rb +++ b/lib/gitlab/user_access.rb @@ -37,8 +37,8 @@ module Gitlab request_cache def can_create_tag?(ref) return false unless can_access_git? - if ProtectedTag.protected?(project, ref) - project.protected_tags.protected_ref_accessible_to?(ref, user, action: :create) + if protected?(ProtectedTag, project, ref) + protected_tag_accessible_to?(ref, action: :create) else user.can?(:push_code, project) end @@ -47,20 +47,24 @@ module Gitlab request_cache def can_delete_branch?(ref) return false unless can_access_git? - if ProtectedBranch.protected?(project, ref) + if protected?(ProtectedBranch, project, ref) user.can?(:delete_protected_branch, project) else user.can?(:push_code, project) end end + def can_update_branch?(ref) + can_push_to_branch?(ref) || can_merge_to_branch?(ref) + end + request_cache def can_push_to_branch?(ref) return false unless can_access_git? - if ProtectedBranch.protected?(project, ref) + if protected?(ProtectedBranch, project, ref) return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user) - project.protected_branches.protected_ref_accessible_to?(ref, user, action: :push) + protected_branch_accessible_to?(ref, action: :push) else user.can?(:push_code, project) end @@ -69,8 +73,8 @@ module Gitlab request_cache def can_merge_to_branch?(ref) return false unless can_access_git? - if ProtectedBranch.protected?(project, ref) - project.protected_branches.protected_ref_accessible_to?(ref, user, action: :merge) + if protected?(ProtectedBranch, project, ref) + protected_branch_accessible_to?(ref, action: :merge) else user.can?(:push_code, project) end @@ -87,5 +91,23 @@ module Gitlab def can_access_git? user && user.can?(:access_git) end + + def protected_branch_accessible_to?(ref, action:) + ProtectedBranch.protected_ref_accessible_to?( + ref, user, + action: action, + protected_refs: project.protected_branches) + end + + def protected_tag_accessible_to?(ref, action:) + ProtectedTag.protected_ref_accessible_to?( + ref, user, + action: action, + protected_refs: project.protected_tags) + end + + request_cache def protected?(kind, project, ref) + kind.protected?(project, ref) + end end end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 916ef365d78..3f25e463412 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -35,7 +35,10 @@ module Gitlab when 'git_receive_pack' Gitlab::GitalyClient.feature_enabled?(:post_receive_pack) when 'git_upload_pack' - Gitlab::GitalyClient.feature_enabled?(:post_upload_pack) + Gitlab::GitalyClient.feature_enabled?( + :post_upload_pack, + status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT + ) when 'info_refs' true else @@ -62,7 +65,7 @@ module Gitlab end def send_git_blob(repository, blob) - params = if Gitlab::GitalyClient.feature_enabled?(:project_raw_show) + params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_raw_show) { 'GitalyServer' => gitaly_server_hash(repository), 'GetBlobRequest' => { diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb index 3d60618006c..d80cd7d2a4e 100644 --- a/lib/mattermost/client.rb +++ b/lib/mattermost/client.rb @@ -24,6 +24,10 @@ module Mattermost json_response session.post(path, options) end + def delete(session, path, options) + json_response session.delete(path, options) + end + def session_get(path, options = {}) with_session do |session| get(session, path, options) @@ -36,6 +40,12 @@ module Mattermost end end + def session_delete(path, options = {}) + with_session do |session| + delete(session, path, options) + end + end + def json_response(response) json_response = JSON.parse(response.body) diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb index 2cdbbdece16..b2511f3af1d 100644 --- a/lib/mattermost/team.rb +++ b/lib/mattermost/team.rb @@ -14,5 +14,12 @@ module Mattermost type: type }.to_json) end + + # The deletion is done async, so the response is fast. + # On the mattermost side, this triggers an soft deletion first, after which + # the actuall data is removed + def destroy(team_id:) + session_delete("/api/v4/teams/#{team_id}?permanent=true") + end end end diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index 003d57adbbd..259a755d724 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -4,6 +4,7 @@ namespace :gitlab do task compile: [ 'yarn:check', 'rake:assets:precompile', + 'gettext:po_to_json', 'webpack:compile', 'fix_urls' ] diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake index a8db5701d0b..9df07ea8d83 100644 --- a/lib/tasks/gitlab/gitaly.rake +++ b/lib/tasks/gitlab/gitaly.rake @@ -19,7 +19,7 @@ namespace :gitlab do Dir.chdir(args.dir) do create_gitaly_configuration - run_command!([command]) + Bundler.with_original_env { run_command!([command]) } end end diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb index 964aa0fe1bc..28b2d86eed2 100644 --- a/lib/tasks/gitlab/task_helpers.rb +++ b/lib/tasks/gitlab/task_helpers.rb @@ -153,7 +153,6 @@ module Gitlab clone_repo(repo, target_dir) unless Dir.exist?(target_dir) checkout_version(version, target_dir) - reset_to_version(version, target_dir) end def clone_repo(repo, target_dir) @@ -161,12 +160,8 @@ module Gitlab end def checkout_version(version, target_dir) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet]) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout --quiet #{version}]) - end - - def reset_to_version(version, target_dir) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} reset --hard #{version}]) + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet origin #{version}]) + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout -f --quiet FETCH_HEAD --]) end end end diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake index 4108cee08b4..9cc986535e1 100644 --- a/lib/tasks/migrate/setup_postgresql.rake +++ b/lib/tasks/migrate/setup_postgresql.rake @@ -4,6 +4,7 @@ require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lowe require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes') require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes') require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like') +require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes') require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like') desc 'GitLab | Sets up PostgreSQL' @@ -12,5 +13,6 @@ task setup_postgresql: :environment do AddUsersLowerUsernameEmailIndexes.new.up AddLowerPathIndexToRoutes.new.up IndexRoutesPathForLike.new.up + AddLowerPathIndexToRedirectRoutes.new.up IndexRedirectRoutesPathForLike.new.up end diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po index cf74abf81bc..04c61906c73 100644 --- a/locale/ja/gitlab.po +++ b/locale/ja/gitlab.po @@ -33,7 +33,8 @@ msgstr "%{commit_author_link}ã¯%{commit_timeago}å‰ã€ã‚³ãƒŸãƒƒãƒˆã—ã¾ã—ãŸã msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "%d 個ã®ãƒ‘イプライン" +msgstr[0] "1 個ã®ãƒ‘イプライン" +msgstr[1] "%d 個ã®ãƒ‘イプライン" msgid "A collection of graphs regarding Continuous Integration" msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•" @@ -1135,8 +1136,7 @@ msgstr "" msgid "" "You are going to remove the fork relationship to source project " "%{forked_from_project}. Are you ABSOLUTELY sure?" -msgstr "å…ƒã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ (%{forked_from_project}) ã¨ã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚\n" -"本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ" +msgstr "å…ƒã®ãƒ—ãƒã‚¸ã‚§ã‚¯ãƒˆ (%{forked_from_project}) ã¨ã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ã‚’削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ" msgid "" "You are going to transfer %{project_name_with_namespace} to another owner. " @@ -1201,4 +1201,3 @@ msgstr "メール通知" msgid "parent" msgid_plural "parents" msgstr[0] "親" - diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index c4918a4c920..78cf6d2dacc 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -6,13 +6,13 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-28 13:32+0200\n" +"POT-Creation-Date: 2017-07-05 08:50-0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-12 09:05-0400\n" -"Last-Translator: Leandro Nunes dos Santos <leandronunes@gmail.com>\n" "Language-Team: Portuguese (Brazil) (https://translate.zanata.org/project/view/GitLab)\n" +"PO-Revision-Date: 2017-07-14 01:17-0400\n" +"Last-Translator: Huang Tao <htve@outlook.com>\n" "Language: pt-BR\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" @@ -644,6 +644,12 @@ msgstr "Todos" msgid "PipelineSchedules|Inactive" msgstr "Inativo" +msgid "PipelineSchedules|Input variable key" +msgstr "PipelineSchedules|Chave da variável de entrada" + +msgid "PipelineSchedules|Input variable value" +msgstr "PipelineSchedules|Valor da variável de entrada" + msgid "PipelineSchedules|Next Run" msgstr "Próxima Execução" @@ -653,12 +659,18 @@ msgstr "Nenhum" msgid "PipelineSchedules|Provide a short description for this pipeline" msgstr "Digite uma descrição curta para esta pipeline" +msgid "PipelineSchedules|Remove variable row" +msgstr "PipelineSchedules|Remova a linha da variável" + msgid "PipelineSchedules|Take ownership" msgstr "Tornar-se proprietário" msgid "PipelineSchedules|Target" msgstr "Destino" +msgid "PipelineSchedules|Variables" +msgstr "PipelineSchedules|Variáveis" + msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Personalizado" @@ -1151,6 +1163,15 @@ msgid "Withdraw Access Request" msgstr "Remover Requisição de Acesso" msgid "" +"You are going to remove %{group_name}.\n" +"Removed groups CANNOT be restored!\n" +"Are you ABSOLUTELY sure?" +msgstr "" +"Você vai remover %{group_name}.\n" +"Grupos removidos NÃO PODEM ser restaurados!\n" +"Você está ABSOLUTAMENTE certo?" + +msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po index 59a7eb6e1b3..2ded61f40d7 100644 --- a/locale/uk/gitlab.po +++ b/locale/uk/gitlab.po @@ -1,16 +1,16 @@ -# Ðндрей Витюк <andruwa13@gmail.com>, 2017. #zanata # Huang Tao <htve@outlook.com>, 2017. #zanata +# Ðндрей Витюк <andruwa13@gmail.com>, 2017. #zanata msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-28 13:32+0200\n" +"POT-Creation-Date: 2017-07-05 08:50-0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-12 09:05-0400\n" -"Last-Translator: Ðндрей Витюк <andruwa13@gmail.com>\n" "Language-Team: Ukrainian (https://translate.zanata.org/project/view/GitLab)\n" +"PO-Revision-Date: 2017-07-25 03:27-0400\n" +"Last-Translator: Ðндрей Витюк <andruwa13@gmail.com>\n" "Language: uk\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -57,7 +57,7 @@ msgid "Add Changelog" msgstr "Додати ÑпиÑок змін (Changelog)" msgid "Add Contribution guide" -msgstr "Додати керівництво Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð¾Ñ€Ñ–Ð²" +msgstr "Додати керівництво Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¸Ð±â€™ÑŽÑ‚Ð¾Ñ€Ñ–Ð²" msgid "Add License" msgstr "Додати ліцензію" @@ -209,7 +209,9 @@ msgstr[1] "Комміта" msgstr[2] "Коммітів" msgid "Commit duration in minutes for last 30 commits" -msgstr "Комміт триваліÑÑ‚ÑŒ у хвилинах за оÑтанні 30 коммітів" +msgstr "" +"ТриваліÑÑ‚ÑŒ коммітів протÑгом декількох хвилин на протÑзі 30 оÑтанніх " +"коммітів" msgid "Commit message" msgstr "Комміт повідомленнÑ" @@ -236,10 +238,10 @@ msgid "Compare" msgstr "ПорівнÑти" msgid "Contribution guide" -msgstr "Керівництво контрибуторів" +msgstr "Керівництво контриб’юторів" msgid "Contributors" -msgstr "Контрибутори" +msgstr "Контриб’ютори" msgid "Copy URL to clipboard" msgstr "Скопіювати URL в буфер обміну" @@ -352,16 +354,16 @@ msgid "Download" msgstr "Завантажити" msgid "Download tar" -msgstr "Завантажити в форматі tar" +msgstr "Завантажити tar" msgid "Download tar.bz2" -msgstr "Завантажити в форматі tar.bz2" +msgstr "Завантажити tar.bz2" msgid "Download tar.gz" -msgstr "Завантажити в форматі tar.gz" +msgstr "Завантажити tar.gz" msgid "Download zip" -msgstr "Завантажити в форматі zip" +msgstr "Завантажити zip" msgid "DownloadArtifacts|Download" msgstr "Завантажити" @@ -379,7 +381,7 @@ msgid "Edit" msgstr "Редагувати" msgid "Edit Pipeline Schedule %{id}" -msgstr "Редагувати Розклад Конвеєра % {id}" +msgstr "Редагувати Розклад Конвеєра %{id}" msgid "Every day (at 4:00am)" msgstr "Кожен день (в 4:00 ранку)" @@ -397,7 +399,7 @@ msgid "Failed to remove the pipeline schedule" msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ розклад Конвеєра" msgid "Files" -msgstr "Файли" +msgstr "Файлів" msgid "Filter by commit message" msgstr "Фільтрувати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð¼Ñ–Ñ‚Ñ–Ð²" @@ -436,7 +438,7 @@ msgid "GoToYourFork|Fork" msgstr "Форк" msgid "Home" -msgstr "Початок" +msgstr "Головна" msgid "Housekeeping successfully started" msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато" @@ -451,13 +453,13 @@ msgid "Introducing Cycle Analytics" msgstr "ПредÑтавлÑємо аналітику циклу" msgid "Jobs for last month" -msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð° оÑтанній міÑÑць" +msgstr "КількіÑÑ‚ÑŒ завдань за оÑтанній міÑÑць" msgid "Jobs for last week" -msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð° оÑтанній тиждень" +msgstr "КількіÑÑ‚ÑŒ завдань за оÑтанній тиждень" msgid "Jobs for last year" -msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð° оÑтанній рік" +msgstr "КількіÑÑ‚ÑŒ завдань за оÑтанній рік" msgid "LFSStatus|Disabled" msgstr "Вимкнено" @@ -508,7 +510,7 @@ msgid "New Issue" msgid_plural "New Issues" msgstr[0] "Ðова проблема" msgstr[1] "Ðові проблеми" -msgstr[2] "Ðовах проблем" +msgstr[2] "Ðових проблем" msgid "New Pipeline Schedule" msgstr "Ðовий розклад Конвеєра" @@ -654,6 +656,12 @@ msgstr "Ð’ÑÑ–" msgid "PipelineSchedules|Inactive" msgstr "Ðеактивні" +msgid "PipelineSchedules|Input variable key" +msgstr "Введіть ім'Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ—" + +msgid "PipelineSchedules|Input variable value" +msgstr "Вхідні Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¸Ñ…" + msgid "PipelineSchedules|Next Run" msgstr "ÐаÑтупний запуÑк" @@ -663,12 +671,18 @@ msgstr "Ðемає" msgid "PipelineSchedules|Provide a short description for this pipeline" msgstr "Задайте короткий Ð¾Ð¿Ð¸Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Конвеєру" +msgid "PipelineSchedules|Remove variable row" +msgstr "Видалити змінні" + msgid "PipelineSchedules|Take ownership" msgstr "Стати влаÑником" msgid "PipelineSchedules|Target" msgstr "Ціль" +msgid "PipelineSchedules|Variables" +msgstr "Змінні" + msgid "PipelineSheduleIntervalPattern|Custom" msgstr "ВлаÑні" @@ -745,7 +759,7 @@ msgid "ProjectLifecycle|Stage" msgstr "Етап" msgid "ProjectNetworkGraph|Graph" -msgstr "Графік" +msgstr "ІÑторіÑ" msgid "Read more" msgstr "Докладніше" @@ -840,7 +854,7 @@ msgid "Source code" msgstr "Код" msgid "StarProject|Star" -msgstr "Старт" +msgstr "ПідпиÑатиÑÑ" msgid "Start a %{new_merge_request} with these changes" msgstr "Почати %{new_merge_request} з цих змін" @@ -976,19 +990,19 @@ msgid "Timeago|%s minutes remaining" msgstr "%s хвилини залишитиÑÑ" msgid "Timeago|%s months ago" -msgstr "%s міÑÑців тому" +msgstr "%s міÑÑці(в) тому" msgid "Timeago|%s months remaining" -msgstr "%s міÑÑці, що залишилиÑÑ" +msgstr "%s міÑÑці(в), що залишилиÑÑ" msgid "Timeago|%s seconds remaining" msgstr "%s Ñекунд, що залишаютьÑÑ" msgid "Timeago|%s weeks ago" -msgstr "%s тижнів тому" +msgstr "%s тижні(в) тому" msgid "Timeago|%s weeks remaining" -msgstr "%s тижнів залишилиÑÑ" +msgstr "%s тижні(в) залишилиÑÑ" msgid "Timeago|%s years ago" msgstr "%s років тому" @@ -1018,7 +1032,7 @@ msgid "Timeago|Past due" msgstr "ПроÑтрочені" msgid "Timeago|a day ago" -msgstr "годин тому" +msgstr "День тому" msgid "Timeago|a month ago" msgstr "міÑÑць тому" @@ -1042,28 +1056,28 @@ msgid "Timeago|about an hour ago" msgstr "Близько години тому" msgid "Timeago|in %s days" -msgstr "через %s днїв" +msgstr "через %s дні(в)" msgid "Timeago|in %s hours" -msgstr "через %s години" +msgstr "через %s годин(и)" msgid "Timeago|in %s minutes" -msgstr "через %s хвилини" +msgstr "через %s хвилин(и)" msgid "Timeago|in %s months" -msgstr "через %s міÑÑців" +msgstr "через %s міÑÑці(в)" msgid "Timeago|in %s seconds" -msgstr "через %s Ñекунд" +msgstr "через %s Ñекунд(и)" msgid "Timeago|in %s weeks" -msgstr "через %s тижні" +msgstr "через %s тижні(в)" msgid "Timeago|in %s years" -msgstr "через %s років" +msgstr "через %s роки(ів)" msgid "Timeago|in 1 day" -msgstr "через день" +msgstr "через 1 день" msgid "Timeago|in 1 hour" msgstr "через годину" @@ -1081,22 +1095,22 @@ msgid "Timeago|in 1 year" msgstr "через рік" msgid "Timeago|less than a minute ago" -msgstr "менш хвилини тому" +msgstr "менше хвилини тому" msgid "Time|hr" msgid_plural "Time|hrs" -msgstr[0] "Година" -msgstr[1] "Годині" -msgstr[2] "Годин" +msgstr[0] "година" +msgstr[1] "години" +msgstr[2] "годин" msgid "Time|min" msgid_plural "Time|mins" msgstr[0] "хвилина" -msgstr[1] "хвилині" +msgstr[1] "хвилини" msgstr[2] "хвилин" msgid "Time|s" -msgstr "Ñекунда" +msgstr "Ñекунд(а)" msgid "Total Time" msgstr "Загальний чаÑ" @@ -1105,7 +1119,7 @@ msgid "Total test time for all commits/merges" msgstr "Загальний чаÑ, щоб перевірити вÑÑ– фікÑації/злиттÑ" msgid "Unstar" -msgstr "ЗнÑти позначку" +msgstr "ВідпиÑатиÑÑŒ" msgid "Upload New File" msgstr "Завантажити новий файл" @@ -1117,7 +1131,7 @@ msgid "UploadLink|click to upload" msgstr "ÐатиÑніть, щоб завантажити" msgid "Use your global notification setting" -msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð¸Ð¹ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ" +msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ" msgid "View open merge request" msgstr "ПереглÑд відкритих запитів на злиттÑ" @@ -1141,6 +1155,15 @@ msgid "Withdraw Access Request" msgstr "СкаÑувати запит доÑтупу" msgid "" +"You are going to remove %{group_name}.\n" +"Removed groups CANNOT be restored!\n" +"Are you ABSOLUTELY sure?" +msgstr "" +"Ви хочете видалити %{group_name}.\n" +"Видалені групи ÐЕ МОЖÐРбуду відновити!\n" +"Ви ÐБСОЛЮТÐО впевнені?" + +msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" @@ -1231,4 +1254,3 @@ msgid_plural "parents" msgstr[0] "джерело" msgstr[1] "джерела" msgstr[2] "джерел" - diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po index 47b72d7be1a..f471e7def25 100644 --- a/locale/zh_CN/gitlab.po +++ b/locale/zh_CN/gitlab.po @@ -29,7 +29,8 @@ msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}" msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "%d æ¡æµæ°´çº¿" +msgstr[0] "1 æ¡æµæ°´çº¿" +msgstr[1] "%d æ¡æµæ°´çº¿" msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒç»é›†æˆæ•°æ®å›¾" @@ -236,7 +237,7 @@ msgstr "创建新目录" msgid "" "Create a personal access token on your account to pull or push via " "%{protocol}." -msgstr "在å¸æˆ·ä¸Šåˆ›å»ºä¸ªäººè®¿é—®ä»¤ç‰Œï¼Œä»¥é€šè¿‡ï¼…{protocol}æ¥æ‹‰å–或推é€ã€‚" +msgstr "在å¸æˆ·ä¸Šåˆ›å»ºä¸ªäººè®¿é—®ä»¤ç‰Œï¼Œä»¥é€šè¿‡ %{protocol} æ¥æ‹‰å–或推é€ã€‚" msgid "Create directory" msgstr "创建目录" @@ -1109,9 +1110,7 @@ msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" -msgstr "å³å°†è¦åˆ 除 %{project_name_with_namespace}。\n" -"å·²åˆ é™¤çš„é¡¹ç›®æ— æ³•æ¢å¤ï¼\n" -"确定继ç»å—?" +msgstr "å³å°†è¦åˆ 除 %{project_name_with_namespace}ã€‚å·²åˆ é™¤çš„é¡¹ç›®æ— æ³•æ¢å¤ï¼ç¡®å®šç»§ç»å—?" msgid "" "You are going to remove the fork relationship to source project " @@ -1179,4 +1178,3 @@ msgstr "通知邮件" msgid "parent" msgid_plural "parents" msgstr[0] "父级" - diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po index 8a4e6da4ea9..1b7c39f8f62 100644 --- a/locale/zh_HK/gitlab.po +++ b/locale/zh_HK/gitlab.po @@ -28,7 +28,8 @@ msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}" msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "%d æ¢æµæ°´ç·š" +msgstr[0] "1 æ¢æµæ°´ç·š" +msgstr[1] "%d æ¢æµæ°´ç·š" msgid "A collection of graphs regarding Continuous Integration" msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ" @@ -1108,9 +1109,7 @@ msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" -msgstr "å³å°‡è¦åˆªé™¤ %{project_name_with_namespace}。\n" -"å·²åˆªé™¤çš„é …ç›®ç„¡æ³•æ¢è¤‡ï¼\n" -"確定繼續嗎?" +msgstr "å³å°‡è¦åˆªé™¤ %{project_name_with_namespace}ã€‚å·²åˆªé™¤çš„é …ç›®ç„¡æ³•æ¢è¤‡ï¼ç¢ºå®šç¹¼çºŒå—Žï¼Ÿ" msgid "" "You are going to remove the fork relationship to source project " @@ -1178,4 +1177,3 @@ msgstr "通知郵件" msgid "parent" msgid_plural "parents" msgstr[0] "父級" - diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index e61cf0e5152..8d30a78145d 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -7,13 +7,13 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-28 13:32+0200\n" +"POT-Creation-Date: 2017-07-05 08:50-0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-11 09:10-0400\n" -"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n" "Language-Team: Chinese (Taiwan) (https://translate.zanata.org/project/view/GitLab)\n" +"PO-Revision-Date: 2017-07-20 09:50-0400\n" +"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n" "Language: zh-TW\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=1; plural=0\n" @@ -32,7 +32,8 @@ msgstr "%{commit_author_link} 在 %{commit_timeago} é€äº¤" msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "%d æ¢æµæ°´ç·š" +msgstr[0] "1 æ¢æµæ°´ç·š" +msgstr[1] "%d æ¢æµæ°´ç·š" msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒçºŒæ•´åˆ (CI) 相關的圖表" @@ -624,6 +625,12 @@ msgstr "所有" msgid "PipelineSchedules|Inactive" msgstr "未啟用" +msgid "PipelineSchedules|Input variable key" +msgstr "變數å稱" + +msgid "PipelineSchedules|Input variable value" +msgstr "變數值" + msgid "PipelineSchedules|Next Run" msgstr "下次執行時間" @@ -633,12 +640,18 @@ msgstr "ç„¡" msgid "PipelineSchedules|Provide a short description for this pipeline" msgstr "請簡單說明æ¤æµæ°´ç·š (pipeline) " +msgid "PipelineSchedules|Remove variable row" +msgstr "刪除變數" + msgid "PipelineSchedules|Take ownership" msgstr "å–得所有權" msgid "PipelineSchedules|Target" msgstr "目標" +msgid "PipelineSchedules|Variables" +msgstr "變數" + msgid "PipelineSheduleIntervalPattern|Custom" msgstr "自訂" @@ -759,13 +772,13 @@ msgid "Revert this merge request" msgstr "還原æ¤åˆä½µè«‹æ±‚ (merge request) " msgid "Save pipeline schedule" -msgstr "ä¿å˜æµæ°´ç·š (pipeline) 排程" +msgstr "儲å˜æµæ°´ç·š (pipeline) 排程" msgid "Schedule a new pipeline" msgstr "建立æµæ°´ç·š (pipeline) 排程" msgid "Scheduling Pipelines" -msgstr "æµæ°´ç·š (pipeline) 計劃" +msgstr "æµæ°´ç·š (pipeline) 排程" msgid "Search branches and tags" msgstr "æœå°‹åˆ†æ”¯ (branch) 和標籤" @@ -849,8 +862,7 @@ msgid "" "specific branches or tags. Those scheduled pipelines will inherit limited " "project access based on their associated user." msgstr "" -"在指定了特定分支 (branch) 或標籤後,æ¤è™•çš„æµæ°´ç·š (pipeline) 排程會ä¸æ–·åœ°é‡è¤‡åŸ·è¡Œã€‚\n" -"æµæ°´ç·šæŽ’程的å˜å–權é™èˆ‡å°ˆæ¡ˆæœ¬èº«ç›¸åŒã€‚" +"在指定了特定分支 (branch) 或標籤後,æ¤è™•çš„æµæ°´ç·š (pipeline) 排程會ä¸æ–·åœ°é‡è¤‡åŸ·è¡Œã€‚æµæ°´ç·šæŽ’程的å˜å–權é™èˆ‡å°ˆæ¡ˆæœ¬èº«ç›¸åŒã€‚" msgid "" "The planning stage shows the time from the previous step to pushing your " @@ -1097,6 +1109,14 @@ msgid "Withdraw Access Request" msgstr "å–消權é™ç”³è«‹" msgid "" +"You are going to remove %{group_name}.\n" +"Removed groups CANNOT be restored!\n" +"Are you ABSOLUTELY sure?" +msgstr "å³å°‡è¦åˆªé™¤ %{group_name}。\n" +"被刪除的群組完全無法救回來喔ï¼\n" +"真的「100%確定ã€è¦é€™éº¼åšå—Žï¼Ÿ" + +msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" @@ -1174,4 +1194,3 @@ msgstr "通知信" msgid "parent" msgid_plural "parents" msgstr[0] "上層" - @@ -48,7 +48,14 @@ module QA module Main autoload :Entry, 'qa/page/main/entry' autoload :Menu, 'qa/page/main/menu' - autoload :Groups, 'qa/page/main/groups' + end + + module Dashboard + autoload :Groups, 'qa/page/dashboard/groups' + end + + module Group + autoload :Show, 'qa/page/group/show' end module Project diff --git a/qa/qa/page/main/groups.rb b/qa/qa/page/dashboard/groups.rb index 169c5ebc967..3690f40dcfe 100644 --- a/qa/qa/page/main/groups.rb +++ b/qa/qa/page/dashboard/groups.rb @@ -1,9 +1,11 @@ module QA module Page - module Main + module Dashboard class Groups < Page::Base def prepare_test_namespace - return if page.has_content?(Runtime::Namespace.name) + if page.has_content?(Runtime::Namespace.name) + return click_link(Runtime::Namespace.name) + end click_on 'New group' diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb new file mode 100644 index 00000000000..296c311d7c6 --- /dev/null +++ b/qa/qa/page/group/show.rb @@ -0,0 +1,11 @@ +module QA + module Page + module Group + class Show < Page::Base + def go_to_new_project + click_link 'New Project' + end + end + end + end +end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index f7c2086d0dd..7ce4e9009f5 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -14,13 +14,6 @@ module QA within_user_menu { click_link 'Admin area' } end - def go_to_new_project - within_user_menu do - find('.header-new-dropdown-toggle').click - click_link('New project') - end - end - def sign_out within_user_menu do find('.header-user-dropdown-toggle').click diff --git a/qa/qa/scenario/gitlab/project/create.rb b/qa/qa/scenario/gitlab/project/create.rb index 99d0fc42a94..b860701c304 100644 --- a/qa/qa/scenario/gitlab/project/create.rb +++ b/qa/qa/scenario/gitlab/project/create.rb @@ -13,8 +13,8 @@ module QA def perform Page::Main::Menu.act { go_to_groups } - Page::Main::Groups.act { prepare_test_namespace } - Page::Main::Menu.act { go_to_new_project } + Page::Dashboard::Groups.act { prepare_test_namespace } + Page::Group::Show.act { go_to_new_project } Page::Project::New.perform do |page| page.choose_test_namespace diff --git a/rubocop/cop/migration/add_column_with_default_to_large_table.rb b/rubocop/cop/migration/add_column_with_default_to_large_table.rb index 2372e6b60ea..87788b0d9c2 100644 --- a/rubocop/cop/migration/add_column_with_default_to_large_table.rb +++ b/rubocop/cop/migration/add_column_with_default_to_large_table.rb @@ -20,8 +20,11 @@ module RuboCop 'necessary'.freeze LARGE_TABLES = %i[ + ci_builds events issues + merge_request_diff_files + merge_request_diffs merge_requests namespaces notes diff --git a/spec/controllers/admin/applications_controller_spec.rb b/spec/controllers/admin/applications_controller_spec.rb index e311b8a63b2..7bd6c0e6117 100644 --- a/spec/controllers/admin/applications_controller_spec.rb +++ b/spec/controllers/admin/applications_controller_spec.rb @@ -28,13 +28,16 @@ describe Admin::ApplicationsController do describe 'POST #create' do it 'creates the application' do + create_params = attributes_for(:application, trusted: true) + expect do - post :create, doorkeeper_application: attributes_for(:application) + post :create, doorkeeper_application: create_params end.to change { Doorkeeper::Application.count }.by(1) application = Doorkeeper::Application.last expect(response).to redirect_to(admin_application_path(application)) + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) end it 'renders the application form on errors' do @@ -49,10 +52,12 @@ describe Admin::ApplicationsController do describe 'PATCH #update' do it 'updates the application' do - patch :update, id: application.id, doorkeeper_application: { redirect_uri: 'http://example.com/' } + patch :update, id: application.id, doorkeeper_application: { redirect_uri: 'http://example.com/', trusted: true } + + application.reload expect(response).to redirect_to(admin_application_path(application)) - expect(application.reload.redirect_uri).to eq 'http://example.com/' + expect(application).to have_attributes(redirect_uri: 'http://example.com/', trusted: true) end it 'renders the application form on errors' do diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb new file mode 100644 index 00000000000..6eb9f7867d5 --- /dev/null +++ b/spec/controllers/admin/dashboard_controller_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Admin::DashboardController do + describe '#index' do + context 'with pending_delete projects' do + render_views + + it 'does not retrieve projects that are pending deletion' do + sign_in(create(:admin)) + + project = create(:project) + pending_delete_project = create(:project, pending_delete: true) + + get :index + + expect(response.body).to match(project.name) + expect(response.body).not_to match(pending_delete_project.name) + end + end + end +end diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index d321bfcea9d..ac7f73c6e81 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -42,8 +42,8 @@ describe Oauth::AuthorizationsController do end it 'deletes session.user_return_to and redirects when skip authorization' do + doorkeeper.update(trusted: true) request.session['user_return_to'] = 'http://example.com' - allow(controller).to receive(:skip_authorization?).and_return(true) get :new, params diff --git a/spec/controllers/projects/badges_controller_spec.rb b/spec/controllers/projects/badges_controller_spec.rb new file mode 100644 index 00000000000..d68200164e4 --- /dev/null +++ b/spec/controllers/projects/badges_controller_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Projects::BadgesController do + let(:project) { pipeline.project } + let!(:pipeline) { create(:ci_empty_pipeline) } + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in(user) + end + + it 'requests the pipeline badge successfully' do + get_badge(:pipeline) + + expect(response).to have_http_status(:ok) + end + + it 'requests the coverage badge successfully' do + get_badge(:coverage) + + expect(response).to have_http_status(:ok) + end + + def get_badge(badge) + get badge, namespace_id: project.namespace.to_param, project_id: project, ref: pipeline.ref, format: :svg + end +end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 18d0be3c103..2c57f3bcf8d 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -7,16 +7,30 @@ describe Projects::IssuesController do describe "GET #index" do context 'external issue tracker' do - let!(:service) do - create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker', project_url: 'http://test.com') + before do + sign_in(user) + project.add_developer(user) + create(:jira_service, project: project) end - it 'redirects to the external issue tracker' do - controller.instance_variable_set(:@project, project) + context 'when GitLab issues disabled' do + it 'returns 404 status' do + project.issues_enabled = false + project.save! - get :index, namespace_id: project.namespace, project_id: project + get :index, namespace_id: project.namespace, project_id: project + + expect(response).to have_http_status(404) + end + end + + context 'when GitLab issues enabled' do + it 'renders the "index" template' do + get :index, namespace_id: project.namespace, project_id: project - expect(response).to redirect_to(service.issue_tracker_path) + expect(response).to have_http_status(200) + expect(response).to render_template(:index) + end end end @@ -42,15 +56,7 @@ describe Projects::IssuesController do it "returns 404 when issues are disabled" do project.issues_enabled = false - project.save - - get :index, namespace_id: project.namespace, project_id: project - expect(response).to have_http_status(404) - end - - it "returns 404 when external issue tracker is enabled" do - controller.instance_variable_set(:@project, project) - allow(project).to receive(:default_issues_tracker?).and_return(false) + project.save! get :index, namespace_id: project.namespace, project_id: project expect(response).to have_http_status(404) @@ -148,14 +154,29 @@ describe Projects::IssuesController do before do sign_in(user) project.team << [user, :developer] + + external = double + allow(project).to receive(:external_issue_tracker).and_return(external) end - it 'redirects to the external issue tracker' do - controller.instance_variable_set(:@project, project) + context 'when GitLab issues disabled' do + it 'returns 404 status' do + project.issues_enabled = false + project.save! - get :new, namespace_id: project.namespace, project_id: project + get :new, namespace_id: project.namespace, project_id: project - expect(response).to redirect_to('http://test.com') + expect(response).to have_http_status(404) + end + end + + context 'when GitLab issues enabled' do + it 'renders the "new" template' do + get :new, namespace_id: project.namespace, project_id: project + + expect(response).to have_http_status(200) + expect(response).to render_template(:new) + end end end end @@ -806,7 +827,7 @@ describe Projects::IssuesController do delete :destroy, namespace_id: project.namespace, project_id: project, id: issue.iid expect(response).to have_http_status(302) - expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now + expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./) end it 'delegates the update of the todos count cache to TodoService' do diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 472e5fc51a0..5a295ae47a6 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -7,6 +7,10 @@ describe Projects::JobsController do let(:pipeline) { create(:ci_pipeline, project: project) } let(:user) { create(:user) } + before do + stub_not_protect_default_branch + end + describe 'GET index' do context 'when scope is pending' do before do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index c193babead0..2fce4b7a85f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -439,7 +439,7 @@ describe Projects::MergeRequestsController do delete :destroy, namespace_id: project.namespace, project_id: project, id: merge_request.iid expect(response).to have_http_status(302) - expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now + expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./) end it 'delegates the update of the todos count cache to TodoService' do diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 734532668d3..c8de275ca3e 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -8,6 +8,7 @@ describe Projects::PipelinesController do let(:feature) { ProjectFeature::DISABLED } before do + stub_not_protect_default_branch project.add_developer(user) project.project_feature.update( builds_access_level: feature) @@ -158,7 +159,7 @@ describe Projects::PipelinesController do context 'when builds are enabled' do let(:feature) { ProjectFeature::ENABLED } - + it 'retries a pipeline without returning any content' do expect(response).to have_http_status(:no_content) expect(build.reload).to be_retried @@ -175,7 +176,7 @@ describe Projects::PipelinesController do describe 'POST cancel.json' do let!(:pipeline) { create(:ci_pipeline, project: project) } let!(:build) { create(:ci_build, :running, pipeline: pipeline) } - + before do post :cancel, namespace_id: project.namespace, project_id: project, @@ -185,7 +186,7 @@ describe Projects::PipelinesController do context 'when builds are enabled' do let(:feature) { ProjectFeature::ENABLED } - + it 'cancels a pipeline without returning any content' do expect(response).to have_http_status(:no_content) expect(pipeline.reload).to be_canceled diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb index 7340a4e16c0..5d2734b8827 100644 --- a/spec/controllers/sent_notifications_controller_spec.rb +++ b/spec/controllers/sent_notifications_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe SentNotificationsController, type: :controller do +describe SentNotificationsController do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:sent_notification) { create(:sent_notification, project: project, noteable: issue, recipient: user) } @@ -23,7 +23,7 @@ describe SentNotificationsController, type: :controller do end it 'sets the flash message' do - expect(controller).to set_flash[:notice].to(/unsubscribed/).now + expect(controller).to set_flash[:notice].to(/unsubscribed/) end it 'redirects to the login page' do @@ -83,7 +83,7 @@ describe SentNotificationsController, type: :controller do end it 'sets the flash message' do - expect(controller).to set_flash[:notice].to(/unsubscribed/).now + expect(controller).to set_flash[:notice].to(/unsubscribed/) end it 'redirects to the issue page' do @@ -109,7 +109,7 @@ describe SentNotificationsController, type: :controller do end it 'sets the flash message' do - expect(controller).to set_flash[:notice].to(/unsubscribed/).now + expect(controller).to set_flash[:notice].to(/unsubscribed/) end it 'redirects to the merge request page' do diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 2b4e8723b48..a22fd8eaf9b 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' describe SessionsController do + include DeviseHelpers + describe '#new' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end context 'when auto sign-in is enabled' do @@ -34,7 +36,7 @@ describe SessionsController do describe '#create' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end context 'when using standard authentications' do @@ -257,7 +259,7 @@ describe SessionsController do describe '#new' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end it 'redirects correctly for referer on same host with params' do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 842d82cdbe9..7aeb6efd86d 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -80,9 +80,9 @@ describe UsersController do it 'renders calendar' do sign_in(user) - get :calendar, username: user.username + get :calendar, username: user.username, format: :json - expect(response).to render_template('calendar') + expect(response).to have_http_status(200) end context 'forked project' do diff --git a/spec/db/production/settings_spec.rb b/spec/db/production/settings_spec.rb index a9d015e0666..79e67330854 100644 --- a/spec/db/production/settings_spec.rb +++ b/spec/db/production/settings_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'rainbow/ext/string' -describe 'seed production settings', lib: true do +describe 'seed production settings' do include StubENV let(:settings_file) { Rails.root.join('db/fixtures/production/010_settings.rb') } let(:settings) { Gitlab::CurrentSettings.current_application_settings } diff --git a/spec/factories/ci/pipeline_variable_variables.rb b/spec/factories/ci/pipeline_variable_variables.rb new file mode 100644 index 00000000000..7c1a7faec08 --- /dev/null +++ b/spec/factories/ci/pipeline_variable_variables.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :ci_pipeline_variable, class: Ci::PipelineVariable do + sequence(:key) { |n| "VARIABLE_#{n}" } + value 'VARIABLE_VALUE' + + pipeline factory: :ci_empty_pipeline + end +end diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb new file mode 100644 index 00000000000..1258dce8940 --- /dev/null +++ b/spec/factories/gpg_keys.rb @@ -0,0 +1,8 @@ +require_relative '../support/gpg_helpers' + +FactoryGirl.define do + factory :gpg_key do + key GpgHelpers::User1.public_key + user + end +end diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb new file mode 100644 index 00000000000..a5aeffbe12d --- /dev/null +++ b/spec/factories/gpg_signature.rb @@ -0,0 +1,11 @@ +require_relative '../support/gpg_helpers' + +FactoryGirl.define do + factory :gpg_signature do + commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) } + project + gpg_key + gpg_key_primary_keyid { gpg_key.primary_keyid } + valid_signature true + end +end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 1bb2db11e7f..485ed48d2de 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -171,10 +171,6 @@ FactoryGirl.define do end after :create do |project, evaluator| - TestEnv.copy_repo(project, - bare_repo: TestEnv.factory_repo_path_bare, - refs: TestEnv::BRANCH_SHA) - if evaluator.create_template args = evaluator.create_template diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb index b2695e0482a..3dbace4b38a 100644 --- a/spec/factories/protected_branches.rb +++ b/spec/factories/protected_branches.rb @@ -3,26 +3,58 @@ FactoryGirl.define do name project - after(:build) do |protected_branch| - protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER) - protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER) + transient do + default_push_level true + default_merge_level true + default_access_level true end trait :developers_can_push do - after(:create) do |protected_branch| - protected_branch.push_access_levels.first.update!(access_level: Gitlab::Access::DEVELOPER) + transient do + default_push_level false + end + + after(:build) do |protected_branch| + protected_branch.push_access_levels.new(access_level: Gitlab::Access::DEVELOPER) end end trait :developers_can_merge do - after(:create) do |protected_branch| - protected_branch.merge_access_levels.first.update!(access_level: Gitlab::Access::DEVELOPER) + transient do + default_merge_level false + end + + after(:build) do |protected_branch| + protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER) end end trait :no_one_can_push do - after(:create) do |protected_branch| - protected_branch.push_access_levels.first.update!(access_level: Gitlab::Access::NO_ACCESS) + transient do + default_push_level false + end + + after(:build) do |protected_branch| + protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS) + end + end + + trait :masters_can_push do + transient do + default_push_level false + end + + after(:build) do |protected_branch| + protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER) + end + end + + after(:build) do |protected_branch, evaluator| + if evaluator.default_access_level && evaluator.default_push_level + protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER) + end + if evaluator.default_access_level && evaluator.default_merge_level + protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER) end end end diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb index d8e90ae1ee1..225588b23cc 100644 --- a/spec/factories/protected_tags.rb +++ b/spec/factories/protected_tags.rb @@ -3,19 +3,43 @@ FactoryGirl.define do name project - after(:build) do |protected_tag| - protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER) + transient do + default_access_level true end trait :developers_can_create do - after(:create) do |protected_tag| - protected_tag.create_access_levels.first.update!(access_level: Gitlab::Access::DEVELOPER) + transient do + default_access_level false + end + + after(:build) do |protected_tag| + protected_tag.create_access_levels.new(access_level: Gitlab::Access::DEVELOPER) end end trait :no_one_can_create do - after(:create) do |protected_tag| - protected_tag.create_access_levels.first.update!(access_level: Gitlab::Access::NO_ACCESS) + transient do + default_access_level false + end + + after(:build) do |protected_tag| + protected_tag.create_access_levels.new(access_level: Gitlab::Access::NO_ACCESS) + end + end + + trait :masters_can_create do + transient do + default_access_level false + end + + after(:build) do |protected_tag| + protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER) + end + end + + after(:build) do |protected_tag, evaluator| + if evaluator.default_access_level + protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER) end end end diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb index f26d3a6a72f..091fdcec3db 100644 --- a/spec/features/abuse_report_spec.rb +++ b/spec/features/abuse_report_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Abuse reports', feature: true do +feature 'Abuse reports' do let(:another_user) { create(:user) } before do diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 8672c009f90..2144f6ba635 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Admin::AbuseReports", feature: true, js: true do +describe "Admin::AbuseReports", js: true do let(:user) { create(:user) } context 'as an admin' do diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index b9e361328df..5f3a37c1dcc 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Admin Appearance', feature: true do +feature 'Admin Appearance' do let!(:appearance) { create(:appearance) } scenario 'Create new appearance' do @@ -63,11 +63,11 @@ feature 'Admin Appearance', feature: true do end def logo_selector - '//img[@src^="/uploads/-/system/appearance/logo"]' + '//img[data-src^="/uploads/-/system/appearance/logo"]' end def header_logo_selector - '//img[@src^="/uploads/-/system/appearance/header_logo"]' + '//img[data-src^="/uploads/-/system/appearance/header_logo"]' end def logo_fixture diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index e55308e393b..cbccf370514 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Admin Broadcast Messages', feature: true do +feature 'Admin Broadcast Messages' do before do sign_in(create(:admin)) create(:broadcast_message, :expired, message: 'Migration to new server') diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb index 6840456e509..bca52bf674c 100644 --- a/spec/features/admin/admin_cohorts_spec.rb +++ b/spec/features/admin/admin_cohorts_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Admin cohorts page', feature: true do +feature 'Admin cohorts page' do before do sign_in(create(:admin)) end diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb index aaeaaa829e1..241c7cbc34e 100644 --- a/spec/features/admin/admin_deploy_keys_spec.rb +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'admin deploy keys', type: :feature do +RSpec.describe 'admin deploy keys' do let!(:deploy_key) { create(:deploy_key, public: true) } let!(:another_deploy_key) { create(:another_deploy_key, public: true) } diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb index e2280b6e3b1..931f4ec3d24 100644 --- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb +++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Admin disables Git access protocol', feature: true do +feature 'Admin disables Git access protocol' do include StubENV let(:project) { create(:empty_project, :empty_repo) } diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb index 15dc6b6c234..e214ae6b78d 100644 --- a/spec/features/admin/admin_disables_two_factor_spec.rb +++ b/spec/features/admin/admin_disables_two_factor_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Admin disables 2FA for a user', feature: true do +feature 'Admin disables 2FA for a user' do scenario 'successfully', js: true do sign_in(create(:admin)) user = create(:user, :two_factor) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index d15d9982884..2e1bfcdcec3 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Admin Groups', feature: true do +feature 'Admin Groups' do include Select2Helper let(:internal) { Gitlab::VisibilityLevel::INTERNAL } diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb index c404e054dba..106e7370a98 100644 --- a/spec/features/admin/admin_health_check_spec.rb +++ b/spec/features/admin/admin_health_check_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature "Admin Health Check", feature: true do +feature "Admin Health Check" do include StubENV before do diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb index 94dace7a1fd..7910d5fb72b 100644 --- a/spec/features/admin/admin_hook_logs_spec.rb +++ b/spec/features/admin/admin_hook_logs_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Admin::HookLogs', feature: true do - let(:project) { create(:project) } +feature 'Admin::HookLogs' do + let(:project) { create(:empty_project) } let(:system_hook) { create(:system_hook) } let(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') } diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 9a438b65e68..141109fd464 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'Admin::Hooks', feature: true do +describe 'Admin::Hooks' do before do - @project = create(:project) + @project = create(:empty_project) sign_in(create(:admin)) @system_hook = create(:system_hook) diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index 2e04a82806f..f979d2f6090 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'admin manage applications', feature: true do +RSpec.describe 'admin manage applications' do before do sign_in(create(:admin)) end @@ -13,19 +13,24 @@ RSpec.describe 'admin manage applications', feature: true do fill_in :doorkeeper_application_name, with: 'test' fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' + check :doorkeeper_application_trusted click_on 'Submit' expect(page).to have_content('Application: test') expect(page).to have_content('Application Id') expect(page).to have_content('Secret') + expect(page).to have_content('Trusted Y') click_on 'Edit' expect(page).to have_content('Edit application') fill_in :doorkeeper_application_name, with: 'test_changed' + uncheck :doorkeeper_application_trusted + click_on 'Submit' expect(page).to have_content('test_changed') expect(page).to have_content('Application Id') expect(page).to have_content('Secret') + expect(page).to have_content('Trusted N') visit admin_applications_path page.within '.oauth-applications' do diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 942cc60e5dd..4f69eafcb9d 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,20 +1,21 @@ require 'spec_helper' -describe "Admin::Projects", feature: true do +describe "Admin::Projects" do include Select2Helper let(:user) { create :user } - let!(:project) { create(:project) } - let!(:current_user) { create(:admin) } + let(:project) { create(:empty_project) } + let(:current_user) { create(:admin) } before do sign_in(current_user) end describe "GET /admin/projects" do - let!(:archived_project) { create :project, :public, :archived } + let!(:archived_project) { create :empty_project, :public, :archived } before do + expect(project).to be_persisted visit admin_projects_path end @@ -39,15 +40,14 @@ describe "Admin::Projects", feature: true do describe "GET /admin/projects/:namespace_id/:id" do before do - visit admin_projects_path - click_link "#{project.name}" - end + expect(project).to be_persisted - it do - expect(current_path).to eq admin_project_path(project) + visit admin_projects_path + click_link project.name end it "has project info" do + expect(current_path).to eq admin_project_path(project) expect(page).to have_content(project.path) expect(page).to have_content(project.name) expect(page).to have_content(project.name_with_namespace) @@ -56,6 +56,9 @@ describe "Admin::Projects", feature: true do end describe 'transfer project' do + # The gitlab-shell transfer will fail for a project without a repository + let(:project) { create(:project, :repository) } + before do create(:group, name: 'Web') diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb index bf0c21cd04a..380cd5d7703 100644 --- a/spec/features/admin/admin_requests_profiles_spec.rb +++ b/spec/features/admin/admin_requests_profiles_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Admin::RequestsProfilesController', feature: true do +describe 'Admin::RequestsProfilesController' do before do FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR) sign_in(create(:admin)) diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index b06e7e5037c..46bab3763cc 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -19,7 +19,7 @@ describe "Admin Runners" do end it 'has all necessary texts' do - expect(page).to have_text "To register a new Runner" + expect(page).to have_text "How to setup" expect(page).to have_text "Runners with last contact more than a minute ago: 1" end @@ -54,7 +54,7 @@ describe "Admin Runners" do end it 'has all necessary texts including no runner message' do - expect(page).to have_text "To register a new Runner" + expect(page).to have_text "How to setup" expect(page).to have_text "Runners with last contact more than a minute ago: 0" expect(page).to have_text 'No runners found' end @@ -163,12 +163,11 @@ describe "Admin Runners" do end it 'has a registration token' do - expect(page).to have_content("Registration token is #{token}") - expect(page).to have_selector('#runners-token', text: token) + expect(page.find('#registration_token')).to have_content(token) end describe 'reload registration token' do - let(:page_token) { find('#runners-token').text } + let(:page_token) { find('#registration_token').text } before do click_button 'Reset runners registration token' diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index a44fa0b86d5..c1eced417cf 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Admin updates settings', feature: true do +feature 'Admin updates settings' do include StubENV before do diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index d01722805c4..97ffc54415c 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do +describe 'Admin > Users > Impersonation Tokens', js: true do let(:admin) { create(:admin) } let!(:user) { create(:user) } diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 3bc8f8aed54..0dde8bb696c 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Admin::Users", feature: true do +describe "Admin::Users" do let!(:user) do create(:omniauth_user, provider: 'twitter', extern_uid: '123456') end @@ -257,7 +257,7 @@ describe "Admin::Users", feature: true do describe "GET /admin/users/:id/projects" do let(:group) { create(:group) } - let!(:project) { create(:project, group: group) } + let!(:project) { create(:empty_project, group: group) } before do group.add_developer(user) diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 113353862be..5b3ee6ee822 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Admin uses repository checks', feature: true do +feature 'Admin uses repository checks' do include StubENV before do diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index 711c8a710f3..d70da7f09e9 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe "Dashboard Issues Feed", feature: true do +describe "Dashboard Issues Feed" do describe "GET /issues" do let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } - let!(:project1) { create(:project) } - let!(:project2) { create(:project) } + let!(:project1) { create(:empty_project) } + let!(:project2) { create(:empty_project) } before do project1.team << [user, :master] diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb index 2f4bb45d74b..a7c12853981 100644 --- a/spec/features/atom/dashboard_spec.rb +++ b/spec/features/atom/dashboard_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Dashboard Feed", feature: true do +describe "Dashboard Feed" do describe "GET /" do let!(:user) { create(:user, name: "Jonh") } @@ -19,7 +19,7 @@ describe "Dashboard Feed", feature: true do end context 'feed content' do - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project, author: user, description: '') } let(:note) { create(:note, noteable: issue, author: user, note: 'Bug confirmed', project: project) } diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb index 011fdce21d8..59e20d7e24d 100644 --- a/spec/features/atom/issues_spec.rb +++ b/spec/features/atom/issues_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe 'Issues Feed', feature: true do +describe 'Issues Feed' do describe 'GET /issues' do let!(:user) { create(:user, email: 'private1@example.com', public_email: 'public1@example.com') } let!(:assignee) { create(:user, email: 'private2@example.com', public_email: 'public2@example.com') } let!(:group) { create(:group) } - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:issue) { create(:issue, author: user, assignees: [assignee], project: project) } before do diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 44ae7204bcf..79069bbca8e 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "User Feed", feature: true do +describe "User Feed" do describe "GET /" do let!(:user) { create(:user) } @@ -19,7 +19,7 @@ describe "User Feed", feature: true do end context 'feed content' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:issue) do create(:issue, project: project, diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb index d883b467c67..c87469696da 100644 --- a/spec/features/boards/add_issues_modal_spec.rb +++ b/spec/features/boards/add_issues_modal_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards add issue modal', :feature, :js do +describe 'Issue Boards add issue modal', :js do let(:project) { create(:empty_project, :public) } let(:board) { create(:board, project: project) } let(:user) { create(:user) } diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index b939fb5e89e..c3711c9b2c5 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards', feature: true, js: true do +describe 'Issue Boards', js: true do include DragTo let(:group) { create(:group, :nested) } diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb index 17b0da80947..f4be56a4463 100644 --- a/spec/features/boards/issue_ordering_spec.rb +++ b/spec/features/boards/issue_ordering_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards', :feature, :js do +describe 'Issue Boards', :js do include DragTo let(:project) { create(:empty_project, :public) } diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb index 8c16148023e..415eda0e058 100644 --- a/spec/features/boards/keyboard_shortcut_spec.rb +++ b/spec/features/boards/keyboard_shortcut_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards shortcut', feature: true, js: true do +describe 'Issue Boards shortcut', js: true do let(:project) { create(:empty_project) } before do diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb index ce05bb71759..1c8b9c46569 100644 --- a/spec/features/boards/modal_filter_spec.rb +++ b/spec/features/boards/modal_filter_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards add issue modal filtering', :feature, :js do +describe 'Issue Boards add issue modal filtering', :js do let(:project) { create(:empty_project, :public) } let(:board) { create(:board, project: project) } let(:planning) { create(:label, project: project, name: 'Planning') } diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 6b267694201..1dbe3dbda11 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards new issue', feature: true, js: true do +describe 'Issue Boards new issue', js: true do let(:project) { create(:empty_project, :public) } let(:board) { create(:board, project: project) } let!(:list) { create(:list, board: board, position: 0) } diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index fa17ef92bbb..3f58fe1c32c 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Issue Boards', feature: true, js: true do +describe 'Issue Boards', js: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:project) { create(:empty_project, :public) } diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb index f88bf237301..f54f2234203 100644 --- a/spec/features/boards/sub_group_project_spec.rb +++ b/spec/features/boards/sub_group_project_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Sub-group project issue boards', :feature, :js do +describe 'Sub-group project issue boards', :js do let(:group) { create(:group) } let(:nested_group_1) { create(:group, parent: group) } let(:project) { create(:empty_project, group: nested_group_1) } diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index adbd82e3057..1e7fd7b62bd 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Contributions Calendar', :feature, :js do +feature 'Contributions Calendar', :js do let(:user) { create(:user) } let(:contributed_project) { create(:empty_project, :public) } let(:issue_note) { create(:note, project: contributed_project) } diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index fb1e47994ef..15ec6f20763 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -77,7 +77,7 @@ describe 'Commits' do end end - describe 'Commit builds', :feature, :js do + describe 'Commit builds', :js do before do visit ci_status_path(pipeline) end @@ -152,7 +152,7 @@ describe 'Commits' do visit ci_status_path(pipeline) end - it 'Renders header', :feature, :js do + it 'Renders header', :js do expect(page).to have_content pipeline.sha[0..7] expect(page).to have_content pipeline.git_commit_message expect(page).to have_content pipeline.user.name @@ -165,7 +165,7 @@ describe 'Commits' do end end - context 'when accessing internal project with disallowed access', :feature, :js do + context 'when accessing internal project with disallowed access', :js do before do project.update( visibility_level: Gitlab::VisibilityLevel::INTERNAL, @@ -203,4 +203,105 @@ describe 'Commits' do end end end + + describe 'GPG signed commits', :js do + it 'changes from unverified to verified when the user changes his email to match the gpg key' do + user = create :user, email: 'unrelated.user@example.org' + project.team << [user, :master] + + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + sign_in(user) + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).not_to have_content 'Verified' + end + + # user changes his email which makes the gpg key verified + Sidekiq::Testing.inline! do + user.skip_reconfirmation! + user.update_attributes!(email: GpgHelpers::User1.emails.first) + end + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).to have_content 'Verified' + end + end + + it 'changes from unverified to verified when the user adds the missing gpg key' do + user = create :user, email: GpgHelpers::User1.emails.first + project.team << [user, :master] + + sign_in(user) + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).not_to have_content 'Verified' + end + + # user adds the gpg key which makes the signature valid + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: user + end + + visit project_commits_path(project, :'signed-commits') + + within '#commits-list' do + expect(page).to have_content 'Unverified' + expect(page).to have_content 'Verified' + end + end + + it 'shows popover badges' do + gpg_user = create :user, email: GpgHelpers::User1.emails.first, username: 'nannie.bernhard', name: 'Nannie Bernhard' + Sidekiq::Testing.inline! do + create :gpg_key, key: GpgHelpers::User1.public_key, user: gpg_user + end + + user = create :user + project.team << [user, :master] + + sign_in(user) + visit project_commits_path(project, :'signed-commits') + + # unverified signature + click_on 'Unverified', match: :first + within '.popover' do + expect(page).to have_content 'This commit was signed with an unverified signature.' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}" + end + + # verified and the gpg user has a gitlab profile + click_on 'Verified' + within '.popover' do + expect(page).to have_content 'This commit was signed with a verified signature.' + expect(page).to have_content 'Nannie Bernhard' + expect(page).to have_content '@nannie.bernhard' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}" + end + + # verified and the gpg user's profile doesn't exist anymore + gpg_user.destroy! + + visit project_commits_path(project, :'signed-commits') + + click_on 'Verified' + within '.popover' do + expect(page).to have_content 'This commit was signed with a verified signature.' + expect(page).to have_content 'Nannie Bernhard' + expect(page).to have_content 'nannie.bernhard@example.com' + expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}" + end + end + end end diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb index 11d5a4f421f..3e6a27eafd8 100644 --- a/spec/features/copy_as_gfm_spec.rb +++ b/spec/features/copy_as_gfm_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Copy as GFM', feature: true, js: true do +describe 'Copy as GFM', js: true do include MarkupHelper include RepoHelpers include ActionView::Helpers::JavaScriptHelper diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index f530063352a..5c60cca10b9 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Cycle Analytics', feature: true, js: true do +feature 'Cycle Analytics', js: true do let(:user) { create(:user) } let(:guest) { create(:user) } let(:project) { create(:project, :repository) } diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb index 203d206b80b..067e4337e6a 100644 --- a/spec/features/dashboard/active_tab_spec.rb +++ b/spec/features/dashboard/active_tab_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'Dashboard Active Tab', js: true, feature: true do +RSpec.describe 'Dashboard Active Tab', js: true do before do sign_in(create(:user)) end diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb index dda4d517e39..ceac6a0a27c 100644 --- a/spec/features/dashboard/archived_projects_spec.rb +++ b/spec/features/dashboard/archived_projects_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -RSpec.describe 'Dashboard Archived Project', feature: true do +RSpec.describe 'Dashboard Archived Project' do let(:user) { create :user } let(:project) { create :project} - let(:archived_project) { create(:project, :archived) } + let(:archived_project) { create(:empty_project, :archived) } before do project.team << [user, :master] diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb index 8949267c82e..05dcdd93f37 100644 --- a/spec/features/dashboard/datetime_on_tooltips_spec.rb +++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Tooltips on .timeago dates', feature: true, js: true do +feature 'Tooltips on .timeago dates', js: true do let(:user) { create(:user) } - let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } let(:created_date) { Date.yesterday.to_time } let(:expected_format) { created_date.in_time_zone.strftime('%b %-d, %Y %l:%M%P') } diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index ffaefb9c632..60a16830cdc 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'Dashboard Group', feature: true do +RSpec.describe 'Dashboard Group' do before do sign_in(create(:user)) end diff --git a/spec/features/dashboard/help_spec.rb b/spec/features/dashboard/help_spec.rb index fa7ea4c96b6..68bfbf22736 100644 --- a/spec/features/dashboard/help_spec.rb +++ b/spec/features/dashboard/help_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'Dashboard Help', feature: true do +RSpec.describe 'Dashboard Help' do before do sign_in(create(:user)) end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 6b666934563..ae68b0f65d5 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, feature: true do +describe 'Navigation bar counter', :use_clean_rails_memory_store_caching do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb index 9b84f67b555..0ce642f32f2 100644 --- a/spec/features/dashboard/issues_filter_spec.rb +++ b/spec/features/dashboard/issues_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Dashboard Issues filtering', js: true do +feature 'Dashboard Issues filtering', :js do include SortingHelper let(:user) { create(:user) } diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb index 69c1a2ed89a..7c0bf8de14c 100644 --- a/spec/features/dashboard/issues_spec.rb +++ b/spec/features/dashboard/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe 'Dashboard Issues', feature: true do +RSpec.describe 'Dashboard Issues' do let(:current_user) { create :user } let(:user) { current_user } # Shared examples depend on this being available let!(:public_project) { create(:empty_project, :public) } @@ -78,5 +78,27 @@ RSpec.describe 'Dashboard Issues', feature: true do expect(page).not_to have_content(project_with_issues_disabled.name_with_namespace) end end + + it 'shows the new issue page', js: true do + original_defaults = Gitlab::Application.routes.default_url_options + + Gitlab::Application.routes.default_url_options = { + host: Capybara.current_session.server.host, + port: Capybara.current_session.server.port, + protocol: 'http' + } + + find('.new-project-item-select-button').trigger('click') + wait_for_requests + find('.select2-results li').click + + expect(page).to have_current_path("/#{project.path_with_namespace}/issues/new") + + page.within('#content-body') do + expect(page).to have_selector('.issue-form') + end + + Gitlab::Application.routes.default_url_options = original_defaults + end end end diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb index 8b7dacef913..8e19fb93665 100644 --- a/spec/features/dashboard/label_filter_spec.rb +++ b/spec/features/dashboard/label_filter_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe 'Dashboard > label filter', feature: true, js: true do +describe 'Dashboard > label filter', js: true do let(:user) { create(:user) } - let(:project) { create(:project, name: 'test', namespace: user.namespace) } - let(:project2) { create(:project, name: 'test2', path: 'test2', namespace: user.namespace) } + let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } + let(:project2) { create(:empty_project, name: 'test2', path: 'test2', namespace: user.namespace) } let(:label) { create(:label, title: 'bug', color: '#ff0000') } let(:label2) { create(:label, title: 'bug') } diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb index d06497041de..5ebef1eb097 100644 --- a/spec/features/dashboard/milestone_filter_spec.rb +++ b/spec/features/dashboard/milestone_filter_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Dashboard > milestone filter', :feature, :js do +feature 'Dashboard > milestone filter', :js do include FilterItemSelectHelper let(:user) { create(:user) } - let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } let(:milestone) { create(:milestone, title: 'v1.0', project: project) } let(:milestone2) { create(:milestone, title: 'v2.0', project: project) } let!(:issue) { create :issue, author: user, project: project, milestone: milestone } diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb index 8340a4f59df..cf32d705365 100644 --- a/spec/features/dashboard/milestone_tabs_spec.rb +++ b/spec/features/dashboard/milestone_tabs_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Dashboard milestone tabs', :js, :feature do +describe 'Dashboard milestone tabs', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let!(:label) { create(:label, project: project) } diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 7a6a448d4c2..488f7397c69 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Dashboard > Milestones', feature: true do +feature 'Dashboard > Milestones' do describe 'as anonymous user' do before do visit dashboard_milestones_path diff --git a/spec/features/dashboard/project_member_activity_index_spec.rb b/spec/features/dashboard/project_member_activity_index_spec.rb index ea0b2e99c3e..f3b538e490e 100644 --- a/spec/features/dashboard/project_member_activity_index_spec.rb +++ b/spec/features/dashboard/project_member_activity_index_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project member activity', feature: true, js: true do +feature 'Project member activity', js: true do let(:user) { create(:user) } let(:project) { create(:empty_project, :public, name: 'x', namespace: user.namespace) } diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index bb29dae1bdc..5f1f0c10339 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Dashboard shortcuts', :feature, :js do +feature 'Dashboard shortcuts', :js do context 'logged in' do before do sign_in(create(:user)) diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb index c5ae9aad9c6..c29bcc7c9e9 100644 --- a/spec/features/dashboard/snippets_spec.rb +++ b/spec/features/dashboard/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Dashboard snippets', feature: true do +describe 'Dashboard snippets' do context 'when the project has snippets' do let(:project) { create(:empty_project, :public) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } diff --git a/spec/features/dashboard/todos/target_state_spec.rb b/spec/features/dashboard/todos/target_state_spec.rb index 030a86d1c01..93da36c08fc 100644 --- a/spec/features/dashboard/todos/target_state_spec.rb +++ b/spec/features/dashboard/todos/target_state_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' feature 'Dashboard > Todo target states' do let(:user) { create(:user) } let(:author) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } before do sign_in(user) diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index 30bab7eeaa7..c2a61cf5aff 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Dashboard Todos' do let(:user) { create(:user) } let(:author) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, due_date: Date.today) } context 'User does not have todos' do @@ -212,7 +212,7 @@ feature 'Dashboard Todos' do note1 = create(:note_on_issue, note: "Hello #{label1.to_reference(format: :name)}", noteable_id: issue.id, noteable_type: 'Issue', project: issue.project) create(:todo, :mentioned, project: project, target: issue, user: user, note_id: note1.id) - project2 = create(:project, :public) + project2 = create(:empty_project, :public) label2 = create(:label, project: project2) issue2 = create(:issue, project: project2) note2 = create(:note_on_issue, note: "Test #{label2.to_reference(format: :name)}", noteable_id: issue2.id, noteable_type: 'Issue', project: project2) diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index 711d3617335..a88fe207e0e 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe 'Dashboard > User filters projects', :feature do +describe 'Dashboard > User filters projects' do let(:user) { create(:user) } - let(:project) { create(:project, name: 'Victorialand', namespace: user.namespace) } + let(:project) { create(:empty_project, name: 'Victorialand', namespace: user.namespace) } let(:user2) { create(:user) } - let(:project2) { create(:project, name: 'Treasure', namespace: user2.namespace) } + let(:project2) { create(:empty_project, name: 'Treasure', namespace: user2.namespace) } before do project.team << [user, :master] diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb index 26d21207678..fa83ad5d17c 100644 --- a/spec/features/discussion_comments/commit_spec.rb +++ b/spec/features/discussion_comments/commit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Merge Request', :feature, :js do +describe 'Discussion Comments Merge Request', :js do include RepoHelpers let(:user) { create(:user) } diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb index 11dbe10e1df..f52ba9c4d09 100644 --- a/spec/features/discussion_comments/issue_spec.rb +++ b/spec/features/discussion_comments/issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Issue', :feature, :js do +describe 'Discussion Comments Issue', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb index db745be6fa1..042f39f47e0 100644 --- a/spec/features/discussion_comments/merge_request_spec.rb +++ b/spec/features/discussion_comments/merge_request_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Merge Request', :feature, :js do +describe 'Discussion Comments Merge Request', :js do let(:user) { create(:user) } let(:project) { create(:project) } let(:merge_request) { create(:merge_request, source_project: project) } diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb index eddbd4bde9b..50ba13499d9 100644 --- a/spec/features/discussion_comments/snippets_spec.rb +++ b/spec/features/discussion_comments/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Discussion Comments Issue', :feature, :js do +describe 'Discussion Comments Issue', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:snippet) { create(:project_snippet, :private, project: project, author: user) } diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 18c06a48111..357d86497d9 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Expand and collapse diffs', js: true, feature: true do +feature 'Expand and collapse diffs', js: true do let(:branch) { 'expand-collapse-diffs' } let(:project) { create(:project, :repository) } diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb index 008d12714cc..84f41eca999 100644 --- a/spec/features/explore/groups_list_spec.rb +++ b/spec/features/explore/groups_list_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Explore Groups page', :js, :feature do +describe 'Explore Groups page', :js do let!(:user) { create :user } let!(:group) { create(:group) } let!(:public_group) { create(:group, :public) } diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb index 7dd69f550ac..b1ccf80c28e 100644 --- a/spec/features/explore/new_menu_spec.rb +++ b/spec/features/explore/new_menu_spec.rb @@ -1,17 +1,13 @@ require 'spec_helper' -feature 'Top Plus Menu', feature: true, js: true do - let(:user) { create :user } - let(:guest_user) { create :user} +feature 'Top Plus Menu', :js do + let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } - let(:public_project) { create(:project, :public) } + let(:public_project) { create(:empty_project, :public) } before do group.add_owner(user) - group.add_guest(guest_user) - - project.add_guest(guest_user) end context 'used by full user' do @@ -39,7 +35,7 @@ feature 'Top Plus Menu', feature: true, js: true do scenario 'click on New snippet shows new snippet page' do visit root_dashboard_path - + click_topmenuitem("New snippet") expect(page).to have_content('New Snippet') @@ -102,7 +98,12 @@ feature 'Top Plus Menu', feature: true, js: true do end context 'used by guest user' do + let(:guest_user) { create(:user) } + before do + group.add_guest(guest_user) + project.add_guest(guest_user) + sign_in(guest_user) end @@ -153,7 +154,7 @@ feature 'Top Plus Menu', feature: true, js: true do scenario 'has no New project for group menu item' do visit group_path(group) - + expect(find('.header-new.dropdown')).not_to have_selector('.header-new-group-project') end end @@ -168,5 +169,5 @@ feature 'Top Plus Menu', feature: true, js: true do def hasnot_topmenuitem(item_name) expect(find('.header-new.dropdown')).not_to have_content(item_name) - end + end end diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb index 8659a868682..300296a2b94 100644 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ b/spec/features/gitlab_flavored_markdown_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "GitLab Flavored Markdown", feature: true do +describe "GitLab Flavored Markdown" do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } @@ -49,7 +49,7 @@ describe "GitLab Flavored Markdown", feature: true do end end - describe "for issues", feature: true, js: true do + describe "for issues", js: true do before do @other_issue = create(:issue, author: user, diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb index efa5e95de89..627a930c997 100644 --- a/spec/features/global_search_spec.rb +++ b/spec/features/global_search_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Global search', feature: true do +feature 'Global search' do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb index 262d9434ddf..d3b25ec3d6c 100644 --- a/spec/features/groups/activity_spec.rb +++ b/spec/features/groups/activity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group activity page', feature: true do +feature 'Group activity page' do let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user } let(:group) { create(:group) } let(:path) { activity_group_path(group) } diff --git a/spec/features/groups/group_name_toggle_spec.rb b/spec/features/groups/group_name_toggle_spec.rb index ea779a3baf0..a7b8b702ab7 100644 --- a/spec/features/groups/group_name_toggle_spec.rb +++ b/spec/features/groups/group_name_toggle_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group name toggle', feature: true, js: true do +feature 'Group name toggle', js: true do let(:group) { create(:group) } let(:nested_group_1) { create(:group, parent: group) } let(:nested_group_2) { create(:group, parent: nested_group_1) } diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index f7ef7f29066..121df1ec635 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Edit group settings', feature: true do +feature 'Edit group settings' do given(:user) { create(:user) } given(:group) { create(:group, path: 'foo') } @@ -49,7 +49,7 @@ feature 'Edit group settings', feature: true do end context 'with a project' do - given!(:project) { create(:project, group: group, path: 'project') } + given!(:project) { create(:empty_project, group: group) } given(:old_project_full_path) { "/#{group.path}/#{project.path}" } given(:new_project_full_path) { "/#{new_group_path}/#{project.path}" } @@ -65,14 +65,14 @@ feature 'Edit group settings', feature: true do update_path(new_group_path) visit new_project_full_path expect(current_path).to eq(new_project_full_path) - expect(find('h1.project-title')).to have_content(project.name) + expect(find('h1.title')).to have_content(project.path) end scenario 'the old project path redirects to the new path' do update_path(new_group_path) visit old_project_full_path expect(current_path).to eq(new_project_full_path) - expect(find('h1.project-title')).to have_content(project.name) + expect(find('h1.title')).to have_content(project.path) end end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index d6b88542ef7..449a99a2c7b 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group issues page', feature: true do +feature 'Group issues page' do let(:path) { issues_group_path(group) } let(:issuable) { create(:issue, project: project, title: "this is my created issuable")} diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb index 88d104d5a06..fb338127861 100644 --- a/spec/features/groups/labels/edit_spec.rb +++ b/spec/features/groups/labels/edit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Edit group label', feature: true do +feature 'Edit group label' do given(:user) { create(:user) } given(:group) { create(:group) } given(:label) { create(:group_label, group: group) } diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb index 8b891c52d08..1dd09d4f203 100644 --- a/spec/features/groups/labels/subscription_spec.rb +++ b/spec/features/groups/labels/subscription_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Labels subscription', feature: true do +feature 'Labels subscription' do let(:user) { create(:user) } let(:group) { create(:group) } let!(:feature) { create(:group_label, group: group, title: 'feature') } diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb index b438f57753c..067a2dc850f 100644 --- a/spec/features/groups/members/leave_group_spec.rb +++ b/spec/features/groups/members/leave_group_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Groups > Members > Leave group', feature: true do +feature 'Groups > Members > Leave group' do let(:user) { create(:user) } let(:other_user) { create(:user) } let(:group) { create(:group) } diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb index f6493c4c50e..5c5d48c3623 100644 --- a/spec/features/groups/members/list_members_spec.rb +++ b/spec/features/groups/members/list_members_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Groups > Members > List members', feature: true do +feature 'Groups > Members > List members' do include Select2Helper let(:user1) { create(:user, name: 'John Doe') } diff --git a/spec/features/groups/members/manage_access_requests_spec.rb b/spec/features/groups/members/manage_access_requests_spec.rb index 51a4d769b9c..b83cd657ef7 100644 --- a/spec/features/groups/members/manage_access_requests_spec.rb +++ b/spec/features/groups/members/manage_access_requests_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Groups > Members > Manage access requests', feature: true do +feature 'Groups > Members > Manage access requests' do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public, :access_requestable) } diff --git a/spec/features/groups/members/manage_members.rb b/spec/features/groups/members/manage_members.rb index 4b226893701..9039b283393 100644 --- a/spec/features/groups/members/manage_members.rb +++ b/spec/features/groups/members/manage_members.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Groups > Members > Manage members', feature: true do +feature 'Groups > Members > Manage members' do include Select2Helper let(:user1) { create(:user, name: 'John Doe') } diff --git a/spec/features/groups/members/request_access_spec.rb b/spec/features/groups/members/request_access_spec.rb index 3764e4792ca..6141981023c 100644 --- a/spec/features/groups/members/request_access_spec.rb +++ b/spec/features/groups/members/request_access_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Groups > Members > Request access', feature: true do +feature 'Groups > Members > Request access' do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public, :access_requestable) } - let!(:project) { create(:project, :private, namespace: group) } + let!(:project) { create(:empty_project, :private, namespace: group) } background do group.add_owner(owner) diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb index 92ff45e0cdc..e175ad04f86 100644 --- a/spec/features/groups/members/sort_members_spec.rb +++ b/spec/features/groups/members/sort_members_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Groups > Members > Sort members', feature: true do +feature 'Groups > Members > Sort members' do let(:owner) { create(:user, name: 'John Doe') } let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } let(:group) { create(:group) } diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index b55078c3bf6..c2241feb9f7 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -feature 'Group merge requests page', feature: true do +feature 'Group merge requests page' do let(:path) { merge_requests_group_path(group) } let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: 'this is my created issuable') } include_examples 'project features apply to issuables', MergeRequest context 'archived issuable' do - let(:project_archived) { create(:project, :archived, :merge_requests_enabled, group: group) } + let(:project_archived) { create(:project, :archived, :merge_requests_enabled, :repository, group: group) } let(:issuable_archived) { create(:merge_request, source_project: project_archived, target_project: project_archived, title: 'issuable of an archived project') } let(:access_level) { ProjectFeature::ENABLED } let(:user) { user_in_group } diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 0f3f005040f..574bbe0e0e1 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Group milestones', :feature, :js do +feature 'Group milestones', :js do let(:group) { create(:group) } let!(:project) { create(:project_empty_repo, group: group) } let(:user) { create(:group_member, :master, user: create(:user), group: group ).user } diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index cbf97d0674b..303013e59d5 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group show page', feature: true do +feature 'Group show page' do let(:group) { create(:group) } let(:path) { group_path(group) } diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 6f8c8999f98..e59a484d992 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Group', feature: true do +feature 'Group' do before do sign_in(create(:admin)) end diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb index 7fe65ee554d..bd4f233cba9 100644 --- a/spec/features/help_pages_spec.rb +++ b/spec/features/help_pages_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Help Pages', feature: true do +describe 'Help Pages' do describe 'Get the main help page' do shared_examples_for 'help page' do |prefix: ''| it 'prefixes links correctly' do diff --git a/spec/features/issuables/close_reopen_report_toggle_spec.rb b/spec/features/issuables/close_reopen_report_toggle_spec.rb index 9a99bb705b7..0e43eed8699 100644 --- a/spec/features/issuables/close_reopen_report_toggle_spec.rb +++ b/spec/features/issuables/close_reopen_report_toggle_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Issuables Close/Reopen/Report toggle', :feature do +describe 'Issuables Close/Reopen/Report toggle' do let(:user) { create(:user) } shared_examples 'an issuable close/reopen/report toggle' do @@ -79,7 +79,7 @@ describe 'Issuables Close/Reopen/Report toggle', :feature do end context 'on a merge request' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:issuable) { create(:merge_request, source_project: project) } before do @@ -96,7 +96,7 @@ describe 'Issuables Close/Reopen/Report toggle', :feature do end context 'when user doesnt have permission to update' do - let(:cant_project) { create(:project) } + let(:cant_project) { create(:project, :repository) } let(:cant_issuable) { create(:merge_request, source_project: cant_project) } before do diff --git a/spec/features/issuables/default_sort_order_spec.rb b/spec/features/issuables/default_sort_order_spec.rb index 56c9b10e757..7c20c96528e 100644 --- a/spec/features/issuables/default_sort_order_spec.rb +++ b/spec/features/issuables/default_sort_order_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Projects > Issuables > Default sort order', feature: true do +describe 'Projects > Issuables > Default sort order' do let(:project) { create(:empty_project, :public) } let(:first_created_issuable) { issuables.order_created_asc.first } diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb index 32fee2d9c34..557de721222 100644 --- a/spec/features/issuables/issuable_list_spec.rb +++ b/spec/features/issuables/issuable_list_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'issuable list', feature: true do +describe 'issuable list' do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/features/issuables/markdown_references_spec.rb b/spec/features/issuables/markdown_references_spec.rb new file mode 100644 index 00000000000..169381d703a --- /dev/null +++ b/spec/features/issuables/markdown_references_spec.rb @@ -0,0 +1,193 @@ +require 'rails_helper' + +describe 'Markdown References', :js do + let(:user) { create(:user) } + let(:actual_project) { create(:project, :public) } + let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project)} + let(:issue_actual_project) { create(:issue, project: actual_project) } + let!(:other_project) { create(:empty_project, :public) } + let!(:issue_other_project) { create(:issue, project: other_project) } + let(:issues) { [issue_actual_project, issue_other_project] } + + def build_note + markdown = "Referencing internal issue #{issue_actual_project.to_reference}, " + + "cross-project #{issue_other_project.to_reference(actual_project)} external JIRA-5 " + + "and non existing #999" + + page.within('#diff-notes-app') do + fill_in 'note_note', with: markdown + end + end + + shared_examples 'correct references' do + before do + remotelink = double(:remotelink, all: [], build: double(save!: true)) + + stub_request(:get, "https://jira.example.com/rest/api/2/issue/JIRA-5") + stub_request(:post, "https://jira.example.com/rest/api/2/issue/JIRA-5/comment") + allow_any_instance_of(JIRA::Resource::Issue).to receive(:remotelink).and_return(remotelink) + + sign_in(user) + visit merge_request_path(merge_request) + build_note + end + + def links_expectations + issues.each do |issue| + if referenced_issues.include?(issue) + expect(page).to have_link(issue.to_reference, href: issue_path(issue)) + else + expect(page).not_to have_link(issue.to_reference, href: issue_path(issue)) + end + end + + if jira_referenced + expect(page).to have_link('JIRA-5', href: 'https://jira.example.com/browse/JIRA-5') + else + expect(page).not_to have_link('JIRA-5', href: 'https://jira.example.com/browse/JIRA-5') + end + + expect(page).not_to have_link('#999') + end + + it 'creates a link to the referenced issue on the preview' do + find('.js-md-preview-button').click + wait_for_requests + + page.within('.md-preview-holder') do + links_expectations + end + end + + it 'creates a link to the referenced issue after submit' do + click_button 'Comment' + wait_for_requests + + page.within('#diff-notes-app') do + links_expectations + end + end + + it 'creates a note on the referenced issues' do + click_button 'Comment' + wait_for_requests + + if referenced_issues.include?(issue_actual_project) + visit issue_path(issue_actual_project) + + page.within('#notes') do + expect(page).to have_content( + "#{user.to_reference} mentioned in merge request #{merge_request.to_reference}" + ) + end + end + + if referenced_issues.include?(issue_other_project) + visit issue_path(issue_other_project) + + page.within('#notes') do + expect(page).to have_content( + "#{user.to_reference} mentioned in merge request #{merge_request.to_reference(other_project)}" + ) + end + end + end + end + + context 'when internal issues tracker is enabled for the other project' do + context 'when only internal issues tracker is enabled for the actual project' do + include_examples 'correct references' do + let(:referenced_issues) { [issue_actual_project, issue_other_project] } + let(:jira_referenced) { false } + end + end + + context 'when both external and internal issues trackers are enabled for the actual project' do + before do + create(:jira_service, project: actual_project) + end + + include_examples 'correct references' do + let(:referenced_issues) { [issue_actual_project, issue_other_project] } + let(:jira_referenced) { true } + end + end + + context 'when only external issues tracker is enabled for the actual project' do + before do + create(:jira_service, project: actual_project) + + actual_project.issues_enabled = false + actual_project.save! + end + + include_examples 'correct references' do + let(:referenced_issues) { [issue_other_project] } + let(:jira_referenced) { true } + end + end + + context 'when no tracker is enabled for the actual project' do + before do + actual_project.issues_enabled = false + actual_project.save! + end + + include_examples 'correct references' do + let(:referenced_issues) { [issue_other_project] } + let(:jira_referenced) { false } + end + end + end + + context 'when internal issues tracker is disabled for the other project' do + before do + other_project.issues_enabled = false + other_project.save! + end + + context 'when only internal issues tracker is enabled for the actual project' do + include_examples 'correct references' do + let(:referenced_issues) { [issue_actual_project] } + let(:jira_referenced) { false } + end + end + + context 'when both external and internal issues trackers are enabled for the actual project' do + before do + create(:jira_service, project: actual_project) + end + + include_examples 'correct references' do + let(:referenced_issues) { [issue_actual_project] } + let(:jira_referenced) { true } + end + end + + context 'when only external issues tracker is enabled for the actual project' do + before do + create(:jira_service, project: actual_project) + + actual_project.issues_enabled = false + actual_project.save! + end + + include_examples 'correct references' do + let(:referenced_issues) { [] } + let(:jira_referenced) { true } + end + end + + context 'when no issues tracker is enabled for the actual project' do + before do + actual_project.issues_enabled = false + actual_project.save! + end + + include_examples 'correct references' do + let(:referenced_issues) { [] } + let(:jira_referenced) { false } + end + end + end +end diff --git a/spec/features/issuables/user_sees_sidebar_spec.rb b/spec/features/issuables/user_sees_sidebar_spec.rb index 948d151a517..2bd1c8aab86 100644 --- a/spec/features/issuables/user_sees_sidebar_spec.rb +++ b/spec/features/issuables/user_sees_sidebar_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe 'Issue Sidebar on Mobile' do include MobileHelpers - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb index 823c779e0d9..6cb0bf6fdfd 100644 --- a/spec/features/issues/award_emoji_spec.rb +++ b/spec/features/issues/award_emoji_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' -describe 'Awards Emoji', feature: true do - let!(:project) { create(:project, :public) } +describe 'Awards Emoji' do + let!(:project) { create(:empty_project, :public) } let!(:user) { create(:user) } let(:issue) do create(:issue, diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb index 76cffc1d8c9..740281c1050 100644 --- a/spec/features/issues/award_spec.rb +++ b/spec/features/issues/award_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Issue awards', js: true, feature: true do +feature 'Issue awards', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project) } describe 'logged in' do diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 034d8afb54d..5acf8fdae84 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Issues > Labels bulk assignment', feature: true do +feature 'Issues > Labels bulk assignment' do let(:user) { create(:user) } - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:issue1) { create(:issue, project: project, title: "Issue 1") } let!(:issue2) { create(:issue, project: project, title: "Issue 2") } let!(:bug) { create(:label, project: project, title: 'bug') } diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb index 6e778f4d7e5..c10b99a4386 100644 --- a/spec/features/issues/create_branch_merge_request_spec.rb +++ b/spec/features/issues/create_branch_merge_request_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js: true do +feature 'Create Branch/Merge Request Dropdown on issue page', js: true do let(:user) { create(:user) } - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let(:issue) { create(:issue, project: project, title: 'Cherry-Coloured Funk') } context 'for team members' do diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb index dd9a7f1253d..80cc8d22999 100644 --- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Resolving all open discussions in a merge request from an issue', feature: true, js: true do +feature 'Resolving all open discussions in a merge request from an issue', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb index 5c291f7b817..ad5fd0fd97b 100644 --- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb +++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Resolve an open discussion in a merge request by creating an issue', feature: true do +feature 'Resolve an open discussion in a merge request by creating an issue' do let(:user) { create(:user) } - let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) } + let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) } let(:merge_request) { create(:merge_request, source_project: project) } let!(:discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion } diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index 2765d5448a4..a403d885de0 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Dropdown assignee', :feature, :js do +describe 'Dropdown assignee', :js do include FilteredSearchHelpers let!(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb index 98b1c5ee1b5..b7d9bbd7e1d 100644 --- a/spec/features/issues/filtered_search/dropdown_author_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Dropdown author', js: true, feature: true do +describe 'Dropdown author', js: true do include FilteredSearchHelpers let!(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb index fdc003f81b3..292fd683271 100644 --- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Dropdown hint', :js, :feature do +describe 'Dropdown hint', :js do include FilteredSearchHelpers let!(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb index 26a0320675f..e8f005d7752 100644 --- a/spec/features/issues/filtered_search/dropdown_label_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Dropdown label', js: true, feature: true do +describe 'Dropdown label', js: true do include FilteredSearchHelpers let(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb index 7c74d8dffff..ace73f4b1a6 100644 --- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Dropdown milestone', :feature, :js do +describe 'Dropdown milestone', :js do include FilteredSearchHelpers let!(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 9fc6391fa98..265bcb3a8e5 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe 'Filter issues', js: true, feature: true do +describe 'Filter issues', js: true do include Devise::Test::IntegrationHelpers include FilteredSearchHelpers let!(:group) { create(:group) } - let!(:project) { create(:project, group: group) } + let!(:project) { create(:empty_project, group: group) } let!(:user) { create(:user, username: 'joe', name: 'Joe') } let!(:user2) { create(:user, username: 'jane') } let!(:label) { create(:label, project: project) } diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb index 4a91ce4be07..5842bb22beb 100644 --- a/spec/features/issues/filtered_search/recent_searches_spec.rb +++ b/spec/features/issues/filtered_search/recent_searches_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Recent searches', js: true, feature: true do +describe 'Recent searches', js: true do include FilteredSearchHelpers let(:project_1) { create(:empty_project, :public) } diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb index b16c5c280c7..115875d72ce 100644 --- a/spec/features/issues/filtered_search/search_bar_spec.rb +++ b/spec/features/issues/filtered_search/search_bar_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Search bar', js: true, feature: true do +describe 'Search bar', js: true do include FilteredSearchHelpers let!(:project) { create(:empty_project) } diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb index a15c3d1d447..d00d0a9c81b 100644 --- a/spec/features/issues/filtered_search/visual_tokens_spec.rb +++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Visual tokens', js: true, feature: true do +describe 'Visual tokens', js: true do include FilteredSearchHelpers include WaitForRequests diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 05742004f06..0ba02ba42ba 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -describe 'New/edit issue', :feature, :js do +describe 'New/edit issue', :js do include ActionView::Helpers::JavaScriptHelper include FormHelper - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:user) { create(:user)} let!(:user2) { create(:user)} let!(:milestone) { create(:milestone, project: project) } diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 9b4cc653af5..1b36f16e8b6 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'GFM autocomplete', feature: true, js: true do +feature 'GFM autocomplete', js: true do let(:user) { create(:user, name: '💃speciÄ…l someone💃', username: 'someone.special') } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let(:label) { create(:label, project: project, title: 'special+') } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/issues/group_label_sidebar_spec.rb b/spec/features/issues/group_label_sidebar_spec.rb index 5531a662c67..a8ac1d605cb 100644 --- a/spec/features/issues/group_label_sidebar_spec.rb +++ b/spec/features/issues/group_label_sidebar_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Group label on issue', :feature do +describe 'Group label on issue' do it 'renders link to the project issues page' do group = create(:group) project = create(:empty_project, :public, namespace: group) diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb index e1c55d246ab..a7a7e02b59c 100644 --- a/spec/features/issues/issue_detail_spec.rb +++ b/spec/features/issues/issue_detail_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Issue Detail', js: true, feature: true do +feature 'Issue Detail', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project, author: user) } context 'when user displays the issue' do diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index f75d2c72672..28f93cfd971 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -feature 'Issue Sidebar', feature: true do +feature 'Issue Sidebar' do include MobileHelpers let(:group) { create(:group, :nested) } - let(:project) { create(:project, :public, namespace: group) } + let(:project) { create(:empty_project, :public, namespace: group) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} let!(:label) { create(:label, project: project, title: 'bug') } diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb index affba35f61c..0f869970460 100644 --- a/spec/features/issues/markdown_toolbar_spec.rb +++ b/spec/features/issues/markdown_toolbar_spec.rb @@ -1,9 +1,9 @@ require 'rails_helper' -feature 'Issue markdown toolbar', feature: true, js: true do - let(:project) { create(:project, :public) } +feature 'Issue markdown toolbar', js: true do + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project) } - let(:user) { create(:user) } + let(:user) { create(:user) } before do sign_in(user) diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 833eb47efb2..2ab7d1a71b7 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' feature 'issue move to another project' do let(:user) { create(:user) } - let(:old_project) { create(:project) } + let(:old_project) { create(:project, :repository) } let(:text) { 'Some issue description' } let(:issue) do @@ -25,8 +25,8 @@ feature 'issue move to another project' do context 'user has permission to move issue' do let!(:mr) { create(:merge_request, source_project: old_project) } - let(:new_project) { create(:project) } - let(:new_project_search) { create(:project) } + let(:new_project) { create(:empty_project) } + let(:new_project_search) { create(:empty_project) } let(:text) { "Text with #{mr.to_reference}" } let(:cross_reference) { old_project.to_reference(new_project) } @@ -63,8 +63,8 @@ feature 'issue move to another project' do end context 'user does not have permission to move the issue to a project', js: true do - let!(:private_project) { create(:project, :private) } - let(:another_project) { create(:project) } + let!(:private_project) { create(:empty_project, :private) } + let(:another_project) { create(:empty_project) } background { another_project.team << [user, :guest] } scenario 'browsing projects in projects select' do diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index 184cde5b9c5..b524260750e 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Issue notes polling', :feature, :js do +feature 'Issue notes polling', :js do include NoteInteractionHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb index 6fb103e5477..29c9b99030a 100644 --- a/spec/features/issues/notes_on_issues_spec.rb +++ b/spec/features/issues/notes_on_issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Create notes on issues', :js, :feature do +describe 'Create notes on issues', :js do let(:user) { create(:user) } shared_examples 'notes with reference' do @@ -35,42 +35,42 @@ describe 'Create notes on issues', :js, :feature do context 'mentioning issue on a private project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :private) } + let(:project) { create(:empty_project, :private) } let(:mention) { create(:issue, project: project) } end end context 'mentioning issue on an internal project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :internal) } + let(:project) { create(:empty_project, :internal) } let(:mention) { create(:issue, project: project) } end end context 'mentioning issue on a public project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:mention) { create(:issue, project: project) } end end context 'mentioning merge request on a private project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, :repository) } let(:mention) { create(:merge_request, source_project: project) } end end context 'mentioning merge request on an internal project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :internal) } + let(:project) { create(:project, :internal, :repository) } let(:mention) { create(:merge_request, source_project: project) } end end context 'mentioning merge request on a public project' do it_behaves_like 'notes with reference' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:mention) { create(:merge_request, source_project: project) } end end diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb index 39a458fe3d0..7a05e8b2ccc 100644 --- a/spec/features/issues/spam_issues_spec.rb +++ b/spec/features/issues/spam_issues_spec.rb @@ -1,9 +1,9 @@ require 'rails_helper' -describe 'New issue', feature: true, js: true do +describe 'New issue', js: true do include StubENV - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:user) { create(:user)} before do diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb index f57b58f68e3..2460ae817f9 100644 --- a/spec/features/issues/todo_spec.rb +++ b/spec/features/issues/todo_spec.rb @@ -1,9 +1,9 @@ require 'rails_helper' -feature 'Manually create a todo item from issue', feature: true, js: true do - let!(:project) { create(:project) } - let!(:issue) { create(:issue, project: project) } - let!(:user) { create(:user)} +feature 'Manually create a todo item from issue', js: true do + let!(:project) { create(:empty_project) } + let!(:issue) { create(:issue, project: project) } + let!(:user) { create(:user)} before do project.team << [user, :master] diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 5a7c4f54cb6..389151e63f0 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' feature 'Multiple issue updating from issues#index', :js do - let!(:project) { create(:project) } + let!(:project) { create(:empty_project) } let!(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index 1cd1f016674..d28ad52ff56 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Issues > User uses quick actions', feature: true, js: true do +feature 'Issues > User uses quick actions', js: true do include QuickActionsHelpers it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do @@ -9,7 +9,7 @@ feature 'Issues > User uses quick actions', feature: true, js: true do describe 'issue-only commands' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } before do project.team << [user, :master] @@ -21,6 +21,16 @@ feature 'Issues > User uses quick actions', feature: true, js: true do wait_for_requests end + describe 'time tracking' do + let(:issue) { create(:issue, project: project) } + + before do + visit project_issue_path(project, issue) + end + + it_behaves_like 'issuable time tracker' + end + describe 'adding a due date from note' do let(:issue) { create(:issue, project: project) } @@ -99,39 +109,50 @@ feature 'Issues > User uses quick actions', feature: true, js: true do end end - describe 'Issuable time tracking' do + describe 'toggling the WIP prefix from the title from note' do let(:issue) { create(:issue, project: project) } - before do - project.team << [user, :developer] + it 'does not recognize the command nor create a note' do + write_note("/wip") + + expect(page).not_to have_content '/wip' end + end - context 'Issue' do - before do - visit project_issue_path(project, issue) - end + describe 'mark issue as duplicate' do + let(:issue) { create(:issue, project: project) } + let(:original_issue) { create(:issue, project: project) } - it_behaves_like 'issuable time tracker' - end + context 'when the current user can update issues' do + it 'does not create a note, and marks the issue as a duplicate' do + write_note("/duplicate ##{original_issue.to_reference}") - context 'Merge Request' do - let(:merge_request) { create(:merge_request, source_project: project) } + expect(page).not_to have_content "/duplicate #{original_issue.to_reference}" + expect(page).to have_content 'Commands applied' + expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}" - before do - visit project_merge_request_path(project, merge_request) + expect(issue.reload).to be_closed end - - it_behaves_like 'issuable time tracker' end - end - describe 'toggling the WIP prefix from the title from note' do - let(:issue) { create(:issue, project: project) } + context 'when the current user cannot update the issue' do + let(:guest) { create(:user) } + before do + project.team << [guest, :guest] + gitlab_sign_out + sign_in(guest) + visit project_issue_path(project, issue) + end - it 'does not recognize the command nor create a note' do - write_note("/wip") + it 'does not create a note, and does not mark the issue as a duplicate' do + write_note("/duplicate ##{original_issue.to_reference}") - expect(page).not_to have_content '/wip' + expect(page).to have_content "/duplicate ##{original_issue.to_reference}" + expect(page).not_to have_content 'Commands applied' + expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}" + + expect(issue.reload).to be_open + end end end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 0016fa10f67..722237481ea 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Issues', feature: true do +describe 'Issues' do include DropzoneHelper include IssueHelpers include SortingHelper diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 2a2213b67ed..c9983f0941f 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Login', feature: true do +feature 'Login' do describe 'initial login after setup' do it 'allows the initial admin to create a password' do # This behavior is dependent on there only being one user diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 534be3ab5a7..b70d3060f05 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -24,7 +24,7 @@ require 'erb' # # See the MarkdownFeature class for setup details. -describe 'GitLab Markdown', feature: true do +describe 'GitLab Markdown' do include Capybara::Node::Matchers include MarkupHelper include MarkdownMatchers @@ -100,7 +100,7 @@ describe 'GitLab Markdown', feature: true do end it 'permits img elements' do - expect(doc).to have_selector('img[src*="smile.png"]') + expect(doc).to have_selector('img[data-src*="smile.png"]') end it 'permits br elements' do diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb index 985f42e484c..63fa72650ac 100644 --- a/spec/features/merge_requests/assign_issues_spec.rb +++ b/spec/features/merge_requests/assign_issues_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Merge request issue assignment', js: true, feature: true do +feature 'Merge request issue assignment', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:issue1) { create(:issue, project: project) } let(:issue2) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, :simple, source_project: project, author: user, description: "fixes #{issue1.to_reference} and #{issue2.to_reference}") } diff --git a/spec/features/merge_requests/award_spec.rb b/spec/features/merge_requests/award_spec.rb index 3b01c763281..e886309133d 100644 --- a/spec/features/merge_requests/award_spec.rb +++ b/spec/features/merge_requests/award_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Merge request awards', js: true, feature: true do +feature 'Merge request awards', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } describe 'logged in' do diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb index f2d6c0d9769..1f5e7b55fb0 100644 --- a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb +++ b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Check if mergeable with unresolved discussions', js: true, feature: true do +feature 'Check if mergeable with unresolved discussions', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) } before do diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb index e4c33d57e8d..4b1e1b9a8d4 100644 --- a/spec/features/merge_requests/cherry_pick_spec.rb +++ b/spec/features/merge_requests/cherry_pick_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Cherry-pick Merge Requests', js: true do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:project, :repository, namespace: group) } let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) } before do diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb index 527837b56be..0e97254eada 100644 --- a/spec/features/merge_requests/closes_issues_spec.rb +++ b/spec/features/merge_requests/closes_issues_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Merge Request closing issues message', feature: true, js: true do +feature 'Merge Request closing issues message', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:issue_1) { create(:issue, project: project)} let(:issue_2) { create(:issue, project: project)} let(:merge_request) do diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb index 5c0909b6a59..2c560632a1b 100644 --- a/spec/features/merge_requests/conflicts_spec.rb +++ b/spec/features/merge_requests/conflicts_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Merge request conflict resolution', js: true, feature: true do +feature 'Merge request conflict resolution', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do # In order to have the diffs collapsed, we need to disable the increase feature diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index e0d97dec586..11a74276898 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Create New Merge Request', feature: true, js: true do +feature 'Create New Merge Request', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do project.team << [user, :master] @@ -63,7 +63,7 @@ feature 'Create New Merge Request', feature: true, js: true do context 'when target project cannot be viewed by the current user' do it 'does not leak the private project name & namespace' do - private_project = create(:project, :private) + private_project = create(:project, :private, :repository) visit project_new_merge_request_path(project, merge_request: { target_project_id: private_project.id }) @@ -74,7 +74,7 @@ feature 'Create New Merge Request', feature: true, js: true do context 'when source project cannot be viewed by the current user' do it 'does not leak the private project name & namespace' do - private_project = create(:project, :private) + private_project = create(:project, :private, :repository) visit project_new_merge_request_path(project, merge_request: { source_project_id: private_project.id }) diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb index 9b7795ace62..68d793ec9ba 100644 --- a/spec/features/merge_requests/created_from_fork_spec.rb +++ b/spec/features/merge_requests/created_from_fork_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' feature 'Merge request created from fork' do given(:user) { create(:user) } - given(:project) { create(:project, :public) } - given(:fork_project) { create(:project, :public) } + given(:project) { create(:project, :public, :repository) } + given(:fork_project) { create(:project, :public, :repository) } given!(:merge_request) do create(:forked_project_link, forked_to_project: fork_project, diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb index 8d7160e2df2..874c6e2ff69 100644 --- a/spec/features/merge_requests/deleted_source_branch_spec.rb +++ b/spec/features/merge_requests/deleted_source_branch_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' # This test serves as a regression test for a bug that caused an error # message to be shown by JavaScript when the source branch was deleted. # Please do not remove "js: true". -describe 'Deleted source branch', feature: true, js: true do +describe 'Deleted source branch', js: true do let(:user) { create(:user) } let(:merge_request) { create(:merge_request) } diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb index 4fc70027193..2d9419d6124 100644 --- a/spec/features/merge_requests/diff_notes_avatars_spec.rb +++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Diff note avatars', feature: true, js: true do +feature 'Diff note avatars', js: true do include NoteInteractionHelpers let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") } let(:path) { "files/ruby/popen.rb" } let(:position) do diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index 93e2d134389..ac7f75bd308 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Diff notes resolve', feature: true, js: true do +feature 'Diff notes resolve', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") } let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) } let(:path) { "files/ruby/popen.rb" } diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb index d9de4e388d5..201be4b9e40 100644 --- a/spec/features/merge_requests/diffs_spec.rb +++ b/spec/features/merge_requests/diffs_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Diffs URL', js: true, feature: true do - let(:project) { create(:project, :public) } +feature 'Diffs URL', js: true do + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } context 'when visit with */* as accept header' do diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb index 55846f8609b..d1cc43e0690 100644 --- a/spec/features/merge_requests/discussion_spec.rb +++ b/spec/features/merge_requests/discussion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Merge Request Discussions', feature: true do +feature 'Merge Request Discussions' do before do sign_in(create(:admin)) end diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb index b7063f35546..7386e78fb13 100644 --- a/spec/features/merge_requests/edit_mr_spec.rb +++ b/spec/features/merge_requests/edit_mr_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Edit Merge Request', feature: true do +feature 'Edit Merge Request' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, :simple, source_project: project) } before do diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb index 754f82900e4..1d52a4659ad 100644 --- a/spec/features/merge_requests/filter_by_labels_spec.rb +++ b/spec/features/merge_requests/filter_by_labels_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -feature 'Issue filtering by Labels', feature: true, js: true do +feature 'Merge Request filtering by Labels', js: true do include FilteredSearchHelpers include MergeRequestHelpers - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let!(:user) { create(:user) } let!(:label) { create(:label, project: project) } diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb index d2af150d852..521fcabc881 100644 --- a/spec/features/merge_requests/filter_by_milestone_spec.rb +++ b/spec/features/merge_requests/filter_by_milestone_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -feature 'Merge Request filtering by Milestone', feature: true do +feature 'Merge Request filtering by Milestone' do include FilteredSearchHelpers include MergeRequestHelpers - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let!(:user) { create(:user)} let(:milestone) { create(:milestone, project: project) } diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb index e8085ec36aa..f0019be86ad 100644 --- a/spec/features/merge_requests/filter_merge_requests_spec.rb +++ b/spec/features/merge_requests/filter_merge_requests_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -describe 'Filter merge requests', feature: true do +describe 'Filter merge requests' do include FilteredSearchHelpers include MergeRequestHelpers - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let!(:group) { create(:group) } let!(:user) { create(:user) } let!(:milestone) { create(:milestone, project: project) } diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb index 171386e16ad..6ffb05c5030 100644 --- a/spec/features/merge_requests/form_spec.rb +++ b/spec/features/merge_requests/form_spec.rb @@ -1,13 +1,13 @@ require 'rails_helper' -describe 'New/edit merge request', feature: true, js: true do - let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } - let(:fork_project) { create(:project, forked_from_project: project) } - let!(:user) { create(:user)} - let!(:user2) { create(:user)} - let!(:milestone) { create(:milestone, project: project) } - let!(:label) { create(:label, project: project) } - let!(:label2) { create(:label, project: project) } +describe 'New/edit merge request', :js do + let!(:project) { create(:project, :public, :repository) } + let(:fork_project) { create(:project, :repository, forked_from_project: project) } + let!(:user) { create(:user) } + let!(:user2) { create(:user) } + let!(:milestone) { create(:milestone, project: project) } + let!(:label) { create(:label, project: project) } + let!(:label2) { create(:label, project: project) } before do project.team << [user, :master] diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb index 6cd62ecec72..429bc277d73 100644 --- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb +++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Clicking toggle commit message link', feature: true, js: true do +feature 'Clicking toggle commit message link', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:issue_1) { create(:issue, project: project)} let(:issue_2) { create(:issue, project: project)} let(:merge_request) do diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb index d3475bee5cc..0b5a595acce 100644 --- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb +++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Merge immediately', :feature, :js do +feature 'Merge immediately', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let!(:merge_request) do create(:merge_request_with_diffs, source_project: project, diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb index 230b04296b3..574f5fe353e 100644 --- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Merge When Pipeline Succeeds', :feature, :js do +feature 'Merge When Pipeline Succeeds', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) do create(:merge_request_with_diffs, source_project: project, diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb index 4adf72a60b0..b1215f9ba63 100644 --- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb +++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Mini Pipeline Graph', :js, :feature do +feature 'Mini Pipeline Graph', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project, head_pipeline: pipeline) } let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master', status: 'running', sha: project.commit.id) } diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb index 651cb9d86fb..5c6eec44ff7 100644 --- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb +++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Only allow merge requests to be merged if the pipeline succeeds', feature: true, js: true do +feature 'Only allow merge requests to be merged if the pipeline succeeds', js: true do let(:merge_request) { create(:merge_request_with_diffs) } let(:project) { merge_request.target_project } diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb index 837366ced3c..b3d6cf8deb4 100644 --- a/spec/features/merge_requests/pipelines_spec.rb +++ b/spec/features/merge_requests/pipelines_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Pipelines for Merge Requests', feature: true, js: true do +feature 'Pipelines for Merge Requests', js: true do given(:user) { create(:user) } given(:merge_request) { create(:merge_request) } given(:project) { merge_request.target_project } diff --git a/spec/features/merge_requests/reset_filters_spec.rb b/spec/features/merge_requests/reset_filters_spec.rb index 275f81f50dc..c1b90e5f875 100644 --- a/spec/features/merge_requests/reset_filters_spec.rb +++ b/spec/features/merge_requests/reset_filters_spec.rb @@ -1,11 +1,11 @@ require 'rails_helper' -feature 'Merge requests filter clear button', feature: true, js: true do +feature 'Merge requests filter clear button', js: true do include FilteredSearchHelpers include MergeRequestHelpers include IssueHelpers - let!(:project) { create(:project, :public) } + let!(:project) { create(:project, :public, :repository) } let!(:user) { create(:user) } let!(:milestone) { create(:milestone, project: project) } let!(:bug) { create(:label, project: project, name: 'bug')} diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb index 3ed76926eab..9bbf2610bcb 100644 --- a/spec/features/merge_requests/target_branch_spec.rb +++ b/spec/features/merge_requests/target_branch_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Target branch', feature: true, js: true do +describe 'Target branch', js: true do let(:user) { create(:user) } let(:merge_request) { create(:merge_request) } let(:project) { merge_request.project } diff --git a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb index 912aa34b0c8..dd989fd49b2 100644 --- a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb +++ b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Toggle Whitespace Changes', js: true, feature: true do +feature 'Toggle Whitespace Changes', js: true do before do sign_in(create(:admin)) merge_request = create(:merge_request) diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb index 01251105f72..4e5ec9fbd2d 100644 --- a/spec/features/merge_requests/toggler_behavior_spec.rb +++ b/spec/features/merge_requests/toggler_behavior_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'toggler_behavior', js: true, feature: true do +feature 'toggler_behavior', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project, author: user) } let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) } let(:fragment_id) { "#note_#{note.id}" } diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb index 43153e2cfa4..cf30a687df8 100644 --- a/spec/features/merge_requests/update_merge_requests_spec.rb +++ b/spec/features/merge_requests/update_merge_requests_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Multiple merge requests updating from merge_requests#index', feature: true do +feature 'Multiple merge requests updating from merge_requests#index' do let!(:user) { create(:user)} - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } before do diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb index f541f495995..d62b035b40b 100644 --- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb +++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe 'Projects > Merge requests > User lists merge requests', feature: true do +describe 'Projects > Merge requests > User lists merge requests' do include MergeRequestHelpers include SortingHelper - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:user) { create(:user) } before do diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb index 35ed08e0a5e..74d21822a59 100644 --- a/spec/features/merge_requests/user_posts_notes_spec.rb +++ b/spec/features/merge_requests/user_posts_notes_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Merge requests > User posts notes', :js do include NoteInteractionHelpers - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) do create(:merge_request, source_project: project, target_project: project) end diff --git a/spec/features/merge_requests/user_sees_system_notes_spec.rb b/spec/features/merge_requests/user_sees_system_notes_spec.rb index 624a425ae52..03dc61c2efa 100644 --- a/spec/features/merge_requests/user_sees_system_notes_spec.rb +++ b/spec/features/merge_requests/user_sees_system_notes_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' feature 'Merge requests > User sees system notes' do - let(:public_project) { create(:project, :public) } - let(:private_project) { create(:project, :private) } + let(:public_project) { create(:project, :public, :repository) } + let(:private_project) { create(:project, :private, :repository) } let(:issue) { create(:issue, project: private_project) } let(:merge_request) { create(:merge_request, source_project: public_project, source_branch: 'markdown') } let!(:note) { create(:note_on_merge_request, :system, noteable: merge_request, project: public_project, note: "mentioned in #{issue.to_reference(public_project)}") } diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb index 434f5a7c0ac..43cab65d287 100644 --- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -feature 'Merge Requests > User uses quick actions', feature: true, js: true do +feature 'Merge Requests > User uses quick actions', js: true do include QuickActionsHelpers let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } let!(:milestone) { create(:milestone, project: project, title: 'ASAP') } @@ -24,6 +24,14 @@ feature 'Merge Requests > User uses quick actions', feature: true, js: true do wait_for_requests end + describe 'time tracking' do + before do + visit project_merge_request_path(project, merge_request) + end + + it_behaves_like 'issuable time tracker' + end + describe 'toggling the WIP prefix in the title from note' do context 'when the current user can toggle the WIP prefix' do it 'adds the WIP: prefix to the title' do @@ -121,7 +129,7 @@ feature 'Merge Requests > User uses quick actions', feature: true, js: true do end describe '/target_branch command in merge request' do - let(:another_project) { create(:project, :public) } + let(:another_project) { create(:project, :public, :repository) } let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } } before do diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_requests/versions_spec.rb index 218d57b49e3..8e231fbc281 100644 --- a/spec/features/merge_requests/versions_spec.rb +++ b/spec/features/merge_requests/versions_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Merge Request versions', js: true, feature: true do +feature 'Merge Request versions', js: true do let(:merge_request) { create(:merge_request, importing: true) } let(:project) { merge_request.source_project } let!(:merge_request_diff1) { merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb index b0fe5f3e1cb..c0221525c9f 100644 --- a/spec/features/merge_requests/widget_deployments_spec.rb +++ b/spec/features/merge_requests/widget_deployments_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Widget Deployments Header', feature: true, js: true do +feature 'Widget Deployments Header', js: true do describe 'when deployed to an environment' do given(:user) { create(:user) } given(:project) { merge_request.target_project } diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb index 46c558659c7..69e31c7481f 100644 --- a/spec/features/merge_requests/widget_spec.rb +++ b/spec/features/merge_requests/widget_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -describe 'Merge request', :feature, :js do +describe 'Merge request', :js do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:merge_request) { create(:merge_request, source_project: project) } before do @@ -200,7 +200,7 @@ describe 'Merge request', :feature, :js do end context 'user can merge into source project but cannot push to fork', js: true do - let(:fork_project) { create(:project, :public) } + let(:fork_project) { create(:project, :public, :repository) } let(:user2) { create(:user) } before do diff --git a/spec/features/merge_requests/wip_message_spec.rb b/spec/features/merge_requests/wip_message_spec.rb index 91cf8fc7218..b422c76249d 100644 --- a/spec/features/merge_requests/wip_message_spec.rb +++ b/spec/features/merge_requests/wip_message_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Work In Progress help message', feature: true do - let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } +feature 'Work In Progress help message' do + let!(:project) { create(:project, :public, :repository) } let!(:user) { create(:user) } before do diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb index ce0c27cbe77..1d05184d6fc 100644 --- a/spec/features/milestone_spec.rb +++ b/spec/features/milestone_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Milestone', feature: true do +feature 'Milestone' do let(:group) { create(:group, :public) } let(:project) { create(:empty_project, :public, namespace: group) } let(:user) { create(:user) } diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb index 626a1f35e62..199a5ba83b3 100644 --- a/spec/features/milestones/show_spec.rb +++ b/spec/features/milestones/show_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'Milestone show', feature: true do +describe 'Milestone show' do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:milestone) { create(:milestone, project: project) } diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb index 42764e808e6..49d8e52f861 100644 --- a/spec/features/oauth_login_spec.rb +++ b/spec/features/oauth_login_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' -feature 'OAuth Login', js: true do +feature 'OAuth Login', :js, :allow_forgery_protection do + include DeviseHelpers + def enter_code(code) fill_in 'user_otp_attempt', with: code click_button 'Verify code' @@ -8,7 +10,7 @@ feature 'OAuth Login', js: true do def stub_omniauth_config(provider) OmniAuth.config.add_mock(provider, OmniAuth::AuthHash.new(provider: provider.to_s, uid: "12345")) - Rails.application.env_config['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: Rails.application) Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider] end diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb index 257d363438c..5e1e7dc078f 100644 --- a/spec/features/password_reset_spec.rb +++ b/spec/features/password_reset_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Password reset', feature: true do +feature 'Password reset' do describe 'throttling' do it 'sends reset instructions when not previously sent' do user = create(:user) diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index fae11a993b5..672022304da 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile account page', feature: true do +describe 'Profile account page' do let(:user) { create(:user) } before do diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb index 9d782ecf63b..56c1f7ae9c7 100644 --- a/spec/features/profiles/account_spec.rb +++ b/spec/features/profiles/account_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Profile > Account', feature: true do +feature 'Profile > Account' do given(:user) { create(:user, username: 'foo') } before do @@ -27,7 +27,7 @@ feature 'Profile > Account', feature: true do end context 'with a project' do - given!(:project) { create(:project, namespace: user.namespace, path: 'project') } + given!(:project) { create(:empty_project, namespace: user.namespace) } given(:new_project_path) { "/#{new_username}/#{project.path}" } given(:old_project_path) { "/#{user.username}/#{project.path}" } @@ -43,14 +43,14 @@ feature 'Profile > Account', feature: true do update_username(new_username) visit new_project_path expect(current_path).to eq(new_project_path) - expect(find('h1.project-title')).to have_content(project.name) + expect(find('h1.title')).to have_content(project.path) end scenario 'the old project path redirects to the new path' do update_username(new_username) visit old_project_path expect(current_path).to eq(new_project_path) - expect(find('h1.project-title')).to have_content(project.name) + expect(find('h1.title')).to have_content(project.path) end end end diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb index e4c236f4c68..35793539e0e 100644 --- a/spec/features/profiles/chat_names_spec.rb +++ b/spec/features/profiles/chat_names_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Profile > Chat', feature: true do +feature 'Profile > Chat' do given(:user) { create(:user) } given(:service) { create(:service) } diff --git a/spec/features/profiles/gpg_keys_spec.rb b/spec/features/profiles/gpg_keys_spec.rb new file mode 100644 index 00000000000..6edc482b47e --- /dev/null +++ b/spec/features/profiles/gpg_keys_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +feature 'Profile > GPG Keys' do + let(:user) { create(:user, email: GpgHelpers::User2.emails.first) } + + before do + login_as(user) + end + + describe 'User adds a key' do + before do + visit profile_gpg_keys_path + end + + scenario 'saves the new key' do + fill_in('Key', with: GpgHelpers::User2.public_key) + click_button('Add key') + + expect(page).to have_content('bette.cartwright@example.com Verified') + expect(page).to have_content('bette.cartwright@example.net Unverified') + expect(page).to have_content(GpgHelpers::User2.fingerprint) + end + end + + scenario 'User sees their key' do + create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) + visit profile_gpg_keys_path + + expect(page).to have_content('bette.cartwright@example.com Verified') + expect(page).to have_content('bette.cartwright@example.net Unverified') + expect(page).to have_content(GpgHelpers::User2.fingerprint) + end + + scenario 'User removes a key via the key index' do + create(:gpg_key, user: user, key: GpgHelpers::User2.public_key) + visit profile_gpg_keys_path + + click_link('Remove') + + expect(page).to have_content('Your GPG keys (0)') + end + + scenario 'User revokes a key via the key index' do + gpg_key = create :gpg_key, user: user, key: GpgHelpers::User2.public_key + gpg_signature = create :gpg_signature, gpg_key: gpg_key, valid_signature: true + + visit profile_gpg_keys_path + + click_link('Revoke') + + expect(page).to have_content('Your GPG keys (0)') + + expect(gpg_signature.reload).to have_attributes( + valid_signature: false, + gpg_key: nil + ) + end +end diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb index 9439a258a75..6541ea6bf57 100644 --- a/spec/features/profiles/keys_spec.rb +++ b/spec/features/profiles/keys_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Profile > SSH Keys', feature: true do +feature 'Profile > SSH Keys' do let(:user) { create(:user) } before do diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb index c7886421c83..45f78444362 100644 --- a/spec/features/profiles/oauth_applications_spec.rb +++ b/spec/features/profiles/oauth_applications_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile > Applications', feature: true do +describe 'Profile > Applications' do let(:user) { create(:user) } before do diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 26d6d6658aa..2c757f99a27 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile > Password', feature: true do +describe 'Profile > Password' do context 'Password authentication enabled' do let(:user) { create(:user, password_automatically_set: true) } diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb index 3c08b6bc091..f3124bbf29e 100644 --- a/spec/features/profiles/personal_access_tokens_spec.rb +++ b/spec/features/profiles/personal_access_tokens_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile > Personal Access Tokens', feature: true, js: true do +describe 'Profile > Personal Access Tokens', js: true do let(:user) { create(:user) } def active_personal_access_tokens diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb index 65fed82c256..9123aa9d155 100644 --- a/spec/features/profiles/preferences_spec.rb +++ b/spec/features/profiles/preferences_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Profile > Preferences', feature: true do +describe 'Profile > Preferences' do let(:user) { create(:user) } before do diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb index 75daef0c38c..6a4173d43e1 100644 --- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Profile > Notifications > User changes notified_of_own_activity setting', feature: true, js: true do +feature 'Profile > Notifications > User changes notified_of_own_activity setting', js: true do let(:user) { create(:user) } before do diff --git a/spec/features/projects/artifacts/browse_spec.rb b/spec/features/projects/artifacts/browse_spec.rb index a34c0c4cecd..f5f7eba8e40 100644 --- a/spec/features/projects/artifacts/browse_spec.rb +++ b/spec/features/projects/artifacts/browse_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Browse artifact', :js, feature: true do - let(:project) { create(:project, :public) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } +feature 'Browse artifact', :js do + let(:project) { create(:empty_project, :public) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) } def browse_path(path) diff --git a/spec/features/projects/artifacts/download_spec.rb b/spec/features/projects/artifacts/download_spec.rb index b76f2be880e..c1bba8c15c4 100644 --- a/spec/features/projects/artifacts/download_spec.rb +++ b/spec/features/projects/artifacts/download_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Download artifact', :js, feature: true do - let(:project) { create(:project, :public) } - let(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project, sha: project.commit.sha, ref: 'master') } +feature 'Download artifact', :js do + let(:project) { create(:empty_project, :public) } + let(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) } let(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) } shared_examples 'downloading' do diff --git a/spec/features/projects/artifacts/file_spec.rb b/spec/features/projects/artifacts/file_spec.rb index 6d48470ca3a..4c268b876ea 100644 --- a/spec/features/projects/artifacts/file_spec.rb +++ b/spec/features/projects/artifacts/file_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Artifact file', :js, feature: true do - let(:project) { create(:project, :public) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } +feature 'Artifact file', :js do + let(:project) { create(:empty_project, :public) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } def visit_file(path) diff --git a/spec/features/projects/artifacts/raw_spec.rb b/spec/features/projects/artifacts/raw_spec.rb index 3f38d720a0f..128e39e7803 100644 --- a/spec/features/projects/artifacts/raw_spec.rb +++ b/spec/features/projects/artifacts/raw_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Raw artifact', :js, feature: true do - let(:project) { create(:project, :public) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } +feature 'Raw artifact', :js do + let(:project) { create(:empty_project, :public) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) } def raw_path(path) diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb index 5c5a7c96763..8cf4bfbf978 100644 --- a/spec/features/projects/badges/coverage_spec.rb +++ b/spec/features/projects/badges/coverage_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' feature 'test coverage badge' do given!(:user) { create(:user) } - given!(:project) { create(:project, :private) } + given!(:project) { create(:empty_project, :private) } context 'when user has access to view badge' do background do @@ -55,7 +55,7 @@ feature 'test coverage badge' do end def create_pipeline - opts = { project: project, ref: 'master', sha: project.commit.id } + opts = { project: project } create(:ci_pipeline, opts).tap do |pipeline| yield pipeline diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb index 161d731f524..89ae891037e 100644 --- a/spec/features/projects/badges/list_spec.rb +++ b/spec/features/projects/badges/list_spec.rb @@ -3,23 +3,23 @@ require 'spec_helper' feature 'list of badges' do background do user = create(:user) - project = create(:project) + project = create(:project, :repository) project.team << [user, :master] sign_in(user) visit project_pipelines_settings_path(project) end scenario 'user wants to see build status badge' do - page.within('.build-status') do - expect(page).to have_content 'build status' + page.within('.pipeline-status') do + expect(page).to have_content 'pipeline status' expect(page).to have_content 'Markdown' expect(page).to have_content 'HTML' expect(page).to have_content 'AsciiDoc' expect(page).to have_css('.highlight', count: 3) - expect(page).to have_xpath("//img[@alt='build status']") + expect(page).to have_xpath("//img[@alt='pipeline status']") page.within('.highlight', match: :first) do - expect(page).to have_content 'badges/master/build.svg' + expect(page).to have_content 'badges/master/pipeline.svg' end end end @@ -40,14 +40,14 @@ feature 'list of badges' do end scenario 'user changes current ref of build status badge', js: true do - page.within('.build-status') do + page.within('.pipeline-status') do first('.js-project-refs-dropdown').click page.within '.project-refs-form' do click_link 'improve/awesome' end - expect(page).to have_content 'badges/improve/awesome/build.svg' + expect(page).to have_content 'badges/improve/awesome/pipeline.svg' end end end diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb new file mode 100644 index 00000000000..b83ea8f4eaa --- /dev/null +++ b/spec/features/projects/badges/pipeline_badge_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +feature 'Pipeline Badge' do + set(:project) { create(:project, :repository, :public) } + let(:ref) { project.default_branch } + + # this can't be tested in the controller, as it bypasses the rails router + # and constructs a route based on the controller being tested + # Keep around until 10.0, see gitlab-org/gitlab-ce#35307 + context 'when the deprecated badge is requested' do + it 'displays the badge' do + visit build_project_badges_path(project, ref: ref, format: :svg) + + expect(page.status_code).to eq(200) + end + end + + context 'when the project has a pipeline' do + let!(:pipeline) { create(:ci_empty_pipeline, project: project, ref: ref, sha: project.commit(ref).sha) } + let!(:job) { create(:ci_build, pipeline: pipeline) } + + context 'when the pipeline was successfull' do + it 'displays so on the badge' do + job.success + + visit pipeline_project_badges_path(project, ref: ref, format: :svg) + + expect(page.status_code).to eq(200) + expect_badge('passed') + end + end + + context 'when the pipeline failed' do + it 'shows displays so on the badge' do + job.drop + + visit pipeline_project_badges_path(project, ref: ref, format: :svg) + + expect(page.status_code).to eq(200) + expect_badge('failed') + end + end + + context 'when the pipeline is running' do + it 'shows displays so on the badge' do + create(:ci_build, pipeline: pipeline, name: 'second build', status_event: 'run') + + visit pipeline_project_badges_path(project, ref: ref, format: :svg) + + expect(page.status_code).to eq(200) + expect_badge('running') + end + end + + context 'when a new pipeline is created' do + it 'shows a fresh badge' do + visit pipeline_project_badges_path(project, ref: ref, format: :svg) + + expect(page.status_code).to eq(200) + expect(page.response_headers['Cache-Control']).to include 'no-cache' + end + end + + def expect_badge(status) + svg = Nokogiri::XML.parse(page.body) + expect(page.response_headers['Content-Type']).to include('image/svg+xml') + expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy + end + end +end diff --git a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb index 7564338b301..1160f674974 100644 --- a/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb +++ b/spec/features/projects/blobs/blob_line_permalink_updater_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Blob button line permalinks (BlobLinePermalinkUpdater)', feature: true, js: true do +feature 'Blob button line permalinks (BlobLinePermalinkUpdater)', js: true do include TreeHelper let(:project) { create(:project, :public, :repository) } diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 3427f639930..3d465e709b9 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'File blob', :js, feature: true do - let(:project) { create(:project, :public) } +feature 'File blob', :js do + let(:project) { create(:project, :public, :repository) } def visit_blob(path, anchor: nil, ref: 'master') visit project_blob_path(project, File.join(ref, path), anchor: anchor) diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb index c9384a09ccd..9855cfd85c4 100644 --- a/spec/features/projects/blobs/edit_spec.rb +++ b/spec/features/projects/blobs/edit_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'Editing file blob', feature: true, js: true do +feature 'Editing file blob', js: true do include TreeHelper - let(:project) { create(:project, :public, :test_repo) } + let(:project) { create(:project, :public) } let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') } let(:branch) { 'master' } let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] } diff --git a/spec/features/projects/blobs/shortcuts_blob_spec.rb b/spec/features/projects/blobs/shortcuts_blob_spec.rb index 9cacda84378..1e3080fa319 100644 --- a/spec/features/projects/blobs/shortcuts_blob_spec.rb +++ b/spec/features/projects/blobs/shortcuts_blob_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Blob shortcuts', feature: true do +feature 'Blob shortcuts' do include TreeHelper let(:project) { create(:project, :public, :repository) } let(:path) { project.repository.ls_files(project.repository.root_ref)[0] } diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index f01860cc434..ad06cee4e81 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Download buttons in branches page', feature: true do +feature 'Download buttons in branches page' do given(:user) { create(:user) } given(:role) { :developer } given(:status) { 'success' } - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } given(:pipeline) do create(:ci_pipeline, diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb index 8c35dac0b3d..0be434a567b 100644 --- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb +++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'New Branch Ref Dropdown', :js, :feature do +describe 'New Branch Ref Dropdown', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:toggle) { find('.create-from .dropdown-menu-toggle') } before do diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb index d18cd3d6adc..6e787de2dd6 100644 --- a/spec/features/projects/branches_spec.rb +++ b/spec/features/projects/branches_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'Branches', feature: true do +describe 'Branches' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } let(:repository) { project.repository } def set_protected_branch_name(branch_name) diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb index 257a7418f16..740331fe42a 100644 --- a/spec/features/projects/commit/builds_spec.rb +++ b/spec/features/projects/commit/builds_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'project commit pipelines', js: true do - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } background do user = create(:user) diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb index 2d18add82b5..7086f56bb1b 100644 --- a/spec/features/projects/commit/cherry_pick_spec.rb +++ b/spec/features/projects/commit/cherry_pick_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Cherry-pick Commits' do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:project, :repository, namespace: group) } let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') } diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index a5736b6072a..2ef74e8857c 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Mini Pipeline Graph in Commit View', :js, :feature do +feature 'Mini Pipeline Graph in Commit View', :js do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do sign_in(user) diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb index 0f48751fa10..82d73fe8531 100644 --- a/spec/features/projects/compare_spec.rb +++ b/spec/features/projects/compare_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" describe "Compare", js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.team << [user, :master] diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb index cf3e1ff451e..2d1a9b931b5 100644 --- a/spec/features/projects/deploy_keys_spec.rb +++ b/spec/features/projects/deploy_keys_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Project deploy keys', :js, :feature do +describe 'Project deploy keys', :js do let(:user) { create(:user) } let(:project) { create(:project_empty_repo) } diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb index 1b0d13e07db..7145e286229 100644 --- a/spec/features/projects/developer_views_empty_project_instructions_spec.rb +++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Developer views empty project instructions', feature: true do +feature 'Developer views empty project instructions' do let(:project) { create(:empty_project, :empty_repo) } let(:developer) { create(:user) } diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb index 4baccb24806..bc102895aaf 100644 --- a/spec/features/projects/diffs/diff_show_spec.rb +++ b/spec/features/projects/diffs/diff_show_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Diff file viewer', :js, feature: true do +feature 'Diff file viewer', :js do let(:project) { create(:project, :public, :repository) } def visit_commit(sha, anchor: nil) diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb index 1fca0dde534..4b5436027f9 100644 --- a/spec/features/projects/edit_spec.rb +++ b/spec/features/projects/edit_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Project edit', feature: true, js: true do +feature 'Project edit', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } before do project.team << [user, :master] @@ -20,7 +20,7 @@ feature 'Project edit', feature: true, js: true do end context 'given project with merge_requests_disabled access level' do - let(:project) { create(:project, :merge_requests_disabled) } + let(:project) { create(:empty_project, :merge_requests_disabled) } it 'hides merge requests section' do expect(page).to have_selector('.merge-requests-feature', visible: false) @@ -36,7 +36,7 @@ feature 'Project edit', feature: true, js: true do end context 'given project with builds_disabled access level' do - let(:project) { create(:project, :builds_disabled) } + let(:project) { create(:empty_project, :builds_disabled) } it 'hides builds select section' do expect(page).to have_selector('.builds-feature', visible: false) diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb index cf0dfcfb1f3..82a722c5960 100644 --- a/spec/features/projects/environments/environment_metrics_spec.rb +++ b/spec/features/projects/environments/environment_metrics_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Environment > Metrics', :feature do +feature 'Environment > Metrics' do include PrometheusHelpers given(:user) { create(:user) } diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index c31b816f7fb..c6b7e611a5c 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Environment', :feature do +feature 'Environment' do given(:project) { create(:empty_project) } given(:user) { create(:user) } given(:role) { :developer } @@ -205,7 +205,7 @@ feature 'Environment', :feature do end feature 'auto-close environment when branch is deleted' do - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } given!(:environment) do create(:environment, :with_review_app, project: project, diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 99b917cb420..36cf307fbe2 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Environments page', :feature, :js do +feature 'Environments page', :js do given(:project) { create(:empty_project) } given(:user) { create(:user) } given(:role) { :developer } @@ -111,7 +111,7 @@ feature 'Environments page', :feature, :js do end context 'with deployments' do - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } given(:deployment) do create(:deployment, environment: environment, diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index 827e02a58d0..37fa61d038e 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'Edit Project Settings', feature: true do +describe 'Edit Project Settings' do let(:member) { create(:user) } - let!(:project) { create(:project, :public, path: 'gitlab', name: 'sample') } + let!(:project) { create(:project, :public, :repository) } let!(:issue) { create(:issue, project: project) } let(:non_member) { create(:user) } @@ -39,14 +39,25 @@ describe 'Edit Project Settings', feature: true do end end - context "When external issue tracker is enabled" do - it "does not hide issues tab" do - project.project_feature.update(issues_access_level: ProjectFeature::DISABLED) + context 'When external issue tracker is enabled and issues enabled on project settings' do + it 'does not hide issues tab' do allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(JiraService.new) visit project_path(project) - expect(page).to have_selector(".shortcuts-issues") + expect(page).to have_selector('.shortcuts-issues') + end + end + + context 'When external issue tracker is enabled and issues disabled on project settings' do + it 'hides issues tab' do + project.issues_enabled = false + project.save! + allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(JiraService.new) + + visit project_path(project) + + expect(page).not_to have_selector('.shortcuts-issues') end end @@ -238,7 +249,7 @@ describe 'Edit Project Settings', feature: true do # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/24056 describe 'project statistic visibility' do - let!(:project) { create(:project, :private) } + let!(:project) { create(:empty_project, :private) } before do project.team << [member, :guest] diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb index d9a561b23a2..f62a9edd37e 100644 --- a/spec/features/projects/files/browse_files_spec.rb +++ b/spec/features/projects/files/browse_files_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'user browses project', feature: true, js: true do - let(:project) { create(:project) } +feature 'user browses project', js: true do + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/features/projects/files/creating_a_file_spec.rb b/spec/features/projects/files/creating_a_file_spec.rb index 55350db4c34..e13bf4b6089 100644 --- a/spec/features/projects/files/creating_a_file_spec.rb +++ b/spec/features/projects/files/creating_a_file_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'User wants to create a file', feature: true do - let(:project) { create(:project) } +feature 'User wants to create a file' do + let(:project) { create(:project, :repository) } let(:user) { create(:user) } background do diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb index 0cd0c9addd0..cebb238dda1 100644 --- a/spec/features/projects/files/dockerfile_dropdown_spec.rb +++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' require 'fileutils' -feature 'User wants to add a Dockerfile file', feature: true do +feature 'User wants to add a Dockerfile file' do before do user = create(:user) - project = create(:project) + project = create(:project, :repository) project.team << [user, :master] sign_in user diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb index a2874483149..d2382d55c0b 100644 --- a/spec/features/projects/files/download_buttons_spec.rb +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Download buttons in files tree', feature: true do +feature 'Download buttons in files tree' do given(:user) { create(:user) } given(:role) { :developer } given(:status) { 'success' } - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } given(:pipeline) do create(:ci_pipeline, diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb index 930e4cf488a..c7e3f657639 100644 --- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb +++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'User uses soft wrap whilst editing file', feature: true, js: true do +feature 'User uses soft wrap whilst editing file', js: true do before do user = create(:user) - project = create(:project) + project = create(:project, :repository) project.team << [user, :master] sign_in user visit project_new_blob_path(project, 'master', file_name: 'test_file-name') diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb index c295380dfc9..20be968e89f 100644 --- a/spec/features/projects/files/editing_a_file_spec.rb +++ b/spec/features/projects/files/editing_a_file_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'User wants to edit a file', feature: true do - let(:project) { create(:project) } +feature 'User wants to edit a file' do + let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:commit_params) do { diff --git a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb index 9a1eaee08de..702b99de733 100644 --- a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb +++ b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'User views files page', feature: true do +feature 'User views files page' do let(:user) { create(:user) } let(:project) { create(:forked_project_with_submodules) } diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb index 772f81c8853..7f97fdb8cc9 100644 --- a/spec/features/projects/files/find_file_keyboard_spec.rb +++ b/spec/features/projects/files/find_file_keyboard_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Find file keyboard shortcuts', feature: true, js: true do +feature 'Find file keyboard shortcuts', js: true do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do project.team << [user, :master] diff --git a/spec/features/projects/files/find_files_spec.rb b/spec/features/projects/files/find_files_spec.rb index 7a99596585f..57d67b28920 100644 --- a/spec/features/projects/files/find_files_spec.rb +++ b/spec/features/projects/files/find_files_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Find files button in the tree header', feature: true do +feature 'Find files button in the tree header' do given(:user) { create(:user) } - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } background do sign_in(user) diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb index a3a7b08c013..e2044c9d5aa 100644 --- a/spec/features/projects/files/gitignore_dropdown_spec.rb +++ b/spec/features/projects/files/gitignore_dropdown_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'User wants to add a .gitignore file', feature: true do +feature 'User wants to add a .gitignore file' do before do user = create(:user) - project = create(:project) + project = create(:project, :repository) project.team << [user, :master] sign_in user visit project_new_blob_path(project, 'master', file_name: '.gitignore') diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index 41afe8014d9..ab242b0b0b5 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'User wants to add a .gitlab-ci.yml file', feature: true do +feature 'User wants to add a .gitlab-ci.yml file' do before do user = create(:user) - project = create(:project) + project = create(:project, :repository) project.team << [user, :master] sign_in user visit project_new_blob_path(project, 'master', file_name: '.gitlab-ci.yml') diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb index 57f4a6f1b6f..95af263bcac 100644 --- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb +++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'project owner creates a license file', feature: true, js: true do +feature 'project owner creates a license file', js: true do let(:project_master) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } background do project.repository.delete_file(project_master, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb index 0604ecb8c8b..1f4b3763b40 100644 --- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'project owner sees a link to create a license file in empty project', feature: true, js: true do +feature 'project owner sees a link to create a license file in empty project', js: true do let(:project_master) { create(:user) } let(:project) { create(:empty_project) } background do diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb index a0846643269..48003eeaa87 100644 --- a/spec/features/projects/files/template_type_dropdown_spec.rb +++ b/spec/features/projects/files/template_type_dropdown_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'Template type dropdown selector', js: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb index d50ddb1f1a9..4238d25e9ee 100644 --- a/spec/features/projects/files/undo_template_spec.rb +++ b/spec/features/projects/files/undo_template_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'Template Undo Button', js: true do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:user) { create(:user) } before do diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb index aa2306069ad..b63e5ae46ee 100644 --- a/spec/features/projects/gfm_autocomplete_load_spec.rb +++ b/spec/features/projects/gfm_autocomplete_load_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe 'GFM autocomplete loading', feature: true, js: true do - let(:project) { create(:project) } +describe 'GFM autocomplete loading', js: true do + let(:project) { create(:empty_project) } before do sign_in(create(:admin)) diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb index 631955a60a1..698baad97ff 100644 --- a/spec/features/projects/group_links_spec.rb +++ b/spec/features/projects/group_links_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Project group links', :feature, :js do +feature 'Project group links', :js do include Select2Helper let(:master) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let!(:group) { create(:group) } background do @@ -35,7 +35,7 @@ feature 'Project group links', :feature, :js do context 'nested group project' do let!(:nested_group) { create(:group, parent: group) } let!(:another_group) { create(:group) } - let!(:project) { create(:project, namespace: nested_group) } + let!(:project) { create(:empty_project, namespace: nested_group) } background do group.add_master(master) diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index 43c2c401f4a..62d244ff259 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' # It looks up for any sensitive word inside the JSON, so if a sensitive word is found # we''l have to either include it adding the model that includes it to the +safe_list+ # or make sure the attribute is blacklisted in the +import_export.yml+ configuration -feature 'Import/Export - project export integration test', feature: true, js: true do +feature 'Import/Export - project export integration test', js: true do include Select2Helper include ExportFileHelper diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index 533ff4612ff..f924725870b 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Import/Export - project import integration test', feature: true, js: true do +feature 'Import/Export - project import integration test', js: true do include Select2Helper let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } @@ -46,7 +46,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end scenario 'invalid project' do - project = create(:project, namespace: namespace) + project = create(:empty_project, namespace: namespace) visit new_project_path @@ -62,7 +62,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end scenario 'project with no name' do - create(:project, namespace: namespace) + create(:empty_project, namespace: namespace) visit new_project_path diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index 74ced0d3b35..3917e72c39e 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Import/Export - Namespace export file cleanup', feature: true, js: true do +feature 'Import/Export - Namespace export file cleanup', js: true do let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index 88bb678362b..d2789d0aa52 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'issuable templates', feature: true, js: true do +feature 'issuable templates', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do project.team << [user, :master] @@ -120,7 +120,7 @@ feature 'issuable templates', feature: true, js: true do context 'user creates a merge request from a forked project using templates' do let(:template_content) { 'this is a test "feature-proposal" template' } let(:fork_user) { create(:user) } - let(:fork_project) { create(:project, :public) } + let(:fork_project) { create(:project, :public, :repository) } let(:merge_request) { create(:merge_request, :with_diffs, source_project: fork_project, target_project: project) } background do diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 411987573fa..037ac00d39f 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' require 'tempfile' -feature 'Jobs', :feature do +feature 'Jobs' do let(:user) { create(:user) } let(:user_access_level) { :developer } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:job) { create(:ci_build, :trace, pipeline: pipeline) } diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb index 652008bae73..2b0aead440c 100644 --- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb +++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Issue prioritization', feature: true do +feature 'Issue prioritization' do let(:user) { create(:user) } - let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } # Labels let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) } diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb index 58421e11e0a..3115a643d5d 100644 --- a/spec/features/projects/labels/subscription_spec.rb +++ b/spec/features/projects/labels/subscription_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Labels subscription', feature: true do +feature 'Labels subscription' do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:empty_project, :public, namespace: group) } diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb index 61f6d734ed3..223f94ff9f9 100644 --- a/spec/features/projects/labels/update_prioritization_spec.rb +++ b/spec/features/projects/labels/update_prioritization_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Prioritize labels', feature: true do +feature 'Prioritize labels' do include DragTo let(:user) { create(:user) } @@ -114,6 +114,12 @@ feature 'Prioritize labels', feature: true do expect(page.all('li').last).to have_content('bug') end end + + it 'shows a help message about prioritized labels' do + visit project_labels_path(project) + + expect(page).to have_content 'Star a label' + end end context 'as a guest' do @@ -128,6 +134,7 @@ feature 'Prioritize labels', feature: true do expect(page).to have_content 'wontfix' expect(page).to have_content 'feature' expect(page).not_to have_css('.prioritized-labels') + expect(page).not_to have_content 'Star a label' end end @@ -139,6 +146,7 @@ feature 'Prioritize labels', feature: true do expect(page).to have_content 'wontfix' expect(page).to have_content 'feature' expect(page).not_to have_css('.prioritized-labels') + expect(page).not_to have_content 'Star a label' end end end diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb index 8b952d2f3a5..3f2579bb01a 100644 --- a/spec/features/projects/main/download_buttons_spec.rb +++ b/spec/features/projects/main/download_buttons_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Download buttons in project main page', feature: true do +feature 'Download buttons in project main page' do given(:user) { create(:user) } given(:role) { :developer } given(:status) { 'success' } - given(:project) { create(:project) } + given(:project) { create(:project, :repository) } given(:pipeline) do create(:ci_pipeline, diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb index 28c8d20aad5..a26e7becdb9 100644 --- a/spec/features/projects/members/anonymous_user_sees_members_spec.rb +++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects > Members > Anonymous user sees members', feature: true do +feature 'Projects > Members > Anonymous user sees members' do let(:user) { create(:user) } let(:group) { create(:group, :public) } let(:project) { create(:empty_project, :public) } diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb index b9154915b34..acda5808313 100644 --- a/spec/features/projects/members/group_links_spec.rb +++ b/spec/features/projects/members/group_links_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects > Members > Anonymous user sees members', feature: true, js: true do +feature 'Projects > Members > Anonymous user sees members', js: true do let(:user) { create(:user) } let(:group) { create(:group, :public) } let(:project) { create(:empty_project, :public) } diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb index 2c99c2c7888..1fb5e000239 100644 --- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb +++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'Projects > Members > Group member cannot leave group project', feature: true do +feature 'Projects > Members > Group member cannot leave group project' do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } background do group.add_developer(user) diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb index 35142273eae..8e3520f9f15 100644 --- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb +++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'Projects > Members > Group member cannot request access to his group project', feature: true do +feature 'Projects > Members > Group member cannot request access to his group project' do let(:user) { create(:user) } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } scenario 'owner does not see the request access button' do group.add_owner(user) diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb index bfc604bb8d6..154f9f4a26c 100644 --- a/spec/features/projects/members/group_members_spec.rb +++ b/spec/features/projects/members/group_members_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects members', feature: true do +feature 'Projects members' do let(:user) { create(:user) } let(:developer) { create(:user) } let(:group) { create(:group, :public, :access_requestable) } diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index 46f5744b32d..6865d721201 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -feature 'Projects > Members > Group requester cannot request access to project', feature: true, js: true do +feature 'Projects > Members > Group requester cannot request access to project', js: true do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public, :access_requestable) } - let(:project) { create(:project, :public, :access_requestable, namespace: group) } + let(:project) { create(:empty_project, :public, :access_requestable, namespace: group) } background do group.add_owner(owner) diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index 301f68a67d3..f9c54d267b5 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -feature 'Project members list', feature: true do +feature 'Project members list' do include Select2Helper let(:user1) { create(:user, name: 'John Doe') } let(:user2) { create(:user, name: 'Mary Jane') } let(:group) { create(:group) } - let(:project) { create(:project, namespace: group) } + let(:project) { create(:empty_project, namespace: group) } background do sign_in(user1) diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb index 14edfb6e673..b4381ea373e 100644 --- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb +++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -feature 'Projects > Members > Master adds member with expiration date', feature: true, js: true do +feature 'Projects > Members > Master adds member with expiration date', js: true do include Select2Helper include ActiveSupport::Testing::TimeHelpers let(:master) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } let!(:new_member) { create(:user) } background do diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb index a359c209556..0f96a7cc70d 100644 --- a/spec/features/projects/members/master_manages_access_requests_spec.rb +++ b/spec/features/projects/members/master_manages_access_requests_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects > Members > Master manages access requests', feature: true do +feature 'Projects > Members > Master manages access requests' do let(:user) { create(:user) } let(:master) { create(:user) } let(:project) { create(:empty_project, :public, :access_requestable) } diff --git a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb index 55852012bae..7f39f5b2513 100644 --- a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb +++ b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Projects > Members > Member cannot request access to his project', feature: true do +feature 'Projects > Members > Member cannot request access to his project' do let(:member) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:empty_project) } background do project.team << [member, :developer] diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb index 3de13aee0ee..1bcf827d33c 100644 --- a/spec/features/projects/members/member_leaves_project_spec.rb +++ b/spec/features/projects/members/member_leaves_project_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Projects > Members > Member leaves project', feature: true do +feature 'Projects > Members > Member leaves project' do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } background do project.team << [user, :developer] diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb index fae52325be0..8b6d0aafcf8 100644 --- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb +++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Projects > Members > Owner cannot leave project', feature: true do - let(:project) { create(:project) } +feature 'Projects > Members > Owner cannot leave project' do + let(:project) { create(:empty_project) } background do sign_in(project.owner) diff --git a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb index a7a5e01465f..d838af6d976 100644 --- a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb +++ b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Projects > Members > Owner cannot request access to his project', feature: true do - let(:project) { create(:project) } +feature 'Projects > Members > Owner cannot request access to his project' do + let(:project) { create(:empty_project) } background do sign_in(project.owner) diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index dc7236fa120..45c2647e6e2 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects > Members > Sorting', feature: true do +feature 'Projects > Members > Sorting' do let(:master) { create(:user, name: 'John Doe') } let(:developer) { create(:user, name: 'Mary Jane', last_sign_in_at: 5.days.ago) } let(:project) { create(:empty_project, namespace: master.namespace, creator: master) } diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index ab86e2da4f6..24c9f708456 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Projects > Members > User requests access', feature: true do +feature 'Projects > Members > User requests access' do let(:user) { create(:user) } - let(:project) { create(:project, :public, :access_requestable) } + let(:project) { create(:project, :public, :access_requestable, :repository) } let(:master) { project.owner } background do diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 8cbd26551bc..85d518c0cc3 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' feature 'Merge Request button' do shared_examples 'Merge request button only shown when allowed' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } - let(:forked_project) { create(:project, :public, forked_from_project: project) } + let(:project) { create(:project, :public, :repository) } + let(:forked_project) { create(:project, :public, :repository, forked_from_project: project) } context 'not logged in' do it 'does not show Create merge request button' do diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb index 6548b4b83e6..a879efef4b5 100644 --- a/spec/features/projects/merge_requests/list_spec.rb +++ b/spec/features/projects/merge_requests/list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' feature 'Merge Requests List' do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } background do project.team << [user, :developer] diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb index 642ca7448a3..b1c38ecc9ab 100644 --- a/spec/features/projects/milestones/milestone_spec.rb +++ b/spec/features/projects/milestones/milestone_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project milestone', :feature do +feature 'Project milestone' do let(:user) { create(:user) } let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } let(:milestone) { create(:milestone, project: project) } diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb index 53cd2711666..4bd1929ac1e 100644 --- a/spec/features/projects/milestones/milestones_sorting_spec.rb +++ b/spec/features/projects/milestones/milestones_sorting_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Milestones sorting', :feature, :js do +feature 'Milestones sorting', :js do include SortingHelper let(:user) { create(:user) } let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb index 3c81db502bc..7cfcccda439 100644 --- a/spec/features/projects/milestones/new_spec.rb +++ b/spec/features/projects/milestones/new_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Creating a new project milestone', :feature, :js do +feature 'Creating a new project milestone', :js do let(:user) { create(:user) } let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) } diff --git a/spec/features/projects/no_password_spec.rb b/spec/features/projects/no_password_spec.rb index d22a6daac08..6aff079dd39 100644 --- a/spec/features/projects/no_password_spec.rb +++ b/spec/features/projects/no_password_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'No Password Alert' do - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:project, :repository, namespace: user.namespace) } context 'with internal auth enabled' do before do diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index a8593709f1b..42f23ee5dec 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Pages', feature: true do +feature 'Pages' do given(:project) { create(:empty_project) } given(:user) { create(:user) } given(:role) { :master } diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 033ccf06124..605415d2af4 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'Pipeline Schedules', :feature, js: true do +feature 'Pipeline Schedules', :js do include PipelineSchedulesHelper - let!(:project) { create(:project) } + let!(:project) { create(:project, :repository) } let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) } let!(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) } let(:scope) { nil } @@ -70,6 +70,17 @@ feature 'Pipeline Schedules', :feature, js: true do expect(first('.branch-name-cell').text).to eq('') end end + + context 'when ref is empty' do + before do + pipeline_schedule.update_attribute(:ref, '') + visit_pipelines_schedules + end + + it 'shows a list of the pipeline schedules with empty ref column' do + expect(first('.branch-name-cell').text).to eq('') + end + end end describe 'POST /projects/pipeline_schedules/new' do @@ -128,6 +139,19 @@ feature 'Pipeline Schedules', :feature, js: true do end end end + + context 'when ref is empty' do + before do + pipeline_schedule.update_attribute(:ref, '') + edit_pipeline_schedule + end + + it 'shows the pipeline schedule with default ref' do + page.within('.js-target-branch-dropdown') do + expect(first('.dropdown-toggle-text').text).to eq('master') + end + end + end end context 'when user creates a new pipeline schedule with variables' do diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 4a08d9088aa..0b626749275 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Pipeline', :feature, :js do +describe 'Pipeline', :js do let(:project) { create(:empty_project) } let(:user) { create(:user) } @@ -42,7 +42,7 @@ describe 'Pipeline', :feature, :js do describe 'GET /:project/pipelines/:id' do include_context 'pipeline builds' - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) } before do @@ -188,7 +188,7 @@ describe 'Pipeline', :feature, :js do describe 'GET /:project/pipelines/:id/builds' do include_context 'pipeline builds' - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } before do @@ -262,7 +262,7 @@ describe 'Pipeline', :feature, :js do end describe 'GET /:project/pipelines/:id/failures' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) } let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) } diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index d776fbc2b12..59dff22eca3 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Pipelines', :feature, :js do +describe 'Pipelines', :js do let(:project) { create(:empty_project) } context 'when user is logged in' do @@ -12,7 +12,7 @@ describe 'Pipelines', :feature, :js do end describe 'GET /:project/pipelines' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let!(:pipeline) do create( @@ -385,7 +385,7 @@ describe 'Pipelines', :feature, :js do end describe 'GET /:project/pipelines/show' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:pipeline) do create(:ci_empty_pipeline, @@ -437,7 +437,7 @@ describe 'Pipelines', :feature, :js do end describe 'POST /:project/pipelines' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do visit new_project_pipeline_path(project) @@ -476,7 +476,7 @@ describe 'Pipelines', :feature, :js do end describe 'Create pipelines' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } before do visit new_project_pipeline_path(project) @@ -512,14 +512,14 @@ describe 'Pipelines', :feature, :js do end context 'when project is public' do - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } it { expect(page).to have_content 'Build with confidence' } it { expect(page).to have_http_status(:success) } end context 'when project is private' do - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, :repository) } it { expect(page).to have_content 'You need to sign in' } end diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb index 89d227eb98f..6001bcfff0a 100644 --- a/spec/features/projects/project_settings_spec.rb +++ b/spec/features/projects/project_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Edit Project Settings', feature: true do +describe 'Edit Project Settings' do include Select2Helper let(:user) { create(:user) } @@ -55,8 +55,7 @@ describe 'Edit Project Settings', feature: true do end context 'when changing project path' do - # Not using empty project because we need a repo to exist - let(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') } + let(:project) { create(:project, :repository, namespace: user.namespace, name: 'gitlabhq') } before(:context) do TestEnv.clean_test_path @@ -97,8 +96,7 @@ describe 'Edit Project Settings', feature: true do end describe 'Transfer project section', js: true do - # Not using empty project because we need a repo to exist - let!(:project) { create(:project, namespace: user.namespace, name: 'gitlabhq') } + let!(:project) { create(:project, :repository, namespace: user.namespace, name: 'gitlabhq') } let!(:group) { create(:group) } before(:context) do diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb index 31c7b492ab7..f0a23729220 100644 --- a/spec/features/projects/ref_switcher_spec.rb +++ b/spec/features/projects/ref_switcher_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' -feature 'Ref switcher', feature: true, js: true do +feature 'Ref switcher', js: true do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :repository) } before do project.team << [user, :master] @@ -19,14 +19,14 @@ feature 'Ref switcher', feature: true, js: true do input.set 'binary' wait_for_requests - expect(find('.dropdown-content ul')).to have_selector('li', count: 6) + expect(find('.dropdown-content ul')).to have_selector('li', count: 7) page.within '.dropdown-content ul' do input.native.send_keys :enter end end - expect(page).to have_title 'binary-encoding' + expect(page).to have_title 'add-pdf-text-binary' end it "user selects ref with special characters" do diff --git a/spec/features/projects/services/jira_service_spec.rb b/spec/features/projects/services/jira_service_spec.rb index 7c29af247d6..ecaa69c49dd 100644 --- a/spec/features/projects/services/jira_service_spec.rb +++ b/spec/features/projects/services/jira_service_spec.rb @@ -1,22 +1,17 @@ require 'spec_helper' -feature 'Setup Jira service', :feature, :js do +feature 'Setup Jira service', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:service) { project.create_jira_service } let(:url) { 'http://jira.example.com' } - - def stub_project_url - WebMock.stub_request(:get, 'http://jira.example.com/rest/api/2/project/GitLabProject') - .with(basic_auth: %w(username password)) - end + let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' } def fill_form(active = true) check 'Active' if active fill_in 'service_url', with: url - fill_in 'service_project_key', with: 'GitLabProject' fill_in 'service_username', with: 'username' fill_in 'service_password', with: 'password' fill_in 'service_jira_issue_transition_id', with: '25' @@ -31,11 +26,10 @@ feature 'Setup Jira service', :feature, :js do describe 'user sets and activates Jira Service' do context 'when Jira connection test succeeds' do - before do - stub_project_url - end - it 'activates the JIRA service' do + server_info = { key: 'value' }.to_json + WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info) + click_link('JIRA') fill_form click_button('Test settings and save changes') @@ -47,10 +41,6 @@ feature 'Setup Jira service', :feature, :js do end context 'when Jira connection test fails' do - before do - stub_project_url.to_return(status: 401) - end - it 'shows errors when some required fields are not filled in' do click_link('JIRA') @@ -64,12 +54,15 @@ feature 'Setup Jira service', :feature, :js do end it 'activates the JIRA service' do + WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)) + .to_raise(JIRA::HTTPError.new(double(message: 'message'))) + click_link('JIRA') fill_form click_button('Test settings and save changes') wait_for_requests - expect(find('.flash-container-page')).to have_content 'Test failed.' + expect(find('.flash-container-page')).to have_content 'Test failed. message' expect(find('.flash-container-page')).to have_content 'Save anyway' find('.flash-alert .flash-action').trigger('click') diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb index 584d3ed8f42..134d7e5e8b7 100644 --- a/spec/features/projects/services/mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/mattermost_slash_command_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Setup Mattermost slash commands', :feature, :js do +feature 'Setup Mattermost slash commands', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:service) { project.create_mattermost_slash_commands_service } @@ -159,7 +159,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do it 'shows the correct trigger url' do value = find_field('request_url').value - expect(value).to match("api/v3/projects/#{project.id}/services/mattermost_slash_commands/trigger") + expect(value).to match("api/v4/projects/#{project.id}/services/mattermost_slash_commands/trigger") end it 'shows a token placeholder' do diff --git a/spec/features/projects/services/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb index 709cd1226c3..824cae261e0 100644 --- a/spec/features/projects/services/slack_service_spec.rb +++ b/spec/features/projects/services/slack_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -feature 'Projects > Slack service > Setup events', feature: true do +feature 'Projects > Slack service > Setup events' do let(:user) { create(:user) } let(:service) { SlackService.new } - let(:project) { create(:project, slack_service: service) } + let(:project) { create(:empty_project, slack_service: service) } background do service.fields diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb index 4efe484262a..6002c589fba 100644 --- a/spec/features/projects/services/slack_slash_command_spec.rb +++ b/spec/features/projects/services/slack_slash_command_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Slack slash commands', feature: true do +feature 'Slack slash commands' do given(:user) { create(:user) } - given(:project) { create(:project) } + given(:project) { create(:empty_project) } given(:service) { project.create_slack_slash_commands_service } background do @@ -40,6 +40,6 @@ feature 'Slack slash commands', feature: true do it 'shows the correct trigger url' do value = find_field('url').value - expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger") + expect(value).to match("api/v4/projects/#{project.id}/services/slack_slash_commands/trigger") end end diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb index 6ae242af87f..1de4918e142 100644 --- a/spec/features/projects/settings/integration_settings_spec.rb +++ b/spec/features/projects/settings/integration_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Integration settings', feature: true do +feature 'Integration settings' do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:role) { :developer } diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb index ecaf65c4ad9..796e2026905 100644 --- a/spec/features/projects/settings/merge_requests_settings_spec.rb +++ b/spec/features/projects/settings/merge_requests_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project settings > Merge Requests', feature: true, js: true do +feature 'Project settings > Merge Requests', :js do let(:project) { create(:empty_project, :public) } let(:user) { create(:user) } diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb index 724cfa10e72..f24d7ff64d0 100644 --- a/spec/features/projects/settings/pipelines_settings_spec.rb +++ b/spec/features/projects/settings/pipelines_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature "Pipelines settings", feature: true do +feature "Pipelines settings" do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:role) { :developer } diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index 98539518f6c..15180d4b498 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Repository settings', feature: true do +feature 'Repository settings' do let(:project) { create(:project_empty_repo) } let(:user) { create(:user) } let(:role) { :developer } diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb index 32d8f1fd16a..1e705d211ea 100644 --- a/spec/features/projects/settings/visibility_settings_spec.rb +++ b/spec/features/projects/settings/visibility_settings_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Visibility settings', feature: true, js: true do +feature 'Visibility settings', js: true do let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace, visibility_level: 20) } + let(:project) { create(:empty_project, namespace: user.namespace, visibility_level: 20) } context 'as owner' do before do diff --git a/spec/features/projects/shortcuts_spec.rb b/spec/features/projects/shortcuts_spec.rb index 8dd70e07b30..2c6d0a56311 100644 --- a/spec/features/projects/shortcuts_spec.rb +++ b/spec/features/projects/shortcuts_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -feature 'Project shortcuts', feature: true do - let(:project) { create(:project, name: 'Victorialand') } +feature 'Project shortcuts' do + let(:project) { create(:empty_project, name: 'Victorialand') } let(:user) { create(:user) } describe 'On a project', js: true do diff --git a/spec/features/projects/show_project_spec.rb b/spec/features/projects/show_project_spec.rb new file mode 100644 index 00000000000..1bc6fae9e7f --- /dev/null +++ b/spec/features/projects/show_project_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe 'Project show page', feature: true do + context 'when project pending delete' do + let(:project) { create(:project, :empty_repo, pending_delete: true) } + + before do + sign_in(project.owner) + end + + it 'shows error message if deletion for project fails' do + project.update_attributes(delete_error: "Something went wrong", pending_delete: false) + + visit project_path(project) + + expect(page).to have_selector('.project-deletion-failed-message') + expect(page).to have_content("This project was scheduled for deletion, but failed with the following message: #{project.delete_error}") + end + end +end diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb index 06d32423a13..7f0e7e3116c 100644 --- a/spec/features/projects/snippets/create_snippet_spec.rb +++ b/spec/features/projects/snippets/create_snippet_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -feature 'Create Snippet', :js, feature: true do +feature 'Create Snippet', :js do include DropzoneHelper let(:user) { create(:user) } - let(:project) { create(:project, :repository, :public) } + let(:project) { create(:empty_project, :public) } def fill_form fill_in 'project_snippet_title', with: 'My Snippet Title' diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb index 52698fe1fa3..08dc7cf6c5b 100644 --- a/spec/features/projects/snippets/show_spec.rb +++ b/spec/features/projects/snippets/show_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project snippet', :js, feature: true do +feature 'Project snippet', :js do let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:snippet) { create(:project_snippet, project: project, file_name: file_name, content: content) } diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb index 513a05151b2..0822684a42c 100644 --- a/spec/features/projects/snippets_spec.rb +++ b/spec/features/projects/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Project snippets', :js, feature: true do +describe 'Project snippets', :js do context 'when the project has snippets' do let(:project) { create(:empty_project, :public) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb index 007910bb931..262dcc0abff 100644 --- a/spec/features/projects/sub_group_issuables_spec.rb +++ b/spec/features/projects/sub_group_issuables_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Subgroup Issuables', :feature, :js, :nested_groups do +describe 'Subgroup Issuables', :js, :nested_groups do let!(:group) { create(:group, name: 'group') } let!(:subgroup) { create(:group, parent: group, name: 'subgroup') } let!(:project) { create(:empty_project, namespace: subgroup, name: 'project') } diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb index 34c5e59c3e5..a6c5a486bcc 100644 --- a/spec/features/projects/tags/download_buttons_spec.rb +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Download buttons in tags page', feature: true do +feature 'Download buttons in tags page' do given(:user) { create(:user) } given(:role) { :developer } given(:status) { 'success' } diff --git a/spec/features/projects/user_browses_files_spec.rb b/spec/features/projects/user_browses_files_spec.rb index 263a3a29a66..b7a0b72db50 100644 --- a/spec/features/projects/user_browses_files_spec.rb +++ b/spec/features/projects/user_browses_files_spec.rb @@ -7,7 +7,7 @@ describe 'User browses files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') } diff --git a/spec/features/projects/user_creates_directory_spec.rb b/spec/features/projects/user_creates_directory_spec.rb index 635bd4493dd..1ba5d83eadf 100644 --- a/spec/features/projects/user_creates_directory_spec.rb +++ b/spec/features/projects/user_creates_directory_spec.rb @@ -5,7 +5,7 @@ feature 'User creates a directory', js: true do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } let(:user) { create(:user) } diff --git a/spec/features/projects/user_creates_files_spec.rb b/spec/features/projects/user_creates_files_spec.rb index 0c7f1a775c1..4b78cc4fc53 100644 --- a/spec/features/projects/user_creates_files_spec.rb +++ b/spec/features/projects/user_creates_files_spec.rb @@ -5,7 +5,7 @@ describe 'User creates files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } diff --git a/spec/features/projects/user_deletes_files_spec.rb b/spec/features/projects/user_deletes_files_spec.rb index 97e60862b4f..95cd316be0e 100644 --- a/spec/features/projects/user_deletes_files_spec.rb +++ b/spec/features/projects/user_deletes_files_spec.rb @@ -5,7 +5,7 @@ describe 'User deletes files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } diff --git a/spec/features/projects/user_edits_files_spec.rb b/spec/features/projects/user_edits_files_spec.rb index eb26f1bc123..8ae89c980b9 100644 --- a/spec/features/projects/user_edits_files_spec.rb +++ b/spec/features/projects/user_edits_files_spec.rb @@ -5,7 +5,7 @@ describe 'User edits files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } diff --git a/spec/features/projects/user_replaces_files_spec.rb b/spec/features/projects/user_replaces_files_spec.rb index 50f2ffc4bbf..e284fdefd4f 100644 --- a/spec/features/projects/user_replaces_files_spec.rb +++ b/spec/features/projects/user_replaces_files_spec.rb @@ -7,7 +7,7 @@ describe 'User replaces files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } diff --git a/spec/features/projects/user_uploads_files_spec.rb b/spec/features/projects/user_uploads_files_spec.rb index 64a1439badd..98871317ca3 100644 --- a/spec/features/projects/user_uploads_files_spec.rb +++ b/spec/features/projects/user_uploads_files_spec.rb @@ -7,7 +7,7 @@ describe 'User uploads files' do "You're not allowed to make changes to this project directly. "\ "A fork of this project has been created that you can make changes in, so you can submit a merge request." end - let(:project) { create(:project, name: 'Shop') } + let(:project) { create(:project, :repository, name: 'Shop') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index 231e8eed4fb..dbe98a38197 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Projects > Wiki > User previews markdown changes', feature: true, js: true do +feature 'Projects > Wiki > User previews markdown changes', js: true do let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } let(:wiki_content) do <<-HEREDOC [regular link](regular) diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb index ea816082479..2c668185666 100644 --- a/spec/features/projects/wiki/shortcuts_spec.rb +++ b/spec/features/projects/wiki/shortcuts_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Wiki shortcuts', :feature, :js do +feature 'Wiki shortcuts', :js do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } let(:wiki_page) do diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index 9d66f482c8d..78c619f6301 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -11,7 +11,7 @@ feature 'Projects > Wiki > User creates wiki page', :js do end context 'in the user namespace' do - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } context 'when wiki is empty' do before do @@ -157,7 +157,7 @@ feature 'Projects > Wiki > User creates wiki page', :js do end context 'in a group namespace' do - let(:project) { create(:project, namespace: create(:group, :public)) } + let(:project) { create(:empty_project, namespace: create(:group, :public)) } context 'when wiki is empty' do before do diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb index 9445b88af8d..9c58e336605 100644 --- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'Projects > Wiki > User views Git access wiki page', :feature do +describe 'Projects > Wiki > User views Git access wiki page' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:wiki_page) do WikiPages::CreateService.new( project, diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index 425195840d8..8271428582d 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -1,18 +1,18 @@ require 'spec_helper' -feature 'Projects > Wiki > User updates wiki page', feature: true do +feature 'Projects > Wiki > User updates wiki page' do let(:user) { create(:user) } + let!(:wiki_page) { WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute } background do project.team << [user, :master] - WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute sign_in(user) visit project_wikis_path(project) end context 'in the user namespace' do - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } context 'the home page' do scenario 'success when the wiki content is not empty' do @@ -51,10 +51,20 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do expect(page).to have_selector('.atwho-view') end end + + scenario 'page has been updated since the user opened the edit page' do + click_link 'Edit' + + wiki_page.update('Update') + + click_button 'Save changes' + + expect(page).to have_content 'Someone edited the page the same time you did.' + end end context 'in a group namespace' do - let(:project) { create(:project, namespace: create(:group, :public)) } + let(:project) { create(:empty_project, namespace: create(:group, :public)) } scenario 'the home page' do click_link 'Edit' diff --git a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb index 13e882ad665..4f94ab1a609 100644 --- a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -feature 'Projects > Wiki > User views the wiki page', feature: true do +feature 'Projects > Wiki > User views the wiki page' do let(:user) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:old_page_version_id) { wiki_page.versions.last.id } let(:wiki_page) do WikiPages::CreateService.new( diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb index 2234af1d795..8d1e6f66039 100644 --- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Projects > Wiki > User views wiki in project page', feature: true do +describe 'Projects > Wiki > User views wiki in project page' do let(:user) { create(:user) } before do diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 10c7e5934e4..3295f7f9174 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Project', feature: true do +feature 'Project' do describe 'description' do let(:project) { create(:project, :repository) } let(:path) { project_path(project) } diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb index 8a3574546c2..3677bf38724 100644 --- a/spec/features/protected_branches_spec.rb +++ b/spec/features/protected_branches_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Protected Branches', feature: true, js: true do +feature 'Protected Branches', js: true do let(:user) { create(:user, :admin) } let(:project) { create(:project, :repository) } diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb index 7a22cf60996..c9ba1a8c088 100644 --- a/spec/features/protected_tags_spec.rb +++ b/spec/features/protected_tags_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projected Tags', feature: true, js: true do +feature 'Projected Tags', js: true do let(:user) { create(:user, :admin) } let(:project) { create(:project, :repository) } diff --git a/spec/features/raven_js_spec.rb b/spec/features/raven_js_spec.rb index e8fa49c18cb..b1f51959d54 100644 --- a/spec/features/raven_js_spec.rb +++ b/spec/features/raven_js_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'RavenJS', :feature, :js do +feature 'RavenJS', :js do let(:raven_path) { '/raven.bundle.js' } it 'should not load raven if sentry is disabled' do diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb index d82ebe02f77..1074eb62b33 100644 --- a/spec/features/reportable_note/commit_spec.rb +++ b/spec/features/reportable_note/commit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Reportable note on commit', :feature, :js do +describe 'Reportable note on commit', :js do include RepoHelpers let(:user) { create(:user) } diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb index cb1cb1a1417..9964a32db2e 100644 --- a/spec/features/reportable_note/issue_spec.rb +++ b/spec/features/reportable_note/issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Reportable note on issue', :feature, :js do +describe 'Reportable note on issue', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb index 8a531b9a9e9..a491abdb6cb 100644 --- a/spec/features/reportable_note/merge_request_spec.rb +++ b/spec/features/reportable_note/merge_request_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Reportable note on merge request', :feature, :js do +describe 'Reportable note on merge request', :js do let(:user) { create(:user) } let(:project) { create(:project) } let(:merge_request) { create(:merge_request, source_project: project) } diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb index f560a0ebfd9..9a14024684c 100644 --- a/spec/features/reportable_note/snippets_spec.rb +++ b/spec/features/reportable_note/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Reportable note on snippets', :feature, :js do +describe 'Reportable note on snippets', :js do let(:user) { create(:user) } let(:project) { create(:empty_project) } diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 12ef23440b7..b29f19f1562 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Search", feature: true do +describe "Search" do include FilteredSearchHelpers let(:user) { create(:user) } @@ -154,7 +154,7 @@ describe "Search", feature: true do end end - describe 'Right header search field', feature: true do + describe 'Right header search field' do it 'allows enter key to search', js: true do visit project_path(project) fill_in 'search', with: 'gitlab' diff --git a/spec/features/security/admin_access_spec.rb b/spec/features/security/admin_access_spec.rb index e180ca53eb5..3ca1303bda6 100644 --- a/spec/features/security/admin_access_spec.rb +++ b/spec/features/security/admin_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Admin::Projects", feature: true do +describe "Admin::Projects" do include AccessMatchers describe "GET /admin/projects" do diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb index 40f773956d1..149bd32e736 100644 --- a/spec/features/security/dashboard_access_spec.rb +++ b/spec/features/security/dashboard_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Dashboard access", feature: true do +describe "Dashboard access" do include AccessMatchers describe "GET /dashboard" do diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb index 87cce32d6c6..bf7be33013e 100644 --- a/spec/features/security/group/internal_access_spec.rb +++ b/spec/features/security/group/internal_access_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -describe 'Internal Group access', feature: true do +describe 'Internal Group access' do include AccessMatchers let(:group) { create(:group, :internal) } - let(:project) { create(:project, :internal, group: group) } + let(:project) { create(:empty_project, :internal, group: group) } let(:project_guest) do create(:user) do |user| project.add_guest(user) @@ -49,6 +49,7 @@ describe 'Internal Group access', feature: true do end describe 'GET /groups/:path/merge_requests' do + let(:project) { create(:project, :internal, :repository, group: group) } subject { merge_requests_group_path(group) } it { is_expected.to be_allowed_for(:admin) } diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb index 1d6b3e77c22..c399d7a0851 100644 --- a/spec/features/security/group/private_access_spec.rb +++ b/spec/features/security/group/private_access_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -describe 'Private Group access', feature: true do +describe 'Private Group access' do include AccessMatchers let(:group) { create(:group, :private) } - let(:project) { create(:project, :private, group: group) } + let(:project) { create(:empty_project, :private, group: group) } let(:project_guest) do create(:user) do |user| project.add_guest(user) @@ -49,6 +49,7 @@ describe 'Private Group access', feature: true do end describe 'GET /groups/:path/merge_requests' do + let(:project) { create(:project, :private, :repository, group: group) } subject { merge_requests_group_path(group) } it { is_expected.to be_allowed_for(:admin) } diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb index d7d76177269..63e4d7ca65c 100644 --- a/spec/features/security/group/public_access_spec.rb +++ b/spec/features/security/group/public_access_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -describe 'Public Group access', feature: true do +describe 'Public Group access' do include AccessMatchers let(:group) { create(:group, :public) } - let(:project) { create(:project, :public, group: group) } + let(:project) { create(:empty_project, :public, group: group) } let(:project_guest) do create(:user) do |user| project.add_guest(user) @@ -49,6 +49,7 @@ describe 'Public Group access', feature: true do end describe 'GET /groups/:path/merge_requests' do + let(:project) { create(:project, :public, :repository, group: group) } subject { merge_requests_group_path(group) } it { is_expected.to be_allowed_for(:admin) } diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb index c19678ab381..41eb7b26578 100644 --- a/spec/features/security/profile_access_spec.rb +++ b/spec/features/security/profile_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Profile access", feature: true do +describe "Profile access" do include AccessMatchers describe "GET /profile/keys" do diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 1000a0bdd89..a7928857b7d 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe "Internal Project Access", feature: true do +describe "Internal Project Access" do include AccessMatchers - set(:project) { create(:project, :internal) } + set(:project) { create(:project, :internal, :repository) } describe "Project should be internal" do describe '#internal?' do diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 94d759393ca..a4396b20afd 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe "Private Project Access", feature: true do +describe "Private Project Access" do include AccessMatchers - set(:project) { create(:project, :private, public_builds: false) } + set(:project) { create(:project, :private, :repository, public_builds: false) } describe "Project should be private" do describe '#private?' do diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index d45e1dbc09b..fccdeb0e5b7 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe "Public Project Access", feature: true do +describe "Public Project Access" do include AccessMatchers - set(:project) { create(:project, :public) } + set(:project) { create(:project, :public, :repository) } describe "Project should be public" do describe '#public?' do diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb index 2420caa88c4..178782af56c 100644 --- a/spec/features/security/project/snippet/internal_access_spec.rb +++ b/spec/features/security/project/snippet/internal_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Internal Project Snippets Access", feature: true do +describe "Internal Project Snippets Access" do include AccessMatchers let(:project) { create(:empty_project, :internal) } diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb index 0b8548a675b..7725c25ca1f 100644 --- a/spec/features/security/project/snippet/private_access_spec.rb +++ b/spec/features/security/project/snippet/private_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Private Project Snippets Access", feature: true do +describe "Private Project Snippets Access" do include AccessMatchers let(:project) { create(:empty_project, :private) } diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb index 153f8f964a6..52aec75dcd0 100644 --- a/spec/features/security/project/snippet/public_access_spec.rb +++ b/spec/features/security/project/snippet/public_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Public Project Snippets Access", feature: true do +describe "Public Project Snippets Access" do include AccessMatchers let(:project) { create(:empty_project, :public) } diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index 5d6d1e79af2..b6367b88e17 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Signup', feature: true do +feature 'Signup' do describe 'signup with no errors' do context "when sending confirmation email" do before do diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb index 97d1c2d65e6..835fd90adc8 100644 --- a/spec/features/snippets/explore_spec.rb +++ b/spec/features/snippets/explore_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Explore Snippets', feature: true do +feature 'Explore Snippets' do let!(:public_snippet) { create(:personal_snippet, :public) } let!(:internal_snippet) { create(:personal_snippet, :internal) } let!(:private_snippet) { create(:personal_snippet, :private) } diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb index fb3e75f2102..3a229612235 100644 --- a/spec/features/snippets/internal_snippet_spec.rb +++ b/spec/features/snippets/internal_snippet_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Internal Snippets', feature: true, js: true do +feature 'Internal Snippets', js: true do let(:internal_snippet) { create(:personal_snippet, :internal) } describe 'normal user' do diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb index 17e93209f0c..f1d0905738b 100644 --- a/spec/features/snippets/notes_on_personal_snippets_spec.rb +++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Comments on personal snippets', :js, feature: true do +describe 'Comments on personal snippets', :js do include NoteInteractionHelpers let!(:user) { create(:user) } diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb index afd945a8555..bdeeca7187e 100644 --- a/spec/features/snippets/public_snippets_spec.rb +++ b/spec/features/snippets/public_snippets_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Public Snippets', :js, feature: true do +feature 'Public Snippets', :js do scenario 'Unauthenticated user should see public snippets' do public_snippet = create(:personal_snippet, :public) diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb index 5483df39a8b..cd66a2cb20c 100644 --- a/spec/features/snippets/search_snippets_spec.rb +++ b/spec/features/snippets/search_snippets_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Search Snippets', feature: true do +feature 'Search Snippets' do scenario 'User searches for snippets by title' do public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') private_snippet = create(:personal_snippet, :private, title: 'Middle and End') diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb index 95fc1d2bb62..5a48f5774ca 100644 --- a/spec/features/snippets/show_spec.rb +++ b/spec/features/snippets/show_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Snippet', :js, feature: true do +feature 'Snippet', :js do let(:project) { create(:project, :repository) } let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content) } diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb index 698d3b5d3e3..a919f5fa20b 100644 --- a/spec/features/snippets/user_creates_snippet_spec.rb +++ b/spec/features/snippets/user_creates_snippet_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User creates snippet', :js, feature: true do +feature 'User creates snippet', :js do include DropzoneHelper let(:user) { create(:user) } diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb index 162c2c9e730..ae5b883c477 100644 --- a/spec/features/snippets/user_deletes_snippet_spec.rb +++ b/spec/features/snippets/user_deletes_snippet_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User deletes snippet', feature: true do +feature 'User deletes snippet' do let(:user) { create(:user) } let(:content) { 'puts "test"' } let(:snippet) { create(:personal_snippet, :public, content: content, author: user) } diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb index c9f9741b4bb..26070e508e2 100644 --- a/spec/features/snippets/user_edits_snippet_spec.rb +++ b/spec/features/snippets/user_edits_snippet_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User edits snippet', :js, feature: true do +feature 'User edits snippet', :js do include DropzoneHelper let(:file_name) { 'test.rb' } diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb index 019310f2326..7bc27486787 100644 --- a/spec/features/snippets/user_snippets_spec.rb +++ b/spec/features/snippets/user_snippets_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User Snippets', feature: true do +feature 'User Snippets' do let(:author) { create(:user) } let!(:public_snippet) { create(:personal_snippet, :public, author: author, title: "This is a public snippet") } let!(:internal_snippet) { create(:personal_snippet, :internal, author: author, title: "This is an internal snippet") } diff --git a/spec/features/snippets_spec.rb b/spec/features/snippets_spec.rb index 70b16bfc810..ae3b876f87c 100644 --- a/spec/features/snippets_spec.rb +++ b/spec/features/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Snippets', feature: true do +describe 'Snippets' do context 'when the project has snippets' do let(:project) { create(:empty_project, :public) } let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) } diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb index 1cef3d5c6f4..35e1ca32f67 100644 --- a/spec/features/tags/master_creates_tag_spec.rb +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Master creates tag', feature: true do +feature 'Master creates tag' do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb index 98af1d6b4f7..e3c904ef3aa 100644 --- a/spec/features/tags/master_deletes_tag_spec.rb +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Master deletes tag', feature: true do +feature 'Master deletes tag' do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb index 1b61fde7227..d6e84a1c685 100644 --- a/spec/features/tags/master_updates_tag_spec.rb +++ b/spec/features/tags/master_updates_tag_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Master updates tag', feature: true do +feature 'Master updates tag' do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb index fb910feae34..27936bc7f52 100644 --- a/spec/features/tags/master_views_tags_spec.rb +++ b/spec/features/tags/master_views_tags_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Master views tags', feature: true do +feature 'Master views tags' do let(:user) { create(:user) } before do diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index dfc362321aa..7e198ce0677 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Task Lists', feature: true do +feature 'Task Lists' do include Warden::Test::Helpers let(:project) { create(:empty_project) } @@ -62,7 +62,7 @@ feature 'Task Lists', feature: true do visit project_issue_path(project, issue) end - describe 'for Issues', feature: true do + describe 'for Issues' do describe 'multiple tasks', js: true do let!(:issue) { create(:issue, description: markdown, author: user, project: project) } diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 47d5f94f54e..604fe326e96 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Triggers', feature: true, js: true do +feature 'Triggers', js: true do let(:trigger_title) { 'trigger desc' } let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb index 352f8ba70ac..d728dc59b3f 100644 --- a/spec/features/unsubscribe_links_spec.rb +++ b/spec/features/unsubscribe_links_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Unsubscribe links', feature: true do +describe 'Unsubscribe links' do include Warden::Test::Helpers let(:recipient) { create(:user) } diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb index 5843f18d89f..e8884bc1a00 100644 --- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User uploads avatar to group', feature: true do +feature 'User uploads avatar to group' do scenario 'they see the new avatar' do user = create(:user) group = create(:group) @@ -18,7 +18,7 @@ feature 'User uploads avatar to group', feature: true do visit group_path(group) - expect(page).to have_selector(%Q(img[src$="/uploads/-/system/group/avatar/#{group.id}/dk.png"])) + expect(page).to have_selector(%Q(img[data-src$="/uploads/-/system/group/avatar/#{group.id}/dk.png"])) # Cheating here to verify something that isn't user-facing, but is important expect(group.reload.avatar.file).to exist diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb index e8171dcaeb0..52003bb0859 100644 --- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb +++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User uploads avatar to profile', feature: true do +feature 'User uploads avatar to profile' do scenario 'they see their new avatar' do user = create(:user) sign_in(user) @@ -16,7 +16,7 @@ feature 'User uploads avatar to profile', feature: true do visit user_path(user) - expect(page).to have_selector(%Q(img[src$="/uploads/-/system/user/avatar/#{user.id}/dk.png"])) + expect(page).to have_selector(%Q(img[data-src$="/uploads/-/system/user/avatar/#{user.id}/dk.png"])) # Cheating here to verify something that isn't user-facing, but is important expect(user.reload.avatar.file).to exist diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb index 01f10ca0933..0654923d9a6 100644 --- a/spec/features/uploads/user_uploads_file_to_note_spec.rb +++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'User uploads file to note', feature: true do +feature 'User uploads file to note' do include DropzoneHelper let(:user) { create(:user) } diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb index 797ed0e6437..b961d2337ed 100644 --- a/spec/features/users/projects_spec.rb +++ b/spec/features/users/projects_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Projects tab on a user profile', :feature, :js do +describe 'Projects tab on a user profile', :js do let(:user) { create(:user) } let!(:project) { create(:empty_project, namespace: user.namespace) } let!(:project2) { create(:empty_project, namespace: user.namespace) } diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb index 42738b137af..13760b4c2fc 100644 --- a/spec/features/users/snippets_spec.rb +++ b/spec/features/users/snippets_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Snippets tab on a user profile', feature: true, js: true do +describe 'Snippets tab on a user profile', js: true do context 'when the user has snippets' do let(:user) { create(:user) } diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 84af13d3e49..ff004d85272 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Users', feature: true, js: true do +feature 'Users', js: true do let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') } scenario 'GET /users/sign_in creates a new user account' do diff --git a/spec/finders/access_requests_finder_spec.rb b/spec/finders/access_requests_finder_spec.rb index c7278e971ae..1d0c15392b2 100644 --- a/spec/finders/access_requests_finder_spec.rb +++ b/spec/finders/access_requests_finder_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AccessRequestsFinder, services: true do +describe AccessRequestsFinder do let(:user) { create(:user) } let(:access_requester) { create(:user) } diff --git a/spec/finders/admin/projects_finder_spec.rb b/spec/finders/admin/projects_finder_spec.rb new file mode 100644 index 00000000000..296d6c51d04 --- /dev/null +++ b/spec/finders/admin/projects_finder_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' + +describe Admin::ProjectsFinder do + describe '#execute' do + let(:user) { create(:user) } + let(:group) { create(:group, :public) } + + let!(:private_project) do + create(:empty_project, :private, name: 'A', path: 'A') + end + + let!(:internal_project) do + create(:empty_project, :internal, group: group, name: 'B', path: 'B') + end + + let!(:public_project) do + create(:empty_project, :public, group: group, name: 'C', path: 'C') + end + + let!(:shared_project) do + create(:empty_project, :private, name: 'D', path: 'D') + end + + let(:params) { {} } + let(:current_user) { user } + let(:project_ids_relation) { nil } + let(:finder) { described_class.new(params: params, current_user: current_user) } + + subject { finder.execute.to_a } + + context 'without a user' do + let(:current_user) { nil } + + it { is_expected.to match_array([shared_project, public_project, internal_project, private_project]) } + end + + context 'with a user' do + it { is_expected.to match_array([shared_project, public_project, internal_project, private_project]) } + end + + context 'filter by namespace_id' do + let(:namespace) { create(:namespace) } + let!(:project_in_namespace) { create(:empty_project, namespace: namespace) } + let(:params) { { namespace_id: namespace.id } } + + it { is_expected.to eq([project_in_namespace]) } + end + + context 'filter by visibility_level' do + before do + private_project.add_master(user) + end + + context 'private' do + let(:params) { { visibility_level: Gitlab::VisibilityLevel::PRIVATE } } + + it { is_expected.to match_array([shared_project, private_project]) } + end + + context 'internal' do + let(:params) { { visibility_level: Gitlab::VisibilityLevel::INTERNAL } } + + it { is_expected.to eq([internal_project]) } + end + + context 'public' do + let(:params) { { visibility_level: Gitlab::VisibilityLevel::PUBLIC } } + + it { is_expected.to eq([public_project]) } + end + end + + context 'filter by push' do + let(:pushed_event) { create(:event, :pushed) } + let!(:project_with_push) { pushed_event.project } + let(:params) { { with_push: true } } + + it { is_expected.to eq([project_with_push]) } + end + + context 'filter by abandoned' do + before do + private_project.update(last_activity_at: Time.zone.now - 6.months - 1.minute) + end + + let(:params) { { abandoned: true } } + + it { is_expected.to eq([private_project]) } + end + + context 'filter by last_repository_check_failed' do + before do + private_project.update(last_repository_check_failed: true) + end + + let(:params) { { last_repository_check_failed: true } } + + it { is_expected.to eq([private_project]) } + end + + context 'filter by archived' do + let!(:archived_project) { create(:empty_project, :public, :archived, name: 'E', path: 'E') } + + context 'archived=false' do + let(:params) { { archived: false } } + + it { is_expected.to match_array([shared_project, public_project, internal_project, private_project]) } + end + + context 'archived=true' do + let(:params) { { archived: true } } + + it { is_expected.to match_array([archived_project, shared_project, public_project, internal_project, private_project]) } + end + end + + context 'filter by personal' do + let!(:personal_project) { create(:empty_project, namespace: user.namespace) } + let(:params) { { personal: true } } + + it { is_expected.to eq([personal_project]) } + end + + context 'filter by name' do + let(:params) { { name: 'C' } } + + it { is_expected.to match_array([shared_project, public_project, private_project]) } + end + + context 'sorting' do + let(:params) { { sort: 'name_asc' } } + + it { is_expected.to eq([private_project, internal_project, public_project, shared_project]) } + end + end +end diff --git a/spec/fixtures/api/schemas/public_api/v4/branch.json b/spec/fixtures/api/schemas/public_api/v4/branch.json new file mode 100644 index 00000000000..a3581178974 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/branch.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "required" : [ + "name", + "commit", + "merged", + "protected", + "developers_can_push", + "developers_can_merge" + ], + "properties" : { + "name": { "type": "string" }, + "commit": { "$ref": "commit/basic.json" }, + "merged": { "type": "boolean" }, + "protected": { "type": "boolean" }, + "developers_can_push": { "type": "boolean" }, + "developers_can_merge": { "type": "boolean" } + }, + "additionalProperties": false +} diff --git a/spec/fixtures/api/schemas/public_api/v4/branches.json b/spec/fixtures/api/schemas/public_api/v4/branches.json new file mode 100644 index 00000000000..854c902b485 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/branches.json @@ -0,0 +1,4 @@ +{ + "type": "array", + "items": { "$ref": "branch.json" } +} diff --git a/spec/fixtures/api/schemas/public_api/v4/commit/basic.json b/spec/fixtures/api/schemas/public_api/v4/commit/basic.json new file mode 100644 index 00000000000..9d99628a286 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/commit/basic.json @@ -0,0 +1,37 @@ +{ + "type": "object", + "required" : [ + "id", + "short_id", + "title", + "created_at", + "parent_ids", + "message", + "author_name", + "author_email", + "authored_date", + "committer_name", + "committer_email", + "committed_date" + ], + "properties" : { + "id": { "type": ["string", "null"] }, + "short_id": { "type": ["string", "null"] }, + "title": { "type": "string" }, + "created_at": { "type": "date" }, + "parent_ids": { + "type": ["array", "null"], + "items": { + "type": "string", + "additionalProperties": false + } + }, + "message": { "type": "string" }, + "author_name": { "type": "string" }, + "author_email": { "type": "string" }, + "authored_date": { "type": "date" }, + "committer_name": { "type": "string" }, + "committer_email": { "type": "string" }, + "committed_date": { "type": "date" } + } +} diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index f5e139685e8..ac5a58ac189 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -62,13 +62,13 @@ describe ApplicationHelper do avatar_url = "/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon(project.full_path).to_s) - .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />" + .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />" allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host) avatar_url = "#{gitlab_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon(project.full_path).to_s) - .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />" + .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />" end it 'gives uploaded icon when present' do @@ -77,7 +77,8 @@ describe ApplicationHelper do allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true) avatar_url = "#{gitlab_host}#{project_avatar_path(project)}" - expect(helper.project_icon(project.full_path).to_s).to match(image_tag(avatar_url)) + expect(helper.project_icon(project.full_path).to_s) + .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />" end end diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index 049475a5408..d16fcf21e45 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -27,11 +27,11 @@ describe AvatarsHelper do it 'displays user avatar' do is_expected.to eq image_tag( - avatar_icon(user, 16), - class: 'avatar has-tooltip s16 ', + LazyImageTagHelper.placeholder_image, + class: 'avatar has-tooltip s16 lazy', alt: "#{user.name}'s avatar", title: user.name, - data: { container: 'body' } + data: { container: 'body', src: avatar_icon(user, 16) } ) end @@ -40,22 +40,8 @@ describe AvatarsHelper do it 'uses provided css_class' do is_expected.to eq image_tag( - avatar_icon(user, 16), - class: "avatar has-tooltip s16 #{options[:css_class]}", - alt: "#{user.name}'s avatar", - title: user.name, - data: { container: 'body' } - ) - end - end - - context 'with lazy parameter' do - let(:options) { { user: user, lazy: true } } - - it 'uses data-src instead of src' do - is_expected.to eq image_tag( - '', - class: 'avatar has-tooltip s16 ', + LazyImageTagHelper.placeholder_image, + class: "avatar has-tooltip s16 #{options[:css_class]} lazy", alt: "#{user.name}'s avatar", title: user.name, data: { container: 'body', src: avatar_icon(user, 16) } @@ -68,11 +54,11 @@ describe AvatarsHelper do it 'uses provided size' do is_expected.to eq image_tag( - avatar_icon(user, options[:size]), - class: "avatar has-tooltip s#{options[:size]} ", + LazyImageTagHelper.placeholder_image, + class: "avatar has-tooltip s#{options[:size]} lazy", alt: "#{user.name}'s avatar", title: user.name, - data: { container: 'body' } + data: { container: 'body', src: avatar_icon(user, options[:size]) } ) end end @@ -82,11 +68,11 @@ describe AvatarsHelper do it 'uses provided url' do is_expected.to eq image_tag( - options[:url], - class: 'avatar has-tooltip s16 ', + LazyImageTagHelper.placeholder_image, + class: 'avatar has-tooltip s16 lazy', alt: "#{user.name}'s avatar", title: user.name, - data: { container: 'body' } + data: { container: 'body', src: options[:url] } ) end end @@ -99,22 +85,22 @@ describe AvatarsHelper do it 'prefers user parameter' do is_expected.to eq image_tag( - avatar_icon(user, 16), - class: 'avatar has-tooltip s16 ', + LazyImageTagHelper.placeholder_image, + class: 'avatar has-tooltip s16 lazy', alt: "#{user.name}'s avatar", title: user.name, - data: { container: 'body' } + data: { container: 'body', src: avatar_icon(user, 16) } ) end end it 'uses user_name and user_email parameter if user is not present' do is_expected.to eq image_tag( - avatar_icon(options[:user_email], 16), - class: 'avatar has-tooltip s16 ', + LazyImageTagHelper.placeholder_image, + class: 'avatar has-tooltip s16 lazy', alt: "#{options[:user_name]}'s avatar", title: options[:user_name], - data: { container: 'body' } + data: { container: 'body', src: avatar_icon(options[:user_email], 16) } ) end end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 8f7f17a484f..9524a101e74 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -8,7 +8,7 @@ describe IssuesHelper do describe "url_for_issue" do let(:issues_url) { ext_project.external_issue_tracker.issues_url} let(:ext_expected) { issues_url.gsub(':id', issue.iid.to_s).gsub(':project_id', ext_project.id.to_s) } - let(:int_expected) { polymorphic_path([@project.namespace, project, issue]) } + let(:int_expected) { polymorphic_path([@project.namespace, @project, issue]) } it "returns internal path if used internal tracker" do @project = project @@ -22,6 +22,12 @@ describe IssuesHelper do expect(url_for_issue(issue.iid)).to match(ext_expected) end + it "returns path to internal issue when internal option passed" do + @project = ext_project + + expect(url_for_issue(issue.iid, ext_project, internal: true)).to match(int_expected) + end + it "returns empty string if project nil" do @project = nil diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb index e5143a0263d..8365b3f5538 100644 --- a/spec/helpers/namespaces_helper_spec.rb +++ b/spec/helpers/namespaces_helper_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe NamespacesHelper, type: :helper do +describe NamespacesHelper do let!(:admin) { create(:admin) } let!(:admin_group) { create(:group, :private) } let!(:user) { create(:user) } diff --git a/spec/initializers/6_validations_spec.rb b/spec/initializers/6_validations_spec.rb index 374517fec37..0877770c167 100644 --- a/spec/initializers/6_validations_spec.rb +++ b/spec/initializers/6_validations_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative '../../config/initializers/6_validations.rb' -describe '6_validations', lib: true do +describe '6_validations' do before :all do FileUtils.mkdir_p('tmp/tests/paths/a/b/c/d') FileUtils.mkdir_p('tmp/tests/paths/a/b/c2') diff --git a/spec/initializers/8_metrics_spec.rb b/spec/initializers/8_metrics_spec.rb index d4189f902fd..4e6052a9f80 100644 --- a/spec/initializers/8_metrics_spec.rb +++ b/spec/initializers/8_metrics_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'instrument_classes', lib: true do +describe 'instrument_classes' do let(:config) { double(:config) } let(:unicorn_sampler) { double(:unicorn_sampler) } diff --git a/spec/initializers/secret_token_spec.rb b/spec/initializers/secret_token_spec.rb index 65c97da2efd..84ad55e9f98 100644 --- a/spec/initializers/secret_token_spec.rb +++ b/spec/initializers/secret_token_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative '../../config/initializers/secret_token' -describe 'create_tokens', lib: true do +describe 'create_tokens' do include StubENV let(:secrets) { ActiveSupport::OrderedOptions.new } diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb index 47b4e431823..ebdabcf93f1 100644 --- a/spec/initializers/settings_spec.rb +++ b/spec/initializers/settings_spec.rb @@ -1,41 +1,41 @@ require 'spec_helper' require_relative '../../config/initializers/1_settings' -describe Settings, lib: true do +describe Settings do describe '#host_without_www' do context 'URL with protocol' do it 'returns the host' do - expect(Settings.host_without_www('http://foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('http://www.foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com' - expect(Settings.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' + expect(described_class.host_without_www('http://foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('http://www.foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com' + expect(described_class.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' - expect(Settings.host_without_www('https://foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('https://www.foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('https://secure.foo.com')).to eq 'secure.foo.com' - expect(Settings.host_without_www('https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'secure.gravatar.com' + expect(described_class.host_without_www('https://foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('https://www.foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('https://secure.foo.com')).to eq 'secure.foo.com' + expect(described_class.host_without_www('https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'secure.gravatar.com' end end context 'URL without protocol' do it 'returns the host' do - expect(Settings.host_without_www('foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('www.foo.com')).to eq 'foo.com' - expect(Settings.host_without_www('secure.foo.com')).to eq 'secure.foo.com' - expect(Settings.host_without_www('www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' + expect(described_class.host_without_www('foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('www.foo.com')).to eq 'foo.com' + expect(described_class.host_without_www('secure.foo.com')).to eq 'secure.foo.com' + expect(described_class.host_without_www('www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' end context 'URL with user/port' do it 'returns the host' do - expect(Settings.host_without_www('bob:pass@foo.com:8080')).to eq 'foo.com' - expect(Settings.host_without_www('bob:pass@www.foo.com:8080')).to eq 'foo.com' - expect(Settings.host_without_www('bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com' - expect(Settings.host_without_www('bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' + expect(described_class.host_without_www('bob:pass@foo.com:8080')).to eq 'foo.com' + expect(described_class.host_without_www('bob:pass@www.foo.com:8080')).to eq 'foo.com' + expect(described_class.host_without_www('bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com' + expect(described_class.host_without_www('bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' - expect(Settings.host_without_www('http://bob:pass@foo.com:8080')).to eq 'foo.com' - expect(Settings.host_without_www('http://bob:pass@www.foo.com:8080')).to eq 'foo.com' - expect(Settings.host_without_www('http://bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com' - expect(Settings.host_without_www('http://bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' + expect(described_class.host_without_www('http://bob:pass@foo.com:8080')).to eq 'foo.com' + expect(described_class.host_without_www('http://bob:pass@www.foo.com:8080')).to eq 'foo.com' + expect(described_class.host_without_www('http://bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com' + expect(described_class.host_without_www('http://bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com' end end end diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb index 70a18f31744..02a9446ad7b 100644 --- a/spec/initializers/trusted_proxies_spec.rb +++ b/spec/initializers/trusted_proxies_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'trusted_proxies', lib: true do +describe 'trusted_proxies' do context 'with default config' do before do set_trusted_proxies([]) diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js index 0132f4b7c93..b3c9bca64cc 100644 --- a/spec/javascripts/filtered_search/dropdown_user_spec.js +++ b/spec/javascripts/filtered_search/dropdown_user_spec.js @@ -12,7 +12,9 @@ describe('Dropdown User', () => { spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {}); - dropdownUser = new gl.DropdownUser(null, null, null, gl.FilteredSearchTokenKeys); + dropdownUser = new gl.DropdownUser({ + tokenKeys: gl.FilteredSearchTokenKeys, + }); }); it('should not return the double quote found in value', () => { @@ -78,7 +80,10 @@ describe('Dropdown User', () => { loadFixtures(fixtureTemplate); authorFilterDropdownElement = document.querySelector('#js-dropdown-author'); const dummyInput = document.createElement('div'); - dropdown = new gl.DropdownUser(null, authorFilterDropdownElement, dummyInput); + dropdown = new gl.DropdownUser({ + dropdown: authorFilterDropdownElement, + input: dummyInput, + }); }); const findCurrentUserElement = () => authorFilterDropdownElement.querySelector('.js-current-user'); diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/javascripts/fixtures/u2f.rb index c9c0b891237..e3d7986f2cf 100644 --- a/spec/javascripts/fixtures/u2f.rb +++ b/spec/javascripts/fixtures/u2f.rb @@ -10,10 +10,12 @@ context 'U2F' do end describe SessionsController, '(JavaScript fixtures)', type: :controller do + include DeviseHelpers + render_views before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end it 'u2f/authenticate.html.raw' do |example| diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js index 45909d4e70e..3daeb91b1e2 100644 --- a/spec/javascripts/integrations/integration_settings_form_spec.js +++ b/spec/javascripts/integrations/integration_settings_form_spec.js @@ -135,10 +135,10 @@ describe('IntegrationSettingsForm', () => { integrationSettingsForm.testSettings(formData); - deferred.resolve({ error: true, message: errorMessage }); + deferred.resolve({ error: true, message: errorMessage, service_response: 'some error' }); const $flashContainer = $('.flash-container'); - expect($flashContainer.find('.flash-text').text()).toEqual(errorMessage); + expect($flashContainer.find('.flash-text').text()).toEqual('Test failed. some error'); expect($flashContainer.find('.flash-action')).toBeDefined(); expect($flashContainer.find('.flash-action').text()).toEqual('Save anyway'); }); diff --git a/spec/javascripts/lazy_loader_spec.js b/spec/javascripts/lazy_loader_spec.js new file mode 100644 index 00000000000..1d81e4e2d1a --- /dev/null +++ b/spec/javascripts/lazy_loader_spec.js @@ -0,0 +1,57 @@ +import LazyLoader from '~/lazy_loader'; + +let lazyLoader = null; + +describe('LazyLoader', function () { + preloadFixtures('issues/issue_with_comment.html.raw'); + + beforeEach(function () { + loadFixtures('issues/issue_with_comment.html.raw'); + lazyLoader = new LazyLoader({ + observerNode: 'body', + }); + // Doing everything that happens normally in onload + lazyLoader.loadCheck(); + }); + describe('behavior', function () { + it('should copy value from data-src to src for img 1', function (done) { + const img = document.querySelectorAll('img[data-src]')[0]; + const originalDataSrc = img.getAttribute('data-src'); + img.scrollIntoView(); + + setTimeout(() => { + expect(img.getAttribute('src')).toBe(originalDataSrc); + expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0); + done(); + }, 100); + }); + + it('should lazy load dynamically added data-src images', function (done) { + const newImg = document.createElement('img'); + const testPath = '/img/testimg.png'; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(newImg.getAttribute('src')).toBe(testPath); + expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0); + done(); + }, 100); + }); + + it('should not alter normal images', function (done) { + const newImg = document.createElement('img'); + const testPath = '/img/testimg.png'; + newImg.setAttribute('src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(newImg).not.toHaveClass('js-lazy-loaded'); + done(); + }, 100); + }); + }); +}); diff --git a/spec/javascripts/repo/repo_commit_section_spec.js b/spec/javascripts/repo/repo_commit_section_spec.js new file mode 100644 index 00000000000..919314ac065 --- /dev/null +++ b/spec/javascripts/repo/repo_commit_section_spec.js @@ -0,0 +1,129 @@ +import Vue from 'vue'; +import repoCommitSection from '~/repo/repo_commit_section.vue'; +import RepoStore from '~/repo/repo_store'; +import Api from '~/api'; + +describe('RepoCommitSection', () => { + const openedFiles = [{ + id: 0, + changed: true, + url: 'url0', + newContent: 'a', + }, { + id: 1, + changed: true, + url: 'url1', + newContent: 'b', + }, { + id: 2, + changed: false, + }]; + + function createComponent() { + const RepoCommitSection = Vue.extend(repoCommitSection); + + return new RepoCommitSection().$mount(); + } + + it('renders a commit section', () => { + RepoStore.isCommitable = true; + RepoStore.openedFiles = openedFiles; + + const vm = createComponent(); + const changedFiles = [...vm.$el.querySelectorAll('.changed-files > li')]; + const branchDropdownItems = [...vm.$el.querySelectorAll('.branch-dropdown .dropdown-menu > li')]; + const commitMessage = vm.$el.querySelector('#commit-message'); + const targetBranch = vm.$el.querySelector('#target-branch'); + const newMergeRequest = vm.$el.querySelector('.new-merge-request'); + const newMergeRequestCheckbox = newMergeRequest.querySelector('input'); + const submitCommit = vm.$el.querySelector('.submit-commit'); + + expect(vm.$el.querySelector(':scope > form')).toBeTruthy(); + expect(vm.$el.querySelector('.staged-files').textContent).toEqual('Staged files (2)'); + expect(changedFiles.length).toEqual(2); + + changedFiles.forEach((changedFile, i) => { + expect(changedFile.textContent).toEqual(openedFiles[i].url); + }); + + expect(commitMessage.tagName).toEqual('TEXTAREA'); + expect(commitMessage.name).toEqual('commit-message'); + expect(branchDropdownItems[0].textContent).toEqual('Target branch'); + expect(branchDropdownItems[1].textContent).toEqual('Create my own branch'); + expect(targetBranch.tagName).toEqual('INPUT'); + expect(targetBranch.name).toEqual('target-branch'); + expect(targetBranch.type).toEqual('text'); + expect(newMergeRequest.textContent).toMatch('Start a new merge request with these changes'); + expect(newMergeRequestCheckbox.type).toEqual('checkbox'); + expect(newMergeRequestCheckbox.id).toEqual('checkboxes-0'); + expect(newMergeRequestCheckbox.name).toEqual('checkboxes'); + expect(newMergeRequestCheckbox.value).toEqual('1'); + expect(newMergeRequestCheckbox.checked).toBeFalsy(); + expect(submitCommit.type).toEqual('submit'); + expect(submitCommit.disabled).toBeTruthy(); + expect(vm.$el.querySelector('.commit-summary').textContent).toEqual('Commit 2 Files'); + }); + + it('does not render if not isCommitable', () => { + RepoStore.isCommitable = false; + RepoStore.openedFiles = [{ + id: 0, + changed: true, + }]; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('does not render if no changedFiles', () => { + RepoStore.isCommitable = true; + RepoStore.openedFiles = []; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('shows commit submit and summary if commitMessage and spinner if submitCommitsLoading', (done) => { + const projectId = 'projectId'; + const commitMessage = 'commitMessage'; + RepoStore.isCommitable = true; + RepoStore.openedFiles = openedFiles; + RepoStore.projectId = projectId; + + const vm = createComponent(); + const commitMessageEl = vm.$el.querySelector('#commit-message'); + const submitCommit = vm.$el.querySelector('.submit-commit'); + + vm.commitMessage = commitMessage; + + Vue.nextTick(() => { + expect(commitMessageEl.value).toBe(commitMessage); + expect(submitCommit.disabled).toBeFalsy(); + + spyOn(vm, 'makeCommit').and.callThrough(); + spyOn(Api, 'commitMultiple'); + + submitCommit.click(); + + Vue.nextTick(() => { + expect(vm.makeCommit).toHaveBeenCalled(); + expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeTruthy(); + + const args = Api.commitMultiple.calls.allArgs()[0]; + const { commit_message, actions } = args[1]; + + expect(args[0]).toBe(projectId); + expect(commit_message).toBe(commitMessage); + expect(actions.length).toEqual(2); + expect(actions[0].action).toEqual('update'); + expect(actions[1].action).toEqual('update'); + expect(actions[0].content).toEqual('a'); + expect(actions[1].content).toEqual('b'); + + done(); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/repo_file_buttons_spec.js b/spec/javascripts/repo/repo_file_buttons_spec.js index 6938a4e01e8..882a8bf3f87 100644 --- a/spec/javascripts/repo/repo_file_buttons_spec.js +++ b/spec/javascripts/repo/repo_file_buttons_spec.js @@ -9,7 +9,7 @@ describe('RepoFileButtons', () => { return new RepoFileButtons().$mount(); } - it('renders Raw, Blame, History, Permalink, Lock, Preview toggle, Replace and Delete', () => { + it('renders Raw, Blame, History, Permalink, Lock and Preview toggle', () => { const activeFile = { extension: 'md', url: 'url', @@ -24,7 +24,6 @@ describe('RepoFileButtons', () => { const raw = vm.$el.querySelector('.raw'); const blame = vm.$el.querySelector('.blame'); const history = vm.$el.querySelector('.history'); - const replace = vm.$el.querySelector('.replace'); expect(vm.$el.id).toEqual('repo-file-buttons'); expect(vm.$el.style.borderBottom).toEqual('1px solid rgb(31, 120, 209)'); @@ -37,10 +36,6 @@ describe('RepoFileButtons', () => { expect(vm.$el.querySelector('.permalink').textContent).toEqual('Permalink'); expect(vm.$el.querySelector('.lock').textContent).toEqual('Lock'); expect(vm.$el.querySelector('.preview').textContent).toEqual(activeFileLabel); - expect(replace.dataset.target).toEqual('#modal-upload-blob'); - expect(replace.dataset.toggle).toEqual('modal'); - expect(replace.textContent).toEqual('Replace'); - expect(vm.$el.querySelector('.delete').textContent).toEqual('Delete'); }); it('renders a white border if not editMode', () => { diff --git a/spec/javascripts/repo/repo_sidebar_spec.js b/spec/javascripts/repo/repo_sidebar_spec.js index a90ca6ac359..07b3b403976 100644 --- a/spec/javascripts/repo/repo_sidebar_spec.js +++ b/spec/javascripts/repo/repo_sidebar_spec.js @@ -23,7 +23,7 @@ describe('RepoSidebar', () => { expect(thead.querySelector('.last-commit').textContent).toEqual('Last Commit'); expect(thead.querySelector('.last-update').textContent).toEqual('Last Update'); expect(tbody.querySelector('.repo-file-options')).toBeFalsy(); - expect(tbody.querySelector('.prev-directory')).toBeTruthy(); + expect(tbody.querySelector('.prev-directory')).toBeFalsy(); expect(tbody.querySelector('.loading-file')).toBeFalsy(); expect(tbody.querySelector('.file')).toBeTruthy(); }); @@ -48,4 +48,14 @@ describe('RepoSidebar', () => { expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5); }); + + it('renders a prev directory if isRoot', () => { + RepoStore.files = [{ + id: 0, + }]; + RepoStore.isRoot = true; + const vm = createComponent(); + + expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy(); + }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js index d4b200875df..ab8a3f6c64c 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js @@ -10,6 +10,7 @@ const deploymentMockData = [ url: '/root/acets-review-apps/environments/15', stop_url: '/root/acets-review-apps/environments/15/stop', metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics', + metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics', external_url: 'http://diplo.', external_url_formatted: 'diplo.', deployed_at: '2017-03-22T22:44:42.258Z', diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js index 2c3d0ddff28..6adcbc73ed7 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js @@ -3,6 +3,7 @@ import memoryUsageComponent from '~/vue_merge_request_widget/components/mr_widge import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service'; const url = '/root/acets-review-apps/environments/15/deployments/1/metrics'; +const monitoringUrl = '/root/acets-review-apps/environments/15/metrics'; const metricsMockData = { success: true, @@ -39,6 +40,7 @@ const createComponent = () => { el: document.createElement('div'), propsData: { metricsUrl: url, + metricsMonitoringUrl: monitoringUrl, memoryMetrics: [], deploymentTime: 0, hasMetrics: false, diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb index 787212581e2..d70749536b8 100644 --- a/spec/lib/banzai/cross_project_reference_spec.rb +++ b/spec/lib/banzai/cross_project_reference_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::CrossProjectReference, lib: true do +describe Banzai::CrossProjectReference do include described_class describe '#project_from_ref' do diff --git a/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb b/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb index 33b812ef425..34f1657b6d3 100644 --- a/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb +++ b/spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::AsciiDocPostProcessingFilter, lib: true do +describe Banzai::Filter::AsciiDocPostProcessingFilter do include FilterSpecHelper it "adds class for elements with data-math-style" do diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb index a6d2ea11fcc..b7c2ff03125 100644 --- a/spec/lib/banzai/filter/autolink_filter_spec.rb +++ b/spec/lib/banzai/filter/autolink_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::AutolinkFilter, lib: true do +describe Banzai::Filter::AutolinkFilter do include FilterSpecHelper let(:link) { 'http://about.gitlab.com/' } diff --git a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb index 2799249ae3e..8224dc5a6b9 100644 --- a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb +++ b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Banzai::Filter::BlockquoteFenceFilter, lib: true do +describe Banzai::Filter::BlockquoteFenceFilter do include FilterSpecHelper it 'converts blockquote fences to blockquote lines' do diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb index 60c27bc0d3c..11d48387544 100644 --- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do +describe Banzai::Filter::CommitRangeReferenceFilter do include FilterSpecHelper let(:project) { create(:project, :public, :repository) } diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index f6893641481..fb2a36d1ba1 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::CommitReferenceFilter, lib: true do +describe Banzai::Filter::CommitReferenceFilter do include FilterSpecHelper let(:project) { create(:project, :public, :repository) } diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb index 086a006c45f..10910f22d4a 100644 --- a/spec/lib/banzai/filter/emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/emoji_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::EmojiFilter, lib: true do +describe Banzai::Filter::EmojiFilter do include FilterSpecHelper before do diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb index b7d82c36ddd..a0d391d981c 100644 --- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do +describe Banzai::Filter::ExternalIssueReferenceFilter do include FilterSpecHelper def helper @@ -108,6 +108,11 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do let(:issue) { ExternalIssue.new("#123", project) } let(:reference) { issue.to_reference } + before do + project.issues_enabled = false + project.save! + end + it_behaves_like "external issue tracker" end diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb index 0f8ec8de7a0..2a3c0cd78b8 100644 --- a/spec/lib/banzai/filter/external_link_filter_spec.rb +++ b/spec/lib/banzai/filter/external_link_filter_spec.rb @@ -17,7 +17,7 @@ shared_examples 'an external link with rel attribute' do end end -describe Banzai::Filter::ExternalLinkFilter, lib: true do +describe Banzai::Filter::ExternalLinkFilter do include FilterSpecHelper it 'ignores elements without an href attribute' do diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb index 082c0d4dd0d..663e3514436 100644 --- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb +++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::GollumTagsFilter, lib: true do +describe Banzai::Filter::GollumTagsFilter do include FilterSpecHelper let(:project) { create(:empty_project) } @@ -22,7 +22,7 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do tag = '[[images/image.jpg]]' doc = filter("See #{tag}", project_wiki: project_wiki) - expect(doc.at_css('img')['src']).to eq "#{project_wiki.wiki_base_path}/images/image.jpg" + expect(doc.at_css('img')['data-src']).to eq "#{project_wiki.wiki_base_path}/images/image.jpg" end it 'does not creates img tag if image does not exist' do @@ -40,7 +40,7 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do tag = '[[http://example.com/image.jpg]]' doc = filter("See #{tag}", project_wiki: project_wiki) - expect(doc.at_css('img')['src']).to eq "http://example.com/image.jpg" + expect(doc.at_css('img')['data-src']).to eq "http://example.com/image.jpg" end it 'does not creates img tag for invalid URL' do diff --git a/spec/lib/banzai/filter/html_entity_filter_spec.rb b/spec/lib/banzai/filter/html_entity_filter_spec.rb index f9e6bd609f0..91e18d876d5 100644 --- a/spec/lib/banzai/filter/html_entity_filter_spec.rb +++ b/spec/lib/banzai/filter/html_entity_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::HtmlEntityFilter, lib: true do +describe Banzai::Filter::HtmlEntityFilter do include FilterSpecHelper let(:unescaped) { 'foo <strike attr="foo">&&&</strike>' } diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb new file mode 100644 index 00000000000..c19de7b784a --- /dev/null +++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Banzai::Filter::ImageLazyLoadFilter, lib: true do + include FilterSpecHelper + + def image(path) + %(<img src="#{path}" />) + end + + it 'transforms the image src to a data-src' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' + end + + it 'works with external images' do + doc = filter(image('https://i.imgur.com/DfssX9C.jpg')) + expect(doc.at_css('img')['data-src']).to eq 'https://i.imgur.com/DfssX9C.jpg' + end +end diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb index 294558b3db2..51920869545 100644 --- a/spec/lib/banzai/filter/image_link_filter_spec.rb +++ b/spec/lib/banzai/filter/image_link_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::ImageLinkFilter, lib: true do +describe Banzai::Filter::ImageLinkFilter do include FilterSpecHelper def image(path) diff --git a/spec/lib/banzai/filter/inline_diff_filter_spec.rb b/spec/lib/banzai/filter/inline_diff_filter_spec.rb index 9e526371294..63c4ab61b86 100644 --- a/spec/lib/banzai/filter/inline_diff_filter_spec.rb +++ b/spec/lib/banzai/filter/inline_diff_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::InlineDiffFilter, lib: true do +describe Banzai::Filter::InlineDiffFilter do include FilterSpecHelper it 'adds inline diff span tags for deletions when using square brackets' do diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb index 9c2399815b9..7cf2f4282f8 100644 --- a/spec/lib/banzai/filter/issuable_state_filter_spec.rb +++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::IssuableStateFilter, lib: true do +describe Banzai::Filter::IssuableStateFilter do include ActionView::Helpers::UrlHelper include FilterSpecHelper diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index a79d365d6c5..045bf3e0cc9 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::IssueReferenceFilter, lib: true do +describe Banzai::Filter::IssueReferenceFilter do include FilterSpecHelper def helper diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 8daef3ca691..1daa6ac7f9e 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'html/pipeline' -describe Banzai::Filter::LabelReferenceFilter, lib: true do +describe Banzai::Filter::LabelReferenceFilter do include FilterSpecHelper let(:project) { create(:empty_project, :public, name: 'sample-project') } diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb index 897288b8ad5..00c407d1b69 100644 --- a/spec/lib/banzai/filter/markdown_filter_spec.rb +++ b/spec/lib/banzai/filter/markdown_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::MarkdownFilter, lib: true do +describe Banzai::Filter::MarkdownFilter do include FilterSpecHelper context 'code block' do diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb index 51883782e19..cade8cb409e 100644 --- a/spec/lib/banzai/filter/math_filter_spec.rb +++ b/spec/lib/banzai/filter/math_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::MathFilter, lib: true do +describe Banzai::Filter::MathFilter do include FilterSpecHelper it 'leaves regular inline code unchanged' do diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 1ad329b6452..683972a3112 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do +describe Banzai::Filter::MergeRequestReferenceFilter do include FilterSpecHelper let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 7fab5613afc..8fe05dc2e53 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::MilestoneReferenceFilter, lib: true do +describe Banzai::Filter::MilestoneReferenceFilter do include FilterSpecHelper let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb index 9b8ecb201f3..8235c411eb7 100644 --- a/spec/lib/banzai/filter/plantuml_filter_spec.rb +++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::PlantumlFilter, lib: true do +describe Banzai::Filter::PlantumlFilter do include FilterSpecHelper it 'should replace plantuml pre tag with img tag' do diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb index b81cdbb8957..fb6b81d4f10 100644 --- a/spec/lib/banzai/filter/redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/redactor_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::RedactorFilter, lib: true do +describe Banzai::Filter::RedactorFilter do include ActionView::Helpers::UrlHelper include FilterSpecHelper diff --git a/spec/lib/banzai/filter/reference_filter_spec.rb b/spec/lib/banzai/filter/reference_filter_spec.rb index ba0fa4a609a..b9ca68e8935 100644 --- a/spec/lib/banzai/filter/reference_filter_spec.rb +++ b/spec/lib/banzai/filter/reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::ReferenceFilter, lib: true do +describe Banzai::Filter::ReferenceFilter do let(:project) { build(:project) } describe '#each_node' do diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 1ce7bd7706e..ddebf2264d9 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::RelativeLinkFilter, lib: true do +describe Banzai::Filter::RelativeLinkFilter do def filter(doc, contexts = {}) contexts.reverse_merge!({ commit: commit, diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index a8a0aa6d395..35a32a46eff 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::SanitizationFilter, lib: true do +describe Banzai::Filter::SanitizationFilter do include FilterSpecHelper describe 'default whitelist' do diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb index 9704db0c221..5f548888223 100644 --- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::SnippetReferenceFilter, lib: true do +describe Banzai::Filter::SnippetReferenceFilter do include FilterSpecHelper let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb index f61fc8ceb9e..5a23e0e70cc 100644 --- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb +++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::SyntaxHighlightFilter, lib: true do +describe Banzai::Filter::SyntaxHighlightFilter do include FilterSpecHelper context "when no language is specified" do diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb index 70b31f3a880..ff6b19459bb 100644 --- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb +++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::TableOfContentsFilter, lib: true do +describe Banzai::Filter::TableOfContentsFilter do include FilterSpecHelper def header(level, text) diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb index 6327ca8bbfd..3bc9635b50e 100644 --- a/spec/lib/banzai/filter/upload_link_filter_spec.rb +++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::UploadLinkFilter, lib: true do +describe Banzai::Filter::UploadLinkFilter do def filter(doc, contexts = {}) contexts.reverse_merge!({ project: project diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index 77561e00573..7ea9df5eda5 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::UserReferenceFilter, lib: true do +describe Banzai::Filter::UserReferenceFilter do include FilterSpecHelper let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/filter/video_link_filter_spec.rb b/spec/lib/banzai/filter/video_link_filter_spec.rb index 00494f545a3..81dda0687f3 100644 --- a/spec/lib/banzai/filter/video_link_filter_spec.rb +++ b/spec/lib/banzai/filter/video_link_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::VideoLinkFilter, lib: true do +describe Banzai::Filter::VideoLinkFilter do def filter(doc, contexts = {}) contexts.reverse_merge!({ project: project diff --git a/spec/lib/banzai/filter/wiki_link_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_filter_spec.rb index 92d88c4172c..ceafd12a68e 100644 --- a/spec/lib/banzai/filter/wiki_link_filter_spec.rb +++ b/spec/lib/banzai/filter/wiki_link_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::Filter::WikiLinkFilter, lib: true do +describe Banzai::Filter::WikiLinkFilter do include FilterSpecHelper let(:namespace) { build_stubbed(:namespace, name: "wiki_link_ns") } diff --git a/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb b/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb index fe70eada7eb..9f1b862ef19 100644 --- a/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb +++ b/spec/lib/banzai/filter/yaml_front_matter_filter_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Banzai::Filter::YamlFrontMatterFilter, lib: true do +describe Banzai::Filter::YamlFrontMatterFilter do include FilterSpecHelper it 'allows for `encoding:` before the frontmatter' do diff --git a/spec/lib/banzai/issuable_extractor_spec.rb b/spec/lib/banzai/issuable_extractor_spec.rb index 866297f94a9..728271e757b 100644 --- a/spec/lib/banzai/issuable_extractor_spec.rb +++ b/spec/lib/banzai/issuable_extractor_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::IssuableExtractor, lib: true do +describe Banzai::IssuableExtractor do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:extractor) { described_class.new(project, user) } diff --git a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb index 1eb90dc1847..601ffbb5456 100644 --- a/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb +++ b/spec/lib/banzai/pipeline/gfm_pipeline_spec.rb @@ -4,26 +4,87 @@ describe Banzai::Pipeline::GfmPipeline do describe 'integration between parsing regular and external issue references' do let(:project) { create(:redmine_project, :public) } - it 'allows to use shorthand external reference syntax for Redmine' do - markdown = '#12' + context 'when internal issue tracker is enabled' do + context 'when shorthand pattern #ISSUE_ID is used' do + it 'links an internal issue if it exists' do + issue = create(:issue, project: project) + markdown = issue.to_reference(project, full: true) - result = described_class.call(markdown, project: project)[:output] - link = result.css('a').first + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first - expect(link['href']).to eq 'http://redmine/projects/project_name_in_redmine/issues/12' + expect(link['href']).to eq( + Gitlab::Routing.url_helpers.project_issue_path(project, issue) + ) + end + + it 'does not link any issue if it does not exist on GitLab' do + markdown = '#12' + + result = described_class.call(markdown, project: project)[:output] + expect(result.css('a')).to be_empty + end + end + + it 'allows to use long external reference syntax for Redmine' do + markdown = 'API_32-12' + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq 'http://redmine/projects/project_name_in_redmine/issues/12' + end + + it 'parses cross-project references to regular issues' do + other_project = create(:empty_project, :public) + issue = create(:issue, project: other_project) + markdown = issue.to_reference(project, full: true) + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq( + Gitlab::Routing.url_helpers.project_issue_path(other_project, issue) + ) + end end - it 'parses cross-project references to regular issues' do - other_project = create(:empty_project, :public) - issue = create(:issue, project: other_project) - markdown = issue.to_reference(project, full: true) + context 'when internal issue tracker is disabled' do + before do + project.issues_enabled = false + project.save! + end + + it 'allows to use shorthand external reference syntax for Redmine' do + markdown = '#12' + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq 'http://redmine/projects/project_name_in_redmine/issues/12' + end + + it 'allows to use long external reference syntax for Redmine' do + markdown = 'API_32-12' + + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first + + expect(link['href']).to eq 'http://redmine/projects/project_name_in_redmine/issues/12' + end + + it 'parses cross-project references to regular issues' do + other_project = create(:empty_project, :public) + issue = create(:issue, project: other_project) + markdown = issue.to_reference(project, full: true) - result = described_class.call(markdown, project: project)[:output] - link = result.css('a').first + result = described_class.call(markdown, project: project)[:output] + link = result.css('a').first - expect(link['href']).to eq( - Gitlab::Routing.url_helpers.project_issue_path(other_project, issue) - ) + expect(link['href']).to eq( + Gitlab::Routing.url_helpers.project_issue_path(other_project, issue) + ) + end end end end diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb index b444ca05b8e..0bf45329657 100644 --- a/spec/lib/banzai/reference_parser/base_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::BaseParser, lib: true do +describe Banzai::ReferenceParser::BaseParser do include ReferenceParserHelpers let(:user) { create(:user) } diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb index a314a6119cb..69bf28cdf85 100644 --- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::CommitParser, lib: true do +describe Banzai::ReferenceParser::CommitParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb index 5dca5e784da..b384a59bfb4 100644 --- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::CommitRangeParser, lib: true do +describe Banzai::ReferenceParser::CommitRangeParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb index d212bbac619..a3256afdbb1 100644 --- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do +describe Banzai::ReferenceParser::ExternalIssueParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb index acdd23f81f3..94b989fe91d 100644 --- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::IssueParser, lib: true do +describe Banzai::ReferenceParser::IssueParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb index ddd699f3c25..cf1b2a92195 100644 --- a/spec/lib/banzai/reference_parser/label_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::LabelParser, lib: true do +describe Banzai::ReferenceParser::LabelParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb index cb69ca16800..775749ae3a7 100644 --- a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::MergeRequestParser, lib: true do +describe Banzai::ReferenceParser::MergeRequestParser do include ReferenceParserHelpers let(:user) { create(:user) } diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb index 72d4f3bc18e..2cfcafa8798 100644 --- a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::MilestoneParser, lib: true do +describe Banzai::ReferenceParser::MilestoneParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb index 620875ece20..c6d0b7be254 100644 --- a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::SnippetParser, lib: true do +describe Banzai::ReferenceParser::SnippetParser do include ReferenceParserHelpers let(:project) { create(:empty_project, :public) } diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb index dfebb971f3a..64f2b607d7c 100644 --- a/spec/lib/banzai/reference_parser/user_parser_spec.rb +++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Banzai::ReferenceParser::UserParser, lib: true do +describe Banzai::ReferenceParser::UserParser do include ReferenceParserHelpers let(:group) { create(:group) } diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb index a5dfb49478a..e49ecadde20 100644 --- a/spec/lib/ci/ansi2html_spec.rb +++ b/spec/lib/ci/ansi2html_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Ansi2html, lib: true do +describe Ci::Ansi2html do subject { described_class } it "prints non-ansi as-is" do diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb index 51cbfd2a848..8e2d2724426 100644 --- a/spec/lib/ci/charts_spec.rb +++ b/spec/lib/ci/charts_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Charts, lib: true do +describe Ci::Charts do context "pipeline_times" do let(:project) { create(:empty_project) } let(:chart) { Ci::Charts::PipelineTime.new(project) } diff --git a/spec/lib/ci/mask_secret_spec.rb b/spec/lib/ci/mask_secret_spec.rb index 3101bed20fb..f7b753b022b 100644 --- a/spec/lib/ci/mask_secret_spec.rb +++ b/spec/lib/ci/mask_secret_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::MaskSecret, lib: true do +describe Ci::MaskSecret do subject { described_class } describe '#mask' do diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb index db680489a8d..4dab58b26a0 100644 --- a/spec/lib/constraints/group_url_constrainer_spec.rb +++ b/spec/lib/constraints/group_url_constrainer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GroupUrlConstrainer, lib: true do +describe GroupUrlConstrainer do let!(:group) { create(:group, path: 'gitlab') } describe '#matches?' do diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb index b6884e37aa3..e4b5dfc574a 100644 --- a/spec/lib/constraints/project_url_constrainer_spec.rb +++ b/spec/lib/constraints/project_url_constrainer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectUrlConstrainer, lib: true do +describe ProjectUrlConstrainer do let!(:project) { create(:empty_project) } let!(:namespace) { project.namespace } diff --git a/spec/lib/constraints/user_url_constrainer_spec.rb b/spec/lib/constraints/user_url_constrainer_spec.rb index ed69b830979..cb3b4ff1391 100644 --- a/spec/lib/constraints/user_url_constrainer_spec.rb +++ b/spec/lib/constraints/user_url_constrainer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UserUrlConstrainer, lib: true do +describe UserUrlConstrainer do let!(:user) { create(:user, username: 'dz') } describe '#matches?' do diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb index 8f51474476d..3652d928c43 100644 --- a/spec/lib/disable_email_interceptor_spec.rb +++ b/spec/lib/disable_email_interceptor_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe DisableEmailInterceptor, lib: true do +describe DisableEmailInterceptor do before do - Mail.register_interceptor(DisableEmailInterceptor) + Mail.register_interceptor(described_class) end it 'does not send emails' do @@ -14,7 +14,7 @@ describe DisableEmailInterceptor, lib: true do # Removing interceptor from the list because unregister_interceptor is # implemented in later version of mail gem # See: https://github.com/mikel/mail/pull/705 - Mail.unregister_interceptor(DisableEmailInterceptor) + Mail.unregister_interceptor(described_class) end def deliver_mail diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb index d70690f589d..b1366e74802 100644 --- a/spec/lib/event_filter_spec.rb +++ b/spec/lib/event_filter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe EventFilter, lib: true do +describe EventFilter do describe '#apply_filter' do let(:source_user) { create(:user) } let!(:public_project) { create(:empty_project, :public) } @@ -16,42 +16,42 @@ describe EventFilter, lib: true do let!(:left_event) { create(:event, :left, project: public_project, target: public_project, author: source_user) } it 'applies push filter' do - events = EventFilter.new(EventFilter.push).apply_filter(Event.all) + events = described_class.new(described_class.push).apply_filter(Event.all) expect(events).to contain_exactly(push_event) end it 'applies merged filter' do - events = EventFilter.new(EventFilter.merged).apply_filter(Event.all) + events = described_class.new(described_class.merged).apply_filter(Event.all) expect(events).to contain_exactly(merged_event) end it 'applies issue filter' do - events = EventFilter.new(EventFilter.issue).apply_filter(Event.all) + events = described_class.new(described_class.issue).apply_filter(Event.all) expect(events).to contain_exactly(created_event, updated_event, closed_event, reopened_event) end it 'applies comments filter' do - events = EventFilter.new(EventFilter.comments).apply_filter(Event.all) + events = described_class.new(described_class.comments).apply_filter(Event.all) expect(events).to contain_exactly(comments_event) end it 'applies team filter' do - events = EventFilter.new(EventFilter.team).apply_filter(Event.all) + events = described_class.new(described_class.team).apply_filter(Event.all) expect(events).to contain_exactly(joined_event, left_event) end it 'applies all filter' do - events = EventFilter.new(EventFilter.all).apply_filter(Event.all) + events = described_class.new(described_class.all).apply_filter(Event.all) expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies no filter' do - events = EventFilter.new(nil).apply_filter(Event.all) + events = described_class.new(nil).apply_filter(Event.all) expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies unknown filter' do - events = EventFilter.new('').apply_filter(Event.all) + events = described_class.new('').apply_filter(Event.all) expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index dfffef8b9b7..9b89f47ae7e 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe ExtractsPath, lib: true do - include ExtractsPath +describe ExtractsPath do + include described_class include RepoHelpers include Gitlab::Routing diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb index 5cc3a3745e4..1076c63b5f2 100644 --- a/spec/lib/feature_spec.rb +++ b/spec/lib/feature_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Feature, lib: true do +describe Feature do describe '.get' do let(:feature) { double(:feature) } let(:key) { 'my_feature' } diff --git a/spec/lib/file_size_validator_spec.rb b/spec/lib/file_size_validator_spec.rb index fda6f9a6c88..49501931dd2 100644 --- a/spec/lib/file_size_validator_spec.rb +++ b/spec/lib/file_size_validator_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe FileSizeValidator, lib: true do - let(:validator) { FileSizeValidator.new(options) } +describe FileSizeValidator do + let(:validator) { described_class.new(options) } let(:attachment) { AttachmentUploader.new } let(:note) { create(:note) } diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index 43d52b941ab..f668f78c2b8 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'nokogiri' module Gitlab - describe Asciidoc, lib: true do + describe Asciidoc do let(:input) { '<b>ascii</b>' } let(:context) { {} } let(:html) { 'H<sub>2</sub>O' } diff --git a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb index 15b3db0ed3d..f29431b937c 100644 --- a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb +++ b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state, lib: true do +describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state do include_context 'unique ips sign in limit' let(:user) { create(:user) } diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 55780518230..a9db0d5164d 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Auth, lib: true do +describe Gitlab::Auth do let(:gl_auth) { described_class } describe 'constants' do diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb index 1c3d2547fec..8772d3d5ada 100644 --- a/spec/lib/gitlab/backup/manager_spec.rb +++ b/spec/lib/gitlab/backup/manager_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Backup::Manager, lib: true do +describe Backup::Manager do include StubENV let(:progress) { StringIO.new } @@ -214,4 +214,56 @@ describe Backup::Manager, lib: true do end end end + + describe '#upload' do + let(:backup_file) { Tempfile.new('backup', Gitlab.config.backup.path) } + let(:backup_filename) { File.basename(backup_file.path) } + + before do + allow(subject).to receive(:tar_file).and_return(backup_filename) + + stub_backup_setting( + upload: { + connection: { + provider: 'AWS', + aws_access_key_id: 'id', + aws_secret_access_key: 'secret' + }, + remote_directory: 'directory', + multipart_chunk_size: 104857600, + encryption: nil, + storage_class: nil + } + ) + + # the Fog mock only knows about directories we create explicitly + Fog.mock! + connection = ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys) + connection.directories.create(key: Gitlab.config.backup.upload.remote_directory) + end + + context 'target path' do + it 'uses the tar filename by default' do + expect_any_instance_of(Fog::Collection).to receive(:create) + .with(hash_including(key: backup_filename)) + .and_return(true) + + Dir.chdir(Gitlab.config.backup.path) do + subject.upload + end + end + + it 'adds the DIRECTORY environment variable if present' do + stub_env('DIRECTORY', 'daily') + + expect_any_instance_of(Fog::Collection).to receive(:create) + .with(hash_including(key: "daily/#{backup_filename}")) + .and_return(true) + + Dir.chdir(Gitlab.config.backup.path) do + subject.upload + end + end + end + end end diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb index 51c1e9d657b..db860b01ba4 100644 --- a/spec/lib/gitlab/backup/repository_spec.rb +++ b/spec/lib/gitlab/backup/repository_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Backup::Repository, lib: true do +describe Backup::Repository do let(:progress) { StringIO.new } let!(:project) { create(:empty_project) } diff --git a/spec/lib/gitlab/badge/build/metadata_spec.rb b/spec/lib/gitlab/badge/pipeline/metadata_spec.rb index 9df96ea04eb..d537ce8803c 100644 --- a/spec/lib/gitlab/badge/build/metadata_spec.rb +++ b/spec/lib/gitlab/badge/pipeline/metadata_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'lib/gitlab/badge/shared/metadata' -describe Gitlab::Badge::Build::Metadata do +describe Gitlab::Badge::Pipeline::Metadata do let(:badge) { double(project: create(:empty_project), ref: 'feature') } let(:metadata) { described_class.new(badge) } @@ -9,13 +9,13 @@ describe Gitlab::Badge::Build::Metadata do describe '#title' do it 'returns build status title' do - expect(metadata.title).to eq 'build status' + expect(metadata.title).to eq 'pipeline status' end end describe '#image_url' do it 'returns valid url' do - expect(metadata.image_url).to include 'badges/feature/build.svg' + expect(metadata.image_url).to include 'badges/feature/pipeline.svg' end end diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/pipeline/status_spec.rb index 6abf4ca46a9..dc835375c66 100644 --- a/spec/lib/gitlab/badge/build/status_spec.rb +++ b/spec/lib/gitlab/badge/pipeline/status_spec.rb @@ -1,36 +1,35 @@ require 'spec_helper' -describe Gitlab::Badge::Build::Status do +describe Gitlab::Badge::Pipeline::Status do let(:project) { create(:project, :repository) } let(:sha) { project.commit.sha } let(:branch) { 'master' } let(:badge) { described_class.new(project, branch) } describe '#entity' do - it 'always says build' do - expect(badge.entity).to eq 'build' + it 'always says pipeline' do + expect(badge.entity).to eq 'pipeline' end end describe '#template' do it 'returns badge template' do - expect(badge.template.key_text).to eq 'build' + expect(badge.template.key_text).to eq 'pipeline' end end describe '#metadata' do it 'returns badge metadata' do - expect(badge.metadata.image_url) - .to include 'badges/master/build.svg' + expect(badge.metadata.image_url).to include 'badges/master/pipeline.svg' end end - context 'build exists' do - let!(:build) { create_build(project, sha, branch) } + context 'pipeline exists' do + let!(:pipeline) { create_pipeline(project, sha, branch) } - context 'build success' do + context 'pipeline success' do before do - build.success! + pipeline.success! end describe '#status' do @@ -40,9 +39,9 @@ describe Gitlab::Badge::Build::Status do end end - context 'build failed' do + context 'pipeline failed' do before do - build.drop! + pipeline.drop! end describe '#status' do @@ -54,10 +53,10 @@ describe Gitlab::Badge::Build::Status do context 'when outdated pipeline for given ref exists' do before do - build.success! + pipeline.success! - old_build = create_build(project, '11eeffdd', branch) - old_build.drop! + old_pipeline = create_pipeline(project, '11eeffdd', branch) + old_pipeline.drop! end it 'does not take outdated pipeline into account' do @@ -67,10 +66,10 @@ describe Gitlab::Badge::Build::Status do context 'when multiple pipelines exist for given sha' do before do - build.drop! + pipeline.drop! - new_build = create_build(project, sha, branch) - new_build.success! + new_pipeline = create_pipeline(project, sha, branch) + new_pipeline.success! end it 'does not take outdated pipeline into account' do @@ -87,7 +86,7 @@ describe Gitlab::Badge::Build::Status do end end - def create_build(project, sha, branch) + def create_pipeline(project, sha, branch) pipeline = create(:ci_empty_pipeline, project: project, sha: sha, diff --git a/spec/lib/gitlab/badge/build/template_spec.rb b/spec/lib/gitlab/badge/pipeline/template_spec.rb index a7e21fb8bb1..20fa4f879c3 100644 --- a/spec/lib/gitlab/badge/build/template_spec.rb +++ b/spec/lib/gitlab/badge/pipeline/template_spec.rb @@ -1,28 +1,28 @@ require 'spec_helper' -describe Gitlab::Badge::Build::Template do - let(:badge) { double(entity: 'build', status: 'success') } +describe Gitlab::Badge::Pipeline::Template do + let(:badge) { double(entity: 'pipeline', status: 'success') } let(:template) { described_class.new(badge) } describe '#key_text' do - it 'is always says build' do - expect(template.key_text).to eq 'build' + it 'is always says pipeline' do + expect(template.key_text).to eq 'pipeline' end end describe '#value_text' do it 'is status value' do - expect(template.value_text).to eq 'success' + expect(template.value_text).to eq 'passed' end end describe 'widths and text anchors' do it 'has fixed width and text anchors' do - expect(template.width).to eq 92 - expect(template.key_width).to eq 38 + expect(template.width).to eq 116 + expect(template.key_width).to eq 62 expect(template.value_width).to eq 54 - expect(template.key_text_anchor).to eq 19 - expect(template.value_text_anchor).to eq 65 + expect(template.key_text_anchor).to eq 31 + expect(template.value_text_anchor).to eq 89 end end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb index d8beb05601c..df66a031fec 100644 --- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BitbucketImport::Importer, lib: true do +describe Gitlab::BitbucketImport::Importer do include ImportSpecHelper before do @@ -58,7 +58,7 @@ describe Gitlab::BitbucketImport::Importer, lib: true do ) end - let(:importer) { Gitlab::BitbucketImport::Importer.new(project) } + let(:importer) { described_class.new(project) } let(:issues_statuses_sample_data) do { diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index 773d0d4d288..d7f7b26740d 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BitbucketImport::ProjectCreator, lib: true do +describe Gitlab::BitbucketImport::ProjectCreator do let(:user) { create(:user) } let(:repo) do @@ -27,7 +27,7 @@ describe Gitlab::BitbucketImport::ProjectCreator, lib: true do it 'creates project' do allow_any_instance_of(Project).to receive(:add_import_job) - project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, 'vim', namespace, user, access_params) + project_creator = described_class.new(repo, 'vim', namespace, user, access_params) project = project_creator.execute expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git") diff --git a/spec/lib/gitlab/blame_spec.rb b/spec/lib/gitlab/blame_spec.rb index 26b1baf75be..7cab04e9fc9 100644 --- a/spec/lib/gitlab/blame_spec.rb +++ b/spec/lib/gitlab/blame_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Blame, lib: true do +describe Gitlab::Blame do let(:project) { create(:project, :repository) } let(:path) { 'files/ruby/popen.rb' } let(:commit) { project.commit('master') } diff --git a/spec/lib/gitlab/chat_name_token_spec.rb b/spec/lib/gitlab/chat_name_token_spec.rb index 8c1e6efa9db..1e9fb9077fc 100644 --- a/spec/lib/gitlab/chat_name_token_spec.rb +++ b/spec/lib/gitlab/chat_name_token_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ChatNameToken, lib: true do +describe Gitlab::ChatNameToken do context 'when using unknown token' do let(:token) { } diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index 643e590438a..6c25b7349e1 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Checks::ChangeAccess, lib: true do +describe Gitlab::Checks::ChangeAccess do describe '#exec' do let(:user) { create(:user) } let(:project) { create(:project, :repository) } diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index bc66ce83d4a..6c4cfa1203e 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Checks::ForcePush, lib: true do +describe Gitlab::Checks::ForcePush do let(:project) { create(:project, :repository) } context "exit code checking" do diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index 114d2490490..5a7a42d84c0 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -48,7 +48,9 @@ describe Gitlab::Ci::Status::Build::Cancelable do describe '#has_action?' do context 'when user is allowed to update build' do before do - build.project.team << [user, :developer] + stub_not_protect_default_branch + + build.project.add_developer(user) end it { is_expected.to have_action } diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index c8a97016f20..8768302eda1 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -7,7 +7,9 @@ describe Gitlab::Ci::Status::Build::Factory do let(:factory) { described_class.new(build, user) } before do - project.team << [user, :developer] + stub_not_protect_default_branch + + project.add_developer(user) end context 'when build is successful' do @@ -225,19 +227,19 @@ describe Gitlab::Ci::Status::Build::Factory do end context 'when user has ability to play action' do - before do - project.add_developer(user) - - create(:protected_branch, :developers_can_merge, - name: build.ref, project: project) - end - it 'fabricates status that has action' do expect(status).to have_action end end context 'when user does not have ability to play action' do + before do + allow(build.project).to receive(:empty_repo?).and_return(false) + + create(:protected_branch, :no_one_can_push, + name: build.ref, project: build.project) + end + it 'fabricates status that has no action' do expect(status).not_to have_action end @@ -262,6 +264,13 @@ describe Gitlab::Ci::Status::Build::Factory do end context 'when user is not allowed to execute manual action' do + before do + allow(build.project).to receive(:empty_repo?).and_return(false) + + create(:protected_branch, :no_one_can_push, + name: build.ref, project: build.project) + end + it 'fabricates status with correct details' do expect(status.text).to eq 'manual' expect(status.group).to eq 'manual' diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index 099d873fc01..21026f2c968 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -48,7 +48,9 @@ describe Gitlab::Ci::Status::Build::Retryable do describe '#has_action?' do context 'when user is allowed to update build' do before do - build.project.team << [user, :developer] + stub_not_protect_default_branch + + build.project.add_developer(user) end it { is_expected.to have_action } diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 23902f26b1a..e0425103f41 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -20,7 +20,9 @@ describe Gitlab::Ci::Status::Build::Stop do describe '#has_action?' do context 'when user is allowed to update build' do before do - build.project.team << [user, :developer] + stub_not_protect_default_branch + + build.project.add_developer(user) end it { is_expected.to have_action } diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index 13f0338b6aa..ebe5af56160 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -300,5 +300,48 @@ describe Gitlab::Ci::Trace::Stream do include_examples 'malicious regexp' end + + context 'multi-line data with rooted regexp' do + let(:data) { "\n65%\n" } + let(:regex) { '^(\d+)\%$' } + + it { is_expected.to eq('65') } + end + + context 'long line' do + let(:data) { 'a' * 80000 + '100%' + 'a' * 80000 } + let(:regex) { '\d+\%' } + + it { is_expected.to eq('100') } + end + + context 'many lines' do + let(:data) { "foo\n" * 80000 + "100%\n" + "foo\n" * 80000 } + let(:regex) { '\d+\%' } + + it { is_expected.to eq('100') } + end + + context 'empty regex' do + let(:data) { 'foo' } + let(:regex) { '' } + + it 'skips processing' do + expect(stream).not_to receive(:read) + + is_expected.to be_nil + end + end + + context 'nil regex' do + let(:data) { 'foo' } + let(:regex) { nil } + + it 'skips processing' do + expect(stream).not_to receive(:read) + + is_expected.to be_nil + end + end end end diff --git a/spec/lib/gitlab/ci_access_spec.rb b/spec/lib/gitlab/ci_access_spec.rb index eaf8f1d0f1c..75b90e76083 100644 --- a/spec/lib/gitlab/ci_access_spec.rb +++ b/spec/lib/gitlab/ci_access_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::CiAccess, lib: true do - let(:access) { Gitlab::CiAccess.new } +describe Gitlab::CiAccess do + let(:access) { described_class.new } describe '#can_do_action?' do context 'when action is :build_download_code' do diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index fe988266ae3..8ff6125ada1 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ClosingIssueExtractor, lib: true do +describe Gitlab::ClosingIssueExtractor do let(:project) { create(:empty_project) } let(:project2) { create(:empty_project) } let(:forked_project) { Projects::ForkService.new(project, project.creator).execute } diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb index 0a1ec66f199..c7be45dbcd3 100644 --- a/spec/lib/gitlab/color_schemes_spec.rb +++ b/spec/lib/gitlab/color_schemes_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ColorSchemes, lib: true do +describe Gitlab::ColorSchemes do describe '.body_classes' do it 'returns a space-separated list of class names' do css = described_class.body_classes diff --git a/spec/lib/gitlab/conflict/file_collection_spec.rb b/spec/lib/gitlab/conflict/file_collection_spec.rb index 27f23ea70dc..a4d7628b03a 100644 --- a/spec/lib/gitlab/conflict/file_collection_spec.rb +++ b/spec/lib/gitlab/conflict/file_collection_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Conflict::FileCollection, lib: true do +describe Gitlab::Conflict::FileCollection do let(:merge_request) { create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start') } let(:file_collection) { described_class.read_only(merge_request) } diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index 585eeb77bd5..5356e9742b4 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Conflict::File, lib: true do +describe Gitlab::Conflict::File do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:rugged) { repository.rugged } @@ -10,7 +10,7 @@ describe Gitlab::Conflict::File, lib: true do let(:index) { rugged.merge_commits(our_commit, their_commit) } let(:conflict) { index.conflicts.last } let(:merge_file_result) { index.merge_file('files/ruby/regex.rb') } - let(:conflict_file) { Gitlab::Conflict::File.new(merge_file_result, conflict, merge_request: merge_request) } + let(:conflict_file) { described_class.new(merge_file_result, conflict, merge_request: merge_request) } describe '#resolve_lines' do let(:section_keys) { conflict_file.sections.map { |section| section[:id] }.compact } @@ -220,7 +220,7 @@ end FILE end - let(:conflict_file) { Gitlab::Conflict::File.new({ data: file }, conflict, merge_request: merge_request) } + let(:conflict_file) { described_class.new({ data: file }, conflict, merge_request: merge_request) } let(:sections) { conflict_file.sections } it 'sets the correct match line headers' do diff --git a/spec/lib/gitlab/conflict/parser_spec.rb b/spec/lib/gitlab/conflict/parser_spec.rb index aed57b75789..fce606a2bb5 100644 --- a/spec/lib/gitlab/conflict/parser_spec.rb +++ b/spec/lib/gitlab/conflict/parser_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::Conflict::Parser, lib: true do - let(:parser) { Gitlab::Conflict::Parser.new } +describe Gitlab::Conflict::Parser do + let(:parser) { described_class.new } describe '#parse' do def parse_text(text) diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb index 3dd76ba5b8a..592448aef96 100644 --- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::CycleAnalytics::StageSummary, models: true do +describe Gitlab::CycleAnalytics::StageSummary do let(:project) { create(:project, :repository) } let(:from) { 1.day.ago } let(:user) { create(:user, :admin) } diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb index 04ec34492e1..6415e4083d6 100644 --- a/spec/lib/gitlab/data_builder/note_spec.rb +++ b/spec/lib/gitlab/data_builder/note_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::DataBuilder::Note, lib: true do +describe Gitlab::DataBuilder::Note do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:data) { described_class.build(note, user) } diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb index 73936969832..cb430b47463 100644 --- a/spec/lib/gitlab/data_builder/push_spec.rb +++ b/spec/lib/gitlab/data_builder/push_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::DataBuilder::Push, lib: true do +describe Gitlab::DataBuilder::Push do let(:project) { create(:project, :repository) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index a2acd15c8fb..d3dbd82e8ba 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::Database::MigrationHelpers, lib: true do +describe Gitlab::Database::MigrationHelpers do let(:model) do ActiveRecord::Migration.new.extend( - Gitlab::Database::MigrationHelpers + described_class ) end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index cbf6c35356e..c5f9aecd867 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Database, lib: true do +describe Gitlab::Database do before do stub_const('MigrationTest', Class.new { include Gitlab::Database }) end diff --git a/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb index df77f4037af..3a93d5e1e97 100644 --- a/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::CartfileLinker, lib: true do +describe Gitlab::DependencyLinker::CartfileLinker do describe '.support?' do it 'supports Cartfile' do expect(described_class.support?('Cartfile')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb index d7a926e800f..4d222564fd0 100644 --- a/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::ComposerJsonLinker, lib: true do +describe Gitlab::DependencyLinker::ComposerJsonLinker do describe '.support?' do it 'supports composer.json' do expect(described_class.support?('composer.json')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb index 3f8335f03ea..a97803b119e 100644 --- a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::GemfileLinker, lib: true do +describe Gitlab::DependencyLinker::GemfileLinker do describe '.support?' do it 'supports Gemfile' do expect(described_class.support?('Gemfile')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb index d4a71403939..24ad7d12f4c 100644 --- a/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::GemspecLinker, lib: true do +describe Gitlab::DependencyLinker::GemspecLinker do describe '.support?' do it 'supports *.gemspec' do expect(described_class.support?('gitlab_git.gemspec')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb index e279e0c9019..ae5ad39ad11 100644 --- a/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::GodepsJsonLinker, lib: true do +describe Gitlab::DependencyLinker::GodepsJsonLinker do describe '.support?' do it 'supports Godeps.json' do expect(described_class.support?('Godeps.json')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb index 8c979ae1869..1e8b72afb7b 100644 --- a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::PackageJsonLinker, lib: true do +describe Gitlab::DependencyLinker::PackageJsonLinker do describe '.support?' do it 'supports package.json' do expect(described_class.support?('package.json')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb index 06007cf97f7..cdfd7ad9826 100644 --- a/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::PodfileLinker, lib: true do +describe Gitlab::DependencyLinker::PodfileLinker do describe '.support?' do it 'supports Podfile' do expect(described_class.support?('Podfile')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb index d722865264b..d4a398c5948 100644 --- a/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::PodspecJsonLinker, lib: true do +describe Gitlab::DependencyLinker::PodspecJsonLinker do describe '.support?' do it 'supports *.podspec.json' do expect(described_class.support?('Reachability.podspec.json')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb index dfc366b5817..ed60ab45955 100644 --- a/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::PodspecLinker, lib: true do +describe Gitlab::DependencyLinker::PodspecLinker do describe '.support?' do it 'supports *.podspec' do expect(described_class.support?('Reachability.podspec')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb b/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb index 64b233f3e68..ef952b3abd5 100644 --- a/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker::RequirementsTxtLinker, lib: true do +describe Gitlab::DependencyLinker::RequirementsTxtLinker do describe '.support?' do it 'supports requirements.txt' do expect(described_class.support?('requirements.txt')).to be_truthy diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb index 3d1cfbcfbf7..10d2f701298 100644 --- a/spec/lib/gitlab/dependency_linker_spec.rb +++ b/spec/lib/gitlab/dependency_linker_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab::DependencyLinker, lib: true do +describe Gitlab::DependencyLinker do describe '.link' do it 'links using GemfileLinker' do blob_name = 'Gemfile' diff --git a/spec/lib/gitlab/diff/diff_refs_spec.rb b/spec/lib/gitlab/diff/diff_refs_spec.rb index a8173558c00..c73708d90a8 100644 --- a/spec/lib/gitlab/diff/diff_refs_spec.rb +++ b/spec/lib/gitlab/diff/diff_refs_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::DiffRefs, lib: true do +describe Gitlab::Diff::DiffRefs do let(:project) { create(:project, :repository) } describe '#compare_in' do diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index f289131cc3a..cd2fa98b14c 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::File, lib: true do +describe Gitlab::Diff::File do include RepoHelpers let(:project) { create(:project, :repository) } diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb index 7d7d4a55e63..cd602ccab8e 100644 --- a/spec/lib/gitlab/diff/highlight_spec.rb +++ b/spec/lib/gitlab/diff/highlight_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::Highlight, lib: true do +describe Gitlab::Diff::Highlight do include RepoHelpers let(:project) { create(:project, :repository) } @@ -10,7 +10,7 @@ describe Gitlab::Diff::Highlight, lib: true do describe '#highlight' do context "with a diff file" do - let(:subject) { Gitlab::Diff::Highlight.new(diff_file, repository: project.repository).highlight } + let(:subject) { described_class.new(diff_file, repository: project.repository).highlight } it 'returns Gitlab::Diff::Line elements' do expect(subject.first).to be_an_instance_of(Gitlab::Diff::Line) @@ -41,7 +41,7 @@ describe Gitlab::Diff::Highlight, lib: true do end context "with diff lines" do - let(:subject) { Gitlab::Diff::Highlight.new(diff_file.diff_lines, repository: project.repository).highlight } + let(:subject) { described_class.new(diff_file.diff_lines, repository: project.repository).highlight } it 'returns Gitlab::Diff::Line elements' do expect(subject.first).to be_an_instance_of(Gitlab::Diff::Line) diff --git a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb index d6e8b8ac4b2..046b096e366 100644 --- a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb +++ b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::InlineDiffMarkdownMarker, lib: true do +describe Gitlab::Diff::InlineDiffMarkdownMarker do describe '#mark' do let(:raw) { "abc 'def'" } let(:inline_diffs) { [2..5] } diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb index 95da344802d..c3bf34c24ae 100644 --- a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb +++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::InlineDiffMarker, lib: true do +describe Gitlab::Diff::InlineDiffMarker do describe '#mark' do context "when the rich text is html safe" do let(:raw) { "abc 'def'" } diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/diff/inline_diff_spec.rb index 8ca3f73509e..15451c2cf99 100644 --- a/spec/lib/gitlab/diff/inline_diff_spec.rb +++ b/spec/lib/gitlab/diff/inline_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::InlineDiff, lib: true do +describe Gitlab::Diff::InlineDiff do describe '.for_lines' do let(:diff) do <<-EOF.strip_heredoc diff --git a/spec/lib/gitlab/diff/line_mapper_spec.rb b/spec/lib/gitlab/diff/line_mapper_spec.rb index 2c7ecd1907e..42750bf9ea1 100644 --- a/spec/lib/gitlab/diff/line_mapper_spec.rb +++ b/spec/lib/gitlab/diff/line_mapper_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::LineMapper, lib: true do +describe Gitlab::Diff::LineMapper do include RepoHelpers let(:project) { create(:project, :repository) } diff --git a/spec/lib/gitlab/diff/parallel_diff_spec.rb b/spec/lib/gitlab/diff/parallel_diff_spec.rb index 0f779339c54..e9fc7be366a 100644 --- a/spec/lib/gitlab/diff/parallel_diff_spec.rb +++ b/spec/lib/gitlab/diff/parallel_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::ParallelDiff, lib: true do +describe Gitlab::Diff::ParallelDiff do include RepoHelpers let(:project) { create(:project, :repository) } diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb index e76128ecd87..c71568e2a65 100644 --- a/spec/lib/gitlab/diff/parser_spec.rb +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Gitlab::Diff::Parser, lib: true do +describe Gitlab::Diff::Parser do include RepoHelpers let(:project) { create(:project) } let(:commit) { project.commit(sample_commit.id) } let(:diff) { commit.raw_diffs.first } - let(:parser) { Gitlab::Diff::Parser.new } + let(:parser) { described_class.new } describe '#parse' do let(:diff) do diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb index b3d46e69ccb..d4a2a852c12 100644 --- a/spec/lib/gitlab/diff/position_spec.rb +++ b/spec/lib/gitlab/diff/position_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::Position, lib: true do +describe Gitlab::Diff::Position do include RepoHelpers let(:project) { create(:project, :repository) } diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb index 93d30b90937..8beebc10040 100644 --- a/spec/lib/gitlab/diff/position_tracer_spec.rb +++ b/spec/lib/gitlab/diff/position_tracer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::PositionTracer, lib: true do +describe Gitlab::Diff::PositionTracer do # Douwe's diary New York City, 2016-06-28 # -------------------------------------------------------------------------- # diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index 08b2577ecc4..f61dbc67ad1 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Email::AttachmentUploader, lib: true do +describe Gitlab::Email::AttachmentUploader do describe "#execute" do let(:project) { build(:project) } let(:message_raw) { fixture_file("emails/attachment.eml") } diff --git a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb index 4a9c9a7fe34..bd36d1d309d 100644 --- a/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_issue_handler_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative '../email_shared_blocks' -describe Gitlab::Email::Handler::CreateIssueHandler, lib: true do +describe Gitlab::Email::Handler::CreateIssueHandler do include_context :email_shared_context it_behaves_like :reply_processing_shared_examples diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index cd0309e248d..0127b012c91 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative '../email_shared_blocks' -describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do +describe Gitlab::Email::Handler::CreateNoteHandler do include_context :email_shared_context it_behaves_like :reply_processing_shared_examples diff --git a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb index 0939e6c4514..66c38498e4e 100644 --- a/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative '../email_shared_blocks' -describe Gitlab::Email::Handler::UnsubscribeHandler, lib: true do +describe Gitlab::Email::Handler::UnsubscribeHandler do include_context :email_shared_context before do diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index c6e3524f743..88565ea5311 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require_relative 'email_shared_blocks' -describe Gitlab::Email::Receiver, lib: true do +describe Gitlab::Email::Receiver do include_context :email_shared_context context "when the email contains a valid email address in a Delivered-To header" do diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 2ea5e6460a3..e21a998adfe 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" # Inspired in great part by Discourse's Email::Receiver -describe Gitlab::Email::ReplyParser, lib: true do +describe Gitlab::Email::ReplyParser do describe '#execute' do def test_parse_body(mail_string) described_class.new(Mail::Message.new(mail_string)).execute diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb index 590d6da4113..c1ed47cf64a 100644 --- a/spec/lib/gitlab/exclusive_lease_spec.rb +++ b/spec/lib/gitlab/exclusive_lease_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ExclusiveLease, type: :clean_gitlab_redis_shared_state do +describe Gitlab::ExclusiveLease, :clean_gitlab_redis_shared_state do let(:unique_key) { SecureRandom.hex(10) } describe '#try_obtain' do diff --git a/spec/lib/gitlab/file_finder_spec.rb b/spec/lib/gitlab/file_finder_spec.rb index 5a32ffd462c..3fb6315a39a 100644 --- a/spec/lib/gitlab/file_finder_spec.rb +++ b/spec/lib/gitlab/file_finder_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::FileFinder, lib: true do +describe Gitlab::FileFinder do describe '#find' do let(:project) { create(:project, :public, :repository) } let(:finder) { described_class.new(project, project.default_branch) } diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb index 252cd4c55c7..dcd1a2d9813 100644 --- a/spec/lib/gitlab/fogbugz_import/client_spec.rb +++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::FogbugzImport::Client, lib: true do +describe Gitlab::FogbugzImport::Client do let(:client) { described_class.new(uri: '', token: '') } let(:one_user) { { 'people' => { 'person' => { "ixPerson" => "2", "sFullName" => "James" } } } } let(:two_users) { { 'people' => { 'person' => [one_user, { "ixPerson" => "3" }] } } } diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 60de91324f0..730fdb112d9 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -91,7 +91,7 @@ describe Gitlab::Git::Commit, seed_helper: true do committer: committer ) end - let(:commit) { described_class.new(gitaly_commit) } + let(:commit) { described_class.new(Gitlab::GitalyClient::Commit.new(repository, gitaly_commit)) } it { expect(commit.short_id).to eq(id[0..10]) } it { expect(commit.id).to eq(id) } @@ -290,69 +290,85 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe '.find_all' do - it 'should return a return a collection of commits' do - commits = described_class.find_all(repository) + shared_examples 'finding all commits' do + it 'should return a return a collection of commits' do + commits = described_class.find_all(repository) - expect(commits).not_to be_empty - expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) - end - - context 'while applying a sort order based on the `order` option' do - it "allows ordering topologically (no parents shown before their children)" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) - - described_class.find_all(repository, order: :topo) + expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) end - it "allows ordering by date" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) - - described_class.find_all(repository, order: :date) + context 'max_count' do + subject do + commits = Gitlab::Git::Commit.find_all( + repository, + max_count: 50 + ) + + commits.map(&:id) + end + + it 'has 33 elements' do + expect(subject.size).to eq(33) + end + + it 'includes the expected commits' do + expect(subject).to include( + SeedRepo::Commit::ID, + SeedRepo::Commit::PARENT_ID, + SeedRepo::FirstCommit::ID + ) + end end - it "applies no sorting by default" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) - - described_class.find_all(repository) + context 'ref + max_count + skip' do + subject do + commits = Gitlab::Git::Commit.find_all( + repository, + ref: 'master', + max_count: 50, + skip: 1 + ) + + commits.map(&:id) + end + + it 'has 24 elements' do + expect(subject.size).to eq(24) + end + + it 'includes the expected commits' do + expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID) + expect(subject).not_to include(SeedRepo::LastCommit::ID) + end end end - context 'max_count' do - subject do - commits = Gitlab::Git::Commit.find_all( - repository, - max_count: 50 - ) + context 'when Gitaly find_all_commits feature is enabled' do + it_behaves_like 'finding all commits' + end - commits.map { |c| c.id } - end + context 'when Gitaly find_all_commits feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'finding all commits' - it 'has 31 elements' do - expect(subject.size).to eq(33) - end - it { is_expected.to include(SeedRepo::Commit::ID) } - it { is_expected.to include(SeedRepo::Commit::PARENT_ID) } - it { is_expected.to include(SeedRepo::FirstCommit::ID) } - end + context 'while applying a sort order based on the `order` option' do + it "allows ordering topologically (no parents shown before their children)" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) - context 'ref + max_count + skip' do - subject do - commits = Gitlab::Git::Commit.find_all( - repository, - ref: 'master', - max_count: 50, - skip: 1 - ) + described_class.find_all(repository, order: :topo) + end - commits.map { |c| c.id } - end + it "allows ordering by date" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) + + described_class.find_all(repository, order: :date) + end + + it "applies no sorting by default" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) - it 'has 23 elements' do - expect(subject.size).to eq(24) + described_class.find_all(repository) + end end - it { is_expected.to include(SeedRepo::Commit::ID) } - it { is_expected.to include(SeedRepo::FirstCommit::ID) } - it { is_expected.not_to include(SeedRepo::LastCommit::ID) } end end end diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 19f45ea1cb2..19391a70cf6 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' require 'fileutils' -describe Gitlab::Git::Hook, lib: true do +describe Gitlab::Git::Hook do before do # We need this because in the spec/spec_helper.rb we define it like this: # allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_call_original + allow_any_instance_of(described_class).to receive(:trigger).and_call_original end describe "#trigger" do @@ -48,7 +48,7 @@ describe Gitlab::Git::Hook, lib: true do it "returns success with no errors" do create_hook(hook_name) - hook = Gitlab::Git::Hook.new(hook_name, project) + hook = described_class.new(hook_name, project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' @@ -66,7 +66,7 @@ describe Gitlab::Git::Hook, lib: true do context "when the hook is unsuccessful" do it "returns failure with errors" do create_failing_hook(hook_name) - hook = Gitlab::Git::Hook.new(hook_name, project) + hook = described_class.new(hook_name, project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' @@ -80,7 +80,7 @@ describe Gitlab::Git::Hook, lib: true do context "when the hook doesn't exist" do it "returns success with no errors" do - hook = Gitlab::Git::Hook.new('unknown_hook', project) + hook = described_class.new('unknown_hook', project) blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 83d067b2c31..50736d353ad 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -378,144 +378,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe "#reset" do - change_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "CHANGELOG") - untracked_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "UNTRACKED") - tracked_path = File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, "files", "ruby", "popen.rb") - - change_text = "New changelog text" - untracked_text = "This file is untracked" - - reset_commit = SeedRepo::LastCommit::ID - - context "--hard" do - before(:all) do - # Modify a tracked file - File.open(change_path, "w") do |f| - f.write(change_text) - end - - # Add an untracked file to the working directory - File.open(untracked_path, "w") do |f| - f.write(untracked_text) - end - - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.reset("HEAD", :hard) - end - - it "should replace the working directory with the content of the index" do - File.open(change_path, "r") do |f| - expect(f.each_line.first).not_to eq(change_text) - end - - File.open(tracked_path, "r") do |f| - expect(f.each_line.to_a[8]).to include('raise RuntimeError, "System commands') - end - end - - it "should not touch untracked files" do - expect(File.exist?(untracked_path)).to be_truthy - end - - it "should move the HEAD to the correct commit" do - new_head = @normal_repo.rugged.head.target.oid - expect(new_head).to eq(reset_commit) - end - - it "should move the tip of the master branch to the correct commit" do - new_tip = @normal_repo.rugged.references["refs/heads/master"] - .target.oid - - expect(new_tip).to eq(reset_commit) - end - - after(:all) do - # Fast-forward to the original HEAD - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end - end - - describe "#checkout" do - new_branch = "foo_branch" - - context "-b" do - before(:all) do - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.checkout(new_branch, { b: true }, "origin/feature") - end - - it "should create a new branch" do - expect(@normal_repo.rugged.branches[new_branch]).not_to be_nil - end - - it "should move the HEAD to the correct commit" do - expect(@normal_repo.rugged.head.target.oid).to( - eq(@normal_repo.rugged.branches["origin/feature"].target.oid) - ) - end - - it "should refresh the repo's #heads collection" do - head_names = @normal_repo.branches.map { |h| h.name } - expect(head_names).to include(new_branch) - end - - after(:all) do - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end - - context "without -b" do - context "and specifying a nonexistent branch" do - it "should not do anything" do - normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - - expect { normal_repo.checkout(new_branch) }.to raise_error(Rugged::ReferenceError) - expect(normal_repo.rugged.branches[new_branch]).to be_nil - expect(normal_repo.rugged.head.target.oid).to( - eq(normal_repo.rugged.branches["master"].target.oid) - ) - - head_names = normal_repo.branches.map { |h| h.name } - expect(head_names).not_to include(new_branch) - end - - after(:all) do - FileUtils.rm_rf(TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end - - context "and with a valid branch" do - before(:all) do - @normal_repo = Gitlab::Git::Repository.new('default', TEST_NORMAL_REPO_PATH) - @normal_repo.rugged.branches.create("feature", "origin/feature") - @normal_repo.checkout("feature") - end - - it "should move the HEAD to the correct commit" do - expect(@normal_repo.rugged.head.target.oid).to( - eq(@normal_repo.rugged.branches["feature"].target.oid) - ) - end - - it "should update the working directory" do - File.open(File.join(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH, ".gitignore"), "r") do |f| - expect(f.read.each_line.to_a).not_to include(".DS_Store\n") - end - end - - after(:all) do - FileUtils.rm_rf(SEED_STORAGE_PATH, TEST_NORMAL_REPO_PATH) - ensure_seeds - end - end - end - end - describe "#delete_branch" do before(:all) do @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH) @@ -1049,19 +911,49 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe '#branches with deleted branch' do - before(:each) do - ref = double() - allow(ref).to receive(:name) { 'bad-branch' } - allow(ref).to receive(:target) { raise Rugged::ReferenceError } - branches = double() - allow(branches).to receive(:each) { [ref].each } - allow(repository.rugged).to receive(:branches) { branches } + describe '#branches' do + subject { repository.branches } + + context 'with local and remote branches' do + let(:repository) do + Gitlab::Git::Repository.new('default', File.join(TEST_MUTABLE_REPO_PATH, '.git')) + end + + before do + create_remote_branch(repository, 'joe', 'remote_branch', 'master') + repository.create_branch('local_branch', 'master') + end + + after do + FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH) + ensure_seeds + end + + it 'returns the local and remote branches' do + expect(subject.any? { |b| b.name == 'joe/remote_branch' }).to eq(true) + expect(subject.any? { |b| b.name == 'local_branch' }).to eq(true) + end end - it 'should return empty branches' do - expect(repository.branches).to eq([]) + # With Gitaly enabled, Gitaly just doesn't return deleted branches. + context 'with deleted branch with Gitaly disabled' do + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + end + + it 'returns no results' do + ref = double() + allow(ref).to receive(:name) { 'bad-branch' } + allow(ref).to receive(:target) { raise Rugged::ReferenceError } + branches = double() + allow(branches).to receive(:each) { [ref].each } + allow(repository.rugged).to receive(:branches) { branches } + + expect(subject).to be_empty + end end + + it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches end describe '#branch_count' do @@ -1208,7 +1100,7 @@ describe Gitlab::Git::Repository, seed_helper: true do end it 'returns the local branches' do - create_remote_branch('joe', 'remote_branch', 'master') + create_remote_branch(@repo, 'joe', 'remote_branch', 'master') @repo.create_branch('local_branch', 'master') expect(@repo.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) @@ -1235,9 +1127,9 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - def create_remote_branch(remote_name, branch_name, source_branch_name) - source_branch = @repo.branches.find { |branch| branch.name == source_branch_name } - rugged = @repo.rugged + def create_remote_branch(repository, remote_name, branch_name, source_branch_name) + source_branch = repository.branches.find { |branch| branch.name == source_branch_name } + rugged = repository.rugged rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) end diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index 78894ba9409..b051a088171 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::RevList, lib: true do +describe Gitlab::Git::RevList do let(:project) { create(:project, :repository) } before do @@ -11,7 +11,7 @@ describe Gitlab::Git::RevList, lib: true do end context "#new_refs" do - let(:rev_list) { Gitlab::Git::RevList.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } + let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } it 'calls out to `popen`' do expect(Gitlab::Popen).to receive(:popen).with([ @@ -33,7 +33,7 @@ describe Gitlab::Git::RevList, lib: true do end context "#missed_ref" do - let(:rev_list) { Gitlab::Git::RevList.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } + let(:rev_list) { described_class.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) } it 'calls out to `popen`' do expect(Gitlab::Popen).to receive(:popen).with([ diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb index 67a9c974298..78d1e120013 100644 --- a/spec/lib/gitlab/git/tag_spec.rb +++ b/spec/lib/gitlab/git/tag_spec.rb @@ -3,23 +3,33 @@ require "spec_helper" describe Gitlab::Git::Tag, seed_helper: true do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } - describe 'first tag' do - let(:tag) { repository.tags.first } + shared_examples 'Gitlab::Git::Repository#tags' do + describe 'first tag' do + let(:tag) { repository.tags.first } - it { expect(tag.name).to eq("v1.0.0") } - it { expect(tag.target).to eq("f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8") } - it { expect(tag.dereferenced_target.sha).to eq("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9") } - it { expect(tag.message).to eq("Release") } - end + it { expect(tag.name).to eq("v1.0.0") } + it { expect(tag.target).to eq("f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8") } + it { expect(tag.dereferenced_target.sha).to eq("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9") } + it { expect(tag.message).to eq("Release") } + end + + describe 'last tag' do + let(:tag) { repository.tags.last } - describe 'last tag' do - let(:tag) { repository.tags.last } + it { expect(tag.name).to eq("v1.2.1") } + it { expect(tag.target).to eq("2ac1f24e253e08135507d0830508febaaccf02ee") } + it { expect(tag.dereferenced_target.sha).to eq("fa1b1e6c004a68b7d8763b86455da9e6b23e36d6") } + it { expect(tag.message).to eq("Version 1.2.1") } + end - it { expect(tag.name).to eq("v1.2.1") } - it { expect(tag.target).to eq("2ac1f24e253e08135507d0830508febaaccf02ee") } - it { expect(tag.dereferenced_target.sha).to eq("fa1b1e6c004a68b7d8763b86455da9e6b23e36d6") } - it { expect(tag.message).to eq("Version 1.2.1") } + it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) } end - it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) } + context 'when Gitaly tags feature is enabled' do + it_behaves_like 'Gitlab::Git::Repository#tags' + end + + context 'when Gitaly tags feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'Gitlab::Git::Repository#tags' + end end diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb index 4b76a43e6b5..98ddd3c3664 100644 --- a/spec/lib/gitlab/git/tree_spec.rb +++ b/spec/lib/gitlab/git/tree_spec.rb @@ -1,8 +1,9 @@ require "spec_helper" describe Gitlab::Git::Tree, seed_helper: true do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } + context :repo do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } let(:tree) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID) } it { expect(tree).to be_kind_of Array } @@ -74,4 +75,24 @@ describe Gitlab::Git::Tree, seed_helper: true do it { expect(submodule.name).to eq('gitlab-shell') } end end + + describe '#where' do + context 'with gitaly disabled' do + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + end + + it 'calls #tree_entries_from_rugged' do + expect(described_class).to receive(:tree_entries_from_rugged) + + described_class.where(repository, SeedRepo::Commit::ID, '/') + end + end + + it 'gets the tree entries from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::CommitService).to receive(:tree_entries) + + described_class.where(repository, SeedRepo::Commit::ID, '/') + end + end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 9a86cfa66e4..2d6ea37d0ac 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::GitAccess, lib: true do +describe Gitlab::GitAccess do let(:pull_access_check) { access.check('git-upload-pack', '_any') } let(:push_access_check) { access.check('git-receive-pack', '_any') } - let(:access) { Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) } + let(:access) { described_class.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) } let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:actor) { user } @@ -280,7 +280,7 @@ describe Gitlab::GitAccess, lib: true do context 'when project is public' do let(:public_project) { create(:project, :public, :repository) } - let(:access) { Gitlab::GitAccess.new(nil, public_project, 'web', authentication_abilities: []) } + let(:access) { described_class.new(nil, public_project, 'web', authentication_abilities: []) } context 'when repository is enabled' do it 'give access to download code' do @@ -441,7 +441,7 @@ describe Gitlab::GitAccess, lib: true do end permissions_matrix[role].each do |action, allowed| - context action do + context action.to_s do subject { access.send(:check_push_access!, changes[action]) } it do diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 797ec8cb23e..0376b4ee783 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::GitAccessWiki, lib: true do - let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) } +describe Gitlab::GitAccessWiki do + let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) } let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:redirected_path) { nil } diff --git a/spec/lib/gitlab/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb index cc8daa535d6..e1fa8ae03f8 100644 --- a/spec/lib/gitlab/git_ref_validator_spec.rb +++ b/spec/lib/gitlab/git_ref_validator_spec.rb @@ -1,25 +1,25 @@ require 'spec_helper' -describe Gitlab::GitRefValidator, lib: true do - it { expect(Gitlab::GitRefValidator.validate('feature/new')).to be_truthy } - it { expect(Gitlab::GitRefValidator.validate('implement_@all')).to be_truthy } - it { expect(Gitlab::GitRefValidator.validate('my_new_feature')).to be_truthy } - it { expect(Gitlab::GitRefValidator.validate('#1')).to be_truthy } - it { expect(Gitlab::GitRefValidator.validate('feature/refs/heads/foo')).to be_truthy } - it { expect(Gitlab::GitRefValidator.validate('feature/~new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/^new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/:new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/?new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/*new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/[new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/new/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature/new.')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature\@{')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature\new')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature//new')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('feature new')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('refs/heads/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('refs/remotes/')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('refs/heads/feature')).to be_falsey } - it { expect(Gitlab::GitRefValidator.validate('refs/remotes/origin')).to be_falsey } +describe Gitlab::GitRefValidator do + it { expect(described_class.validate('feature/new')).to be_truthy } + it { expect(described_class.validate('implement_@all')).to be_truthy } + it { expect(described_class.validate('my_new_feature')).to be_truthy } + it { expect(described_class.validate('#1')).to be_truthy } + it { expect(described_class.validate('feature/refs/heads/foo')).to be_truthy } + it { expect(described_class.validate('feature/~new/')).to be_falsey } + it { expect(described_class.validate('feature/^new/')).to be_falsey } + it { expect(described_class.validate('feature/:new/')).to be_falsey } + it { expect(described_class.validate('feature/?new/')).to be_falsey } + it { expect(described_class.validate('feature/*new/')).to be_falsey } + it { expect(described_class.validate('feature/[new/')).to be_falsey } + it { expect(described_class.validate('feature/new/')).to be_falsey } + it { expect(described_class.validate('feature/new.')).to be_falsey } + it { expect(described_class.validate('feature\@{')).to be_falsey } + it { expect(described_class.validate('feature\new')).to be_falsey } + it { expect(described_class.validate('feature//new')).to be_falsey } + it { expect(described_class.validate('feature new')).to be_falsey } + it { expect(described_class.validate('refs/heads/')).to be_falsey } + it { expect(described_class.validate('refs/remotes/')).to be_falsey } + it { expect(described_class.validate('refs/heads/feature')).to be_falsey } + it { expect(described_class.validate('refs/remotes/origin')).to be_falsey } end diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb index 36f0e6507c8..4702a978f19 100644 --- a/spec/lib/gitlab/git_spec.rb +++ b/spec/lib/gitlab/git_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Gitlab::Git, lib: true do +describe Gitlab::Git do let(:committer_email) { 'user@example.org' } let(:committer_name) { 'John Doe' } describe 'committer_hash' do it "returns a hash containing the given email and name" do - committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: committer_name) + committer_hash = described_class.committer_hash(email: committer_email, name: committer_name) expect(committer_hash[:email]).to eq(committer_email) expect(committer_hash[:name]).to eq(committer_name) @@ -15,7 +15,7 @@ describe Gitlab::Git, lib: true do context 'when email is nil' do it "returns nil" do - committer_hash = Gitlab::Git.committer_hash(email: nil, name: committer_name) + committer_hash = described_class.committer_hash(email: nil, name: committer_name) expect(committer_hash).to be_nil end @@ -23,7 +23,7 @@ describe Gitlab::Git, lib: true do context 'when name is nil' do it "returns nil" do - committer_hash = Gitlab::Git.committer_hash(email: committer_email, name: nil) + committer_hash = described_class.committer_hash(email: committer_email, name: nil) expect(committer_hash).to be_nil end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index b3b4a1e2218..0868c793a33 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -2,9 +2,13 @@ require 'spec_helper' describe Gitlab::GitalyClient::CommitService do let(:project) { create(:project, :repository) } + let(:storage_name) { project.repository_storage } + let(:relative_path) { project.path_with_namespace + '.git' } let(:repository) { project.repository } let(:repository_message) { repository.gitaly_repository } - let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } + let(:commit) { project.commit(revision) } + let(:client) { described_class.new(repository) } describe '#diff_from_parent' do context 'when a commit has a parent' do @@ -20,7 +24,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) - described_class.new(repository).diff_from_parent(commit) + client.diff_from_parent(commit) end end @@ -38,12 +42,12 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) - described_class.new(repository).diff_from_parent(initial_commit) + client.diff_from_parent(initial_commit) end end it 'returns a Gitlab::Git::DiffCollection' do - ret = described_class.new(repository).diff_from_parent(commit) + ret = client.diff_from_parent(commit) expect(ret).to be_kind_of(Gitlab::Git::DiffCollection) end @@ -53,7 +57,7 @@ describe Gitlab::GitalyClient::CommitService do expect(Gitlab::Git::DiffCollection).to receive(:new).with(kind_of(Enumerable), options) - described_class.new(repository).diff_from_parent(commit, options) + client.diff_from_parent(commit, options) end end @@ -68,7 +72,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) - described_class.new(repository).commit_deltas(commit) + client.commit_deltas(commit) end end @@ -83,7 +87,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) - described_class.new(repository).commit_deltas(initial_commit) + client.commit_deltas(initial_commit) end end end @@ -91,6 +95,7 @@ describe Gitlab::GitalyClient::CommitService do describe '#between' do let(:from) { 'master' } let(:to) { '4b825dc642cb6eb9a060e54bf8d69288fbee4904' } + it 'sends an RPC request' do request = Gitaly::CommitsBetweenRequest.new( repository: repository_message, from: from, to: to @@ -102,4 +107,17 @@ describe Gitlab::GitalyClient::CommitService do described_class.new(repository).between(from, to) end end + + describe '#tree_entries' do + let(:path) { '/' } + + it 'sends a get_tree_entries message' do + expect_any_instance_of(Gitaly::CommitService::Stub) + .to receive(:get_tree_entries) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return([]) + + client.tree_entries(repository, revision, path) + end + end end diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb index 2960c9a79ad..00a31ac0b96 100644 --- a/spec/lib/gitlab/gitaly_client/diff_spec.rb +++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitalyClient::Diff, lib: true do +describe Gitlab::GitalyClient::Diff do let(:diff_fields) do { to_path: ".gitmodules", diff --git a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb index 07650013052..cd3242b9326 100644 --- a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb +++ b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitalyClient::DiffStitcher, lib: true do +describe Gitlab::GitalyClient::DiffStitcher do describe 'enumeration' do it 'combines segregated diff messages together' do diff_1 = OpenStruct.new( diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index 1e8ed9d645b..0b1c890f956 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -6,6 +6,17 @@ describe Gitlab::GitalyClient::RefService do let(:relative_path) { project.path_with_namespace + '.git' } let(:client) { described_class.new(project.repository) } + describe '#branches' do + it 'sends a find_all_branches message' do + expect_any_instance_of(Gitaly::RefService::Stub) + .to receive(:find_all_branches) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return([]) + + client.branches + end + end + describe '#branch_names' do it 'sends a find_all_branch_names message' do expect_any_instance_of(Gitaly::RefService::Stub) diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb new file mode 100644 index 00000000000..5a9f3fc130c --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::RepositoryService do + set(:project) { create(:empty_project) } + let(:storage_name) { project.repository_storage } + let(:relative_path) { project.path_with_namespace + '.git' } + let(:client) { described_class.new(project.repository) } + + describe '#exists?' do + it 'sends an exists message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:exists) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_call_original + + client.exists? + end + end +end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 558ddb3fbd6..921e786a55c 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' # We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want # those stubs while testing the GitalyClient itself. -describe Gitlab::GitalyClient, lib: true, skip_gitaly_mock: true do +describe Gitlab::GitalyClient, skip_gitaly_mock: true do describe '.stub' do # Notice that this is referring to gRPC "stubs", not rspec stubs before do diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb index 3a31f93efa5..426b43f8b51 100644 --- a/spec/lib/gitlab/github_import/branch_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::BranchFormatter, lib: true do +describe Gitlab::GithubImport::BranchFormatter do let(:project) { create(:project, :repository) } let(:commit) { create(:commit, project: project) } let(:repo) { double } diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb index 21f2a9e225b..66273255b6f 100644 --- a/spec/lib/gitlab/github_import/client_spec.rb +++ b/spec/lib/gitlab/github_import/client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::Client, lib: true do +describe Gitlab::GithubImport::Client do let(:token) { '123456' } let(:github_provider) { Settingslogic.new('app_id' => 'asd123', 'app_secret' => 'asd123', 'name' => 'github', 'args' => { 'client_options' => {} }) } diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb index cc38872e426..ef89634685a 100644 --- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::CommentFormatter, lib: true do +describe Gitlab::GithubImport::CommentFormatter do let(:client) { double } let(:project) { create(:empty_project) } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') } diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index 9d5e20841b5..d00a2deaf7b 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::Importer, lib: true do +describe Gitlab::GithubImport::Importer do shared_examples 'Gitlab::GithubImport::Importer#execute' do let(:expected_not_called) { [] } diff --git a/spec/lib/gitlab/github_import/issuable_formatter_spec.rb b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb index 6bc5f98ed2c..05294d227bd 100644 --- a/spec/lib/gitlab/github_import/issuable_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::IssuableFormatter, lib: true do +describe Gitlab::GithubImport::IssuableFormatter do let(:raw_data) do double(number: 42) end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index a4089592cf2..39b15926193 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::IssueFormatter, lib: true do +describe Gitlab::GithubImport::IssueFormatter do let(:client) { double } let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') } diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb index 565435824fd..2cc7ac0b446 100644 --- a/spec/lib/gitlab/github_import/label_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::LabelFormatter, lib: true do +describe Gitlab::GithubImport::LabelFormatter do let(:project) { create(:empty_project) } let(:raw) { double(name: 'improvements', color: 'e6e6e6') } diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb index 6d38041c468..310e0536fd7 100644 --- a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::MilestoneFormatter, lib: true do +describe Gitlab::GithubImport::MilestoneFormatter do let(:project) { create(:empty_project) } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index a73b1f4ff5d..948e7469a18 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::ProjectCreator, lib: true do +describe Gitlab::GithubImport::ProjectCreator do let(:user) { create(:user) } let(:namespace) { create(:group, owner: user) } diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index b7c59918a76..2e42f6239b7 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::PullRequestFormatter, lib: true do +describe Gitlab::GithubImport::PullRequestFormatter do let(:client) { double } let(:project) { create(:project, :repository) } let(:source_sha) { create(:commit, project: project).id } diff --git a/spec/lib/gitlab/github_import/release_formatter_spec.rb b/spec/lib/gitlab/github_import/release_formatter_spec.rb index 13b15e669ab..1357cb636ae 100644 --- a/spec/lib/gitlab/github_import/release_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/release_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::ReleaseFormatter, lib: true do +describe Gitlab::GithubImport::ReleaseFormatter do let!(:project) { create(:empty_project, namespace: create(:namespace, path: 'octocat')) } let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } diff --git a/spec/lib/gitlab/github_import/user_formatter_spec.rb b/spec/lib/gitlab/github_import/user_formatter_spec.rb index db792233657..98e3a7c28b9 100644 --- a/spec/lib/gitlab/github_import/user_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/user_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::UserFormatter, lib: true do +describe Gitlab::GithubImport::UserFormatter do let(:client) { double } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') } diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb index 1bd29b8a563..de50265bc14 100644 --- a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::WikiFormatter, lib: true do +describe Gitlab::GithubImport::WikiFormatter do let(:project) do create(:project, namespace: create(:namespace, path: 'gitlabhq'), diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb index cd8e805466a..50e8d7183ce 100644 --- a/spec/lib/gitlab/gitlab_import/client_spec.rb +++ b/spec/lib/gitlab/gitlab_import/client_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Gitlab::GitlabImport::Client, lib: true do +describe Gitlab::GitlabImport::Client do include ImportSpecHelper let(:token) { '123456' } - let(:client) { Gitlab::GitlabImport::Client.new(token) } + let(:client) { described_class.new(token) } before do stub_omniauth_provider('gitlab') diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb index 4f588da0a83..16b14474b89 100644 --- a/spec/lib/gitlab/gitlab_import/importer_spec.rb +++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitlabImport::Importer, lib: true do +describe Gitlab::GitlabImport::Importer do include ImportSpecHelper describe '#execute' do diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb index 483f65cd053..da48d8f0670 100644 --- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb +++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitlabImport::ProjectCreator, lib: true do +describe Gitlab::GitlabImport::ProjectCreator do let(:user) { create(:user) } let(:repo) do { @@ -23,7 +23,7 @@ describe Gitlab::GitlabImport::ProjectCreator, lib: true do it 'creates project' do allow_any_instance_of(Project).to receive(:add_import_job) - project_creator = Gitlab::GitlabImport::ProjectCreator.new(repo, namespace, user, access_params) + project_creator = described_class.new(repo, namespace, user, access_params) project = project_creator.execute expect(project.import_url).to eq("https://oauth2:asdffg@gitlab.com/asd/vim.git") diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb index 85949ae8dc4..37985c062b4 100644 --- a/spec/lib/gitlab/google_code_import/client_spec.rb +++ b/spec/lib/gitlab/google_code_import/client_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::GoogleCodeImport::Client, lib: true do +describe Gitlab::GoogleCodeImport::Client do let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) } subject { described_class.new(raw_data) } diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 622a0f513f4..85f40825005 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::GoogleCodeImport::Importer, lib: true do +describe Gitlab::GoogleCodeImport::Importer do let(:mapped_user) { create(:user, username: "thilo123") } let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) } let(:client) { Gitlab::GoogleCodeImport::Client.new(raw_data) } diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb index 499a896ee76..aad53938d52 100644 --- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb +++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GoogleCodeImport::ProjectCreator, lib: true do +describe Gitlab::GoogleCodeImport::ProjectCreator do let(:user) { create(:user) } let(:repo) do Gitlab::GoogleCodeImport::Repository.new( @@ -18,7 +18,7 @@ describe Gitlab::GoogleCodeImport::ProjectCreator, lib: true do it 'creates project' do allow_any_instance_of(Project).to receive(:add_import_job) - project_creator = Gitlab::GoogleCodeImport::ProjectCreator.new(repo, namespace, user) + project_creator = described_class.new(repo, namespace, user) project = project_creator.execute expect(project.import_url).to eq("https://vim.googlecode.com/git/") diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb new file mode 100644 index 00000000000..ddb8dd9f0f4 --- /dev/null +++ b/spec/lib/gitlab/gpg/commit_spec.rb @@ -0,0 +1,127 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Gpg::Commit do + describe '#signature' do + let!(:project) { create :project, :repository, path: 'sample-project' } + let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' } + + context 'unisgned commit' do + it 'returns nil' do + expect(described_class.new(project.commit).signature).to be_nil + end + end + + context 'known and verified public key' do + let!(:gpg_key) do + create :gpg_key, key: GpgHelpers::User1.public_key, user: create(:user, email: GpgHelpers::User1.emails.first) + end + + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, git_commit: raw_commit, project: project + end + + it 'returns a valid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + valid_signature: true + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + + context 'known but unverified public key' do + let!(:gpg_key) { create :gpg_key, key: GpgHelpers::User1.public_key } + + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, git_commit: raw_commit, project: project + end + + it 'returns an invalid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: GpgHelpers::User1.names.first, + gpg_key_user_email: GpgHelpers::User1.emails.first, + valid_signature: false + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + + context 'unknown public key' do + let!(:commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + allow(raw_commit).to receive :save! + + create :commit, + git_commit: raw_commit, + project: project + end + + it 'returns an invalid signature' do + expect(described_class.new(commit).signature).to have_attributes( + commit_sha: commit_sha, + project: project, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + gpg_key_user_name: nil, + gpg_key_user_email: nil, + valid_signature: false + ) + end + + it 'returns the cached signature on second call' do + gpg_commit = described_class.new(commit) + + expect(gpg_commit).to receive(:using_keychain).and_call_original + gpg_commit.signature + + # consecutive call + expect(gpg_commit).not_to receive(:using_keychain).and_call_original + gpg_commit.signature + end + end + end +end diff --git a/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb new file mode 100644 index 00000000000..c4e04ee46a2 --- /dev/null +++ b/spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb @@ -0,0 +1,173 @@ +require 'rails_helper' + +RSpec.describe Gitlab::Gpg::InvalidGpgSignatureUpdater do + describe '#run' do + let!(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' } + let!(:project) { create :project, :repository, path: 'sample-project' } + let!(:raw_commit) do + raw_commit = double(:raw_commit, signature: [ + GpgHelpers::User1.signed_commit_signature, + GpgHelpers::User1.signed_commit_base_data + ], sha: commit_sha) + + allow(raw_commit).to receive :save! + + raw_commit + end + + let!(:commit) do + create :commit, git_commit: raw_commit, project: project + end + + before do + allow_any_instance_of(Project).to receive(:commit).and_return(commit) + end + + context 'gpg signature did have an associated gpg key which was removed later' do + let!(:user) { create :user, email: GpgHelpers::User1.emails.first } + + let!(:valid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + end + + it 'assigns the gpg key to the signature when the missing gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(valid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'does not assign the gpg key when an unrelated gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + create :gpg_key, + key: GpgHelpers::User2.public_key, + user: user + + expect(valid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + end + + context 'gpg signature did not have an associated gpg key' do + let!(:user) { create :user, email: GpgHelpers::User1.emails.first } + + let!(:invalid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + end + + it 'updates the signature to being valid when the missing gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'keeps the signature at being invalid when an unrelated gpg key is added' do + # InvalidGpgSignatureUpdater is called by the after_create hook + create :gpg_key, + key: GpgHelpers::User2.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + end + end + + context 'gpg signature did have an associated unverified gpg key' do + let!(:user) do + create(:user, email: 'unrelated@example.com').tap do |user| + user.skip_reconfirmation! + end + end + + let!(:invalid_gpg_signature) do + create :gpg_signature, + project: project, + commit_sha: commit_sha, + gpg_key: nil, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + end + + it 'updates the signature to being valid when the user updates the email address' do + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload.valid_signature).to be_falsey + + # InvalidGpgSignatureUpdater is called by the after_update hook + user.update_attributes!(email: GpgHelpers::User1.emails.first) + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: true + ) + end + + it 'keeps the signature at being invalid when the changed email address is still unrelated' do + gpg_key = create :gpg_key, + key: GpgHelpers::User1.public_key, + user: user + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + + # InvalidGpgSignatureUpdater is called by the after_update hook + user.update_attributes!(email: 'still.unrelated@example.com') + + expect(invalid_gpg_signature.reload).to have_attributes( + project: project, + commit_sha: commit_sha, + gpg_key: gpg_key, + gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid, + valid_signature: false + ) + end + end + end +end diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb new file mode 100644 index 00000000000..8041518117d --- /dev/null +++ b/spec/lib/gitlab/gpg_spec.rb @@ -0,0 +1,83 @@ +require 'rails_helper' + +describe Gitlab::Gpg do + describe '.fingerprints_from_key' do + before do + # make sure that each method is using the temporary keychain + expect(described_class).to receive(:using_tmp_keychain).and_call_original + end + + it 'returns CurrentKeyChain.fingerprints_from_key' do + expect(Gitlab::Gpg::CurrentKeyChain).to receive(:fingerprints_from_key).with(GpgHelpers::User1.public_key) + + described_class.fingerprints_from_key(GpgHelpers::User1.public_key) + end + end + + describe '.primary_keyids_from_key' do + it 'returns the keyid' do + expect( + described_class.primary_keyids_from_key(GpgHelpers::User1.public_key) + ).to eq [GpgHelpers::User1.primary_keyid] + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.primary_keyids_from_key('bogus') + ).to eq [] + end + end + + describe '.user_infos_from_key' do + it 'returns the names and emails' do + user_infos = described_class.user_infos_from_key(GpgHelpers::User1.public_key) + expect(user_infos).to eq([{ + name: GpgHelpers::User1.names.first, + email: GpgHelpers::User1.emails.first + }]) + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.user_infos_from_key('bogus') + ).to eq [] + end + end +end + +describe Gitlab::Gpg::CurrentKeyChain do + around do |example| + Gitlab::Gpg.using_tmp_keychain do + example.run + end + end + + describe '.add' do + it 'stores the key in the keychain' do + expect(GPGME::Key.find(:public, GpgHelpers::User1.fingerprint)).to eq [] + + described_class.add(GpgHelpers::User1.public_key) + + keys = GPGME::Key.find(:public, GpgHelpers::User1.fingerprint) + expect(keys.count).to eq 1 + expect(keys.first).to have_attributes( + email: GpgHelpers::User1.emails.first, + fingerprint: GpgHelpers::User1.fingerprint + ) + end + end + + describe '.fingerprints_from_key' do + it 'returns the fingerprint' do + expect( + described_class.fingerprints_from_key(GpgHelpers::User1.public_key) + ).to eq [GpgHelpers::User1.fingerprint] + end + + it 'returns an empty array when the key is invalid' do + expect( + described_class.fingerprints_from_key('bogus') + ).to eq [] + end + end +end diff --git a/spec/lib/gitlab/graphs/commits_spec.rb b/spec/lib/gitlab/graphs/commits_spec.rb index abb5a26060f..3f9382a9143 100644 --- a/spec/lib/gitlab/graphs/commits_spec.rb +++ b/spec/lib/gitlab/graphs/commits_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Graphs::Commits, lib: true do +describe Gitlab::Graphs::Commits do let!(:project) { create(:empty_project, :public) } let!(:commit1) { create(:commit, git_commit: RepoHelpers.sample_commit, project: project, committed_date: Time.now) } diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb index 3de73a9ff65..8abc4320c59 100644 --- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb +++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::HealthChecks::FsShardsCheck do def command_exists?(command) _, status = Gitlab::Popen.popen(%W{ #{command} 1 echo }) - status == 0 + status.zero? rescue Errno::ENOENT false end @@ -64,9 +64,7 @@ describe Gitlab::HealthChecks::FsShardsCheck do it 'cleans up files used for testing' do expect(described_class).to receive(:storage_write_test).with(any_args).and_call_original - subject - - expect(Dir.entries(tmp_dir).count).to eq(2) + expect { subject }.not_to change(Dir.entries(tmp_dir), :count) end context 'read test fails' do @@ -88,8 +86,6 @@ describe Gitlab::HealthChecks::FsShardsCheck do end describe '#metrics' do - subject { described_class.metrics } - context 'storage points to not existing folder' do let(:storages_paths) do { @@ -104,14 +100,15 @@ describe Gitlab::HealthChecks::FsShardsCheck do end it 'provides metrics' do - expect(subject).to all(have_attributes(labels: { shard: :default })) - expect(subject).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - - expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) + metrics = described_class.metrics + + expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) end end @@ -121,15 +118,19 @@ describe Gitlab::HealthChecks::FsShardsCheck do end it 'provides metrics' do - expect(subject).to all(have_attributes(labels: { shard: :default })) - - expect(subject).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) + metrics = described_class.metrics + + expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 1)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) + end - expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) + it 'cleans up files used for metrics' do + expect { described_class.metrics }.not_to change(Dir.entries(tmp_dir), :count) end end end @@ -150,18 +151,16 @@ describe Gitlab::HealthChecks::FsShardsCheck do end describe '#metrics' do - subject { described_class.metrics } - it 'provides metrics' do - expect(subject).to all(have_attributes(labels: { shard: :default })) - - expect(subject).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) - - expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) - expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) + metrics = described_class.metrics + + expect(metrics).to all(have_attributes(labels: { shard: :default })) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_accessible, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0)) + expect(metrics).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0)) end end end diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 07687b470c5..29e61d15726 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Highlight, lib: true do +describe Gitlab::Highlight do include RepoHelpers let(:project) { create(:project, :repository) } @@ -12,7 +12,7 @@ describe Gitlab::Highlight, lib: true do let(:blob) { repository.blob_at_branch(branch, path) } let(:highlighter) do - Gitlab::Highlight.new(blob.path, blob.data, repository: repository) + described_class.new(blob.path, blob.data, repository: repository) end before do @@ -42,7 +42,7 @@ describe Gitlab::Highlight, lib: true do let(:path) { 'files/whitespace' } let(:blob) { repository.blob_at_branch(branch, path) } let(:lines) do - Gitlab::Highlight.highlight(blob.path, blob.data, repository: repository).lines + described_class.highlight(blob.path, blob.data, repository: repository).lines end it 'strips extra LFs' do diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb index 0dba4132101..785035d993f 100644 --- a/spec/lib/gitlab/i18n_spec.rb +++ b/spec/lib/gitlab/i18n_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::I18n, lib: true do +describe Gitlab::I18n do let(:user) { create(:user, preferred_language: 'es') } describe '.locale=' do diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 977174a5fd2..6a41afe0c25 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -102,6 +102,7 @@ pipelines: - statuses - builds - trigger_requests +- variables - auto_canceled_by - auto_canceled_pipelines - auto_canceled_jobs @@ -112,6 +113,8 @@ pipelines: - artifacts - pipeline_schedule - merge_requests +pipeline_variables: +- pipeline stages: - project - pipeline diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb index 63bab0f0d0d..574748756bd 100644 --- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb +++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::AttributeCleaner, lib: true do +describe Gitlab::ImportExport::AttributeCleaner do let(:relation_class){ double('relation_class').as_null_object } let(:unsafe_hash) do { diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb index e24d070706a..65f073b2df3 100644 --- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb @@ -7,7 +7,7 @@ require 'spec_helper' # to be included as part of the export, or blacklist them using the import_export.yml configuration file. # Likewise, new models added to import_export.yml, will need to be added with their correspondent attributes # to this spec. -describe 'Import/Export attribute configuration', lib: true do +describe 'Import/Export attribute configuration' do include ConfigurationHelper let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb index 08a42fd27a2..a7b292c8558 100644 --- a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::AvatarRestorer, lib: true do +describe Gitlab::ImportExport::AvatarRestorer do include UploadHelpers let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index 579a31ead58..814f85de03b 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::AvatarSaver, lib: true do +describe Gitlab::ImportExport::AvatarSaver do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb index b88b9c18c15..690c7625c52 100644 --- a/spec/lib/gitlab/import_export/file_importer_spec.rb +++ b/spec/lib/gitlab/import_export/file_importer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::FileImporter, lib: true do +describe Gitlab::ImportExport::FileImporter do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') } let(:export_path) { "#{Dir.tmpdir}/file_importer_spec" } let(:valid_file) { "#{shared.export_path}/valid.json" } diff --git a/spec/lib/gitlab/import_export/fork_spec.rb b/spec/lib/gitlab/import_export/fork_spec.rb index 70796781532..08588a76fe6 100644 --- a/spec/lib/gitlab/import_export/fork_spec.rb +++ b/spec/lib/gitlab/import_export/fork_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe 'forked project import', services: true do +describe 'forked project import' do let(:user) { create(:user) } - let!(:project_with_repo) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project, name: 'test-repo-restorer-no-repo', path: 'test-repo-restorer-no-repo') } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } diff --git a/spec/lib/gitlab/import_export/hash_util_spec.rb b/spec/lib/gitlab/import_export/hash_util_spec.rb index 1c3a0b23ece..366582dece3 100644 --- a/spec/lib/gitlab/import_export/hash_util_spec.rb +++ b/spec/lib/gitlab/import_export/hash_util_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::HashUtil, lib: true do +describe Gitlab::ImportExport::HashUtil do let(:stringified_array) { [{ 'test' => 1 }] } let(:stringified_array_with_date) { [{ 'test_date' => '2016-04-06 06:17:44 +0200' }] } diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb index f3fd0d82875..07415d41f93 100644 --- a/spec/lib/gitlab/import_export/import_export_spec.rb +++ b/spec/lib/gitlab/import_export/import_export_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport, services: true do +describe Gitlab::ImportExport do describe 'export filename' do let(:group) { create(:group, :nested) } let(:project) { create(:empty_project, :public, path: 'project-path', namespace: group) } diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb index 3e0291c9ae9..f66a2ab7dda 100644 --- a/spec/lib/gitlab/import_export/members_mapper_spec.rb +++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::MembersMapper, services: true do +describe Gitlab::ImportExport::MembersMapper do describe 'map members' do let(:user) { create(:admin) } let(:project) { create(:empty_project, :public, name: 'searchable_project') } diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb index 349be4596b6..f2b66c4421c 100644 --- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb +++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::ImportExport::MergeRequestParser do let(:user) { create(:user) } - let!(:project) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } let(:forked_from_project) { create(:project) } let(:fork_link) { create(:forked_project_link, forked_from_project: project) } diff --git a/spec/lib/gitlab/import_export/model_configuration_spec.rb b/spec/lib/gitlab/import_export/model_configuration_spec.rb index 2ede5cdd2ad..5cb8f2589c8 100644 --- a/spec/lib/gitlab/import_export/model_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/model_configuration_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' # Part of the test security suite for the Import/Export feature # Finds if a new model has been added that can potentially be part of the Import/Export # If it finds a new model, it will show a +failure_message+ with the options available. -describe 'Import/Export model configuration', lib: true do +describe 'Import/Export model configuration' do include ConfigurationHelper let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index d50d238ddcd..d1ec0e45bbd 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' include ImportExport::CommonUtil -describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do +describe Gitlab::ImportExport::ProjectTreeRestorer do describe 'restore project tree' do before(:context) do @user = create(:user) diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 22a65e24f26..0c7e733b01f 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::ProjectTreeSaver, services: true do +describe Gitlab::ImportExport::ProjectTreeSaver do describe 'saves the project tree into a json object' do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) } diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb index d700af142be..e9f5273725d 100644 --- a/spec/lib/gitlab/import_export/reader_spec.rb +++ b/spec/lib/gitlab/import_export/reader_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::Reader, lib: true do +describe Gitlab::ImportExport::Reader do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') } let(:test_config) { 'spec/support/import_export/import_export.yml' } let(:project_tree_hash) do diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb index 5417c7534ea..baa90af84f7 100644 --- a/spec/lib/gitlab/import_export/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::RelationFactory, lib: true do +describe Gitlab::ImportExport::RelationFactory do let(:project) { create(:empty_project) } let(:members_mapper) { double('members_mapper').as_null_object } let(:user) { create(:admin) } diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb index 30b6a0d8845..d3be2965bf4 100644 --- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::ImportExport::RepoRestorer, services: true do +describe Gitlab::ImportExport::RepoRestorer do describe 'bundle a project Git repo' do let(:user) { create(:user) } - let!(:project_with_repo) { create(:project, :test_repo, name: 'test-repo-restorer', path: 'test-repo-restorer') } + let!(:project_with_repo) { create(:project, name: 'test-repo-restorer', path: 'test-repo-restorer') } let!(:project) { create(:empty_project) } let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb index a7f4e11271e..87af13e0beb 100644 --- a/spec/lib/gitlab/import_export/repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::RepoSaver, services: true do +describe Gitlab::ImportExport::RepoSaver do describe 'bundle a project Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 4ef3db3721f..11f4c16ff96 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -195,6 +195,7 @@ MergeRequestDiffFile: - a_mode - b_mode - too_large +- binary Ci::Pipeline: - id - project_id @@ -396,6 +397,7 @@ Project: - build_allow_git_fetch - last_repository_updated_at - ci_config_path +- delete_error Author: - name ProjectFeature: diff --git a/spec/lib/gitlab/import_export/version_checker_spec.rb b/spec/lib/gitlab/import_export/version_checker_spec.rb index 2405ac5abfe..e7d50f75682 100644 --- a/spec/lib/gitlab/import_export/version_checker_spec.rb +++ b/spec/lib/gitlab/import_export/version_checker_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' include ImportExport::CommonUtil -describe Gitlab::ImportExport::VersionChecker, services: true do +describe Gitlab::ImportExport::VersionChecker do let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') } describe 'bundle a project Git repo' do diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 071e5fac3f0..78137aeff5e 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ImportExport::WikiRepoSaver, services: true do +describe Gitlab::ImportExport::WikiRepoSaver do describe 'bundle a wiki Git repo' do let(:user) { create(:user) } let!(:project) { create(:empty_project, :public, name: 'searchable_project') } diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb index 698bd72d0f8..c959add7a36 100644 --- a/spec/lib/gitlab/incoming_email_spec.rb +++ b/spec/lib/gitlab/incoming_email_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::IncomingEmail, lib: true do +describe Gitlab::IncomingEmail do describe "self.enabled?" do context "when reply by email is enabled" do before do diff --git a/spec/lib/gitlab/issuable_metadata_spec.rb b/spec/lib/gitlab/issuable_metadata_spec.rb index f9f4b290dbf..2455969a183 100644 --- a/spec/lib/gitlab/issuable_metadata_spec.rb +++ b/spec/lib/gitlab/issuable_metadata_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::IssuableMetadata, lib: true do +describe Gitlab::IssuableMetadata do let(:user) { create(:user) } let!(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) } diff --git a/spec/lib/gitlab/issuable_sorter_spec.rb b/spec/lib/gitlab/issuable_sorter_spec.rb index c9a434b2bcf..aeb32ef96d6 100644 --- a/spec/lib/gitlab/issuable_sorter_spec.rb +++ b/spec/lib/gitlab/issuable_sorter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::IssuableSorter, lib: true do +describe Gitlab::IssuableSorter do let(:namespace1) { build(:namespace, id: 1) } let(:project1) { build(:project, id: 1, namespace: namespace1) } diff --git a/spec/lib/gitlab/key_fingerprint_spec.rb b/spec/lib/gitlab/key_fingerprint_spec.rb index d09f51f3bfc..d7bebaca675 100644 --- a/spec/lib/gitlab/key_fingerprint_spec.rb +++ b/spec/lib/gitlab/key_fingerprint_spec.rb @@ -1,12 +1,12 @@ require "spec_helper" -describe Gitlab::KeyFingerprint, lib: true do +describe Gitlab::KeyFingerprint do let(:key) { "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" } let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" } describe "#fingerprint" do it "generates the key's fingerprint" do - expect(Gitlab::KeyFingerprint.new(key).fingerprint).to eq(fingerprint) + expect(described_class.new(key).fingerprint).to eq(fingerprint) end end end diff --git a/spec/lib/gitlab/lazy_spec.rb b/spec/lib/gitlab/lazy_spec.rb index b5ca89dd242..37a3ac74316 100644 --- a/spec/lib/gitlab/lazy_spec.rb +++ b/spec/lib/gitlab/lazy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Lazy, lib: true do +describe Gitlab::Lazy do let(:dummy) { double(:dummy) } context 'when not calling any methods' do diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb index 756fcb0fcaf..6a47350be81 100644 --- a/spec/lib/gitlab/ldap/access_spec.rb +++ b/spec/lib/gitlab/ldap/access_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::LDAP::Access, lib: true do - let(:access) { Gitlab::LDAP::Access.new user } +describe Gitlab::LDAP::Access do + let(:access) { described_class.new user } let(:user) { create(:omniauth_user) } describe '.allowed?' do diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb index 9454878b057..d17d440d833 100644 --- a/spec/lib/gitlab/ldap/adapter_spec.rb +++ b/spec/lib/gitlab/ldap/adapter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::LDAP::Adapter, lib: true do +describe Gitlab::LDAP::Adapter do include LdapHelpers let(:ldap) { double(:ldap) } @@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter, lib: true do expect(adapter).to receive(:ldap_search) do |arg| expect(arg[:filter].to_s).to eq('(uid=johndoe)') expect(arg[:base]).to eq('dc=example,dc=com') - expect(arg[:attributes]).to match(%w{uid cn mail dn}) + expect(arg[:attributes]).to match(%w{uid cn dn uid userid sAMAccountName mail email userPrincipalName}) end.and_return({}) adapter.users('uid', 'johndoe') @@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter, lib: true do expect(adapter).to receive(:ldap_search).with( base: 'uid=johndoe,ou=users,dc=example,dc=com', scope: Net::LDAP::SearchScope_BaseObject, - attributes: %w{uid cn mail dn}, + attributes: %w{uid cn dn uid userid sAMAccountName mail email userPrincipalName}, filter: nil ).and_return({}) @@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter, lib: true do it 'uses the right uid attribute when non-default' do stub_ldap_config(uid: 'sAMAccountName') expect(adapter).to receive(:ldap_search).with( - hash_including(attributes: %w{sAMAccountName cn mail dn}) + hash_including(attributes: %w{sAMAccountName cn dn uid userid sAMAccountName mail email userPrincipalName}) ).and_return({}) adapter.users('sAMAccountName', 'johndoe') diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb index 7a2f774b948..57a91193004 100644 --- a/spec/lib/gitlab/ldap/auth_hash_spec.rb +++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Gitlab::LDAP::AuthHash, lib: true do +describe Gitlab::LDAP::AuthHash do let(:auth_hash) do - Gitlab::LDAP::AuthHash.new( + described_class.new( OmniAuth::AuthHash.new( uid: '123456', provider: 'ldapmain', diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb index f689b47fec4..01b6282af0c 100644 --- a/spec/lib/gitlab/ldap/authentication_spec.rb +++ b/spec/lib/gitlab/ldap/authentication_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::LDAP::Authentication, lib: true do +describe Gitlab::LDAP::Authentication do let(:user) { create(:omniauth_user, extern_uid: dn) } let(:dn) { 'uid=john,ou=people,dc=example,dc=com' } let(:login) { 'john' } diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb index cab2e9908ff..292ec064a67 100644 --- a/spec/lib/gitlab/ldap/config_spec.rb +++ b/spec/lib/gitlab/ldap/config_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Gitlab::LDAP::Config, lib: true do +describe Gitlab::LDAP::Config do include LdapHelpers - let(:config) { Gitlab::LDAP::Config.new('ldapmain') } + let(:config) { described_class.new('ldapmain') } - describe '#initalize' do + describe '#initialize' do it 'requires a provider' do - expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError + expect{ described_class.new }.to raise_error ArgumentError end it 'works' do @@ -15,7 +15,7 @@ describe Gitlab::LDAP::Config, lib: true do end it 'raises an error if a unknown provider is used' do - expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error(RuntimeError) + expect{ described_class.new 'unknown' }.to raise_error(RuntimeError) end end @@ -23,9 +23,9 @@ describe Gitlab::LDAP::Config, lib: true do it 'constructs basic options' do stub_ldap_config( options: { - 'host' => 'ldap.example.com', - 'port' => 386, - 'method' => 'plain' + 'host' => 'ldap.example.com', + 'port' => 386, + 'encryption' => 'plain' } ) @@ -39,24 +39,140 @@ describe Gitlab::LDAP::Config, lib: true do it 'includes authentication options when auth is configured' do stub_ldap_config( options: { - 'host' => 'ldap.example.com', - 'port' => 686, - 'method' => 'ssl', - 'bind_dn' => 'uid=admin,dc=example,dc=com', - 'password' => 'super_secret' + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true, + 'bind_dn' => 'uid=admin,dc=example,dc=com', + 'password' => 'super_secret' } ) - expect(config.adapter_options).to eq( - host: 'ldap.example.com', - port: 686, - encryption: :simple_tls, + expect(config.adapter_options).to include({ auth: { method: :simple, username: 'uid=admin,dc=example,dc=com', password: 'super_secret' } + }) + end + + it 'sets encryption method to simple_tls when configured as simple_tls' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls' + } ) + + expect(config.adapter_options[:encryption]).to include({ method: :simple_tls }) + end + + it 'sets encryption method to start_tls when configured as start_tls' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'start_tls' + } + ) + + expect(config.adapter_options[:encryption]).to include({ method: :start_tls }) + end + + context 'when verify_certificates is enabled' do + it 'sets tls_options to OpenSSL defaults' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true + } + ) + + expect(config.adapter_options[:encryption]).to include({ tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS }) + end + end + + context 'when verify_certificates is disabled' do + it 'sets verify_mode to OpenSSL VERIFY_NONE' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => false + } + ) + + expect(config.adapter_options[:encryption]).to include({ + tls_options: { + verify_mode: OpenSSL::SSL::VERIFY_NONE + } + }) + end + end + + context 'when ca_file is specified' do + it 'passes it through in tls_options' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'ca_file' => '/etc/ca.pem' + } + ) + + expect(config.adapter_options[:encryption][:tls_options]).to include({ ca_file: '/etc/ca.pem' }) + end + end + + context 'when ca_file is a blank string' do + it 'does not add the ca_file key to tls_options' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'ca_file' => ' ' + } + ) + + expect(config.adapter_options[:encryption][:tls_options]).not_to have_key(:ca_file) + end + end + + context 'when ssl_version is specified' do + it 'passes it through in tls_options' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'ssl_version' => 'TLSv1_2' + } + ) + + expect(config.adapter_options[:encryption][:tls_options]).to include({ ssl_version: 'TLSv1_2' }) + end + end + + context 'when ssl_version is a blank string' do + it 'does not add the ssl_version key to tls_options' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'ssl_version' => ' ' + } + ) + + expect(config.adapter_options[:encryption][:tls_options]).not_to have_key(:ssl_version) + end end end @@ -64,11 +180,11 @@ describe Gitlab::LDAP::Config, lib: true do it 'constructs basic options' do stub_ldap_config( options: { - 'host' => 'ldap.example.com', - 'port' => 386, - 'base' => 'ou=users,dc=example,dc=com', - 'method' => 'plain', - 'uid' => 'uid' + 'host' => 'ldap.example.com', + 'port' => 386, + 'base' => 'ou=users,dc=example,dc=com', + 'encryption' => 'plain', + 'uid' => 'uid' } ) @@ -76,7 +192,7 @@ describe Gitlab::LDAP::Config, lib: true do host: 'ldap.example.com', port: 386, base: 'ou=users,dc=example,dc=com', - method: 'plain', + encryption: 'plain', filter: '(uid=%{username})' ) expect(config.omniauth_options.keys).not_to include(:bind_dn, :password) @@ -98,6 +214,100 @@ describe Gitlab::LDAP::Config, lib: true do password: 'super_secret' ) end + + context 'when verify_certificates is enabled' do + it 'specifies disable_verify_certificates as false' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true + } + ) + + expect(config.omniauth_options).to include({ disable_verify_certificates: false }) + end + end + + context 'when verify_certificates is disabled' do + it 'specifies disable_verify_certificates as true' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => false + } + ) + + expect(config.omniauth_options).to include({ disable_verify_certificates: true }) + end + end + + context 'when ca_file is present' do + it 'passes it through' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true, + 'ca_file' => '/etc/ca.pem' + } + ) + + expect(config.omniauth_options).to include({ ca_file: '/etc/ca.pem' }) + end + end + + context 'when ca_file is blank' do + it 'does not include the ca_file option' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true, + 'ca_file' => ' ' + } + ) + + expect(config.omniauth_options).not_to have_key(:ca_file) + end + end + + context 'when ssl_version is present' do + it 'passes it through' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true, + 'ssl_version' => 'TLSv1_2' + } + ) + + expect(config.omniauth_options).to include({ ssl_version: 'TLSv1_2' }) + end + end + + context 'when ssl_version is blank' do + it 'does not include the ssl_version option' do + stub_ldap_config( + options: { + 'host' => 'ldap.example.com', + 'port' => 686, + 'encryption' => 'simple_tls', + 'verify_certificates' => true, + 'ssl_version' => ' ' + } + ) + + expect(config.omniauth_options).not_to have_key(:ssl_version) + end + end end describe '#has_auth?' do diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index b796d8bf076..175ceec44d7 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::LDAP::User, lib: true do - let(:ldap_user) { Gitlab::LDAP::User.new(auth_hash) } +describe Gitlab::LDAP::User do + let(:ldap_user) { described_class.new(auth_hash) } let(:gl_user) { ldap_user.gl_user } let(:info) do { @@ -13,7 +13,7 @@ describe Gitlab::LDAP::User, lib: true do let(:auth_hash) do OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info) end - let(:ldap_user_upper_case) { Gitlab::LDAP::User.new(auth_hash_upper_case) } + let(:ldap_user_upper_case) { described_class.new(auth_hash_upper_case) } let(:info_upper_case) do { name: 'John', diff --git a/spec/lib/gitlab/lfs_token_spec.rb b/spec/lib/gitlab/lfs_token_spec.rb index e9c1163e22a..3a20dad16d0 100644 --- a/spec/lib/gitlab/lfs_token_spec.rb +++ b/spec/lib/gitlab/lfs_token_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::LfsToken, lib: true do +describe Gitlab::LfsToken do describe '#token' do shared_examples 'an LFS token generator' do it 'returns a randomly generated token' do diff --git a/spec/lib/gitlab/markup_helper_spec.rb b/spec/lib/gitlab/markup_helper_spec.rb index 93b91b849f2..09e518ff989 100644 --- a/spec/lib/gitlab/markup_helper_spec.rb +++ b/spec/lib/gitlab/markup_helper_spec.rb @@ -1,40 +1,40 @@ require 'spec_helper' -describe Gitlab::MarkupHelper, lib: true do +describe Gitlab::MarkupHelper do describe '#markup?' do %w(textile rdoc org creole wiki mediawiki rst adoc ad asciidoc mdown md markdown).each do |type| it "returns true for #{type} files" do - expect(Gitlab::MarkupHelper.markup?("README.#{type}")).to be_truthy + expect(described_class.markup?("README.#{type}")).to be_truthy end end it 'returns false when given a non-markup filename' do - expect(Gitlab::MarkupHelper.markup?('README.rb')).not_to be_truthy + expect(described_class.markup?('README.rb')).not_to be_truthy end end describe '#gitlab_markdown?' do %w(mdown mkd mkdn md markdown).each do |type| it "returns true for #{type} files" do - expect(Gitlab::MarkupHelper.gitlab_markdown?("README.#{type}")).to be_truthy + expect(described_class.gitlab_markdown?("README.#{type}")).to be_truthy end end it 'returns false when given a non-markdown filename' do - expect(Gitlab::MarkupHelper.gitlab_markdown?('README.rb')).not_to be_truthy + expect(described_class.gitlab_markdown?('README.rb')).not_to be_truthy end end describe '#asciidoc?' do %w(adoc ad asciidoc ADOC).each do |type| it "returns true for #{type} files" do - expect(Gitlab::MarkupHelper.asciidoc?("README.#{type}")).to be_truthy + expect(described_class.asciidoc?("README.#{type}")).to be_truthy end end it 'returns false when given a non-asciidoc filename' do - expect(Gitlab::MarkupHelper.asciidoc?('README.rb')).not_to be_truthy + expect(described_class.asciidoc?('README.rb')).not_to be_truthy end end end diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb index c2ab015d5cb..6af1564da19 100644 --- a/spec/lib/gitlab/middleware/go_spec.rb +++ b/spec/lib/gitlab/middleware/go_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Middleware::Go, lib: true do +describe Gitlab::Middleware::Go do let(:app) { double(:app) } let(:middleware) { described_class.new(app) } diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb index 19ab17419fc..d5f4da3ce36 100644 --- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb +++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Gitlab::OAuth::AuthHash, lib: true do +describe Gitlab::OAuth::AuthHash do let(:auth_hash) do - Gitlab::OAuth::AuthHash.new( + described_class.new( OmniAuth::AuthHash.new( provider: provider_ascii, uid: uid_ascii, diff --git a/spec/lib/gitlab/o_auth/provider_spec.rb b/spec/lib/gitlab/o_auth/provider_spec.rb index 1e2a1f8c039..30faf107e3f 100644 --- a/spec/lib/gitlab/o_auth/provider_spec.rb +++ b/spec/lib/gitlab/o_auth/provider_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::OAuth::Provider, lib: true do +describe Gitlab::OAuth::Provider do describe '#config_for' do context 'for an LDAP provider' do context 'when the provider exists' do diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index ea29cb9caf1..47aa19d5fd9 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::OAuth::User, lib: true do - let(:oauth_user) { Gitlab::OAuth::User.new(auth_hash) } +describe Gitlab::OAuth::User do + let(:oauth_user) { described_class.new(auth_hash) } let(:gl_user) { oauth_user.gl_user } let(:uid) { 'my-uid' } let(:provider) { 'my-provider' } diff --git a/spec/lib/gitlab/optimistic_locking_spec.rb b/spec/lib/gitlab/optimistic_locking_spec.rb index acce2be93f2..81f81d4f963 100644 --- a/spec/lib/gitlab/optimistic_locking_spec.rb +++ b/spec/lib/gitlab/optimistic_locking_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::OptimisticLocking, lib: true do +describe Gitlab::OptimisticLocking do let!(:pipeline) { create(:ci_pipeline) } let!(:pipeline2) { Ci::Pipeline.find(pipeline.id) } diff --git a/spec/lib/gitlab/other_markup_spec.rb b/spec/lib/gitlab/other_markup_spec.rb index c0f5fa9dc1f..e26f39e193e 100644 --- a/spec/lib/gitlab/other_markup_spec.rb +++ b/spec/lib/gitlab/other_markup_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::OtherMarkup, lib: true do +describe Gitlab::OtherMarkup do let(:context) { {} } context "XSS Checks" do diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb index 1eea710c80b..2f989397f7e 100644 --- a/spec/lib/gitlab/path_regex_spec.rb +++ b/spec/lib/gitlab/path_regex_spec.rb @@ -1,7 +1,7 @@ # coding: utf-8 require 'spec_helper' -describe Gitlab::PathRegex, lib: true do +describe Gitlab::PathRegex do # Pass in a full path to remove the format segment: # `/ci/lint(.:format)` -> `/ci/lint` def without_format(path) @@ -36,9 +36,12 @@ describe Gitlab::PathRegex, lib: true do described_class::PROJECT_WILDCARD_ROUTES.include?(path.split('/').first) end - def failure_message(missing_words, constant_name, migration_helper) + def failure_message(constant_name, migration_helper, missing_words: [], additional_words: []) missing_words = Array(missing_words) - <<-MSG + additional_words = Array(additional_words) + message = "" + if missing_words.any? + message += <<-MISSING Found new routes that could cause conflicts with existing namespaced routes for groups or projects. @@ -51,7 +54,18 @@ describe Gitlab::PathRegex, lib: true do Make sure to make a note of the renamed records in the release blog post. - MSG + MISSING + end + + if additional_words.any? + message += <<-ADDITIONAL + Why are <#{additional_words.join(', ')}> in `#{constant_name}`? + If they are really required, update these specs to reflect that. + + ADDITIONAL + end + + message end let(:all_routes) do @@ -68,9 +82,23 @@ describe Gitlab::PathRegex, lib: true do let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } } let(:top_level_words) do - routes_not_starting_in_wildcard.map do |route| + words = routes_not_starting_in_wildcard.map do |route| route.split('/')[1] end.compact.uniq + + words + ee_top_level_words + files_in_public + Array(API::API.prefix.to_s) + end + + let(:ee_top_level_words) do + ['unsubscribes'] + end + + let(:files_in_public) do + git = Gitlab.config.git.bin_path + `cd #{Rails.root} && #{git} ls-files public` + .split("\n") + .map { |entry| entry.gsub('public/', '') } + .uniq end # All routes that start with a namespaced path, that have 1 or more @@ -115,18 +143,29 @@ describe Gitlab::PathRegex, lib: true do let(:paths_after_group_id) do group_routes.map do |route| route.gsub(STARTING_WITH_GROUP, '').split('/').first - end.uniq + end.uniq + ee_paths_after_group_id + end + + let(:ee_paths_after_group_id) do + %w(analytics + ldap + ldap_group_links + notification_setting + audit_events + pipeline_quota hooks) end describe 'TOP_LEVEL_ROUTES' do it 'includes all the top level namespaces' do failure_block = lambda do missing_words = top_level_words - described_class::TOP_LEVEL_ROUTES - failure_message(missing_words, 'TOP_LEVEL_ROUTES', 'rename_root_paths') + additional_words = described_class::TOP_LEVEL_ROUTES - top_level_words + failure_message('TOP_LEVEL_ROUTES', 'rename_root_paths', + missing_words: missing_words, additional_words: additional_words) end expect(described_class::TOP_LEVEL_ROUTES) - .to include(*top_level_words), failure_block + .to contain_exactly(*top_level_words), failure_block end end @@ -134,11 +173,13 @@ describe Gitlab::PathRegex, lib: true do it "don't contain a second wildcard" do failure_block = lambda do missing_words = paths_after_group_id - described_class::GROUP_ROUTES - failure_message(missing_words, 'GROUP_ROUTES', 'rename_child_paths') + additional_words = described_class::GROUP_ROUTES - paths_after_group_id + failure_message('GROUP_ROUTES', 'rename_child_paths', + missing_words: missing_words, additional_words: additional_words) end expect(described_class::GROUP_ROUTES) - .to include(*paths_after_group_id), failure_block + .to contain_exactly(*paths_after_group_id), failure_block end end @@ -147,7 +188,7 @@ describe Gitlab::PathRegex, lib: true do aggregate_failures do all_wildcard_paths.each do |path| expect(wildcards_include?(path)) - .to be(true), failure_message(path, 'PROJECT_WILDCARD_ROUTES', 'rename_wildcard_paths') + .to be(true), failure_message('PROJECT_WILDCARD_ROUTES', 'rename_wildcard_paths', missing_words: path) end end end diff --git a/spec/lib/gitlab/polling_interval_spec.rb b/spec/lib/gitlab/polling_interval_spec.rb index 5ea8ecb1c30..eb8e618156b 100644 --- a/spec/lib/gitlab/polling_interval_spec.rb +++ b/spec/lib/gitlab/polling_interval_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::PollingInterval, lib: true do +describe Gitlab::PollingInterval do let(:polling_interval) { described_class } describe '.set_header' do diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index af50ecdb2ab..4567f220c11 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Gitlab::Popen', lib: true, no_db: true do +describe 'Gitlab::Popen' do let(:path) { Rails.root.join('tmp').to_s } before do diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 3d22784909d..d17b436b910 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ProjectSearchResults, lib: true do +describe Gitlab::ProjectSearchResults do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:query) { 'hello world' } diff --git a/spec/lib/gitlab/project_transfer_spec.rb b/spec/lib/gitlab/project_transfer_spec.rb index e2d6b1b9ab7..10c5fb148cd 100644 --- a/spec/lib/gitlab/project_transfer_spec.rb +++ b/spec/lib/gitlab/project_transfer_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::ProjectTransfer, lib: true do +describe Gitlab::ProjectTransfer do before do @root_dir = File.join(Rails.root, "public", "uploads") - @project_transfer = Gitlab::ProjectTransfer.new + @project_transfer = described_class.new allow(@project_transfer).to receive(:root_dir).and_return(@root_dir) @project_path_was = "test_project_was" diff --git a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb index 61d48b05454..d7df4e35c31 100644 --- a/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb +++ b/spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Prometheus::AdditionalMetricsParser, lib: true do +describe Gitlab::Prometheus::AdditionalMetricsParser do include Prometheus::MetricBuilders let(:parser_error_class) { Gitlab::Prometheus::ParsingError } diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb index 4909aec5a4d..e42e034f4fb 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery, lib: true do +describe Gitlab::Prometheus::Queries::AdditionalMetricsDeploymentQuery do include Prometheus::MetricBuilders let(:client) { double('prometheus_client') } diff --git a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb index 8e6e3bb5946..e9fd66d45fe 100644 --- a/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery, lib: true do +describe Gitlab::Prometheus::Queries::AdditionalMetricsEnvironmentQuery do include Prometheus::MetricBuilders let(:client) { double('prometheus_client') } diff --git a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb b/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb index d957dd932c4..ffe3ad85baa 100644 --- a/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Prometheus::Queries::DeploymentQuery, lib: true do +describe Gitlab::Prometheus::Queries::DeploymentQuery do let(:environment) { create(:environment, slug: 'environment-slug') } let(:deployment) { create(:deployment, environment: environment) } diff --git a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb index d2796ab72da..2b488101496 100644 --- a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb +++ b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Prometheus::Queries::MatchedMetricsQuery, lib: true do +describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do include Prometheus::MetricBuilders let(:metric_group_class) { Gitlab::Prometheus::MetricGroup } diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb index 46eaadae206..de625324092 100644 --- a/spec/lib/gitlab/prometheus_client_spec.rb +++ b/spec/lib/gitlab/prometheus_client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::PrometheusClient, lib: true do +describe Gitlab::PrometheusClient do include PrometheusHelpers subject { described_class.new(api_url: 'https://prometheus.example.com') } diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 84cfd934fa0..1a0357534f2 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Gitlab::ReferenceExtractor, lib: true do +describe Gitlab::ReferenceExtractor do let(:project) { create(:empty_project) } before do project.team << [project.creator, :developer] end - subject { Gitlab::ReferenceExtractor.new(project, project.creator) } + subject { described_class.new(project, project.creator) } it 'accesses valid user objects' do @u_foo = create(:user, username: 'foo') @@ -183,11 +183,34 @@ describe Gitlab::ReferenceExtractor, lib: true do context 'with an external issue tracker' do let(:project) { create(:jira_project) } + let(:issue) { create(:issue, project: project) } + + context 'when GitLab issues are enabled' do + it 'returns both JIRA and internal issues' do + subject.analyze("JIRA-123 and FOOBAR-4567 and #{issue.to_reference}") + expect(subject.issues).to eq [ExternalIssue.new('JIRA-123', project), + ExternalIssue.new('FOOBAR-4567', project), + issue] + end + + it 'returns only JIRA issues if the internal one does not exists' do + subject.analyze("JIRA-123 and FOOBAR-4567 and #999") + expect(subject.issues).to eq [ExternalIssue.new('JIRA-123', project), + ExternalIssue.new('FOOBAR-4567', project)] + end + end - it 'returns JIRA issues for a JIRA-integrated project' do - subject.analyze('JIRA-123 and FOOBAR-4567') - expect(subject.issues).to eq [ExternalIssue.new('JIRA-123', project), - ExternalIssue.new('FOOBAR-4567', project)] + context 'when GitLab issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + + it 'returns only JIRA issues' do + subject.analyze("JIRA-123 and FOOBAR-4567 and #{issue.to_reference}") + expect(subject.issues).to eq [ExternalIssue.new('JIRA-123', project), + ExternalIssue.new('FOOBAR-4567', project)] + end end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 251f82849bf..68a57826647 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -1,7 +1,7 @@ # coding: utf-8 require 'spec_helper' -describe Gitlab::Regex, lib: true do +describe Gitlab::Regex do describe '.project_name_regex' do subject { described_class.project_name_regex } diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb index a91c8655cdd..e272bdb9284 100644 --- a/spec/lib/gitlab/request_context_spec.rb +++ b/spec/lib/gitlab/request_context_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -describe Gitlab::RequestContext, lib: true do +describe Gitlab::RequestContext do describe '#client_ip' do - subject { Gitlab::RequestContext.client_ip } + subject { described_class.client_ip } let(:app) { -> (env) {} } let(:env) { Hash.new } @@ -16,7 +16,7 @@ describe Gitlab::RequestContext, lib: true do before do allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip) - Gitlab::RequestContext.new(app).call(env) + described_class.new(app).call(env) end it { is_expected.to eq(ip) } diff --git a/spec/lib/gitlab/request_forgery_protection_spec.rb b/spec/lib/gitlab/request_forgery_protection_spec.rb new file mode 100644 index 00000000000..305de613866 --- /dev/null +++ b/spec/lib/gitlab/request_forgery_protection_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe Gitlab::RequestForgeryProtection, :allow_forgery_protection do + let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) } + let(:env) do + { + 'rack.input' => '', + 'rack.session' => { + _csrf_token: csrf_token + } + } + end + + describe '.call' do + context 'when the request method is GET' do + before do + env['REQUEST_METHOD'] = 'GET' + end + + it 'does not raise an exception' do + expect { described_class.call(env) }.not_to raise_exception + end + end + + context 'when the request method is POST' do + before do + env['REQUEST_METHOD'] = 'POST' + end + + context 'when the CSRF token is valid' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it 'does not raise an exception' do + expect { described_class.call(env) }.not_to raise_exception + end + end + + context 'when the CSRF token is invalid' do + before do + env['HTTP_X_CSRF_TOKEN'] = 'foo' + end + + it 'raises an ActionController::InvalidAuthenticityToken exception' do + expect { described_class.call(env) }.to raise_exception(ActionController::InvalidAuthenticityToken) + end + end + end + end + + describe '.verified?' do + context 'when the request method is GET' do + before do + env['REQUEST_METHOD'] = 'GET' + end + + it 'returns true' do + expect(described_class.verified?(env)).to be_truthy + end + end + + context 'when the request method is POST' do + before do + env['REQUEST_METHOD'] = 'POST' + end + + context 'when the CSRF token is valid' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it 'returns true' do + expect(described_class.verified?(env)).to be_truthy + end + end + + context 'when the CSRF token is invalid' do + before do + env['HTTP_X_CSRF_TOKEN'] = 'foo' + end + + it 'returns false' do + expect(described_class.verified?(env)).to be_falsey + end + end + end + end +end diff --git a/spec/lib/gitlab/request_profiler_spec.rb b/spec/lib/gitlab/request_profiler_spec.rb index ae9c06ebb7d..fd8cbf39bce 100644 --- a/spec/lib/gitlab/request_profiler_spec.rb +++ b/spec/lib/gitlab/request_profiler_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::RequestProfiler, lib: true do +describe Gitlab::RequestProfiler do describe '.profile_token' do it 'returns a token' do expect(described_class.profile_token).to be_present diff --git a/spec/lib/gitlab/route_map_spec.rb b/spec/lib/gitlab/route_map_spec.rb index e8feb21e4d7..d672f7b5675 100644 --- a/spec/lib/gitlab/route_map_spec.rb +++ b/spec/lib/gitlab/route_map_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::RouteMap, lib: true do +describe Gitlab::RouteMap do describe '#initialize' do context 'when the data is not YAML' do it 'raises an error' do diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index a4d2367b72a..2827a18515e 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Saml::User, lib: true do +describe Gitlab::Saml::User do let(:saml_user) { described_class.new(auth_hash) } let(:gl_user) { saml_user.gl_user } let(:uid) { 'my-uid' } diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 5b1b8f9516a..b90d8dede0f 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' require 'stringio' -describe Gitlab::Shell, lib: true do +describe Gitlab::Shell do let(:project) { double('Project', id: 7, path: 'diaspora') } - let(:gitlab_shell) { Gitlab::Shell.new } + let(:gitlab_shell) { described_class.new } let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } } before do @@ -30,7 +30,7 @@ describe Gitlab::Shell, lib: true do allow(Gitlab.config.gitlab_shell).to receive(:secret_file).and_return(secret_file) allow(Gitlab.config.gitlab_shell).to receive(:path).and_return('tmp/tests/shell-secret-test') FileUtils.mkdir('tmp/tests/shell-secret-test') - Gitlab::Shell.ensure_secret_token! + described_class.ensure_secret_token! end after do @@ -39,7 +39,7 @@ describe Gitlab::Shell, lib: true do end it 'creates and links the secret token file' do - secret_token = Gitlab::Shell.secret_token + secret_token = described_class.secret_token expect(File.exist?(secret_file)).to be(true) expect(File.read(secret_file).chomp).to eq(secret_token) @@ -59,7 +59,7 @@ describe Gitlab::Shell, lib: true do end end - describe Gitlab::Shell::KeyAdder, lib: true do + describe Gitlab::Shell::KeyAdder do describe '#add_key' do it 'removes trailing garbage' do io = spy(:io) diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb index 2ae79b50e77..873ed14f804 100644 --- a/spec/lib/gitlab/sherlock/collection_spec.rb +++ b/spec/lib/gitlab/sherlock/collection_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::Collection, lib: true do +describe Gitlab::Sherlock::Collection do let(:collection) { described_class.new } let(:transaction) do diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb index 4989d14def3..394421504e0 100644 --- a/spec/lib/gitlab/sherlock/file_sample_spec.rb +++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::FileSample, lib: true do +describe Gitlab::Sherlock::FileSample do let(:sample) { described_class.new(__FILE__, [], 150.4, 2) } describe '#id' do diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb index 39c6b2a4844..f2f8040fa0b 100644 --- a/spec/lib/gitlab/sherlock/line_profiler_spec.rb +++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::LineProfiler, lib: true do +describe Gitlab::Sherlock::LineProfiler do let(:profiler) { described_class.new } describe '#profile' do diff --git a/spec/lib/gitlab/sherlock/line_sample_spec.rb b/spec/lib/gitlab/sherlock/line_sample_spec.rb index f9b61f8684e..5f02f6a3213 100644 --- a/spec/lib/gitlab/sherlock/line_sample_spec.rb +++ b/spec/lib/gitlab/sherlock/line_sample_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::LineSample, lib: true do +describe Gitlab::Sherlock::LineSample do let(:sample) { described_class.new(150.0, 4) } describe '#duration' do diff --git a/spec/lib/gitlab/sherlock/location_spec.rb b/spec/lib/gitlab/sherlock/location_spec.rb index 5739afa6b1e..b295a624b35 100644 --- a/spec/lib/gitlab/sherlock/location_spec.rb +++ b/spec/lib/gitlab/sherlock/location_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::Location, lib: true do +describe Gitlab::Sherlock::Location do let(:location) { described_class.new(__FILE__, 1) } describe 'from_ruby_location' do diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb index b98ab0b14a2..2016023df06 100644 --- a/spec/lib/gitlab/sherlock/middleware_spec.rb +++ b/spec/lib/gitlab/sherlock/middleware_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::Middleware, lib: true do +describe Gitlab::Sherlock::Middleware do let(:app) { double(:app) } let(:middleware) { described_class.new(app) } diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb index d97b5eef573..426071c7f92 100644 --- a/spec/lib/gitlab/sherlock/query_spec.rb +++ b/spec/lib/gitlab/sherlock/query_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::Query, lib: true do +describe Gitlab::Sherlock::Query do let(:started_at) { Time.utc(2015, 1, 1) } let(:finished_at) { started_at + 5 } diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index 6ae1aa20ea7..4a14dfbec56 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Sherlock::Transaction, lib: true do +describe Gitlab::Sherlock::Transaction do let(:transaction) { described_class.new('POST', '/cat_pictures') } describe '#id' do diff --git a/spec/lib/gitlab/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb index 28d7f9858c3..f0ecf59406a 100644 --- a/spec/lib/gitlab/slash_commands/command_spec.rb +++ b/spec/lib/gitlab/slash_commands/command_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SlashCommands::Command, service: true do +describe Gitlab::SlashCommands::Command do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb index d919f7260db..e52aaed7328 100644 --- a/spec/lib/gitlab/slash_commands/deploy_spec.rb +++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SlashCommands::Deploy, service: true do +describe Gitlab::SlashCommands::Deploy do describe '#execute' do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb index 4de50d4a8bb..5dfb1b506bc 100644 --- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SlashCommands::IssueNew, service: true do +describe Gitlab::SlashCommands::IssueNew do describe '#execute' do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb index 06fff0afc50..e5409fe2339 100644 --- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SlashCommands::IssueSearch, service: true do +describe Gitlab::SlashCommands::IssueSearch do describe '#execute' do let!(:issue) { create(:issue, project: project, title: 'find me') } let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') } diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb index 1899f664ccd..f67a17c7922 100644 --- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SlashCommands::IssueShow, service: true do +describe Gitlab::SlashCommands::IssueShow do describe '#execute' do let(:issue) { create(:issue, project: project) } let(:project) { create(:empty_project) } diff --git a/spec/lib/gitlab/sql/glob_spec.rb b/spec/lib/gitlab/sql/glob_spec.rb index 451c583310d..f0bb4294d62 100644 --- a/spec/lib/gitlab/sql/glob_spec.rb +++ b/spec/lib/gitlab/sql/glob_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SQL::Glob, lib: true do +describe Gitlab::SQL::Glob do describe '.to_like' do it 'matches * as %' do expect(glob('apple', '*')).to be(true) diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb index 849edb09476..5346881444d 100644 --- a/spec/lib/gitlab/sql/union_spec.rb +++ b/spec/lib/gitlab/sql/union_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SQL::Union, lib: true do +describe Gitlab::SQL::Union do let(:relation_1) { User.where(email: 'alice@example.com').select(:id) } let(:relation_2) { User.where(email: 'bob@example.com').select(:id) } diff --git a/spec/lib/gitlab/string_range_marker_spec.rb b/spec/lib/gitlab/string_range_marker_spec.rb index 7c77772b3f6..abeaa7f0ddb 100644 --- a/spec/lib/gitlab/string_range_marker_spec.rb +++ b/spec/lib/gitlab/string_range_marker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::StringRangeMarker, lib: true do +describe Gitlab::StringRangeMarker do describe '#mark' do context "when the rich text is html safe" do let(:raw) { "abc <def>" } diff --git a/spec/lib/gitlab/string_regex_marker_spec.rb b/spec/lib/gitlab/string_regex_marker_spec.rb index 2f5cf6c6e3b..d715f9bd641 100644 --- a/spec/lib/gitlab/string_regex_marker_spec.rb +++ b/spec/lib/gitlab/string_regex_marker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::StringRegexMarker, lib: true do +describe Gitlab::StringRegexMarker do describe '#mark' do let(:raw) { %{"name": "AFNetworking"} } let(:rich) { %{<span class="key">"name"</span><span class="punctuation">: </span><span class="value">"AFNetworking"</span>}.html_safe } diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index 66045917cb3..bed58d407ef 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -46,10 +46,28 @@ describe Gitlab::UntrustedRegexp do context 'malicious regexp' do let(:text) { malicious_text } let(:regexp) { malicious_regexp } - + include_examples 'malicious regexp' end + context 'empty regexp' do + let(:regexp) { '' } + let(:text) { 'foo' } + + it 'returns an array of nil matches' do + is_expected.to eq([nil, nil, nil, nil]) + end + end + + context 'empty capture group regexp' do + let(:regexp) { '()' } + let(:text) { 'foo' } + + it 'returns an array of nil matches in an array' do + is_expected.to eq([[nil], [nil], [nil], [nil]]) + end + end + context 'no capture group' do let(:regexp) { '.+' } let(:text) { 'foo' } diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb index fcfd8d58b70..6106f13c774 100644 --- a/spec/lib/gitlab/upgrader_spec.rb +++ b/spec/lib/gitlab/upgrader_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::Upgrader, lib: true do - let(:upgrader) { Gitlab::Upgrader.new } +describe Gitlab::Upgrader do + let(:upgrader) { described_class.new } let(:current_version) { Gitlab::VERSION } describe 'current_version_raw' do diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index a504d299307..f5b4882815f 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::UrlBlocker, lib: true do +describe Gitlab::UrlBlocker do describe '#blocked_url?' do it 'allows imports from configured web host and port' do import_url = "http://#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.port}/t.git" diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index e9a6e273516..50422020823 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::UrlBuilder, lib: true do +describe Gitlab::UrlBuilder do describe '.build' do context 'when passing a Commit' do it 'returns a proper URL' do diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb index 6bce724a3f6..308b1a128be 100644 --- a/spec/lib/gitlab/url_sanitizer_spec.rb +++ b/spec/lib/gitlab/url_sanitizer_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::UrlSanitizer, lib: true do +describe Gitlab::UrlSanitizer do let(:credentials) { { user: 'blah', password: 'password' } } let(:url_sanitizer) do described_class.new("https://github.com/me/project.git", credentials: credentials) diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index daf097f8d51..68429d792f2 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -1,11 +1,19 @@ require 'spec_helper' describe Gitlab::UsageData do - let!(:project) { create(:empty_project) } - let!(:project2) { create(:empty_project) } - let!(:board) { create(:board, project: project) } + let(:projects) { create_list(:project, 3) } + let!(:board) { create(:board, project: projects[0]) } describe '#data' do + before do + create(:jira_service, project: projects[0]) + create(:jira_service, project: projects[1]) + create(:prometheus_service, project: projects[1]) + create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true) + create(:service, project: projects[1], type: 'SlackService', active: true) + create(:service, project: projects[2], type: 'SlackService', active: true) + end + subject { described_class.data } it "gathers usage data" do @@ -25,7 +33,7 @@ describe Gitlab::UsageData do count_data = subject[:counts] expect(count_data[:boards]).to eq(1) - expect(count_data[:projects]).to eq(2) + expect(count_data[:projects]).to eq(3) expect(count_data.keys).to match_array(%i( boards @@ -49,6 +57,9 @@ describe Gitlab::UsageData do notes projects projects_imported_from_github + projects_jira_active + projects_slack_notifications_active + projects_slack_slash_active projects_prometheus_active pages_domains protected_branches @@ -59,6 +70,16 @@ describe Gitlab::UsageData do web_hooks )) end + + it 'gathers projects data correctly' do + count_data = subject[:counts] + + expect(count_data[:projects]).to eq(3) + expect(count_data[:projects_prometheus_active]).to eq(1) + expect(count_data[:projects_jira_active]).to eq(2) + expect(count_data[:projects_slack_notifications_active]).to eq(2) + expect(count_data[:projects_slack_slash_active]).to eq(1) + end end describe '#license_usage_data' do diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 0d87cf25dbb..5ebaf6c1507 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Gitlab::UserAccess, lib: true do - let(:access) { Gitlab::UserAccess.new(user, project: project) } +describe Gitlab::UserAccess do + let(:access) { described_class.new(user, project: project) } let(:project) { create(:project) } let(:user) { create(:user) } @@ -28,7 +28,7 @@ describe Gitlab::UserAccess, lib: true do describe 'push to empty project' do let(:empty_project) { create(:project_empty_repo) } - let(:project_access) { Gitlab::UserAccess.new(user, project: empty_project) } + let(:project_access) { described_class.new(user, project: empty_project) } it 'returns true if user is master' do empty_project.team << [user, :master] diff --git a/spec/lib/gitlab/user_activities_spec.rb b/spec/lib/gitlab/user_activities_spec.rb index a4ea0ac59e9..6bce2ee13cf 100644 --- a/spec/lib/gitlab/user_activities_spec.rb +++ b/spec/lib/gitlab/user_activities_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::UserActivities, :clean_gitlab_redis_shared_state, lib: true do +describe Gitlab::UserActivities, :clean_gitlab_redis_shared_state do let(:now) { Time.now } describe '.record' do diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index 00941aec380..111c873f79c 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Utils, lib: true do +describe Gitlab::Utils do delegate :to_boolean, :boolean_to_yes_no, to: :described_class describe '.to_boolean' do diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb index 706ee9bec58..e7e1a92ae54 100644 --- a/spec/lib/gitlab/version_info_spec.rb +++ b/spec/lib/gitlab/version_info_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Gitlab::VersionInfo', lib: true, no_db: true do +describe 'Gitlab::VersionInfo' do before do @unknown = Gitlab::VersionInfo.new @v0_0_1 = Gitlab::VersionInfo.new(0, 0, 1) diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb index db9d2807be6..48a67773de9 100644 --- a/spec/lib/gitlab/visibility_level_spec.rb +++ b/spec/lib/gitlab/visibility_level_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::VisibilityLevel, lib: true do +describe Gitlab::VisibilityLevel do describe '.level_value' do it 'converts "public" to integer value' do expect(described_class.level_value('public')).to eq(Gitlab::VisibilityLevel::PUBLIC) diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 124f66a6e0e..654397ccffb 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Workhorse, lib: true do +describe Gitlab::Workhorse do let(:project) { create(:project, :repository) } let(:repository) { project.repository } @@ -63,13 +63,13 @@ describe Gitlab::Workhorse, lib: true do end context 'without ca_pem' do - subject { Gitlab::Workhorse.terminal_websocket(terminal) } + subject { described_class.terminal_websocket(terminal) } it { is_expected.to eq(workhorse) } end context 'with ca_pem' do - subject { Gitlab::Workhorse.terminal_websocket(terminal(ca_pem: "foo")) } + subject { described_class.terminal_websocket(terminal(ca_pem: "foo")) } it { is_expected.to eq(workhorse(ca_pem: "foo")) } end @@ -237,7 +237,8 @@ describe Gitlab::Workhorse, lib: true do context 'when action is not enabled by feature flag' do it 'does not include Gitaly params in the returned value' do - allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(false) + status_opt_out = Gitlab::GitalyClient::MigrationStatus::OPT_OUT + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag, status: status_opt_out).and_return(false) expect(subject).not_to include(gitaly_params) end @@ -325,7 +326,7 @@ describe Gitlab::Workhorse, lib: true do subject { described_class.send_git_blob(repository, blob) } - context 'when Gitaly project_raw_show feature is enabled' do + context 'when Gitaly workhorse_raw_show feature is enabled' do it 'sets the header correctly' do key, command, params = decode_workhorse_header(subject) @@ -345,7 +346,7 @@ describe Gitlab::Workhorse, lib: true do end end - context 'when Gitaly project_raw_show feature is disabled', skip_gitaly_mock: true do + context 'when Gitaly workhorse_raw_show feature is disabled', skip_gitaly_mock: true do it 'sets the header correctly' do key, command, params = decode_workhorse_header(subject) diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb index c4c107c9eea..f97136f0191 100644 --- a/spec/lib/gitlab_spec.rb +++ b/spec/lib/gitlab_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Gitlab, lib: true do +describe Gitlab do describe '.com?' do it 'is true when on GitLab.com' do stub_config_setting(url: 'https://gitlab.com') diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb index 5892f3481a4..0c15ba22bf2 100644 --- a/spec/lib/repository_cache_spec.rb +++ b/spec/lib/repository_cache_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe RepositoryCache, lib: true do +describe RepositoryCache do let(:project) { create(:empty_project) } let(:backend) { double('backend').as_null_object } - let(:cache) { RepositoryCache.new('example', project.id, backend) } + let(:cache) { described_class.new('example', project.id, backend) } describe '#cache_key' do it 'includes the namespace' do diff --git a/spec/lib/system_check/simple_executor_spec.rb b/spec/lib/system_check/simple_executor_spec.rb index 795f11ee1f8..025ea2673b4 100644 --- a/spec/lib/system_check/simple_executor_spec.rb +++ b/spec/lib/system_check/simple_executor_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'rake_helper' -describe SystemCheck::SimpleExecutor, lib: true do +describe SystemCheck::SimpleExecutor do class SimpleCheck < SystemCheck::BaseCheck set_name 'my simple check' diff --git a/spec/lib/system_check_spec.rb b/spec/lib/system_check_spec.rb index 23d9beddb08..4d9e17fa6ec 100644 --- a/spec/lib/system_check_spec.rb +++ b/spec/lib/system_check_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'rake_helper' -describe SystemCheck, lib: true do +describe SystemCheck do class SimpleCheck < SystemCheck::BaseCheck def check? true @@ -19,7 +19,7 @@ describe SystemCheck, lib: true do end describe '.run' do - subject { SystemCheck } + subject { described_class } it 'detects execution of SimpleCheck' do is_expected.to execute_check(SimpleCheck) diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 8c1c9bf135f..09e5094cf84 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -91,6 +91,36 @@ describe Emails::Profile do end end + describe 'user added gpg key' do + let(:gpg_key) { create(:gpg_key) } + + subject { Notify.new_gpg_key_email(gpg_key.id) } + + it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' + it_behaves_like 'a user cannot unsubscribe through footer link' + + it 'is sent to the new user' do + is_expected.to deliver_to gpg_key.user.email + end + + it 'has the correct subject' do + is_expected.to have_subject /^GPG key was added to your account$/i + end + + it 'contains the new gpg key title' do + is_expected.to have_body_text /#{gpg_key.fingerprint}/ + end + + it 'includes a link to gpg keys page' do + is_expected.to have_body_text /#{profile_gpg_keys_path}/ + end + + context 'with GPG key that does not exist' do + it { expect { Notify.new_gpg_key_email('foo') }.not_to raise_error } + end + end + describe 'user added email' do let(:email) { create(:email) } diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index dc7a0d80752..aa019288700 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Ability, lib: true do +describe Ability do context 'using a nil subject' do it 'has no permissions' do - expect(Ability.policy_for(nil, nil)).to be_banned + expect(described_class.policy_for(nil, nil)).to be_banned end end @@ -98,7 +98,7 @@ describe Ability, lib: true do user2 = build(:user, external: true) users = [user1, user2] - expect(project).to receive(:owner).twice.and_return(user1) + expect(project).to receive(:owner).at_least(:once).and_return(user1) expect(described_class.users_that_can_read_project(users, project)) .to eq([user1]) @@ -109,7 +109,7 @@ describe Ability, lib: true do user2 = build(:user, external: true) users = [user1, user2] - expect(project.team).to receive(:members).twice.and_return([user1]) + expect(project.team).to receive(:members).at_least(:once).and_return([user1]) expect(described_class.users_that_can_read_project(users, project)) .to eq([user1]) @@ -140,7 +140,7 @@ describe Ability, lib: true do user2 = build(:user, external: true) users = [user1, user2] - expect(project).to receive(:owner).twice.and_return(user1) + expect(project).to receive(:owner).at_least(:once).and_return(user1) expect(described_class.users_that_can_read_project(users, project)) .to eq([user1]) @@ -151,7 +151,7 @@ describe Ability, lib: true do user2 = build(:user, external: true) users = [user1, user2] - expect(project.team).to receive(:members).twice.and_return([user1]) + expect(project.team).to receive(:members).at_least(:once).and_return([user1]) expect(described_class.users_that_can_read_project(users, project)) .to eq([user1]) diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index c1bf5551fe0..d4da30b1641 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe AbuseReport, type: :model do +RSpec.describe AbuseReport do subject { create(:abuse_report) } let(:user) { create(:admin) } diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb index 1060bf3cbf4..7cd3a84d592 100644 --- a/spec/models/appearance_spec.rb +++ b/spec/models/appearance_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Appearance, type: :model do +RSpec.describe Appearance do subject { build(:appearance) } it { is_expected.to be_valid } diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index e600eab6565..359753b600e 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe ApplicationSetting, models: true do - let(:setting) { ApplicationSetting.create_from_defaults } +describe ApplicationSetting do + let(:setting) { described_class.create_from_defaults } it { expect(setting).to be_valid } it { expect(setting.uuid).to be_present } @@ -159,10 +159,10 @@ describe ApplicationSetting, models: true do context 'redis unavailable' do it 'returns an ApplicationSetting' do allow(Rails.cache).to receive(:fetch).and_call_original - allow(ApplicationSetting).to receive(:last).and_return(:last) + allow(described_class).to receive(:last).and_return(:last) expect(Rails.cache).to receive(:fetch).with(ApplicationSetting::CACHE_KEY).and_raise(ArgumentError) - expect(ApplicationSetting.current).to eq(:last) + expect(described_class.current).to eq(:last) end end end diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb index 2a9a27752c1..87e60d9c16b 100644 --- a/spec/models/award_emoji_spec.rb +++ b/spec/models/award_emoji_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AwardEmoji, models: true do +describe AwardEmoji do describe 'Associations' do it { is_expected.to belong_to(:awardable) } it { is_expected.to belong_to(:user) } diff --git a/spec/models/blob_viewer/base_spec.rb b/spec/models/blob_viewer/base_spec.rb index 574438838d8..e3640097dff 100644 --- a/spec/models/blob_viewer/base_spec.rb +++ b/spec/models/blob_viewer/base_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::Base, model: true do +describe BlobViewer::Base do include FakeBlobHelpers let(:project) { build(:empty_project) } diff --git a/spec/models/blob_viewer/changelog_spec.rb b/spec/models/blob_viewer/changelog_spec.rb index 9066c5a05ac..db41eca0fc8 100644 --- a/spec/models/blob_viewer/changelog_spec.rb +++ b/spec/models/blob_viewer/changelog_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::Changelog, model: true do +describe BlobViewer::Changelog do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/blob_viewer/composer_json_spec.rb b/spec/models/blob_viewer/composer_json_spec.rb index df4f1f4815c..82f6f7e5046 100644 --- a/spec/models/blob_viewer/composer_json_spec.rb +++ b/spec/models/blob_viewer/composer_json_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::ComposerJson, model: true do +describe BlobViewer::ComposerJson do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/gemspec_spec.rb b/spec/models/blob_viewer/gemspec_spec.rb index 81e932de290..14cc5f3c0fd 100644 --- a/spec/models/blob_viewer/gemspec_spec.rb +++ b/spec/models/blob_viewer/gemspec_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::Gemspec, model: true do +describe BlobViewer::Gemspec do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb index 0c6c24ece21..7a4f9866375 100644 --- a/spec/models/blob_viewer/gitlab_ci_yml_spec.rb +++ b/spec/models/blob_viewer/gitlab_ci_yml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::GitlabCiYml, model: true do +describe BlobViewer::GitlabCiYml do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/license_spec.rb b/spec/models/blob_viewer/license_spec.rb index 944ddd32b92..222ed166ee0 100644 --- a/spec/models/blob_viewer/license_spec.rb +++ b/spec/models/blob_viewer/license_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::License, model: true do +describe BlobViewer::License do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb index 5c9a9c81963..96fb1b08c99 100644 --- a/spec/models/blob_viewer/package_json_spec.rb +++ b/spec/models/blob_viewer/package_json_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::PackageJson, model: true do +describe BlobViewer::PackageJson do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/podspec_json_spec.rb b/spec/models/blob_viewer/podspec_json_spec.rb index 42a00940bc5..f510077a87b 100644 --- a/spec/models/blob_viewer/podspec_json_spec.rb +++ b/spec/models/blob_viewer/podspec_json_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::PodspecJson, model: true do +describe BlobViewer::PodspecJson do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/podspec_spec.rb b/spec/models/blob_viewer/podspec_spec.rb index 6c9f0f42d53..7c38083550c 100644 --- a/spec/models/blob_viewer/podspec_spec.rb +++ b/spec/models/blob_viewer/podspec_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::Podspec, model: true do +describe BlobViewer::Podspec do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/readme_spec.rb b/spec/models/blob_viewer/readme_spec.rb index 02679dbb544..926df21ffda 100644 --- a/spec/models/blob_viewer/readme_spec.rb +++ b/spec/models/blob_viewer/readme_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::Readme, model: true do +describe BlobViewer::Readme do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/blob_viewer/route_map_spec.rb b/spec/models/blob_viewer/route_map_spec.rb index 4854e0262d9..115731b4970 100644 --- a/spec/models/blob_viewer/route_map_spec.rb +++ b/spec/models/blob_viewer/route_map_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::RouteMap, model: true do +describe BlobViewer::RouteMap do include FakeBlobHelpers let(:project) { build(:project) } diff --git a/spec/models/blob_viewer/server_side_spec.rb b/spec/models/blob_viewer/server_side_spec.rb index f047953d540..2639eec9e84 100644 --- a/spec/models/blob_viewer/server_side_spec.rb +++ b/spec/models/blob_viewer/server_side_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BlobViewer::ServerSide, model: true do +describe BlobViewer::ServerSide do include FakeBlobHelpers let(:project) { build(:empty_project) } diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 333f4139a96..a8ca1d110e4 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BroadcastMessage, models: true do +describe BroadcastMessage do subject { build(:broadcast_message) } it { is_expected.to be_valid } @@ -24,26 +24,26 @@ describe BroadcastMessage, models: true do it 'returns message if time match' do message = create(:broadcast_message) - expect(BroadcastMessage.current).to include(message) + expect(described_class.current).to include(message) end it 'returns multiple messages if time match' do message1 = create(:broadcast_message) message2 = create(:broadcast_message) - expect(BroadcastMessage.current).to contain_exactly(message1, message2) + expect(described_class.current).to contain_exactly(message1, message2) end it 'returns empty list if time not come' do create(:broadcast_message, :future) - expect(BroadcastMessage.current).to be_empty + expect(described_class.current).to be_empty end it 'returns empty list if time has passed' do create(:broadcast_message, :expired) - expect(BroadcastMessage.current).to be_empty + expect(described_class.current).to be_empty end end diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb index b02971cab82..8581bcbb08b 100644 --- a/spec/models/chat_name_spec.rb +++ b/spec/models/chat_name_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatName, models: true do +describe ChatName do subject { create(:chat_name) } it { is_expected.to belong_to(:service) } diff --git a/spec/models/chat_team_spec.rb b/spec/models/chat_team_spec.rb index 5283561a83f..e0e5f73e6fe 100644 --- a/spec/models/chat_team_spec.rb +++ b/spec/models/chat_team_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatTeam, type: :model do +describe ChatTeam do subject { create(:chat_team) } # Associations diff --git a/spec/models/ci/artifact_blob_spec.rb b/spec/models/ci/artifact_blob_spec.rb index 968593d7e9b..a10a8af5303 100644 --- a/spec/models/ci/artifact_blob_spec.rb +++ b/spec/models/ci/artifact_blob_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::ArtifactBlob, models: true do +describe Ci::ArtifactBlob do let(:build) { create(:ci_build, :artifacts) } let(:entry) { build.artifacts_metadata_entry('other_artifacts_0.1.2/another-subdirectory/banana_sample.gif') } diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 0b521d720f3..86afa856ea7 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Build, :models do +describe Ci::Build do let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } @@ -225,7 +225,7 @@ describe Ci::Build, :models do it 'expects to have retried builds instead the original ones' do project.add_developer(user) - retried_rspec = Ci::Build.retry(rspec_test, user) + retried_rspec = described_class.retry(rspec_test, user) expect(staging.depends_on_builds.map(&:id)) .to contain_exactly(build.id, retried_rspec.id, rubocop_test.id) @@ -620,9 +620,9 @@ describe Ci::Build, :models do describe '#first_pending' do let!(:first) { create(:ci_build, pipeline: pipeline, status: 'pending', created_at: Date.yesterday) } let!(:second) { create(:ci_build, pipeline: pipeline, status: 'pending') } - subject { Ci::Build.first_pending } + subject { described_class.first_pending } - it { is_expected.to be_a(Ci::Build) } + it { is_expected.to be_a(described_class) } it('returns with the first pending build') { is_expected.to eq(first) } end @@ -945,7 +945,7 @@ describe Ci::Build, :models do end context 'when build is retried' do - let!(:new_build) { Ci::Build.retry(build, user) } + let!(:new_build) { described_class.retry(build, user) } it 'does not return any of them' do is_expected.not_to include(build, new_build) @@ -953,7 +953,7 @@ describe Ci::Build, :models do end context 'when other build is retried' do - let!(:retried_build) { Ci::Build.retry(other_build, user) } + let!(:retried_build) { described_class.retry(other_build, user) } before do retried_build.success @@ -1468,6 +1468,12 @@ describe Ci::Build, :models do it { is_expected.to include(predefined_trigger_variable) } end + context 'when pipeline has a variable' do + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline) } + + it { is_expected.to include(pipeline_variable.to_runner_variable) } + end + context 'when a job was triggered by a pipeline schedule' do let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) } diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb index 62e15093089..51123e73fe6 100644 --- a/spec/models/ci/group_spec.rb +++ b/spec/models/ci/group_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Group, models: true do +describe Ci::Group do subject do described_class.new('test', name: 'rspec', jobs: jobs) end diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb index 24b914face9..145189e7469 100644 --- a/spec/models/ci/group_variable_spec.rb +++ b/spec/models/ci/group_variable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::GroupVariable, models: true do +describe Ci::GroupVariable do subject { build(:ci_group_variable) } it { is_expected.to include_module(HasVariable) } diff --git a/spec/models/ci/legacy_stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb index d43c33d3807..0c33c1466b7 100644 --- a/spec/models/ci/legacy_stage_spec.rb +++ b/spec/models/ci/legacy_stage_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::LegacyStage, :models do +describe Ci::LegacyStage do let(:stage) { build(:ci_stage) } let(:pipeline) { stage.pipeline } let(:stage_name) { stage.name } diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index 6427deda31e..9a278212efc 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PipelineSchedule, models: true do +describe Ci::PipelineSchedule do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:owner) } @@ -46,7 +46,7 @@ describe Ci::PipelineSchedule, models: true do end it 'updates next_run_at automatically' do - expect(Ci::PipelineSchedule.last.next_run_at).to eq(expected_next_run_at) + expect(described_class.last.next_run_at).to eq(expected_next_run_at) end end @@ -61,7 +61,7 @@ describe Ci::PipelineSchedule, models: true do it 'updates next_run_at automatically' do pipeline_schedule.update!(cron: new_cron) - expect(Ci::PipelineSchedule.last.next_run_at).to eq(expected_next_run_at) + expect(described_class.last.next_run_at).to eq(expected_next_run_at) end end end diff --git a/spec/models/ci/pipeline_schedule_variable_spec.rb b/spec/models/ci/pipeline_schedule_variable_spec.rb index 0de76a57b7f..dc8427f28bc 100644 --- a/spec/models/ci/pipeline_schedule_variable_spec.rb +++ b/spec/models/ci/pipeline_schedule_variable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PipelineScheduleVariable, models: true do +describe Ci::PipelineScheduleVariable do subject { build(:ci_pipeline_schedule_variable) } it { is_expected.to include_module(HasVariable) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ba0696fa210..b0efa689a07 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Pipeline, models: true do +describe Ci::Pipeline do include EmailHelpers let(:user) { create(:user) } @@ -17,6 +17,7 @@ describe Ci::Pipeline, models: true do it { is_expected.to have_many(:statuses) } it { is_expected.to have_many(:trigger_requests) } + it { is_expected.to have_many(:variables) } it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:auto_canceled_pipelines) } it { is_expected.to have_many(:auto_canceled_jobs) } @@ -734,6 +735,8 @@ describe Ci::Pipeline, models: true do context 'on failure and build retry' do before do + stub_not_protect_default_branch + build.drop project.add_developer(user) @@ -999,6 +1002,8 @@ describe Ci::Pipeline, models: true do let(:latest_status) { pipeline.statuses.latest.pluck(:status) } before do + stub_not_protect_default_branch + project.add_developer(user) end diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb new file mode 100644 index 00000000000..2ce78e34b0c --- /dev/null +++ b/spec/models/ci/pipeline_variable_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +describe Ci::PipelineVariable, models: true do + subject { build(:ci_pipeline_variable) } + + it { is_expected.to include_module(HasVariable) } + it { is_expected.to validate_uniqueness_of(:key).scoped_to(:pipeline_id) } +end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 4b9cce28e0e..8d12a9c09ca 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Runner, models: true do +describe Ci::Runner do describe 'validation' do context 'when runner is not allowed to pick untagged jobs' do context 'when runner does not have tags' do @@ -50,7 +50,7 @@ describe Ci::Runner, models: true do end describe '.online' do - subject { Ci::Runner.online } + subject { described_class.online } before do @runner1 = FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.year.ago) @@ -352,13 +352,13 @@ describe Ci::Runner, models: true do end context 'does not give owned runner' do - subject { Ci::Runner.assignable_for(project) } + subject { described_class.assignable_for(project) } it { is_expected.to be_empty } end context 'does not give shared runner' do - subject { Ci::Runner.assignable_for(another_project) } + subject { described_class.assignable_for(another_project) } it { is_expected.to be_empty } end @@ -366,13 +366,13 @@ describe Ci::Runner, models: true do context 'with unlocked runner' do context 'does not give owned runner' do - subject { Ci::Runner.assignable_for(project) } + subject { described_class.assignable_for(project) } it { is_expected.to be_empty } end context 'does give a specific runner' do - subject { Ci::Runner.assignable_for(another_project) } + subject { described_class.assignable_for(another_project) } it { is_expected.to contain_exactly(runner) } end @@ -384,13 +384,13 @@ describe Ci::Runner, models: true do end context 'does not give owned runner' do - subject { Ci::Runner.assignable_for(project) } + subject { described_class.assignable_for(project) } it { is_expected.to be_empty } end context 'does not give a locked runner' do - subject { Ci::Runner.assignable_for(another_project) } + subject { described_class.assignable_for(another_project) } it { is_expected.to be_empty } end diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 92c15c13c18..de51f4879fd 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Trigger, models: true do +describe Ci::Trigger do let(:project) { create :empty_project } describe 'associations' do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 890ffaae494..e4ff551151e 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Variable, models: true do +describe Ci::Variable do subject { build(:ci_variable) } describe 'validations' do diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index ba9c3f66d21..07e10b44938 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CommitRange, models: true do +describe CommitRange do describe 'modules' do subject { described_class } diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 528b211c9d6..2285c338599 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Commit, models: true do +describe Commit do let(:project) { create(:project, :public, :repository) } let(:commit) { project.commit } diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 1e074c7ad26..6fb4794ea5f 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CommitStatus, :models do +describe CommitStatus do let(:project) { create(:project, :repository) } let(:pipeline) do diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb index da003dbf794..04f3cecae00 100644 --- a/spec/models/compare_spec.rb +++ b/spec/models/compare_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Compare, models: true do +describe Compare do include RepoHelpers let(:project) { create(:project, :public, :repository) } diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb index a6fccb668e3..5c0dfaeb4d3 100644 --- a/spec/models/concerns/case_sensitivity_spec.rb +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CaseSensitivity, models: true do +describe CaseSensitivity do describe '.iwhere' do let(:connection) { ActiveRecord::Base.connection } let(:model) { Class.new { include CaseSensitivity } } diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb index f3e148f95f0..2322eb206fb 100644 --- a/spec/models/concerns/discussion_on_diff_spec.rb +++ b/spec/models/concerns/discussion_on_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiscussionOnDiff, model: true do +describe DiscussionOnDiff do subject { create(:diff_note_on_merge_request).to_discussion } describe "#truncated_diff_lines" do diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index e2a29e0ae70..1ad811736af 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -174,25 +174,25 @@ describe Commit, 'Mentionable' do it "is false when message doesn't reference anything" do allow(commit.raw).to receive(:message).and_return "WIP: Do something" - expect(commit.matches_cross_reference_regex?).to be false + expect(commit.matches_cross_reference_regex?).to be_falsey end it 'is true if issue #number mentioned in title' do allow(commit.raw).to receive(:message).and_return "#1" - expect(commit.matches_cross_reference_regex?).to be true + expect(commit.matches_cross_reference_regex?).to be_truthy end it 'is true if references an MR' do allow(commit.raw).to receive(:message).and_return "See merge request !12" - expect(commit.matches_cross_reference_regex?).to be true + expect(commit.matches_cross_reference_regex?).to be_truthy end it 'is true if references a commit' do allow(commit.raw).to receive(:message).and_return "a1b2c3d4" - expect(commit.matches_cross_reference_regex?).to be true + expect(commit.matches_cross_reference_regex?).to be_truthy end it 'is true if issue referenced by url' do @@ -200,7 +200,7 @@ describe Commit, 'Mentionable' do allow(commit.raw).to receive(:message).and_return Gitlab::UrlBuilder.build(issue) - expect(commit.matches_cross_reference_regex?).to be true + expect(commit.matches_cross_reference_regex?).to be_truthy end context 'with external issue tracker' do @@ -209,7 +209,13 @@ describe Commit, 'Mentionable' do it 'is true if external issues referenced' do allow(commit.raw).to receive(:message).and_return 'JIRA-123' - expect(commit.matches_cross_reference_regex?).to be true + expect(commit.matches_cross_reference_regex?).to be_truthy + end + + it 'is true if internal issues referenced' do + allow(commit.raw).to receive(:message).and_return '#123' + + expect(commit.matches_cross_reference_regex?).to be_truthy end end end diff --git a/spec/models/concerns/noteable_spec.rb b/spec/models/concerns/noteable_spec.rb index bdae742ff1d..485a6e165a1 100644 --- a/spec/models/concerns/noteable_spec.rb +++ b/spec/models/concerns/noteable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Noteable, model: true do +describe Noteable do let!(:active_diff_note1) { create(:diff_note_on_merge_request) } let(:project) { active_diff_note1.project } subject { active_diff_note1.noteable } diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb index a9f4ef9ee5e..431f1482615 100644 --- a/spec/models/concerns/participable_spec.rb +++ b/spec/models/concerns/participable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Participable, models: true do +describe Participable do let(:model) do Class.new do include Participable diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb index 3934992c143..1616c2ea985 100644 --- a/spec/models/concerns/resolvable_discussion_spec.rb +++ b/spec/models/concerns/resolvable_discussion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Discussion, ResolvableDiscussion, models: true do +describe Discussion, ResolvableDiscussion do subject { described_class.new([first_note, second_note, third_note]) } let(:first_note) { create(:discussion_note_on_merge_request) } diff --git a/spec/models/concerns/resolvable_note_spec.rb b/spec/models/concerns/resolvable_note_spec.rb index 1503ccdff11..53eaa6f8461 100644 --- a/spec/models/concerns/resolvable_note_spec.rb +++ b/spec/models/concerns/resolvable_note_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Note, ResolvableNote, models: true do +describe Note, ResolvableNote do let(:project) { create(:project) } let(:merge_request) { create(:merge_request, source_project: project) } subject { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) } diff --git a/spec/models/concerns/uniquify_spec.rb b/spec/models/concerns/uniquify_spec.rb index 83187d732e4..914730718e7 100644 --- a/spec/models/concerns/uniquify_spec.rb +++ b/spec/models/concerns/uniquify_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Uniquify, models: true do +describe Uniquify do let(:uniquify) { described_class.new } describe "#string" do diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index 9053485939e..f2f1928926c 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#code', feature: true do +describe 'CycleAnalytics#code' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index fc7d18bd40e..985e1bf80be 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#issue', models: true do +describe 'CycleAnalytics#issue' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index 4f33f3c6d69..6fbb2a2d102 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#plan', feature: true do +describe 'CycleAnalytics#plan' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index 4744b9e05ea..f8681c0a2f9 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#production', feature: true do +describe 'CycleAnalytics#production' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index febb18c9884..0ac58695b35 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#review', feature: true do +describe 'CycleAnalytics#review' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index f78d7a23105..b66d5623910 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#staging', feature: true do +describe 'CycleAnalytics#staging' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index fd58bd1d6ad..690c09bc2dc 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'CycleAnalytics#test', feature: true do +describe 'CycleAnalytics#test' do extend CycleAnalyticsHelpers::TestGeneration let(:project) { create(:project, :repository) } diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 8ef8218cf74..2aece75b817 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeployKey, models: true do +describe DeployKey do include EmailHelpers describe "Associations" do diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index aacc178a19e..f10b65ba9d8 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeployKeysProject, models: true do +describe DeployKeysProject do describe "Associations" do it { is_expected.to belong_to(:deploy_key) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index bb84d3fc13d..6447095078b 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Deployment, models: true do +describe Deployment do subject { build(:deployment) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/diff_discussion_spec.rb b/spec/models/diff_discussion_spec.rb index 45b2f6e4beb..2704698f6c9 100644 --- a/spec/models/diff_discussion_spec.rb +++ b/spec/models/diff_discussion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffDiscussion, model: true do +describe DiffDiscussion do include RepoHelpers subject { described_class.new([diff_note]) } diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb index 297c2108dc2..4aa9ec789a3 100644 --- a/spec/models/diff_note_spec.rb +++ b/spec/models/diff_note_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffNote, models: true do +describe DiffNote do include RepoHelpers let(:merge_request) { create(:merge_request) } diff --git a/spec/models/diff_viewer/base_spec.rb b/spec/models/diff_viewer/base_spec.rb index 3755f4a56f3..b26de3f3b97 100644 --- a/spec/models/diff_viewer/base_spec.rb +++ b/spec/models/diff_viewer/base_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffViewer::Base, model: true do +describe DiffViewer::Base do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/models/diff_viewer/server_side_spec.rb b/spec/models/diff_viewer/server_side_spec.rb index 2d926e06936..92e613f92de 100644 --- a/spec/models/diff_viewer/server_side_spec.rb +++ b/spec/models/diff_viewer/server_side_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DiffViewer::ServerSide, model: true do +describe DiffViewer::ServerSide do let(:project) { create(:project, :repository) } let(:commit) { project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') } let(:diff_file) { commit.diffs.diff_file_with_new_path('files/ruby/popen.rb') } diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb index 0221e23ced8..a46f7ed6507 100644 --- a/spec/models/discussion_spec.rb +++ b/spec/models/discussion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Discussion, model: true do +describe Discussion do subject { described_class.new([first_note, second_note, third_note]) } let(:first_note) { create(:diff_note_on_merge_request) } diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index fe4de1b2afb..1d6fabe48b1 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Email, models: true do +describe Email do describe 'validations' do it_behaves_like 'an object with email-formated attributes', :email do subject { build(:email) } diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 0a2cd8c2957..ebf2c070116 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Environment, models: true do +describe Environment do set(:project) { create(:empty_project) } subject(:environment) { create(:environment, project: project) } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 10b9bf9f43a..4a4b84c9566 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Event, models: true do +describe Event do describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:target) } diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index cd50bda8996..c8748daf46b 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ExternalIssue, models: true do +describe ExternalIssue do let(:project) { double('project', id: 1, to_reference: 'namespace1/project1') } let(:issue) { described_class.new('EXT-1234', project) } diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 152e97e09bf..aedc74deb78 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GenericCommitStatus, models: true do +describe GenericCommitStatus do let(:project) { create(:empty_project) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:external_url) { 'http://example.gitlab.com/status' } diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb index a14efda3eda..5584a1a5a31 100644 --- a/spec/models/global_milestone_spec.rb +++ b/spec/models/global_milestone_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GlobalMilestone, models: true do +describe GlobalMilestone do let(:user) { create(:user) } let(:user2) { create(:user) } let(:group) { create(:group) } @@ -72,7 +72,7 @@ describe GlobalMilestone, models: true do project3 ] - @global_milestones = GlobalMilestone.build_collection(projects, {}) + @global_milestones = described_class.build_collection(projects, {}) end it 'has all project milestones' do @@ -106,7 +106,7 @@ describe GlobalMilestone, models: true do it 'returns the quantity of global milestones in each possible state' do expected_count = { opened: 1, closed: 2, all: 2 } - count = GlobalMilestone.states_count(Project.all) + count = described_class.states_count(Project.all) expect(count).to eq(expected_count) end @@ -120,7 +120,7 @@ describe GlobalMilestone, models: true do it 'returns 0 as the quantity of global milestones in each state' do expected_count = { opened: 0, closed: 0, all: 0 } - count = GlobalMilestone.states_count(Project.all) + count = described_class.states_count(Project.all) expect(count).to eq(expected_count) end @@ -141,7 +141,7 @@ describe GlobalMilestone, models: true do ] milestones_relation = Milestone.where(id: milestones.map(&:id)) - @global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones_relation) + @global_milestone = described_class.new(milestone1_project1.title, milestones_relation) end it 'has exactly one group milestone' do @@ -157,7 +157,7 @@ describe GlobalMilestone, models: true do let(:milestone) { create(:milestone, title: "git / test", project: project1) } it 'strips out slashes and spaces' do - global_milestone = GlobalMilestone.new(milestone.title, Milestone.where(id: milestone.id)) + global_milestone = described_class.new(milestone.title, Milestone.where(id: milestone.id)) expect(global_milestone.safe_title).to eq('git-test') end @@ -171,7 +171,7 @@ describe GlobalMilestone, models: true do create(:active_milestone, title: title), create(:closed_milestone, title: title) ] - global_milestone = GlobalMilestone.new(title, milestones) + global_milestone = described_class.new(title, milestones) expect(global_milestone.state).to eq('active') end @@ -184,7 +184,7 @@ describe GlobalMilestone, models: true do create(:closed_milestone, title: title), create(:closed_milestone, title: title) ] - global_milestone = GlobalMilestone.new(title, milestones) + global_milestone = described_class.new(title, milestones) expect(global_milestone.state).to eq('closed') end diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb new file mode 100644 index 00000000000..59c074199db --- /dev/null +++ b/spec/models/gpg_key_spec.rb @@ -0,0 +1,157 @@ +require 'rails_helper' + +describe GpgKey do + describe "associations" do + it { is_expected.to belong_to(:user) } + end + + describe "validation" do + it { is_expected.to validate_presence_of(:user) } + + it { is_expected.to validate_presence_of(:key) } + it { is_expected.to validate_uniqueness_of(:key) } + + it { is_expected.to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey\n-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey\n-----BEGIN PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK----------END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----BEGIN PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value("key\n-----END PGP PUBLIC KEY BLOCK-----").for(:key) } + it { is_expected.not_to allow_value('BEGIN PGP').for(:key) } + end + + context 'callbacks' do + describe 'extract_fingerprint' do + it 'extracts the fingerprint from the gpg key' do + gpg_key = described_class.new(key: GpgHelpers::User1.public_key) + gpg_key.valid? + expect(gpg_key.fingerprint).to eq GpgHelpers::User1.fingerprint + end + end + + describe 'extract_primary_keyid' do + it 'extracts the primary keyid from the gpg key' do + gpg_key = described_class.new(key: GpgHelpers::User1.public_key) + gpg_key.valid? + expect(gpg_key.primary_keyid).to eq GpgHelpers::User1.primary_keyid + end + end + end + + describe '#key=' do + it 'strips white spaces' do + key = <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mQENBFMOSOgBCADFCYxmnXFbrDhfvlf03Q/bQuT+nZu46BFGbo7XkUjDowFXJQhP + -----END PGP PUBLIC KEY BLOCK----- + KEY + + expect(described_class.new(key: " #{key} ").key).to eq(key) + end + + it 'does not strip when the key is nil' do + expect(described_class.new(key: nil).key).to be_nil + end + end + + describe '#user_infos' do + it 'returns the user infos from the gpg key' do + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key + expect(Gitlab::Gpg).to receive(:user_infos_from_key).with(gpg_key.key) + + gpg_key.user_infos + end + end + + describe '#verified_user_infos' do + it 'returns the user infos if it is verified' do + user = create :user, email: GpgHelpers::User1.emails.first + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key, user: user + + expect(gpg_key.verified_user_infos).to eq([{ + name: GpgHelpers::User1.names.first, + email: GpgHelpers::User1.emails.first + }]) + end + + it 'returns an empty array if the user info is not verified' do + user = create :user, email: 'unrelated@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User1.public_key, user: user + + expect(gpg_key.verified_user_infos).to eq([]) + end + end + + describe '#emails_with_verified_status' do + it 'email is verified if the user has the matching email' do + user = create :user, email: 'bette.cartwright@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.emails_with_verified_status).to eq( + 'bette.cartwright@example.com' => true, + 'bette.cartwright@example.net' => false + ) + end + end + + describe '#verified?' do + it 'returns true one of the email addresses in the key belongs to the user' do + user = create :user, email: 'bette.cartwright@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.verified?).to be_truthy + end + + it 'returns false if one of the email addresses in the key does not belong to the user' do + user = create :user, email: 'someone.else@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.verified?).to be_falsey + end + end + + describe 'notification' do + include EmailHelpers + + let(:user) { create(:user) } + + it 'sends a notification' do + perform_enqueued_jobs do + create(:gpg_key, user: user) + end + + should_email(user) + end + end + + describe '#revoke' do + it 'invalidates all associated gpg signatures and destroys the key' do + gpg_key = create :gpg_key + gpg_signature = create :gpg_signature, valid_signature: true, gpg_key: gpg_key + + unrelated_gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key + unrelated_gpg_signature = create :gpg_signature, valid_signature: true, gpg_key: unrelated_gpg_key + + gpg_key.revoke + + expect(gpg_signature.reload).to have_attributes( + valid_signature: false, + gpg_key: nil + ) + + expect(gpg_key.destroyed?).to be true + + # unrelated signature is left untouched + expect(unrelated_gpg_signature.reload).to have_attributes( + valid_signature: true, + gpg_key: unrelated_gpg_key + ) + + expect(unrelated_gpg_key.destroyed?).to be false + end + end +end diff --git a/spec/models/gpg_signature_spec.rb b/spec/models/gpg_signature_spec.rb new file mode 100644 index 00000000000..9a9b1900aa5 --- /dev/null +++ b/spec/models/gpg_signature_spec.rb @@ -0,0 +1,28 @@ +require 'rails_helper' + +RSpec.describe GpgSignature do + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to belong_to(:gpg_key) } + end + + describe 'validation' do + subject { described_class.new } + it { is_expected.to validate_presence_of(:commit_sha) } + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:gpg_key_primary_keyid) } + end + + describe '#commit' do + it 'fetches the commit through the project' do + commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' + project = create :project + commit = create :commit, project: project + gpg_signature = create :gpg_signature, commit_sha: commit_sha + + expect_any_instance_of(Project).to receive(:commit).with(commit_sha).and_return(commit) + + gpg_signature.commit + end + end +end diff --git a/spec/models/group_label_spec.rb b/spec/models/group_label_spec.rb index 555a876daeb..f7828059295 100644 --- a/spec/models/group_label_spec.rb +++ b/spec/models/group_label_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GroupLabel, models: true do +describe GroupLabel do describe 'relationships' do it { is_expected.to belong_to(:group) } end diff --git a/spec/models/group_milestone_spec.rb b/spec/models/group_milestone_spec.rb index 916afb7aaf5..ac76c927c39 100644 --- a/spec/models/group_milestone_spec.rb +++ b/spec/models/group_milestone_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GroupMilestone, models: true do +describe GroupMilestone do let(:group) { create(:group) } let(:project) { create(:empty_project, group: group) } let(:project_milestone) do @@ -9,7 +9,7 @@ describe GroupMilestone, models: true do describe '.build' do it 'returns milestone with group assigned' do - milestone = GroupMilestone.build( + milestone = described_class.build( group, [project], project_milestone.title @@ -25,7 +25,7 @@ describe GroupMilestone, models: true do end it 'returns array of milestones, each with group assigned' do - milestones = GroupMilestone.build_collection(group, [project], {}) + milestones = described_class.build_collection(group, [project], {}) expect(milestones).to all(have_attributes(group: group)) end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 770176451fe..112bd605a64 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Group, models: true do +describe Group do let!(:group) { create(:group, :access_requestable) } describe 'associations' do @@ -236,6 +236,7 @@ describe Group, models: true do describe '#has_owner?' do before do @members = setup_group_members(group) + create(:group_member, :invited, :owner, group: group) end it { expect(group.has_owner?(@members[:owner])).to be_truthy } @@ -244,11 +245,13 @@ describe Group, models: true do it { expect(group.has_owner?(@members[:reporter])).to be_falsey } it { expect(group.has_owner?(@members[:guest])).to be_falsey } it { expect(group.has_owner?(@members[:requester])).to be_falsey } + it { expect(group.has_owner?(nil)).to be_falsey } end describe '#has_master?' do before do @members = setup_group_members(group) + create(:group_member, :invited, :master, group: group) end it { expect(group.has_master?(@members[:owner])).to be_falsey } @@ -257,6 +260,7 @@ describe Group, models: true do it { expect(group.has_master?(@members[:reporter])).to be_falsey } it { expect(group.has_master?(@members[:guest])).to be_falsey } it { expect(group.has_master?(@members[:requester])).to be_falsey } + it { expect(group.has_master?(nil)).to be_falsey } end describe '#lfs_enabled?' do @@ -353,7 +357,7 @@ describe Group, models: true do subject { build(:group, :nested) } it { is_expected.to be_valid } - it { expect(subject.parent).to be_kind_of(Group) } + it { expect(subject.parent).to be_kind_of(described_class) } end describe '#members_with_parents', :nested_groups do diff --git a/spec/models/guest_spec.rb b/spec/models/guest_spec.rb index c60bd7af958..0e9b94aac97 100644 --- a/spec/models/guest_spec.rb +++ b/spec/models/guest_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Guest, lib: true do +describe Guest do let(:public_project) { build_stubbed(:empty_project, :public) } let(:private_project) { build_stubbed(:empty_project, :private) } let(:internal_project) { build_stubbed(:empty_project, :internal) } @@ -8,13 +8,13 @@ describe Guest, lib: true do describe '.can_pull?' do context 'when project is private' do it 'does not allow to pull the repo' do - expect(Guest.can?(:download_code, private_project)).to eq(false) + expect(described_class.can?(:download_code, private_project)).to eq(false) end end context 'when project is internal' do it 'does not allow to pull the repo' do - expect(Guest.can?(:download_code, internal_project)).to eq(false) + expect(described_class.can?(:download_code, internal_project)).to eq(false) end end @@ -23,7 +23,7 @@ describe Guest, lib: true do it 'does not allow to pull the repo' do public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED) - expect(Guest.can?(:download_code, public_project)).to eq(false) + expect(described_class.can?(:download_code, public_project)).to eq(false) end end @@ -31,13 +31,13 @@ describe Guest, lib: true do it 'does not allow to pull the repo' do public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::PRIVATE) - expect(Guest.can?(:download_code, public_project)).to eq(false) + expect(described_class.can?(:download_code, public_project)).to eq(false) end end context 'when repository is enabled' do it 'allows to pull the repo' do - expect(Guest.can?(:download_code, public_project)).to eq(true) + expect(described_class.can?(:download_code, public_project)).to eq(true) end end end diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb index 0af270014b5..5dd31b1b5de 100644 --- a/spec/models/hooks/project_hook_spec.rb +++ b/spec/models/hooks/project_hook_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectHook, models: true do +describe ProjectHook do describe 'associations' do it { is_expected.to belong_to :project } end @@ -13,7 +13,7 @@ describe ProjectHook, models: true do it 'returns hooks for push events only' do hook = create(:project_hook, push_events: true) create(:project_hook, push_events: false) - expect(ProjectHook.push_hooks).to eq([hook]) + expect(described_class.push_hooks).to eq([hook]) end end @@ -21,7 +21,7 @@ describe ProjectHook, models: true do it 'returns hooks for tag push events only' do hook = create(:project_hook, tag_push_events: true) create(:project_hook, tag_push_events: false) - expect(ProjectHook.tag_push_hooks).to eq([hook]) + expect(described_class.tag_push_hooks).to eq([hook]) end end end diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index 8e871a41a8c..e32eaafc13f 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ServiceHook, models: true do +describe ServiceHook do describe 'associations' do it { is_expected.to belong_to :service } end diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index 559778257fa..eadc232a989 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe SystemHook, models: true do +describe SystemHook do context 'default attributes' do let(:system_hook) { build(:system_hook) } @@ -122,7 +122,7 @@ describe SystemHook, models: true do it 'returns hooks for repository update events only' do hook = create(:system_hook, repository_update_events: true) create(:system_hook, repository_update_events: false) - expect(SystemHook.repository_update_hooks).to eq([hook]) + expect(described_class.repository_update_hooks).to eq([hook]) end end diff --git a/spec/models/hooks/web_hook_log_spec.rb b/spec/models/hooks/web_hook_log_spec.rb index c649cf3b589..19bc88b1333 100644 --- a/spec/models/hooks/web_hook_log_spec.rb +++ b/spec/models/hooks/web_hook_log_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe WebHookLog, models: true do +describe WebHookLog do it { is_expected.to belong_to(:web_hook) } it { is_expected.to serialize(:request_headers).as(Hash) } diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 53157c24477..388120160ab 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WebHook, models: true do +describe WebHook do let(:hook) { build(:project_hook) } describe 'associations' do diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index b3aed66a5b6..4ca6556d0f4 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe Identity, models: true do +RSpec.describe Identity do describe 'relations' do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb index 08712f2a768..6ceff7d24d4 100644 --- a/spec/models/issue/metrics_spec.rb +++ b/spec/models/issue/metrics_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issue::Metrics, models: true do +describe Issue::Metrics do let(:project) { create(:empty_project) } subject { create(:issue, project: project) } diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index bf97c6ececd..d72790eefe5 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issue, models: true do +describe Issue do describe "Associations" do it { is_expected.to belong_to(:milestone) } it { is_expected.to have_many(:assignees) } diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index f27920f9feb..d41717d0223 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Key, models: true do +describe Key do include EmailHelpers describe "Associations" do diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index c18ed8574b1..e2b49bc2de7 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe LabelLink, models: true do +describe LabelLink do it { expect(build(:label_link)).to be_valid } it { is_expected.to belong_to(:label) } diff --git a/spec/models/label_priority_spec.rb b/spec/models/label_priority_spec.rb index d18c2f7949a..9dcb0f06b20 100644 --- a/spec/models/label_priority_spec.rb +++ b/spec/models/label_priority_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe LabelPriority, models: true do +describe LabelPriority do describe 'relationships' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:label) } diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 31190fe5685..8914845ea82 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Label, models: true do +describe Label do describe 'modules' do it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Subscribable) } diff --git a/spec/models/legacy_diff_discussion_spec.rb b/spec/models/legacy_diff_discussion_spec.rb index 6eb4a2aaf39..dae97b69c84 100644 --- a/spec/models/legacy_diff_discussion_spec.rb +++ b/spec/models/legacy_diff_discussion_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe LegacyDiffDiscussion, models: true do +describe LegacyDiffDiscussion do subject { create(:legacy_diff_note_on_merge_request).to_discussion } describe '#reply_attributes' do diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb index 7bc278e350f..8c74f1f9e86 100644 --- a/spec/models/lfs_objects_project_spec.rb +++ b/spec/models/lfs_objects_project_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe LfsObjectsProject, models: true do +describe LfsObjectsProject do subject { create(:lfs_objects_project, project: project) } let(:project) { create(:empty_project) } diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb index db2c2619968..a6cc01bea5f 100644 --- a/spec/models/list_spec.rb +++ b/spec/models/list_spec.rb @@ -13,12 +13,6 @@ describe List do it { is_expected.to validate_presence_of(:position) } it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than_or_equal_to(0) } - it 'validates uniqueness of label scoped to board_id' do - create(:list) - - expect(subject).to validate_uniqueness_of(:label_id).scoped_to(:board_id) - end - context 'when list_type is set to closed' do subject { described_class.new(list_type: :closed) } diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 494a88368ba..8bfd70b8d46 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Member, models: true do +describe Member do describe "Associations" do it { is_expected.to belong_to(:user) } end describe "Validation" do - subject { Member.new(access_level: Member::GUEST) } + subject { described_class.new(access_level: Member::GUEST) } it { is_expected.to validate_presence_of(:user) } it { is_expected.to validate_presence_of(:source) } diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index 37014268a70..5a3b5b1f517 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GroupMember, models: true do +describe GroupMember do describe '.access_level_roles' do it 'returns Gitlab::Access.options_with_owner' do expect(described_class.access_level_roles).to eq(Gitlab::Access.options_with_owner) diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index cf9c701e8c5..025fb2bf441 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectMember, models: true do +describe ProjectMember do describe 'associations' do it { is_expected.to belong_to(:project).with_foreign_key(:source_id) } end @@ -139,7 +139,7 @@ describe ProjectMember, models: true do @project_1.team << [@user_1, :developer] @project_2.team << [@user_2, :reporter] - ProjectMember.truncate_teams([@project_1.id, @project_2.id]) + described_class.truncate_teams([@project_1.id, @project_2.id]) end it { expect(@project_1.users).to be_empty } diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb index 9afed311e27..9353d5c3c8a 100644 --- a/spec/models/merge_request/metrics_spec.rb +++ b/spec/models/merge_request/metrics_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequest::Metrics, models: true do +describe MergeRequest::Metrics do subject { create(:merge_request) } describe "when recording the default set of metrics on merge request save" do diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb index dbfd1526518..9d4a0ecf8c0 100644 --- a/spec/models/merge_request_diff_commit_spec.rb +++ b/spec/models/merge_request_diff_commit_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe MergeRequestDiffCommit, type: :model do +describe MergeRequestDiffCommit do let(:merge_request) { create(:merge_request) } subject { merge_request.commits.first } diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb index 7276f5b5061..faa47660a74 100644 --- a/spec/models/merge_request_diff_file_spec.rb +++ b/spec/models/merge_request_diff_file_spec.rb @@ -1,8 +1,33 @@ require 'rails_helper' -describe MergeRequestDiffFile, type: :model do +describe MergeRequestDiffFile do + describe '#diff' do + let(:unpacked) { 'unpacked' } + let(:packed) { [unpacked].pack('m0') } + + before do + subject.diff = packed + end + + context 'when the diff is marked as binary' do + before do + subject.binary = true + end + + it 'unpacks from base 64' do + expect(subject.diff).to eq(unpacked) + end + end + + context 'when the diff is not marked as binary' do + it 'returns the raw diff' do + expect(subject.diff).to eq(packed) + end + end + end + describe '#utf8_diff' do - it 'does not raise error when a hash value is in binary' do + it 'does not raise error when the diff is binary' do subject.diff = "\x05\x00\x68\x65\x6c\x6c\x6f" expect { subject.utf8_diff }.not_to raise_error diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index edc2f4bb9f0..0cfaa17676e 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequestDiff, models: true do +describe MergeRequestDiff do describe 'create new record' do subject { create(:merge_request).merge_request_diff } @@ -98,13 +98,22 @@ describe MergeRequestDiff, models: true do end it 'saves empty state' do - allow_any_instance_of(MergeRequestDiff).to receive_message_chain(:compare, :commits) + allow_any_instance_of(described_class).to receive_message_chain(:compare, :commits) .and_return([]) mr_diff = create(:merge_request).merge_request_diff expect(mr_diff.empty?).to be_truthy end + + it 'saves binary diffs correctly' do + path = 'files/images/icn-time-tracking.pdf' + mr_diff = create(:merge_request, source_branch: 'add-pdf-text-binary', target_branch: 'master').merge_request_diff + diff_file = mr_diff.merge_request_diff_files.find_by(new_path: path) + + expect(diff_file).to be_binary + expect(diff_file.diff).to eq(mr_diff.compare.diffs(paths: [path]).to_a.first.diff) + end end describe '#commit_shas' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1eadc28869f..b2dd02553c1 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequest, models: true do +describe MergeRequest do include RepoHelpers subject { create(:merge_request) } @@ -155,13 +155,53 @@ describe MergeRequest, models: true do expect { subject.cache_merge_request_closes_issues!(subject.author) }.to change(subject.merge_requests_closing_issues, :count).by(1) end - it 'does not cache issues from external trackers' do - subject.project.update_attribute(:has_external_issue_tracker, true) - issue = ExternalIssue.new('JIRA-123', subject.project) - commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") - allow(subject).to receive(:commits).and_return([commit]) + context 'when both internal and external issue trackers are enabled' do + before do + subject.project.has_external_issue_tracker = true + subject.project.save! + end + + it 'does not cache issues from external trackers' do + issue = ExternalIssue.new('JIRA-123', subject.project) + commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") + allow(subject).to receive(:commits).and_return([commit]) - expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) + expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) + end + + it 'caches an internal issue' do + issue = create(:issue, project: subject.project) + commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") + allow(subject).to receive(:commits).and_return([commit]) + + expect { subject.cache_merge_request_closes_issues!(subject.author) } + .to change(subject.merge_requests_closing_issues, :count).by(1) + end + end + + context 'when only external issue tracker enabled' do + before do + subject.project.has_external_issue_tracker = true + subject.project.issues_enabled = false + subject.project.save! + end + + it 'does not cache issues from external trackers' do + issue = ExternalIssue.new('JIRA-123', subject.project) + commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") + allow(subject).to receive(:commits).and_return([commit]) + + expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) + end + + it 'does not cache an internal issue' do + issue = create(:issue, project: subject.project) + commit = double('commit1', safe_message: "Fixes #{issue.to_reference}") + allow(subject).to receive(:commits).and_return([commit]) + + expect { subject.cache_merge_request_closes_issues!(subject.author) } + .not_to change(subject.merge_requests_closing_issues, :count) + end end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 2649d04bee3..aa376e242e8 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestone, models: true do +describe Milestone do describe "Validation" do before do allow(subject).to receive(:set_iid).and_return(false) @@ -37,13 +37,13 @@ describe Milestone, models: true do describe "unique milestone title" do context "per project" do it "does not accept the same title in a project twice" do - new_milestone = Milestone.new(project: milestone.project, title: milestone.title) + new_milestone = described_class.new(project: milestone.project, title: milestone.title) expect(new_milestone).not_to be_valid end it "accepts the same title in another project" do project = create(:empty_project) - new_milestone = Milestone.new(project: project, title: milestone.title) + new_milestone = described_class.new(project: project, title: milestone.title) expect(new_milestone).to be_valid end @@ -58,7 +58,7 @@ describe Milestone, models: true do end it "does not accept the same title in a group twice" do - new_milestone = Milestone.new(group: group, title: milestone.title) + new_milestone = described_class.new(group: group, title: milestone.title) expect(new_milestone).not_to be_valid end @@ -66,7 +66,7 @@ describe Milestone, models: true do it "does not accept the same title of a child project milestone" do create(:milestone, project: group.projects.first) - new_milestone = Milestone.new(group: group, title: milestone.title) + new_milestone = described_class.new(group: group, title: milestone.title) expect(new_milestone).not_to be_valid end @@ -214,7 +214,7 @@ describe Milestone, models: true do # The call to `#try` is because this returns a relation with a Postgres DB, # and an array of IDs with a MySQL DB. - let(:milestone_ids) { Milestone.upcoming_ids_by_projects(projects).map { |id| id.try(:id) || id } } + let(:milestone_ids) { described_class.upcoming_ids_by_projects(projects).map { |id| id.try(:id) || id } } it 'returns the next upcoming open milestone ID for each project' do expect(milestone_ids).to contain_exactly(current_milestone_project_1.id, current_milestone_project_2.id) diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index a4090b37f65..f12fe226e6b 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Namespace, models: true do +describe Namespace do let!(:namespace) { create(:namespace) } describe 'associations' do @@ -133,7 +133,7 @@ describe Namespace, models: true do it "sums all project storage counters in the namespace" do project1 project2 - statistics = Namespace.with_statistics.find(namespace.id) + statistics = described_class.with_statistics.find(namespace.id) expect(statistics.storage_size).to eq 666 expect(statistics.repository_size).to eq 111 @@ -142,7 +142,7 @@ describe Namespace, models: true do end it "correctly handles namespaces without projects" do - statistics = Namespace.with_statistics.find(namespace.id) + statistics = described_class.with_statistics.find(namespace.id) expect(statistics.storage_size).to eq 0 expect(statistics.repository_size).to eq 0 @@ -151,7 +151,7 @@ describe Namespace, models: true do end end - describe '#move_dir', repository: true do + describe '#move_dir' do before do @namespace = create :namespace @project = create(:project_empty_repo, namespace: @namespace) @@ -230,7 +230,7 @@ describe Namespace, models: true do end end - describe '#rm_dir', 'callback', repository: true do + describe '#rm_dir', 'callback' do let!(:project) { create(:project_empty_repo, namespace: namespace) } let(:repository_storage_path) { Gitlab.config.repositories.storages.default['path'] } let(:path_in_dir) { File.join(repository_storage_path, namespace.full_path) } @@ -286,9 +286,9 @@ describe Namespace, models: true do @namespace = create(:namespace, name: 'WoW', path: 'woW') end - it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) } - it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) } - it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) } + it { expect(described_class.find_by_path_or_name('wow')).to eq(@namespace) } + it { expect(described_class.find_by_path_or_name('WOW')).to eq(@namespace) } + it { expect(described_class.find_by_path_or_name('unknown')).to eq(nil) } end describe ".clean_path" do @@ -296,8 +296,8 @@ describe Namespace, models: true do let!(:namespace) { create(:namespace, path: "JohnGitLab-etc1") } it "cleans the path and makes sure it's available" do - expect(Namespace.clean_path("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2") - expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name") + expect(described_class.clean_path("-john+gitlab-ETC%.git@gmail.com")).to eq("johngitlab-ETC2") + expect(described_class.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name") end end diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb index 0fe8a591a45..c364dd6643b 100644 --- a/spec/models/network/graph_spec.rb +++ b/spec/models/network/graph_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Network::Graph, models: true do +describe Network::Graph do let(:project) { create(:project, :repository) } let!(:note_on_commit) { create(:note_on_commit, project: project) } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index e2b80cb6e61..cbe6d42ef53 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Note, models: true do +describe Note do include RepoHelpers describe 'associations' do @@ -525,7 +525,7 @@ describe Note, models: true do it "has a discussion id" do # The discussion_id is set in `after_initialize`, so `reload` won't work - reloaded_note = Note.find(note.id) + reloaded_note = described_class.find(note.id) expect(reloaded_note.discussion_id).not_to be_nil expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/) diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb index cc235ad467e..07e296424ca 100644 --- a/spec/models/notification_setting_spec.rb +++ b/spec/models/notification_setting_spec.rb @@ -1,21 +1,28 @@ require 'rails_helper' -RSpec.describe NotificationSetting, type: :model do +RSpec.describe NotificationSetting do describe "Associations" do it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:source) } end describe "Validation" do - subject { NotificationSetting.new(source_id: 1, source_type: 'Project') } + subject { described_class.new(source_id: 1, source_type: 'Project') } it { is_expected.to validate_presence_of(:user) } it { is_expected.to validate_presence_of(:level) } - it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:source_id, :source_type]).with_message(/already exists in source/) } + + describe 'user_id' do + before do + subject.user = create(:user) + end + + it { is_expected.to validate_uniqueness_of(:user_id).scoped_to([:source_type, :source_id]).with_message(/already exists in source/) } + end context "events" do let(:user) { create(:user) } - let(:notification_setting) { NotificationSetting.new(source_id: 1, source_type: 'Project', user_id: user.id) } + let(:notification_setting) { described_class.new(source_id: 1, source_type: 'Project', user_id: user.id) } before do notification_setting.level = "custom" diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index f9d060d4e0e..7d835511dfb 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PagesDomain, models: true do +describe PagesDomain do describe 'associations' do it { is_expected.to belong_to(:project) } end @@ -11,7 +11,7 @@ describe PagesDomain, models: true do context 'is unique' do let(:domain) { 'my.domain.com' } - it { is_expected.to validate_uniqueness_of(:domain) } + it { is_expected.to validate_uniqueness_of(:domain).case_insensitive } end { diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb index fa781195608..b2f2a3ce914 100644 --- a/spec/models/personal_access_token_spec.rb +++ b/spec/models/personal_access_token_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PersonalAccessToken, models: true do +describe PersonalAccessToken do describe '.build' do let(:personal_access_token) { build(:personal_access_token) } let(:invalid_personal_access_token) { build(:personal_access_token, :invalid) } diff --git a/spec/models/project_label_spec.rb b/spec/models/project_label_spec.rb index 9cdbfa44e5b..add7e85f388 100644 --- a/spec/models/project_label_spec.rb +++ b/spec/models/project_label_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectLabel, models: true do +describe ProjectLabel do describe 'relationships' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index 95c35162d96..4684c970885 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AsanaService, models: true do +describe AsanaService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -35,7 +35,7 @@ describe AsanaService, models: true do end before do - @asana = AsanaService.new + @asana = described_class.new allow(@asana).to receive_messages( project: project, project_id: project.id, diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb index 96f00af898e..5cb6d63659e 100644 --- a/spec/models/project_services/assembla_service_spec.rb +++ b/spec/models/project_services/assembla_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AssemblaService, models: true do +describe AssemblaService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -11,7 +11,7 @@ describe AssemblaService, models: true do let(:project) { create(:project, :repository) } before do - @assembla_service = AssemblaService.new + @assembla_service = described_class.new allow(@assembla_service).to receive_messages( project_id: project.id, project: project, diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 99190d763f2..82f02126de1 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BambooService, :use_clean_rails_memory_store_caching, models: true do +describe BambooService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers let(:bamboo_url) { 'http://gitlab.com/bamboo' } diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index 5f17bbde390..43f7bcb1a19 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BugzillaService, models: true do +describe BugzillaService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index b4ee6691e67..3f7eb33e08a 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BuildkiteService, :use_clean_rails_memory_store_caching, models: true do +describe BuildkiteService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers let(:project) { create(:empty_project) } diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb index 56ff3596190..ed8347edffd 100644 --- a/spec/models/project_services/campfire_service_spec.rb +++ b/spec/models/project_services/campfire_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CampfireService, models: true do +describe CampfireService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -29,7 +29,7 @@ describe CampfireService, models: true do let(:project) { create(:project, :repository) } before do - @campfire_service = CampfireService.new + @campfire_service = described_class.new allow(@campfire_service).to receive_messages( project_id: project.id, project: project, diff --git a/spec/models/project_services/chat_message/issue_message_spec.rb b/spec/models/project_services/chat_message/issue_message_spec.rb index c159ab00ab1..4bb1db684e6 100644 --- a/spec/models/project_services/chat_message/issue_message_spec.rb +++ b/spec/models/project_services/chat_message/issue_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatMessage::IssueMessage, models: true do +describe ChatMessage::IssueMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb index 61f17031172..b600a36f578 100644 --- a/spec/models/project_services/chat_message/merge_message_spec.rb +++ b/spec/models/project_services/chat_message/merge_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatMessage::MergeMessage, models: true do +describe ChatMessage::MergeMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/note_message_spec.rb b/spec/models/project_services/chat_message/note_message_spec.rb index 7996536218a..a09c2f9935c 100644 --- a/spec/models/project_services/chat_message/note_message_spec.rb +++ b/spec/models/project_services/chat_message/note_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatMessage::NoteMessage, models: true do +describe ChatMessage::NoteMessage do subject { described_class.new(args) } let(:color) { '#345' } diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb index c794f659c41..19c2862264f 100644 --- a/spec/models/project_services/chat_message/push_message_spec.rb +++ b/spec/models/project_services/chat_message/push_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatMessage::PushMessage, models: true do +describe ChatMessage::PushMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_message/wiki_page_message_spec.rb b/spec/models/project_services/chat_message/wiki_page_message_spec.rb index 17355c1e6f1..c4adee4f489 100644 --- a/spec/models/project_services/chat_message/wiki_page_message_spec.rb +++ b/spec/models/project_services/chat_message/wiki_page_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatMessage::WikiPageMessage, models: true do +describe ChatMessage::WikiPageMessage do subject { described_class.new(args) } let(:args) do diff --git a/spec/models/project_services/chat_notification_service_spec.rb b/spec/models/project_services/chat_notification_service_spec.rb index 8fbe42248ae..413ceed73bf 100644 --- a/spec/models/project_services/chat_notification_service_spec.rb +++ b/spec/models/project_services/chat_notification_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNotificationService, models: true do +describe ChatNotificationService do describe 'Associations' do before do allow(subject).to receive(:activated?).and_return(true) diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index 9e574762232..7e1b1a4f2af 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CustomIssueTrackerService, models: true do +describe CustomIssueTrackerService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index c9ac256ff38..5b0f24ce306 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DroneCiService, :use_clean_rails_memory_store_caching, models: true do +describe DroneCiService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers describe 'associations' do diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb index ef10df9e092..22cd9d5e31e 100644 --- a/spec/models/project_services/external_wiki_service_spec.rb +++ b/spec/models/project_services/external_wiki_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ExternalWikiService, models: true do +describe ExternalWikiService do include ExternalWikiHelper describe "Associations" do it { is_expected.to belong_to :project } diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index 56ace04dd58..5e8e880985e 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe FlowdockService, models: true do +describe FlowdockService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -29,7 +29,7 @@ describe FlowdockService, models: true do let(:project) { create(:project, :repository) } before do - @flowdock_service = FlowdockService.new + @flowdock_service = described_class.new allow(@flowdock_service).to receive_messages( project_id: project.id, project: project, diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index 65c9e714bd1..4c61bc0af95 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GemnasiumService, models: true do +describe GemnasiumService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -31,7 +31,7 @@ describe GemnasiumService, models: true do let(:project) { create(:project, :repository) } before do - @gemnasium_service = GemnasiumService.new + @gemnasium_service = described_class.new allow(@gemnasium_service).to receive_messages( project_id: project.id, project: project, diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index d45e0a441d4..d19dab8fd39 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GitlabIssueTrackerService, models: true do +describe GitlabIssueTrackerService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -27,7 +27,7 @@ describe GitlabIssueTrackerService, models: true do context 'with absolute urls' do before do - allow(GitlabIssueTrackerService).to receive(:default_url_options).and_return(script_name: "/gitlab/root") + allow(described_class).to receive(:default_url_options).and_return(script_name: "/gitlab/root") end it 'gives the correct path' do @@ -39,7 +39,7 @@ describe GitlabIssueTrackerService, models: true do context 'with relative urls' do before do - allow(GitlabIssueTrackerService).to receive(:default_url_options).and_return(script_name: "/gitlab/root") + allow(described_class).to receive(:default_url_options).and_return(script_name: "/gitlab/root") end it 'gives the correct path' do diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index c7c8e9651ab..7614bb897e8 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe HipchatService, models: true do +describe HipchatService do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -25,7 +25,7 @@ describe HipchatService, models: true do end describe "Execute" do - let(:hipchat) { HipchatService.new } + let(:hipchat) { described_class.new } let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' } diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index a5c4938b54e..cb9ca76fc3f 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'socket' require 'json' -describe IrkerService, models: true do +describe IrkerService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -27,7 +27,7 @@ describe IrkerService, models: true do end describe 'Execute' do - let(:irker) { IrkerService.new } + let(:irker) { described_class.new } let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:sample_data) do diff --git a/spec/models/project_services/issue_tracker_service_spec.rb b/spec/models/project_services/issue_tracker_service_spec.rb index 869b25b933b..e6a1752576b 100644 --- a/spec/models/project_services/issue_tracker_service_spec.rb +++ b/spec/models/project_services/issue_tracker_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe IssueTrackerService, models: true do +describe IssueTrackerService do describe 'Validations' do let(:project) { create :project } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 105afed1337..8f34b44930e 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe JiraService, models: true do +describe JiraService do include Gitlab::Routing describe "Associations" do @@ -15,7 +15,6 @@ describe JiraService, models: true do end it { is_expected.to validate_presence_of(:url) } - it { is_expected.to validate_presence_of(:project_key) } it_behaves_like 'issue tracker service URL attribute', :url end @@ -34,7 +33,6 @@ describe JiraService, models: true do active: true, username: 'username', password: 'test', - project_key: 'TEST', jira_issue_transition_id: 24, url: 'http://jira.test.com' ) @@ -80,7 +78,7 @@ describe JiraService, models: true do let(:merge_request) { create(:merge_request) } before do - @jira_service = JiraService.new + @jira_service = described_class.new allow(@jira_service).to receive_messages( project_id: project.id, project: project, @@ -88,7 +86,6 @@ describe JiraService, models: true do url: 'http://jira.example.com', username: 'gitlab_jira_username', password: 'gitlab_jira_password', - project_key: 'GitLabProject', jira_issue_transition_id: "custom-id" ) @@ -170,7 +167,7 @@ describe JiraService, models: true do stub_config_setting(relative_url_root: '/gitlab') stub_config_setting(url: Settings.send(:build_gitlab_url)) - allow(JiraService).to receive(:default_url_options) do + allow(described_class).to receive(:default_url_options) do { script_name: '/gitlab' } end @@ -196,26 +193,42 @@ describe JiraService, models: true do project: create(:project), url: 'http://jira.example.com', username: 'jira_username', - password: 'jira_password', - project_key: 'GitLabProject' + password: 'jira_password' ) end - def test_settings(api_url) - project_url = "http://#{api_url}/rest/api/2/project/GitLabProject" + def test_settings(api_url = nil) + api_url ||= 'jira.example.com' + test_url = "http://#{api_url}/rest/api/2/serverInfo" - WebMock.stub_request(:get, project_url).with(basic_auth: %w(jira_username jira_password)) + WebMock.stub_request(:get, test_url).with(basic_auth: %w(jira_username jira_password)).to_return(body: { url: 'http://url' }.to_json ) - jira_service.test_settings + jira_service.test(nil) end - it 'tries to get JIRA project with URL when API URL not set' do - test_settings('jira.example.com') + context 'when the test succeeds' do + it 'tries to get JIRA project with URL when API URL not set' do + test_settings('jira.example.com') + end + + it 'returns correct result' do + expect(test_settings).to eq( { success: true, result: { 'url' => 'http://url' } }) + end + + it 'tries to get JIRA project with API URL if set' do + jira_service.update(api_url: 'http://jira.api.com') + test_settings('jira.api.com') + end end - it 'tries to get JIRA project with API URL if set' do - jira_service.update(api_url: 'http://jira.api.com') - test_settings('jira.api.com') + context 'when the test fails' do + it 'returns result with the error' do + test_url = 'http://jira.example.com/rest/api/2/serverInfo' + WebMock.stub_request(:get, test_url).with(basic_auth: %w(jira_username jira_password)) + .to_raise(JIRA::HTTPError.new(double(message: 'Some specific failure.'))) + + expect(jira_service.test(nil)).to eq( { success: false, result: 'Some specific failure.' }) + end end end @@ -224,7 +237,7 @@ describe JiraService, models: true do context "when a password was previously set" do before do - @jira_service = JiraService.create!( + @jira_service = described_class.create!( project: project, properties: { url: 'http://jira.example.com/web', @@ -305,7 +318,7 @@ describe JiraService, models: true do context 'when no password was previously set' do before do - @jira_service = JiraService.create( + @jira_service = described_class.create( project: project, properties: { url: 'http://jira.example.com/rest/api/2', diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index b66bb5321ab..55b96a0c12e 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe KubernetesService, :use_clean_rails_memory_store_caching, models: true do +describe KubernetesService, :use_clean_rails_memory_store_caching do include KubernetesHelpers include ReactiveCachingHelpers diff --git a/spec/models/project_services/mattermost_service_spec.rb b/spec/models/project_services/mattermost_service_spec.rb index 490d6aedffc..10c62ca55a7 100644 --- a/spec/models/project_services/mattermost_service_spec.rb +++ b/spec/models/project_services/mattermost_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe MattermostService, models: true do +describe MattermostService do it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index fa38d23e82f..4c21c8b88bd 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MattermostSlashCommandsService, :models do +describe MattermostSlashCommandsService do it_behaves_like "chat slash commands service" context 'Mattermost API' do diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb index fb95c4cda35..f89be20ad78 100644 --- a/spec/models/project_services/microsoft_teams_service_spec.rb +++ b/spec/models/project_services/microsoft_teams_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MicrosoftTeamsService, models: true do +describe MicrosoftTeamsService do let(:chat_service) { described_class.new } let(:webhook_url) { 'https://example.gitlab.com/' } diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb index f4c1a9c94b6..f7d2372eca2 100644 --- a/spec/models/project_services/pivotaltracker_service_spec.rb +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PivotaltrackerService, models: true do +describe PivotaltrackerService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -26,7 +26,7 @@ describe PivotaltrackerService, models: true do describe 'Execute' do let(:service) do - PivotaltrackerService.new.tap do |service| + described_class.new.tap do |service| service.token = 'secret_api_token' end end diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index 3fb134ec3b7..bf39e8d7a39 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PrometheusService, :use_clean_rails_memory_store_caching, models: true do +describe PrometheusService, :use_clean_rails_memory_store_caching do include PrometheusHelpers include ReactiveCachingHelpers diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index 9171d9604ee..54b8c658ff6 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PushoverService, models: true do +describe PushoverService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } @@ -29,7 +29,7 @@ describe PushoverService, models: true do end describe 'Execute' do - let(:pushover) { PushoverService.new } + let(:pushover) { described_class.new } let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:sample_data) do diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 441b3f896ca..2ac14eab5e1 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe RedmineService, models: true do +describe RedmineService do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 9a3ecc66d83..13cf4d1915e 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe SlackService, models: true do +describe SlackService do it_behaves_like "slack or mattermost notifications" end diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb index 5766aa340e2..aea674c4f6b 100644 --- a/spec/models/project_services/slack_slash_commands_service_spec.rb +++ b/spec/models/project_services/slack_slash_commands_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackSlashCommandsService, :models do +describe SlackSlashCommandsService do it_behaves_like "chat slash commands service" describe '#trigger' do diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 3f3a74d0f96..47fd0c79e0b 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe TeamcityService, :use_clean_rails_memory_store_caching, models: true do +describe TeamcityService, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers let(:teamcity_url) { 'http://gitlab.com/teamcity' } diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index 5fe4885eeb4..1b439bcfad1 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectSnippet, models: true do +describe ProjectSnippet do describe "Associations" do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index fdcb011d685..473b7a88d61 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Project, models: true do +describe Project do describe 'associations' do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:namespace) } @@ -77,7 +77,7 @@ describe Project, models: true do context 'after initialized' do it "has a project_feature" do - expect(Project.new.project_feature).to be_present + expect(described_class.new.project_feature).to be_present end end @@ -438,7 +438,7 @@ describe Project, models: true do end it 'returns valid url to repo' do - project = Project.new(path: 'somewhere') + project = described_class.new(path: 'somewhere') expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git') end @@ -533,15 +533,48 @@ describe Project, models: true do end context 'with external issues tracker' do + let!(:internal_issue) { create(:issue, project: project) } before do - allow(project).to receive(:default_issues_tracker?).and_return(false) + allow(project).to receive(:external_issue_tracker).and_return(true) end - it 'returns an ExternalIssue' do - issue = project.get_issue('FOO-1234', user) - expect(issue).to be_kind_of(ExternalIssue) - expect(issue.iid).to eq 'FOO-1234' - expect(issue.project).to eq project + context 'when internal issues are enabled' do + it 'returns interlan issue' do + issue = project.get_issue(internal_issue.iid, user) + + expect(issue).to be_kind_of(Issue) + expect(issue.iid).to eq(internal_issue.iid) + expect(issue.project).to eq(project) + end + + it 'returns an ExternalIssue when internal issue does not exists' do + issue = project.get_issue('FOO-1234', user) + + expect(issue).to be_kind_of(ExternalIssue) + expect(issue.iid).to eq('FOO-1234') + expect(issue.project).to eq(project) + end + end + + context 'when internal issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + + it 'returns always an External issues' do + issue = project.get_issue(internal_issue.iid, user) + expect(issue).to be_kind_of(ExternalIssue) + expect(issue.iid).to eq(internal_issue.iid.to_s) + expect(issue.project).to eq(project) + end + + it 'returns an ExternalIssue when internal issue does not exists' do + issue = project.get_issue('FOO-1234', user) + expect(issue).to be_kind_of(ExternalIssue) + expect(issue.iid).to eq('FOO-1234') + expect(issue.project).to eq(project) + end end end end @@ -884,7 +917,7 @@ describe Project, models: true do end describe '.with_shared_runners' do - subject { Project.with_shared_runners } + subject { described_class.with_shared_runners } context 'when shared runners are enabled for project' do let!(:project) { create(:empty_project, shared_runners_enabled: true) } @@ -909,10 +942,10 @@ describe Project, models: true do let!(:project2) { create(:empty_project, :public, group: group) } it 'returns total project count' do - expect(Project).to receive(:count).once.and_call_original + expect(described_class).to receive(:count).once.and_call_original 3.times do - expect(Project.cached_count).to eq(2) + expect(described_class.cached_count).to eq(2) end end end @@ -957,7 +990,7 @@ describe Project, models: true do user1.toggle_star(project1) user2.toggle_star(project2) - expect(Project.starred_by(user1)).to contain_exactly(project1) + expect(described_class.starred_by(user1)).to contain_exactly(project1) end end @@ -1979,7 +2012,7 @@ describe Project, models: true do let!(:path) { project1.namespace.full_path } it 'returns correct project' do - expect(Project.inside_path(path)).to eq([project1]) + expect(described_class.inside_path(path)).to eq([project1]) end end @@ -2183,7 +2216,7 @@ describe Project, models: true do context 'with a user' do let(:projects) do - Project.all.public_or_visible_to_user(user) + described_class.all.public_or_visible_to_user(user) end it 'includes projects the user has access to' do @@ -2197,7 +2230,7 @@ describe Project, models: true do context 'without a user' do it 'only includes public projects' do - projects = Project.all.public_or_visible_to_user + projects = described_class.all.public_or_visible_to_user expect(projects).to eq([public_project]) end diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb index c5ffbda9821..be1b37730b1 100644 --- a/spec/models/project_statistics_spec.rb +++ b/spec/models/project_statistics_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ProjectStatistics, models: true do +describe ProjectStatistics do let(:project) { create :empty_project } let(:statistics) { project.statistics } diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 49f2f8c0ad1..68228a038a8 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe ProjectTeam, models: true do +describe ProjectTeam do let(:master) { create(:user) } let(:reporter) { create(:user) } let(:guest) { create(:user) } diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index 1f314791479..7fcbeb459e0 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -1,11 +1,11 @@ require "spec_helper" -describe ProjectWiki, models: true do +describe ProjectWiki do let(:project) { create(:empty_project) } let(:repository) { project.repository } let(:user) { project.owner } let(:gitlab_shell) { Gitlab::Shell.new } - let(:project_wiki) { ProjectWiki.new(project, user) } + let(:project_wiki) { described_class.new(project, user) } subject { project_wiki } @@ -21,7 +21,7 @@ describe ProjectWiki, models: true do describe '#web_url' do it 'returns the full web URL to the wiki' do - expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/wikis/home") + expect(subject.web_url).to match("https?://[^\/]+/#{project.path_with_namespace}/wikis/home") end end diff --git a/spec/models/protectable_dropdown_spec.rb b/spec/models/protectable_dropdown_spec.rb index 4c9bade592b..5c5dcd9f5c9 100644 --- a/spec/models/protectable_dropdown_spec.rb +++ b/spec/models/protectable_dropdown_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectableDropdown, models: true do +describe ProtectableDropdown do let(:project) { create(:project, :repository) } let(:subject) { described_class.new(project, :branches) } diff --git a/spec/models/protected_branch/merge_access_level_spec.rb b/spec/models/protected_branch/merge_access_level_spec.rb index 1e7242e9fa8..f70503eadbc 100644 --- a/spec/models/protected_branch/merge_access_level_spec.rb +++ b/spec/models/protected_branch/merge_access_level_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe ProtectedBranch::MergeAccessLevel, :models do +describe ProtectedBranch::MergeAccessLevel do it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) } end diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb index de68351198c..f161f345761 100644 --- a/spec/models/protected_branch/push_access_level_spec.rb +++ b/spec/models/protected_branch/push_access_level_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -describe ProtectedBranch::PushAccessLevel, :models do +describe ProtectedBranch::PushAccessLevel do it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::MASTER, Gitlab::Access::DEVELOPER, Gitlab::Access::NO_ACCESS]) } end diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index ca347cf92c9..a54af3bfe59 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedBranch, models: true do +describe ProtectedBranch do subject { build_stubbed(:protected_branch) } describe 'Associations' do @@ -101,17 +101,17 @@ describe ProtectedBranch, models: true do production = create(:protected_branch, name: "production") staging = create(:protected_branch, name: "staging") - expect(ProtectedBranch.matching("production")).to include(production) - expect(ProtectedBranch.matching("production")).not_to include(staging) + expect(described_class.matching("production")).to include(production) + expect(described_class.matching("production")).not_to include(staging) end it "accepts a list of protected branches to search from, so as to avoid a DB call" do production = build(:protected_branch, name: "production") staging = build(:protected_branch, name: "staging") - expect(ProtectedBranch.matching("production")).to be_empty - expect(ProtectedBranch.matching("production", protected_refs: [production, staging])).to include(production) - expect(ProtectedBranch.matching("production", protected_refs: [production, staging])).not_to include(staging) + expect(described_class.matching("production")).to be_empty + expect(described_class.matching("production", protected_refs: [production, staging])).to include(production) + expect(described_class.matching("production", protected_refs: [production, staging])).not_to include(staging) end end @@ -120,17 +120,17 @@ describe ProtectedBranch, models: true do production = create(:protected_branch, name: "production/*") staging = create(:protected_branch, name: "staging/*") - expect(ProtectedBranch.matching("production/some-branch")).to include(production) - expect(ProtectedBranch.matching("production/some-branch")).not_to include(staging) + expect(described_class.matching("production/some-branch")).to include(production) + expect(described_class.matching("production/some-branch")).not_to include(staging) end it "accepts a list of protected branches to search from, so as to avoid a DB call" do production = build(:protected_branch, name: "production/*") staging = build(:protected_branch, name: "staging/*") - expect(ProtectedBranch.matching("production/some-branch")).to be_empty - expect(ProtectedBranch.matching("production/some-branch", protected_refs: [production, staging])).to include(production) - expect(ProtectedBranch.matching("production/some-branch", protected_refs: [production, staging])).not_to include(staging) + expect(described_class.matching("production/some-branch")).to be_empty + expect(described_class.matching("production/some-branch", protected_refs: [production, staging])).to include(production) + expect(described_class.matching("production/some-branch", protected_refs: [production, staging])).not_to include(staging) end end end @@ -142,23 +142,23 @@ describe ProtectedBranch, models: true do it 'returns true when the branch matches a protected branch via direct match' do create(:protected_branch, project: project, name: "foo") - expect(ProtectedBranch.protected?(project, 'foo')).to eq(true) + expect(described_class.protected?(project, 'foo')).to eq(true) end it 'returns true when the branch matches a protected branch via wildcard match' do create(:protected_branch, project: project, name: "production/*") - expect(ProtectedBranch.protected?(project, 'production/some-branch')).to eq(true) + expect(described_class.protected?(project, 'production/some-branch')).to eq(true) end it 'returns false when the branch does not match a protected branch via direct match' do - expect(ProtectedBranch.protected?(project, 'foo')).to eq(false) + expect(described_class.protected?(project, 'foo')).to eq(false) end it 'returns false when the branch does not match a protected branch via wildcard match' do create(:protected_branch, project: project, name: "production/*") - expect(ProtectedBranch.protected?(project, 'staging/some-branch')).to eq(false) + expect(described_class.protected?(project, 'staging/some-branch')).to eq(false) end end @@ -168,25 +168,25 @@ describe ProtectedBranch, models: true do it 'returns false when default_protected_branch is unprotected' do stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) - expect(ProtectedBranch.protected?(project, 'master')).to be false + expect(described_class.protected?(project, 'master')).to be false end it 'returns false when default_protected_branch lets developers push' do stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) - expect(ProtectedBranch.protected?(project, 'master')).to be false + expect(described_class.protected?(project, 'master')).to be false end it 'returns true when default_branch_protection does not let developers push but let developer merge branches' do stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) - expect(ProtectedBranch.protected?(project, 'master')).to be true + expect(described_class.protected?(project, 'master')).to be true end it 'returns true when default_branch_protection is in full protection' do stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) - expect(ProtectedBranch.protected?(project, 'master')).to be true + expect(described_class.protected?(project, 'master')).to be true end end end diff --git a/spec/models/protected_tag_spec.rb b/spec/models/protected_tag_spec.rb index 51353852a93..e5a0f6ec23f 100644 --- a/spec/models/protected_tag_spec.rb +++ b/spec/models/protected_tag_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedTag, models: true do +describe ProtectedTag do describe 'Associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/redirect_route_spec.rb b/spec/models/redirect_route_spec.rb index 71827421dd7..80943877095 100644 --- a/spec/models/redirect_route_spec.rb +++ b/spec/models/redirect_route_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe RedirectRoute, models: true do +describe RedirectRoute do let(:group) { create(:group) } let!(:redirect_route) { group.redirect_routes.create(path: 'gitlabb') } @@ -11,7 +11,7 @@ describe RedirectRoute, models: true do describe 'validations' do it { is_expected.to validate_presence_of(:source) } it { is_expected.to validate_presence_of(:path) } - it { is_expected.to validate_uniqueness_of(:path) } + it { is_expected.to validate_uniqueness_of(:path).case_insensitive } end describe '.matching_path_and_descendants' do @@ -21,7 +21,7 @@ describe RedirectRoute, models: true do let!(:redirect5) { group.redirect_routes.create(path: 'gitlabb/test/baz') } it 'returns correct routes' do - expect(RedirectRoute.matching_path_and_descendants('gitlabb/test')).to match_array([redirect2, redirect3, redirect4, redirect5]) + expect(described_class.matching_path_and_descendants('gitlabb/test')).to match_array([redirect2, redirect3, redirect4, redirect5]) end end end diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 527005b2b69..3f86347c3ae 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Release, type: :model do +RSpec.describe Release do let(:release) { create(:release) } it { expect(release).to be_valid } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 7635b0868e7..07ed66e127a 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Repository, models: true do +describe Repository do include RepoHelpers TestBlob = Struct.new(:path) @@ -956,21 +956,25 @@ describe Repository, models: true do end end - describe '#exists?' do + shared_examples 'repo exists check' do it 'returns true when a repository exists' do expect(repository.exists?).to eq(true) end - it 'returns false when a repository does not exist' do - allow(repository).to receive(:refs_directory_exists?).and_return(false) + it 'returns false if no full path can be constructed' do + allow(repository).to receive(:path_with_namespace).and_return(nil) expect(repository.exists?).to eq(false) end + end - it 'returns false when there is no namespace' do - allow(repository).to receive(:path_with_namespace).and_return(nil) + describe '#exists?' do + context 'when repository_exists is disabled' do + it_behaves_like 'repo exists check' + end - expect(repository.exists?).to eq(false) + context 'when repository_exists is enabled', skip_gitaly_mock: true do + it_behaves_like 'repo exists check' end end diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb index 1754253e0f2..bdacc60fb53 100644 --- a/spec/models/route_spec.rb +++ b/spec/models/route_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Route, models: true do +describe Route do let(:group) { create(:group, path: 'git_lab', name: 'git_lab') } let(:route) { group.route } @@ -15,7 +15,7 @@ describe Route, models: true do it { is_expected.to validate_presence_of(:source) } it { is_expected.to validate_presence_of(:path) } - it { is_expected.to validate_uniqueness_of(:path) } + it { is_expected.to validate_uniqueness_of(:path).case_insensitive } end describe 'callbacks' do @@ -34,7 +34,7 @@ describe Route, models: true do context 'after create' do it 'calls #delete_conflicting_redirects' do route.destroy - new_route = Route.new(source: group, path: group.path) + new_route = described_class.new(source: group, path: group.path) expect(new_route).to receive(:delete_conflicting_redirects) new_route.save! end @@ -49,7 +49,7 @@ describe Route, models: true do let!(:another_group_nested) { create(:group, path: 'another', name: 'another', parent: similar_group) } it 'returns correct routes' do - expect(Route.inside_path('git_lab')).to match_array([nested_group.route, deep_nested_group.route]) + expect(described_class.inside_path('git_lab')).to match_array([nested_group.route, deep_nested_group.route]) end end diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb index 5710edbc9e0..8b6b02916ae 100644 --- a/spec/models/sent_notification_spec.rb +++ b/spec/models/sent_notification_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SentNotification, model: true do +describe SentNotification do describe 'validation' do describe 'note validity' do context "when the project doesn't match the noteable's project" do @@ -38,7 +38,7 @@ describe SentNotification, model: true do let(:issue) { create(:issue) } it 'creates a new SentNotification' do - expect { described_class.record(issue, user.id) }.to change { SentNotification.count }.by(1) + expect { described_class.record(issue, user.id) }.to change { described_class.count }.by(1) end end @@ -47,7 +47,7 @@ describe SentNotification, model: true do let(:note) { create(:diff_note_on_merge_request) } it 'creates a new SentNotification' do - expect { described_class.record_note(note, user.id) }.to change { SentNotification.count }.by(1) + expect { described_class.record_note(note, user.id) }.to change { described_class.count }.by(1) end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 134882648b9..dba9fc4ac9b 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Service, models: true do +describe Service do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/snippet_blob_spec.rb b/spec/models/snippet_blob_spec.rb index 120b390586b..7c71c458fcc 100644 --- a/spec/models/snippet_blob_spec.rb +++ b/spec/models/snippet_blob_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SnippetBlob, models: true do +describe SnippetBlob do let(:snippet) { create(:snippet) } subject { described_class.new(snippet) } diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 1e5c96fe593..904bf2c6144 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Snippet, models: true do +describe Snippet do describe 'modules' do subject { described_class } diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb index 838fba6c92d..0d6b4384ada 100644 --- a/spec/models/spam_log_spec.rb +++ b/spec/models/spam_log_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SpamLog, models: true do +describe SpamLog do let(:admin) { create(:admin) } describe 'associations' do diff --git a/spec/models/subscription_spec.rb b/spec/models/subscription_spec.rb index 9ab112bb2ee..9e4c2620d82 100644 --- a/spec/models/subscription_spec.rb +++ b/spec/models/subscription_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Subscription, models: true do +describe Subscription do describe 'relationships' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:subscribable) } diff --git a/spec/models/system_note_metadata_spec.rb b/spec/models/system_note_metadata_spec.rb index d238e28209a..1e3f587e460 100644 --- a/spec/models/system_note_metadata_spec.rb +++ b/spec/models/system_note_metadata_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SystemNoteMetadata, models: true do +describe SystemNoteMetadata do describe 'associations' do it { is_expected.to belong_to(:note) } end diff --git a/spec/models/timelog_spec.rb b/spec/models/timelog_spec.rb index ebc694213b6..6e30798356c 100644 --- a/spec/models/timelog_spec.rb +++ b/spec/models/timelog_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Timelog, type: :model do +RSpec.describe Timelog do subject { build(:timelog) } let(:issue) { create(:issue) } let(:merge_request) { create(:merge_request) } diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index 3f80e1ac534..3e8f3848eca 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Todo, models: true do +describe Todo do let(:issue) { create(:issue) } describe 'relationships' do diff --git a/spec/models/tree_spec.rb b/spec/models/tree_spec.rb index a87983b7492..6bdb62a0864 100644 --- a/spec/models/tree_spec.rb +++ b/spec/models/tree_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Tree, models: true do +describe Tree do let(:repository) { create(:project, :repository).repository } let(:sha) { repository.root_ref } diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index 2dea2c6015f..345382ea8c7 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Upload, type: :model do +describe Upload do describe 'assocations' do it { is_expected.to belong_to(:model) } end diff --git a/spec/models/user_agent_detail_spec.rb b/spec/models/user_agent_detail_spec.rb index a8c25766e73..b4669f8c1c2 100644 --- a/spec/models/user_agent_detail_spec.rb +++ b/spec/models/user_agent_detail_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe UserAgentDetail, type: :model do +describe UserAgentDetail do describe '.submittable?' do it 'is submittable when not already submitted' do detail = build(:user_agent_detail) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a1d6d7e6e0b..ec98a3f3498 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe User, models: true do +describe User do include Gitlab::CurrentSettings describe 'modules' do @@ -114,7 +114,9 @@ describe User, models: true do end it 'validates uniqueness' do - expect(subject).to validate_uniqueness_of(:username).case_insensitive + user = build(:user) + + expect(user).to validate_uniqueness_of(:username).case_insensitive end end @@ -259,7 +261,7 @@ describe User, models: true do it "returns users with 2fa enabled via OTP" do user_with_2fa = create(:user, :two_factor_via_otp) user_without_2fa = create(:user) - users_with_two_factor = User.with_two_factor.pluck(:id) + users_with_two_factor = described_class.with_two_factor.pluck(:id) expect(users_with_two_factor).to include(user_with_2fa.id) expect(users_with_two_factor).not_to include(user_without_2fa.id) @@ -268,7 +270,7 @@ describe User, models: true do it "returns users with 2fa enabled via U2F" do user_with_2fa = create(:user, :two_factor_via_u2f) user_without_2fa = create(:user) - users_with_two_factor = User.with_two_factor.pluck(:id) + users_with_two_factor = described_class.with_two_factor.pluck(:id) expect(users_with_two_factor).to include(user_with_2fa.id) expect(users_with_two_factor).not_to include(user_without_2fa.id) @@ -277,7 +279,7 @@ describe User, models: true do it "returns users with 2fa enabled via OTP and U2F" do user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f) user_without_2fa = create(:user) - users_with_two_factor = User.with_two_factor.pluck(:id) + users_with_two_factor = described_class.with_two_factor.pluck(:id) expect(users_with_two_factor).to eq([user_with_2fa.id]) expect(users_with_two_factor).not_to include(user_without_2fa.id) @@ -288,7 +290,7 @@ describe User, models: true do it "excludes users with 2fa enabled via OTP" do user_with_2fa = create(:user, :two_factor_via_otp) user_without_2fa = create(:user) - users_without_two_factor = User.without_two_factor.pluck(:id) + users_without_two_factor = described_class.without_two_factor.pluck(:id) expect(users_without_two_factor).to include(user_without_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id) @@ -297,7 +299,7 @@ describe User, models: true do it "excludes users with 2fa enabled via U2F" do user_with_2fa = create(:user, :two_factor_via_u2f) user_without_2fa = create(:user) - users_without_two_factor = User.without_two_factor.pluck(:id) + users_without_two_factor = described_class.without_two_factor.pluck(:id) expect(users_without_two_factor).to include(user_without_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id) @@ -306,7 +308,7 @@ describe User, models: true do it "excludes users with 2fa enabled via OTP and U2F" do user_with_2fa = create(:user, :two_factor_via_otp, :two_factor_via_u2f) user_without_2fa = create(:user) - users_without_two_factor = User.without_two_factor.pluck(:id) + users_without_two_factor = described_class.without_two_factor.pluck(:id) expect(users_without_two_factor).to include(user_without_2fa.id) expect(users_without_two_factor).not_to include(user_with_2fa.id) @@ -322,8 +324,8 @@ describe User, models: true do create(:todo, user: current_user, author: user_2, state: :done) create(:todo, user: current_user, author: user_3, state: :pending) - expect(User.todo_authors(current_user.id, 'pending')).to eq [user_3] - expect(User.todo_authors(current_user.id, 'done')).to eq [user_2] + expect(described_class.todo_authors(current_user.id, 'pending')).to eq [user_3] + expect(described_class.todo_authors(current_user.id, 'done')).to eq [user_2] end end end @@ -348,6 +350,26 @@ describe User, models: true do end end + describe 'after update hook' do + describe '.update_invalid_gpg_signatures' do + let(:user) do + create(:user, email: 'tula.torphy@abshire.ca').tap do |user| + user.skip_reconfirmation! + end + end + + it 'does nothing when the name is updated' do + expect(user).not_to receive(:update_invalid_gpg_signatures) + user.update_attributes!(name: 'Bette') + end + + it 'synchronizes the gpg keys when the email is updated' do + expect(user).to receive(:update_invalid_gpg_signatures) + user.update_attributes!(email: 'shawnee.ritchie@denesik.com') + end + end + end + describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") } let(:user) { create(:user) } @@ -607,39 +629,39 @@ describe User, models: true do let(:user) { double } it 'filters by active users by default' do - expect(User).to receive(:active).and_return([user]) + expect(described_class).to receive(:active).and_return([user]) - expect(User.filter(nil)).to include user + expect(described_class.filter(nil)).to include user end it 'filters by admins' do - expect(User).to receive(:admins).and_return([user]) + expect(described_class).to receive(:admins).and_return([user]) - expect(User.filter('admins')).to include user + expect(described_class.filter('admins')).to include user end it 'filters by blocked' do - expect(User).to receive(:blocked).and_return([user]) + expect(described_class).to receive(:blocked).and_return([user]) - expect(User.filter('blocked')).to include user + expect(described_class.filter('blocked')).to include user end it 'filters by two_factor_disabled' do - expect(User).to receive(:without_two_factor).and_return([user]) + expect(described_class).to receive(:without_two_factor).and_return([user]) - expect(User.filter('two_factor_disabled')).to include user + expect(described_class.filter('two_factor_disabled')).to include user end it 'filters by two_factor_enabled' do - expect(User).to receive(:with_two_factor).and_return([user]) + expect(described_class).to receive(:with_two_factor).and_return([user]) - expect(User.filter('two_factor_enabled')).to include user + expect(described_class.filter('two_factor_enabled')).to include user end it 'filters by wop' do - expect(User).to receive(:without_projects).and_return([user]) + expect(described_class).to receive(:without_projects).and_return([user]) - expect(User.filter('wop')).to include user + expect(described_class.filter('wop')).to include user end end @@ -660,9 +682,9 @@ describe User, models: true do project.request_access(user_without_project2) end - it { expect(User.without_projects).not_to include user } - it { expect(User.without_projects).to include user_without_project } - it { expect(User.without_projects).to include user_without_project2 } + it { expect(described_class.without_projects).not_to include user } + it { expect(described_class.without_projects).to include user_without_project } + it { expect(described_class.without_projects).to include user_without_project2 } end describe 'user creation' do @@ -678,7 +700,7 @@ describe User, models: true do end describe 'with defaults' do - let(:user) { User.new } + let(:user) { described_class.new } it "applies defaults to user" do expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit) @@ -688,7 +710,7 @@ describe User, models: true do end describe 'with default overrides' do - let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true) } + let(:user) { described_class.new(projects_limit: 123, can_create_group: false, can_create_team: true) } it "applies defaults to user" do expect(user.projects_limit).to eq(123) @@ -738,18 +760,18 @@ describe User, models: true do it 'finds by primary email' do user = create(:user, email: 'foo@example.com') - expect(User.find_by_any_email(user.email)).to eq user + expect(described_class.find_by_any_email(user.email)).to eq user end it 'finds by secondary email' do email = create(:email, email: 'foo@example.com') user = email.user - expect(User.find_by_any_email(email.email)).to eq user + expect(described_class.find_by_any_email(email.email)).to eq user end it 'returns nil when nothing found' do - expect(User.find_by_any_email('')).to be_nil + expect(described_class.find_by_any_email('')).to be_nil end end @@ -897,12 +919,12 @@ describe User, models: true do let!(:user) { create(:user, username: username) } it 'gets the correct user' do - expect(User.by_login(user.email.upcase)).to eq user - expect(User.by_login(user.email)).to eq user - expect(User.by_login(username.downcase)).to eq user - expect(User.by_login(username)).to eq user - expect(User.by_login(nil)).to be_nil - expect(User.by_login('')).to be_nil + expect(described_class.by_login(user.email.upcase)).to eq user + expect(described_class.by_login(user.email)).to eq user + expect(described_class.by_login(username.downcase)).to eq user + expect(described_class.by_login(username)).to eq user + expect(described_class.by_login(nil)).to be_nil + expect(described_class.by_login('')).to be_nil end end @@ -936,12 +958,12 @@ describe User, models: true do let!(:route) { user.namespace.route } it 'returns the user' do - expect(User.find_by_full_path(route.path)).to eq(user) + expect(described_class.find_by_full_path(route.path)).to eq(user) end it 'is case-insensitive' do - expect(User.find_by_full_path(route.path.upcase)).to eq(user) - expect(User.find_by_full_path(route.path.downcase)).to eq(user) + expect(described_class.find_by_full_path(route.path.upcase)).to eq(user) + expect(described_class.find_by_full_path(route.path.downcase)).to eq(user) end end @@ -950,18 +972,18 @@ describe User, models: true do context 'without the follow_redirects option' do it 'returns nil' do - expect(User.find_by_full_path(redirect_route.path)).to eq(nil) + expect(described_class.find_by_full_path(redirect_route.path)).to eq(nil) end end context 'with the follow_redirects option set to true' do it 'returns the user' do - expect(User.find_by_full_path(redirect_route.path, follow_redirects: true)).to eq(user) + expect(described_class.find_by_full_path(redirect_route.path, follow_redirects: true)).to eq(user) end it 'is case-insensitive' do - expect(User.find_by_full_path(redirect_route.path.upcase, follow_redirects: true)).to eq(user) - expect(User.find_by_full_path(redirect_route.path.downcase, follow_redirects: true)).to eq(user) + expect(described_class.find_by_full_path(redirect_route.path.upcase, follow_redirects: true)).to eq(user) + expect(described_class.find_by_full_path(redirect_route.path.downcase, follow_redirects: true)).to eq(user) end end end @@ -969,12 +991,12 @@ describe User, models: true do context 'without a route or a redirect route matching the given path' do context 'without the follow_redirects option' do it 'returns nil' do - expect(User.find_by_full_path('unknown')).to eq(nil) + expect(described_class.find_by_full_path('unknown')).to eq(nil) end end context 'with the follow_redirects option set to true' do it 'returns nil' do - expect(User.find_by_full_path('unknown', follow_redirects: true)).to eq(nil) + expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil) end end end @@ -984,7 +1006,7 @@ describe User, models: true do let!(:group) { create(:group, path: 'group_path', owner: user) } it 'returns nil' do - expect(User.find_by_full_path('group_path')).to eq(nil) + expect(described_class.find_by_full_path('group_path')).to eq(nil) end end @@ -992,7 +1014,7 @@ describe User, models: true do let!(:group) { create(:group, path: 'group_path') } it 'returns nil' do - expect(User.find_by_full_path('group_path')).to eq(nil) + expect(described_class.find_by_full_path('group_path')).to eq(nil) end end end @@ -1042,7 +1064,7 @@ describe User, models: true do end describe '#requires_ldap_check?' do - let(:user) { User.new } + let(:user) { described_class.new } it 'is false when LDAP is disabled' do # Create a condition which would otherwise cause 'true' to be returned @@ -1213,7 +1235,7 @@ describe User, models: true do describe '#sort' do before do - User.delete_all + described_class.delete_all @user = create :user, created_at: Date.today, last_sign_in_at: Date.today, name: 'Alpha' @user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega' @user2 = create :user, created_at: Date.today - 2, last_sign_in_at: nil, name: 'Beta' @@ -1221,34 +1243,34 @@ describe User, models: true do context 'when sort by recent_sign_in' do it 'sorts users by the recent sign-in time' do - expect(User.sort('recent_sign_in').first).to eq(@user) + expect(described_class.sort('recent_sign_in').first).to eq(@user) end it 'pushes users who never signed in to the end' do - expect(User.sort('recent_sign_in').third).to eq(@user2) + expect(described_class.sort('recent_sign_in').third).to eq(@user2) end end context 'when sort by oldest_sign_in' do it 'sorts users by the oldest sign-in time' do - expect(User.sort('oldest_sign_in').first).to eq(@user1) + expect(described_class.sort('oldest_sign_in').first).to eq(@user1) end it 'pushes users who never signed in to the end' do - expect(User.sort('oldest_sign_in').third).to eq(@user2) + expect(described_class.sort('oldest_sign_in').third).to eq(@user2) end end it 'sorts users in descending order by their creation time' do - expect(User.sort('created_desc').first).to eq(@user) + expect(described_class.sort('created_desc').first).to eq(@user) end it 'sorts users in ascending order by their creation time' do - expect(User.sort('created_asc').first).to eq(@user2) + expect(described_class.sort('created_asc').first).to eq(@user2) end it 'sorts users by id in descending order when nil is passed' do - expect(User.sort(nil).first).to eq(@user2) + expect(described_class.sort(nil).first).to eq(@user2) end end @@ -1768,7 +1790,7 @@ describe User, models: true do describe '.ghost' do it "creates a ghost user if one isn't already present" do - ghost = User.ghost + ghost = described_class.ghost expect(ghost).to be_ghost expect(ghost).to be_persisted @@ -1776,16 +1798,16 @@ describe User, models: true do it "does not create a second ghost user if one is already present" do expect do - User.ghost - User.ghost - end.to change { User.count }.by(1) - expect(User.ghost).to eq(User.ghost) + described_class.ghost + described_class.ghost + end.to change { described_class.count }.by(1) + expect(described_class.ghost).to eq(described_class.ghost) end context "when a regular user exists with the username 'ghost'" do it "creates a ghost user with a non-conflicting username" do create(:user, username: 'ghost') - ghost = User.ghost + ghost = described_class.ghost expect(ghost).to be_persisted expect(ghost.username).to eq('ghost1') @@ -1795,7 +1817,7 @@ describe User, models: true do context "when a regular user exists with the email 'ghost@example.com'" do it "creates a ghost user with a non-conflicting email" do create(:user, email: 'ghost@example.com') - ghost = User.ghost + ghost = described_class.ghost expect(ghost).to be_persisted expect(ghost.email).to eq('ghost1@example.com') @@ -1808,7 +1830,7 @@ describe User, models: true do end it 'creates a ghost user' do - expect(User.ghost).to be_persisted + expect(described_class.ghost).to be_persisted end end end @@ -1887,13 +1909,13 @@ describe User, models: true do context '.active' do before do - User.ghost + described_class.ghost create(:user, name: 'user', state: 'active') create(:user, name: 'user', state: 'blocked') end it 'only counts active and non internal users' do - expect(User.active.count).to eq(1) + expect(described_class.active.count).to eq(1) end end diff --git a/spec/models/wiki_directory_spec.rb b/spec/models/wiki_directory_spec.rb index 1caaa557085..fb8575cfe2b 100644 --- a/spec/models/wiki_directory_spec.rb +++ b/spec/models/wiki_directory_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -RSpec.describe WikiDirectory, models: true do +RSpec.describe WikiDirectory do describe 'validations' do subject { build(:wiki_directory) } @@ -10,7 +10,7 @@ RSpec.describe WikiDirectory, models: true do describe '#initialize' do context 'when there are pages' do let(:pages) { [build(:wiki_page)] } - let(:directory) { WikiDirectory.new('/path_up_to/dir', pages) } + let(:directory) { described_class.new('/path_up_to/dir', pages) } it 'sets the slug attribute' do expect(directory.slug).to eq('/path_up_to/dir') @@ -22,7 +22,7 @@ RSpec.describe WikiDirectory, models: true do end context 'when there are no pages' do - let(:directory) { WikiDirectory.new('/path_up_to/dir') } + let(:directory) { described_class.new('/path_up_to/dir') } it 'sets the slug attribute' do expect(directory.slug).to eq('/path_up_to/dir') diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 4a73552b8a6..55d9c4ddd7b 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -1,17 +1,17 @@ require "spec_helper" -describe WikiPage, models: true do +describe WikiPage do let(:project) { create(:empty_project) } let(:user) { project.owner } let(:wiki) { ProjectWiki.new(project, user) } - subject { WikiPage.new(wiki) } + subject { described_class.new(wiki) } describe '.group_by_directory' do context 'when there are no pages' do it 'returns an empty array' do - expect(WikiPage.group_by_directory(nil)).to eq([]) - expect(WikiPage.group_by_directory([])).to eq([]) + expect(described_class.group_by_directory(nil)).to eq([]) + expect(described_class.group_by_directory([])).to eq([]) end end @@ -39,7 +39,7 @@ describe WikiPage, models: true do it 'returns an array with pages and directories' do expected_grouped_entries = [page_1, dir_1, dir_1_1, dir_2] - grouped_entries = WikiPage.group_by_directory(wiki.pages) + grouped_entries = described_class.group_by_directory(wiki.pages) grouped_entries.each_with_index do |page_or_dir, i| expected_page_or_dir = expected_grouped_entries[i] @@ -56,7 +56,7 @@ describe WikiPage, models: true do expected_order = ['page_1', 'dir_1/page_2', 'dir_1/dir_1_1/page_3', 'dir_2/page_4', 'dir_2/page_5'] - grouped_entries = WikiPage.group_by_directory(wiki.pages) + grouped_entries = described_class.group_by_directory(wiki.pages) actual_order = grouped_entries.map do |page_or_dir| @@ -72,7 +72,7 @@ describe WikiPage, models: true do it 'removes hyphens from a name' do name = 'a-name--with-hyphens' - expect(WikiPage.unhyphenize(name)).to eq('a name with hyphens') + expect(described_class.unhyphenize(name)).to eq('a name with hyphens') end end @@ -81,7 +81,7 @@ describe WikiPage, models: true do before do create_page("test page", "test content") @page = wiki.wiki.paged("test page") - @wiki_page = WikiPage.new(wiki, @page, true) + @wiki_page = described_class.new(wiki, @page, true) end it "sets the slug attribute" do @@ -208,6 +208,18 @@ describe WikiPage, models: true do expect(@page.update("more content")).to be_truthy end end + + context 'with same last commit sha' do + it 'returns true' do + expect(@page.update('more content', last_commit_sha: @page.last_commit_sha)).to be_truthy + end + end + + context 'with different last commit sha' do + it 'raises exception' do + expect { @page.update('more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError) + end + end end describe "#destroy" do @@ -331,6 +343,30 @@ describe WikiPage, models: true do end end + describe '#last_commit_sha' do + before do + create_page("Update", "content") + @page = wiki.find_page("Update") + end + + after do + destroy_page("Update") + end + + it 'returns commit sha' do + expect(@page.last_commit_sha).to eq @page.commit.sha + end + + it 'is changed after page updated' do + last_commit_sha_before_update = @page.last_commit_sha + + @page.update("new content") + @page = wiki.find_page("Update") + + expect(@page.last_commit_sha).not_to eq last_commit_sha_before_update + end + end + private def remove_temp_repo(path) diff --git a/spec/policies/base_policy_spec.rb b/spec/policies/base_policy_spec.rb index e1963091a72..c03d95b34db 100644 --- a/spec/policies/base_policy_spec.rb +++ b/spec/policies/base_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BasePolicy, models: true do +describe BasePolicy do describe '.class_for' do it 'detects policy class based on the subject ancestors' do expect(DeclarativePolicy.class_for(GenericCommitStatus.new)).to eq(CommitStatusPolicy) diff --git a/spec/policies/ci/build_policy_spec.rb b/spec/policies/ci/build_policy_spec.rb index 9f3212b1a63..a83a83a7349 100644 --- a/spec/policies/ci/build_policy_spec.rb +++ b/spec/policies/ci/build_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::BuildPolicy, :models do +describe Ci::BuildPolicy do let(:user) { create(:user) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) { create(:ci_empty_pipeline, project: project) } @@ -96,87 +96,57 @@ describe Ci::BuildPolicy, :models do end end - describe 'rules for manual actions' do + describe 'rules for protected ref' do let(:project) { create(:project) } + let(:build) { create(:ci_build, ref: 'some-ref', pipeline: pipeline) } before do project.add_developer(user) end - shared_examples 'protected ref' do - context 'when build is a manual action' do - let(:build) do - create(:ci_build, :manual, ref: 'some-ref', pipeline: pipeline) - end - - it 'does not include ability to update build' do - expect(policy).to be_disallowed :update_build - end + context 'when no one can push or merge to the branch' do + before do + create(:protected_branch, :no_one_can_push, + name: build.ref, project: project) end - context 'when build is not a manual action' do - let(:build) do - create(:ci_build, ref: 'some-ref', pipeline: pipeline) - end - - it 'includes ability to update build' do - expect(policy).to be_allowed :update_build - end + it 'does not include ability to update build' do + expect(policy).to be_disallowed :update_build end end - context 'when build is against a protected branch' do + context 'when developers can push to the branch' do before do - create(:protected_branch, :no_one_can_push, - name: 'some-ref', project: project) + create(:protected_branch, :developers_can_merge, + name: build.ref, project: project) end - it_behaves_like 'protected ref' + it 'includes ability to update build' do + expect(policy).to be_allowed :update_build + end end - context 'when build is against a protected tag' do + context 'when no one can create the tag' do before do create(:protected_tag, :no_one_can_create, - name: 'some-ref', project: project) + name: build.ref, project: project) build.update(tag: true) end - it_behaves_like 'protected ref' + it 'does not include ability to update build' do + expect(policy).to be_disallowed :update_build + end end - context 'when build is against a protected tag but it is not a tag' do + context 'when no one can create the tag but it is not a tag' do before do create(:protected_tag, :no_one_can_create, - name: 'some-ref', project: project) + name: build.ref, project: project) end - context 'when build is a manual action' do - let(:build) do - create(:ci_build, :manual, ref: 'some-ref', pipeline: pipeline) - end - - it 'includes ability to update build' do - expect(policy).to be_allowed :update_build - end - end - end - - context 'when branch build is assigned to is not protected' do - context 'when build is a manual action' do - let(:build) { create(:ci_build, :manual, pipeline: pipeline) } - - it 'includes ability to update build' do - expect(policy).to be_allowed :update_build - end - end - - context 'when build is not a manual action' do - let(:build) { create(:ci_build, pipeline: pipeline) } - - it 'includes ability to update build' do - expect(policy).to be_allowed :update_build - end + it 'includes ability to update build' do + expect(policy).to be_allowed :update_build end end end diff --git a/spec/policies/ci/pipeline_policy_spec.rb b/spec/policies/ci/pipeline_policy_spec.rb new file mode 100644 index 00000000000..b11b06d301f --- /dev/null +++ b/spec/policies/ci/pipeline_policy_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Ci::PipelinePolicy, :models do + let(:user) { create(:user) } + let(:pipeline) { create(:ci_empty_pipeline, project: project) } + + let(:policy) do + described_class.new(user, pipeline) + end + + describe 'rules' do + describe 'rules for protected ref' do + let(:project) { create(:project) } + + before do + project.add_developer(user) + end + + context 'when no one can push or merge to the branch' do + before do + create(:protected_branch, :no_one_can_push, + name: pipeline.ref, project: project) + end + + it 'does not include ability to update pipeline' do + expect(policy).to be_disallowed :update_pipeline + end + end + + context 'when developers can push to the branch' do + before do + create(:protected_branch, :developers_can_merge, + name: pipeline.ref, project: project) + end + + it 'includes ability to update pipeline' do + expect(policy).to be_allowed :update_pipeline + end + end + + context 'when no one can create the tag' do + before do + create(:protected_tag, :no_one_can_create, + name: pipeline.ref, project: project) + + pipeline.update(tag: true) + end + + it 'does not include ability to update pipeline' do + expect(policy).to be_disallowed :update_pipeline + end + end + + context 'when no one can create the tag but it is not a tag' do + before do + create(:protected_tag, :no_one_can_create, + name: pipeline.ref, project: project) + end + + it 'includes ability to update pipeline' do + expect(policy).to be_allowed :update_pipeline + end + end + end + end +end diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb index ed4010e723b..3d3e3d3755b 100644 --- a/spec/policies/ci/trigger_policy_spec.rb +++ b/spec/policies/ci/trigger_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::TriggerPolicy, :models do +describe Ci::TriggerPolicy do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:trigger) { create(:ci_trigger, project: project, owner: owner) } diff --git a/spec/policies/deploy_key_policy_spec.rb b/spec/policies/deploy_key_policy_spec.rb index f15f4a11f02..ca7b7fe7ef7 100644 --- a/spec/policies/deploy_key_policy_spec.rb +++ b/spec/policies/deploy_key_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeployKeyPolicy, models: true do +describe DeployKeyPolicy do subject { described_class.new(current_user, deploy_key) } describe 'updating a deploy_key' do diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb index bb0fa0c0e9c..a6bf70c1e09 100644 --- a/spec/policies/global_policy_spec.rb +++ b/spec/policies/global_policy_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe GlobalPolicy, models: true do +describe GlobalPolicy do let(:current_user) { create(:user) } let(:user) { create(:user) } - subject { GlobalPolicy.new(current_user, [user]) } + subject { described_class.new(current_user, [user]) } describe "reading the list of users" do context "for a logged in user" do @@ -30,5 +30,25 @@ describe GlobalPolicy, models: true do it { is_expected.to be_allowed(:read_users_list) } end end + + context "for an admin" do + let(:current_user) { create(:admin) } + + context "when the public level is restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it { is_expected.to be_allowed(:read_users_list) } + end + + context "when the public level is not restricted" do + before do + stub_application_setting(restricted_visibility_levels: []) + end + + it { is_expected.to be_allowed(:read_users_list) } + end + end end end diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 06db0ea56e3..b17a93e3fbe 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GroupPolicy, models: true do +describe GroupPolicy do let(:guest) { create(:user) } let(:reporter) { create(:user) } let(:developer) { create(:user) } diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb index c978cbd6185..279b96fb2af 100644 --- a/spec/policies/issue_policy_spec.rb +++ b/spec/policies/issue_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe IssuePolicy, models: true do +describe IssuePolicy do let(:guest) { create(:user) } let(:author) { create(:user) } let(:assignee) { create(:user) } diff --git a/spec/policies/personal_snippet_policy_spec.rb b/spec/policies/personal_snippet_policy_spec.rb index 4d6350fc653..b70c8646a3d 100644 --- a/spec/policies/personal_snippet_policy_spec.rb +++ b/spec/policies/personal_snippet_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PersonalSnippetPolicy, models: true do +describe PersonalSnippetPolicy do let(:regular_user) { create(:user) } let(:external_user) { create(:user, :external) } let(:admin_user) { create(:user, :admin) } diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index ca435dd0218..1f51ced1beb 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectPolicy, models: true do +describe ProjectPolicy do let(:guest) { create(:user) } let(:reporter) { create(:user) } let(:dev) { create(:user) } @@ -103,6 +103,48 @@ describe ProjectPolicy, models: true do end end + context 'issues feature' do + subject { described_class.new(owner, project) } + + context 'when the feature is disabled' do + it 'does not include the issues permissions' do + project.issues_enabled = false + project.save! + + expect_disallowed :read_issue, :create_issue, :update_issue, :admin_issue + end + end + + context 'when the feature is disabled and external tracker configured' do + it 'does not include the issues permissions' do + create(:jira_service, project: project) + + project.issues_enabled = false + project.save! + + expect_disallowed :read_issue, :create_issue, :update_issue, :admin_issue + end + end + end + + context 'when a project has pending invites, and the current user is anonymous' do + let(:group) { create(:group, :public) } + let(:project) { create(:empty_project, :public, namespace: group) } + let(:user_permissions) { [:create_project, :create_issue, :create_note, :upload_file] } + let(:anonymous_permissions) { guest_permissions - user_permissions } + + subject { described_class.new(nil, project) } + + before do + create(:group_member, :invited, group: group) + end + + it 'does not grant owner access' do + expect_allowed(*anonymous_permissions) + expect_disallowed(*user_permissions) + end + end + context 'abilities for non-public projects' do let(:project) { create(:empty_project, namespace: owner.namespace) } diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb index 2799f03fb9b..bae35ee31c6 100644 --- a/spec/policies/project_snippet_policy_spec.rb +++ b/spec/policies/project_snippet_policy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProjectSnippetPolicy, models: true do +describe ProjectSnippetPolicy do let(:regular_user) { create(:user) } let(:external_user) { create(:user, :external) } let(:project) { create(:empty_project, :public) } diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb index 0251d5dcf1c..6593a6ca3b9 100644 --- a/spec/policies/user_policy_spec.rb +++ b/spec/policies/user_policy_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe UserPolicy, models: true do +describe UserPolicy do let(:current_user) { create(:user) } let(:user) { create(:user) } - subject { UserPolicy.new(current_user, user) } + subject { described_class.new(current_user, user) } describe "reading a user's information" do it { is_expected.to be_allowed(:read_user) } diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index c64499fc8c0..5a2e1b2cf2d 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -1,25 +1,31 @@ require 'spec_helper' -require 'mime/types' describe API::Branches do let(:user) { create(:user) } - let!(:project) { create(:project, :repository, creator: user) } - let!(:master) { create(:project_member, :master, user: user, project: project) } - let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } } - let!(:branch_name) { 'feature' } - let!(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } - let(:branch_with_dot) { CreateBranchService.new(project, user).execute("with.1.2.3", "master")[:branch] } + let(:guest) { create(:user).tap { |u| project.add_guest(u) } } + let(:project) { create(:project, :repository, creator: user, path: 'my.project') } + let(:branch_name) { 'feature' } + let(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } + let(:branch_with_dot) { project.repository.find_branch('ends-with.json') } + let(:branch_with_slash) { project.repository.find_branch('improve/awesome') } + + let(:project_id) { project.id } + let(:current_user) { nil } + + before do + project.add_master(user) + end describe "GET /projects/:id/repository/branches" do - let(:route) { "/projects/#{project.id}/repository/branches" } + let(:route) { "/projects/#{project_id}/repository/branches" } shared_examples_for 'repository branches' do it 'returns the repository branches' do get api(route, current_user), per_page: 100 - expect(response).to have_http_status(200) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branches') expect(response).to include_pagination_headers - expect(json_response).to be_an Array branch_names = json_response.map { |x| x['name'] } expect(branch_names).to match_array(project.repository.branch_names) end @@ -34,10 +40,9 @@ describe API::Branches do end context 'when unauthenticated', 'and project is public' do - it_behaves_like 'repository branches' do - let(:project) { create(:project, :public, :repository) } - let(:current_user) { nil } - end + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'repository branches' end context 'when unauthenticated', 'and project is private' do @@ -47,9 +52,15 @@ describe API::Branches do end end - context 'when authenticated', 'as a developer' do - it_behaves_like 'repository branches' do - let(:current_user) { user } + context 'when authenticated', 'as a master' do + let(:current_user) { user } + + it_behaves_like 'repository branches' + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository branches' end end @@ -61,31 +72,15 @@ describe API::Branches do end describe "GET /projects/:id/repository/branches/:branch" do - let(:route) { "/projects/#{project.id}/repository/branches/#{branch_name}" } + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}" } - shared_examples_for 'repository branch' do |merged: false| + shared_examples_for 'repository branch' do it 'returns the repository branch' do get api(route, current_user) - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['merged']).to eq(merged) - expect(json_response['protected']).to eq(false) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - - json_commit = json_response['commit'] - expect(json_commit['id']).to eq(branch_sha) - expect(json_commit).to have_key('short_id') - expect(json_commit).to have_key('title') - expect(json_commit).to have_key('message') - expect(json_commit).to have_key('author_name') - expect(json_commit).to have_key('author_email') - expect(json_commit).to have_key('authored_date') - expect(json_commit).to have_key('committer_name') - expect(json_commit).to have_key('committer_email') - expect(json_commit).to have_key('committed_date') - expect(json_commit).to have_key('parent_ids') + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) end context 'when branch does not exist' do @@ -107,10 +102,9 @@ describe API::Branches do end context 'when unauthenticated', 'and project is public' do - it_behaves_like 'repository branch' do - let(:project) { create(:project, :public, :repository) } - let(:current_user) { nil } - end + let(:project) { create(:project, :public, :repository) } + + it_behaves_like 'repository branch' end context 'when unauthenticated', 'and project is private' do @@ -120,22 +114,41 @@ describe API::Branches do end end - context 'when authenticated', 'as a developer' do + context 'when authenticated', 'as a master' do let(:current_user) { user } + it_behaves_like 'repository branch' context 'when branch contains a dot' do let(:branch_name) { branch_with_dot.name } - let(:branch_sha) { project.commit('master').sha } it_behaves_like 'repository branch' end - context 'when branch is merged' do - let(:branch_name) { 'merge-test' } - let(:branch_sha) { project.commit('merge-test').sha } + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } + + it_behaves_like '404 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } + + it_behaves_like 'repository branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository branch' - it_behaves_like 'repository branch', merged: true + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository branch' + end end end @@ -147,268 +160,348 @@ describe API::Branches do end describe 'PUT /projects/:id/repository/branches/:branch/protect' do - context "when a protected branch doesn't already exist" do - it 'protects a single branch' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/protect" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - end - - it "protects a single branch with dots in the name" do - put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/protect", user) + shared_examples_for 'repository new protected branch' do + it 'protects a single branch' do + put api(route, current_user) - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_with_dot.name) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) end it 'protects a single branch and developers can push' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: true + put api(route, current_user), developers_can_push: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(true) expect(json_response['developers_can_merge']).to eq(false) end it 'protects a single branch and developers can merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_merge: true + put api(route, current_user), developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(false) expect(json_response['developers_can_merge']).to eq(true) end it 'protects a single branch and developers can push and merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: true, developers_can_merge: true + put api(route, current_user), developers_can_push: true, developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(true) expect(json_response['developers_can_merge']).to eq(true) end + + context 'when branch does not exist' do + let(:branch_name) { 'unknown' } + + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + let(:message) { '404 Branch Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { put api(route, current_user) } + end + end end - context 'for an existing protected branch' do - before do - project.repository.add_branch(user, protected_branch.name, 'master') + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { put api(route) } + let(:message) { '404 Project Not Found' } end + end + + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { put api(route, guest) } + end + end + + context 'when authenticated', 'as a master' do + let(:current_user) { user } - context "when developers can push and merge" do - let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') } + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository new protected branch' - it 'updates that a developer cannot push or merge' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: false, developers_can_merge: false + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) + it_behaves_like 'repository new protected branch' end - it "doesn't result in 0 access levels when 'developers_can_push' is switched off" do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: false + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(protected_branch.reload.push_access_levels.first).to be_present - expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + end end - it "doesn't result in 0 access levels when 'developers_can_merge' is switched off" do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_merge: false + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(protected_branch.reload.merge_access_levels.first).to be_present - expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + it_behaves_like 'repository new protected branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository new protected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository new protected branch' + end end end - context "when developers cannot push or merge" do - let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') } + context 'when protected branch already exists' do + before do + project.repository.add_branch(user, protected_branch.name, 'master') + end + + context 'when developers can push and merge' do + let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') } + + it 'updates that a developer cannot push or merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: false, developers_can_merge: false + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(false) + expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + end + end + + context 'when developers cannot push or merge' do + let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') } - it 'updates that a developer can push and merge' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), - developers_can_push: true, developers_can_merge: true + it 'updates that a developer can push and merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: true, developers_can_merge: true - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch.name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(true) + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(true) + end end end end + end - context "multiple API calls" do - it "returns success when `protect` is called twice" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + describe 'PUT /projects/:id/repository/branches/:branch/unprotect' do + let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/unprotect" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) + shared_examples_for 'repository unprotected branch' do + it 'unprotects a single branch' do + put api(route, current_user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq(CGI.unescape(branch_name)) + expect(json_response['protected']).to eq(false) end - it "returns success when `protect` is called twice with `developers_can_push` turned on" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true + context 'when branch does not exist' do + let(:branch_name) { 'unknown' } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(false) + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + let(:message) { '404 Branch Not Found' } + end end - it "returns success when `protect` is called twice with `developers_can_merge` turned on" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true + context 'when repository is disabled' do + include_context 'disabled repository' - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(true) + it_behaves_like '403 response' do + let(:request) { put api(route, current_user) } + end end end - it "returns a 404 error if branch not found" do - put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - expect(response).to have_http_status(404) + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { put api(route) } + let(:message) { '404 Project Not Found' } + end end - it "returns a 403 error if guest" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", guest) - expect(response).to have_http_status(403) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { put api(route, guest) } + end end - end - describe "PUT /projects/:id/repository/branches/:branch/unprotect" do - it "unprotects a single branch" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response).to have_http_status(200) + context 'when authenticated', 'as a master' do + let(:current_user) { user } + + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository unprotected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository unprotected branch' + end + + context 'when branch contains a slash' do + let(:branch_name) { branch_with_slash.name } + + it_behaves_like '404 response' do + let(:request) { put api(route, current_user) } + end + end + + context 'when branch contains an escaped slash' do + let(:branch_name) { CGI.escape(branch_with_slash.name) } - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(false) + it_behaves_like 'repository unprotected branch' + end + + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } + + it_behaves_like 'repository unprotected branch' + + context 'when branch contains a dot' do + let(:branch_name) { branch_with_dot.name } + + it_behaves_like 'repository unprotected branch' + end + end + end end + end - it "update branches with dots in branch name" do - put api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}/unprotect", user) + describe 'POST /projects/:id/repository/branches' do + let(:route) { "/projects/#{project_id}/repository/branches" } - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_with_dot.name) - expect(json_response['protected']).to eq(false) + shared_examples_for 'repository new branch' do + it 'creates a new branch' do + post api(route, current_user), branch: 'feature1', ref: branch_sha + + expect(response).to have_gitlab_http_status(201) + expect(response).to match_response_schema('public_api/v4/branch') + expect(json_response['name']).to eq('feature1') + expect(json_response['commit']['id']).to eq(branch_sha) + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { post api(route, current_user) } + end + end end - it "returns success when unprotect branch" do - put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - expect(response).to have_http_status(404) + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { post api(route) } + let(:message) { '404 Project Not Found' } + end end - it "returns success when unprotect branch again" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response).to have_http_status(200) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { post api(route, guest) } + end end - end - describe "POST /projects/:id/repository/branches" do - it "creates a new branch" do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'feature1', - ref: branch_sha + context 'when authenticated', 'as a master' do + let(:current_user) { user } - expect(response).to have_http_status(201) + context "when a protected branch doesn't already exist" do + it_behaves_like 'repository new branch' - expect(json_response['name']).to eq('feature1') - expect(json_response['commit']['id']).to eq(branch_sha) - end + context 'requesting with the escaped project full path' do + let(:project_id) { CGI.escape(project.full_path) } - it "denies for user without push access" do - post api("/projects/#{project.id}/repository/branches", guest), - branch: branch_name, - ref: branch_sha - expect(response).to have_http_status(403) + it_behaves_like 'repository new branch' + end + end end it 'returns 400 if branch name is invalid' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new design', - ref: branch_sha - expect(response).to have_http_status(400) + post api(route, user), branch: 'new design', ref: branch_sha + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') end it 'returns 400 if branch already exists' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design1', - ref: branch_sha - expect(response).to have_http_status(201) - - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design1', - ref: branch_sha - expect(response).to have_http_status(400) + post api(route, user), branch: 'new_design1', ref: branch_sha + + expect(response).to have_gitlab_http_status(201) + + post api(route, user), branch: 'new_design1', ref: branch_sha + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Branch already exists') end it 'returns 400 if ref name is invalid' do - post api("/projects/#{project.id}/repository/branches", user), - branch: 'new_design3', - ref: 'foo' - expect(response).to have_http_status(400) + post api(route, user), branch: 'new_design3', ref: 'foo' + + expect(response).to have_gitlab_http_status(400) expect(json_response['message']).to eq('Invalid reference name') end end - describe "DELETE /projects/:id/repository/branches/:branch" do + describe 'DELETE /projects/:id/repository/branches/:branch' do before do allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) end - it "removes branch" do + it 'removes branch' do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response).to have_http_status(204) + expect(response).to have_gitlab_http_status(204) end - it "removes a branch with dots in the branch name" do + it 'removes a branch with dots in the branch name' do delete api("/projects/#{project.id}/repository/branches/#{branch_with_dot.name}", user) - expect(response).to have_http_status(204) + expect(response).to have_gitlab_http_status(204) end it 'returns 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) - expect(response).to have_http_status(404) + + expect(response).to have_gitlab_http_status(404) end end - describe "DELETE /projects/:id/repository/merged_branches" do + describe 'DELETE /projects/:id/repository/merged_branches' do before do allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true) end @@ -416,13 +509,14 @@ describe API::Branches do it 'returns 202 with json body' do delete api("/projects/#{project.id}/repository/merged_branches", user) - expect(response).to have_http_status(202) + expect(response).to have_gitlab_http_status(202) expect(json_response['message']).to eql('202 Accepted') end it 'returns a 403 error if guest' do delete api("/projects/#{project.id}/repository/merged_branches", guest) - expect(response).to have_http_status(403) + + expect(response).to have_gitlab_http_status(403) end end end diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index a19870a95e8..1754ba66a96 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Events, api: true do +describe API::Events do include ApiHelpers let(:user) { create(:user) } let(:non_member) { create(:user) } diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb new file mode 100644 index 00000000000..9b24658771f --- /dev/null +++ b/spec/requests/api/group_milestones_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe API::GroupMilestones do + let(:user) { create(:user) } + let(:group) { create(:group, :private) } + let(:project) { create(:empty_project, namespace: group) } + let!(:group_member) { create(:group_member, group: group, user: user) } + let!(:closed_milestone) { create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone') } + let!(:milestone) { create(:milestone, group: group, title: 'version2', description: 'open milestone') } + + it_behaves_like 'group and project milestones', "/groups/:id/milestones" do + let(:route) { "/groups/#{group.id}/milestones" } + end + + def setup_for_group + context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + context_group.add_developer(user) + public_project.update(namespace: context_group) + context_group.reload + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 656f098aea8..1d7adc6ac45 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -510,7 +510,7 @@ describe API::Groups do describe "POST /groups/:id/projects/:project_id" do let(:project) { create(:empty_project) } - let(:project_path) { project.full_path.gsub('/', '%2F') } + let(:project_path) { CGI.escape(project.full_path) } before(:each) do allow_any_instance_of(Projects::TransferService) diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 25ec44fa036..7a1bd76af7a 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -10,8 +10,16 @@ describe API::Helpers do let(:key) { create(:key, user: user) } let(:params) { {} } - let(:env) { { 'REQUEST_METHOD' => 'GET' } } - let(:request) { Rack::Request.new(env) } + let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) } + let(:env) do + { + 'rack.input' => '', + 'rack.session' => { + _csrf_token: csrf_token + }, + 'REQUEST_METHOD' => 'GET' + } + end let(:header) { } before do @@ -58,7 +66,7 @@ describe API::Helpers do describe ".current_user" do subject { current_user } - describe "Warden authentication" do + describe "Warden authentication", :allow_forgery_protection do before do doorkeeper_guard_returns false end @@ -99,7 +107,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'PUT' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end context "POST request" do @@ -107,7 +125,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'POST' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end context "DELETE request" do @@ -115,7 +143,17 @@ describe API::Helpers do env['REQUEST_METHOD'] = 'DELETE' end - it { is_expected.to be_nil } + context 'without CSRF token' do + it { is_expected.to be_nil } + end + + context 'with CSRF token' do + before do + env['HTTP_X_CSRF_TOKEN'] = csrf_token + end + + it { is_expected.to eq(user) } + end end end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index cab3089c6b1..ce9b9ac1eb3 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -6,7 +6,7 @@ describe API::Internal do let(:project) { create(:project, :repository) } let(:secret_token) { Gitlab::Shell.secret_token } - describe "GET /internal/check", no_db: true do + describe "GET /internal/check" do it do get api("/internal/check"), secret_token: secret_token diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 9837fedb522..33cea02153e 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -71,7 +71,6 @@ describe API::Issues do expect(response).to have_http_status(401) end end - context "when authenticated" do let(:first_issue) { json_response.first } @@ -105,6 +104,42 @@ describe API::Issues do expect(json_response.second['id']).to eq(closed_issue.id) end + it 'returns issues assigned to me' do + issue2 = create(:issue, assignees: [user2], project: project) + + get api('/issues', user2), scope: 'assigned-to-me' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues authored by the given author id' do + issue2 = create(:issue, author: user2, project: project) + + get api('/issues', user), author_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues assigned to the given assignee id' do + issue2 = create(:issue, assignees: [user2], project: project) + + get api('/issues', user), assignee_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues authored by the given author id and assigned to the given assignee id' do + issue2 = create(:issue, author: user2, assignees: [user2], project: project) + + get api('/issues', user), author_id: user2.id, assignee_id: user2.id, scope: 'all' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + it 'returns issues matching given search string for title' do get api("/issues", user), search: issue.title @@ -693,6 +728,19 @@ describe API::Issues do expect(json_response['confidential']).to be_falsy end + context 'links exposure' do + it 'exposes related resources full URIs' do + get api("/projects/#{project.id}/issues/#{issue.iid}", user) + + links = json_response['_links'] + + expect(links['self']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}") + expect(links['notes']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}/notes") + expect(links['award_emoji']).to end_with("/api/v4/projects/#{project.id}/issues/#{issue.iid}/award_emoji") + expect(links['project']).to end_with("/api/v4/projects/#{project.id}") + end + end + it "returns a project issue by internal id" do get api("/projects/#{project.id}/issues/#{issue.iid}", user) diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 8d647eb1c7e..f56baf9663d 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Jobs, :api do +describe API::Jobs do let!(:project) do create(:project, :repository, public_builds: false) end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 9098ae6bcda..2760c4ffde2 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -26,6 +26,100 @@ describe API::MergeRequests do project.team << [user, :reporter] end + describe 'GET /merge_requests' do + context 'when unauthenticated' do + it 'returns authentication error' do + get api('/merge_requests') + + expect(response).to have_http_status(401) + end + end + + context 'when authenticated' do + let!(:project2) { create(:empty_project, :public, namespace: user.namespace) } + let!(:merge_request2) { create(:merge_request, :simple, author: user, assignee: user, source_project: project2, target_project: project2) } + let(:user2) { create(:user) } + + it 'returns an array of all merge requests' do + get api('/merge_requests', user), scope: :all + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request2.id) + end + + it 'does not return unauthorized merge requests' do + private_project = create(:empty_project, :private) + merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch') + + get api('/merge_requests', user), scope: :all + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .not_to include(merge_request3.id) + end + + it 'returns an array of merge requests created by current user if no scope is given' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests authored by the given user' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user), author_id: user2.id, scope: :all + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests assigned to the given user' do + merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user), assignee_id: user2.id, scope: :all + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests assigned to me' do + merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2), scope: 'assigned-to-me' + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + + it 'returns an array of merge requests created by me' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + + get api('/merge_requests', user2), scope: 'created-by-me' + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(merge_request3.id) + end + end + end + describe "GET /projects/:id/merge_requests" do context "when unauthenticated" do it "returns authentication error" do @@ -794,18 +888,24 @@ describe API::MergeRequests do it 'handles external issues' do jira_project = create(:jira_project, :public, name: 'JIR_EXT1') - issue = ExternalIssue.new("#{jira_project.name}-123", jira_project) - merge_request = create(:merge_request, :simple, author: user, assignee: user, source_project: jira_project) - merge_request.update_attribute(:description, "Closes #{issue.to_reference(jira_project)}") + ext_issue = ExternalIssue.new("#{jira_project.name}-123", jira_project) + issue = create(:issue, project: jira_project) + description = "Closes #{ext_issue.to_reference(jira_project)}\ncloses #{issue.to_reference}" + merge_request = create(:merge_request, + :simple, author: user, assignee: user, source_project: jira_project, description: description) get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.iid}/closes_issues", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect(json_response.length).to eq(2) + expect(json_response.second['title']).to eq(ext_issue.title) + expect(json_response.second['id']).to eq(ext_issue.id) + expect(json_response.second['confidential']).to be_nil expect(json_response.first['title']).to eq(issue.title) expect(json_response.first['id']).to eq(issue.id) + expect(json_response.first['confidential']).not_to be_nil end it 'returns 403 if the user has no access to the merge request' do diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb new file mode 100644 index 00000000000..fe8fdbfd7e4 --- /dev/null +++ b/spec/requests/api/project_milestones_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe API::ProjectMilestones do + let(:user) { create(:user) } + let!(:project) { create(:empty_project, namespace: user.namespace ) } + let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') } + let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') } + + before do + project.team << [user, :developer] + end + + it_behaves_like 'group and project milestones', "/projects/:id/milestones" do + let(:route) { "/projects/#{project.id}/milestones" } + end + + describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do + it 'creates an activity event when an milestone is closed' do + expect(Event).to receive(:create) + + put api("/projects/#{project.id}/milestones/#{milestone.id}", user), + state_event: 'close' + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6dbde8bad31..6ed68fcff09 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -159,6 +159,31 @@ describe API::Projects do expect(json_response.first).to include 'statistics' end + context 'when external issue tracker is enabled' do + let!(:jira_service) { create(:jira_service, project: project) } + + it 'includes open_issues_count' do + get api('/projects', user) + + expect(response.status).to eq 200 + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.first.keys).to include('open_issues_count') + expect(json_response.find { |hash| hash['id'] == project.id }.keys).to include('open_issues_count') + end + + it 'does not include open_issues_count if issues are disabled' do + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) + + get api('/projects', user) + + expect(response.status).to eq 200 + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count') + end + end + context 'and with simple=true' do it 'returns a simplified version of all the projects' do expected_keys = %w(id http_url_to_repo web_url name name_with_namespace path path_with_namespace) @@ -743,7 +768,7 @@ describe API::Projects do dot_user = create(:user, username: 'dot.user') project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace) - get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) + get api("/projects/#{CGI.escape(project.full_path)}", dot_user) expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end @@ -790,6 +815,38 @@ describe API::Projects do expect(json_response).not_to include("import_error") end + context 'links exposure' do + it 'exposes related resources full URIs' do + get api("/projects/#{project.id}", user) + + links = json_response['_links'] + + expect(links['self']).to end_with("/api/v4/projects/#{project.id}") + expect(links['issues']).to end_with("/api/v4/projects/#{project.id}/issues") + expect(links['merge_requests']).to end_with("/api/v4/projects/#{project.id}/merge_requests") + expect(links['repo_branches']).to end_with("/api/v4/projects/#{project.id}/repository/branches") + expect(links['labels']).to end_with("/api/v4/projects/#{project.id}/labels") + expect(links['events']).to end_with("/api/v4/projects/#{project.id}/events") + expect(links['members']).to end_with("/api/v4/projects/#{project.id}/members") + end + + it 'filters related URIs when their feature is not enabled' do + project = create(:empty_project, :public, + :merge_requests_disabled, + :issues_disabled, + creator_id: user.id, + namespace: user.namespace) + + get api("/projects/#{project.id}", user) + + links = json_response['_links'] + + expect(links.has_key?('merge_requests')).to be_falsy + expect(links.has_key?('issues')).to be_falsy + expect(links['self']).to end_with("/api/v4/projects/#{project.id}") + end + end + describe 'permissions' do context 'all projects' do before do diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index b71ac6c30b5..c3ed5cd8ece 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -11,7 +11,7 @@ describe API::Settings, 'Settings' do expect(json_response).to be_an Hash expect(json_response['default_projects_limit']).to eq(42) expect(json_response['password_authentication_enabled']).to be_truthy - expect(json_response['repository_storage']).to eq('default') + expect(json_response['repository_storages']).to eq(['default']) expect(json_response['koding_enabled']).to be_falsey expect(json_response['koding_url']).to be_nil expect(json_response['plantuml_enabled']).to be_falsey @@ -33,7 +33,7 @@ describe API::Settings, 'Settings' do put api("/application/settings", admin), default_projects_limit: 3, password_authentication_enabled: false, - repository_storage: 'custom', + repository_storages: ['custom'], koding_enabled: true, koding_url: 'http://koding.example.com', plantuml_enabled: true, @@ -47,7 +47,6 @@ describe API::Settings, 'Settings' do expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['password_authentication_enabled']).to be_falsey - expect(json_response['repository_storage']).to eq('custom') expect(json_response['repository_storages']).to eq(['custom']) expect(json_response['koding_enabled']).to be_truthy expect(json_response['koding_url']).to eq('http://koding.example.com') diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index 92533f4dfea..9fc73c6e092 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe API::Todos do - let(:project_1) { create(:empty_project, :test_repo) } + let(:project_1) { create(:project) } let(:project_2) { create(:empty_project) } let(:author_1) { create(:user) } let(:author_2) { create(:user) } diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 16ddade27d9..153596c2975 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -22,6 +22,7 @@ describe API::Triggers do before do stub_ci_pipeline_to_return_yaml_file + trigger.update(owner: user) end context 'Handles errors' do @@ -36,12 +37,6 @@ describe API::Triggers do expect(response).to have_http_status(404) end - - it 'returns unauthorized if token is for different project' do - post api("/projects/#{project2.id}/trigger/pipeline"), options.merge(ref: 'master') - - expect(response).to have_http_status(401) - end end context 'Have a commit' do @@ -61,7 +56,7 @@ describe API::Triggers do post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'other-branch') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No pipeline created') + expect(json_response['message']).to eq('base' => ["Reference not found"]) end context 'Validates variables' do @@ -87,12 +82,18 @@ describe API::Triggers do post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: variables, ref: 'master') expect(response).to have_http_status(201) - expect(pipeline.builds.reload.first.trigger_request.variables).to eq(variables) + expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables) end end end context 'when triggering a pipeline from a trigger token' do + it 'does not leak the presence of project when token is for different project' do + post api("/projects/#{project2.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' } + + expect(response).to have_http_status(404) + end + it 'creates builds from the ref given in the URL, not in the body' do expect do post api("/projects/#{project.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' } diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 877bde3b9a6..66b165b438b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -55,17 +55,22 @@ describe API::Users do context "when public level is restricted" do before do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) - allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true) end - it "renders 403" do - get api("/users") - expect(response).to have_http_status(403) + context 'when authenticate as a regular user' do + it "renders 403" do + get api("/users", user) + + expect(response).to have_gitlab_http_status(403) + end end - it "renders 404" do - get api("/users/#{user.id}") - expect(response).to have_http_status(404) + context 'when authenticate as an admin' do + it "renders 200" do + get api("/users", admin) + + expect(response).to have_gitlab_http_status(200) + end end end diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb index 63c5707b2e4..5cdc528e190 100644 --- a/spec/requests/api/v3/groups_spec.rb +++ b/spec/requests/api/v3/groups_spec.rb @@ -502,7 +502,7 @@ describe API::V3::Groups do describe "POST /groups/:id/projects/:project_id" do let(:project) { create(:empty_project) } - let(:project_path) { "#{project.namespace.path}%2F#{project.path}" } + let(:project_path) { CGI.escape(project.full_path) } before(:each) do allow_any_instance_of(Projects::TransferService) diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index af44ffa2331..bbfcaab1ea1 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -720,7 +720,7 @@ describe API::V3::Projects do dot_user = create(:user, username: 'dot.user') project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace) - get v3_api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) + get v3_api("/projects/#{CGI.escape(project.full_path)}", dot_user) expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end diff --git a/spec/requests/api/v3/triggers_spec.rb b/spec/requests/api/v3/triggers_spec.rb index d3de6bf13bc..60212660fb6 100644 --- a/spec/requests/api/v3/triggers_spec.rb +++ b/spec/requests/api/v3/triggers_spec.rb @@ -52,7 +52,8 @@ describe API::V3::Triggers do it 'returns bad request with no builds created if there\'s no commit for that ref' do post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No builds created') + expect(json_response['message']['base']) + .to contain_exactly('Reference not found') end context 'Validates variables' do diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c969d08d0dd..49e815ee16c 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -69,6 +69,72 @@ describe Ci::API::Builds do end end + context 'when an old image syntax is used' do + before do + build.update!(options: { image: 'codeclimate' }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "image" => "codeclimate" }) + end + end + + context 'when a new image syntax is used' do + before do + build.update!(options: { image: { name: 'codeclimate' } }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "image" => "codeclimate" }) + end + end + + context 'when an old service syntax is used' do + before do + build.update!(options: { services: ['mysql'] }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "services" => ["mysql"] }) + end + end + + context 'when a new service syntax is used' do + before do + build.update!(options: { services: [name: 'mysql'] }) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + expect(json_response["options"]).to eq({ "services" => ["mysql"] }) + end + end + + context 'when no image or service is defined' do + before do + build.update!(options: {}) + end + + it 'starts a build' do + register_builds info: { platform: :darwin } + + expect(response).to have_http_status(201) + + expect(json_response["options"]).to be_empty + end + end + context 'when there is a pending build' do it 'starts a build' do register_builds info: { platform: :darwin } diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 26b03c0f148..e481ca916ab 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,7 +5,14 @@ describe Ci::API::Triggers do let!(:trigger_token) { 'secure token' } let!(:project) { create(:project, :repository, ci_id: 10) } let!(:project2) { create(:empty_project, ci_id: 11) } - let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } + + let!(:trigger) do + create(:ci_trigger, + project: project, + token: trigger_token, + owner: create(:user)) + end + let(:options) do { token: trigger_token @@ -14,6 +21,8 @@ describe Ci::API::Triggers do before do stub_ci_pipeline_to_return_yaml_file + + project.add_developer(trigger.owner) end context 'Handles errors' do @@ -47,7 +56,8 @@ describe Ci::API::Triggers do it 'returns bad request with no builds created if there\'s no commit for that ref' do post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options expect(response).to have_http_status(400) - expect(json_response['message']).to eq('No builds created') + expect(json_response['message']['base']) + .to contain_exactly('Reference not found') end context 'Validates variables' do diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index d043ab2a974..d4a3e8b13e1 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe 'Git HTTP requests', lib: true do +describe 'Git HTTP requests' do include GitHttpHelpers include WorkhorseHelpers include UserActivitiesHelpers diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index e78d2cfdb33..e5d9d3df5a8 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'cycle analytics events', api: true do +describe 'cycle analytics events' do let(:user) { create(:user) } let(:project) { create(:project, :repository, public_builds: false) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } diff --git a/spec/routing/environments_spec.rb b/spec/routing/environments_spec.rb index 624f3c43f0a..9b70c2a24be 100644 --- a/spec/routing/environments_spec.rb +++ b/spec/routing/environments_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'environments routing', :routing do +describe 'environments routing' do let(:project) { create(:empty_project) } let(:environment) do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 65314b688a4..c02409b2e0b 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -610,7 +610,7 @@ describe 'project routing' do end end - describe Projects::Registry::TagsController, :routing do + describe Projects::Registry::TagsController, 'routing' do describe '#destroy' do it 'correctly routes to a destroy action' do expect(delete('/gitlab/gitlabhq/registry/repository/1/tags/rc1')) diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb index b92c1c28ba8..1332572fffc 100644 --- a/spec/serializers/build_details_entity_spec.rb +++ b/spec/serializers/build_details_entity_spec.rb @@ -9,47 +9,96 @@ describe BuildDetailsEntity do describe '#as_json' do let(:project) { create(:project, :repository) } - let!(:build) { create(:ci_build, :failed, project: project) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, :failed, pipeline: pipeline) } let(:request) { double('request') } - let(:entity) { described_class.new(build, request: request, current_user: user, project: project) } + + let(:entity) do + described_class.new(build, request: request, + current_user: user, + project: project) + end + subject { entity.as_json } before do allow(request).to receive(:current_user).and_return(user) end + it 'contains the needed key value pairs' do + expect(subject).to include(:coverage, :erased_at, :duration) + expect(subject).to include(:runner, :pipeline) + expect(subject).to include(:raw_path, :new_issue_path) + end + context 'when the user has access to issues and merge requests' do - let!(:merge_request) do - create(:merge_request, source_project: project, source_branch: build.ref) - end + context 'when merge request orginates from the same project' do + let(:merge_request) do + create(:merge_request, source_project: project, source_branch: build.ref) + end - before do - allow(build).to receive(:merge_request).and_return(merge_request) - end + before do + allow(build).to receive(:merge_request).and_return(merge_request) + end + + it 'contains the needed key value pairs' do + expect(subject).to include(:merge_request) + expect(subject).to include(:new_issue_path) + end - it 'contains the needed key value pairs' do - expect(subject).to include(:coverage, :erased_at, :duration) - expect(subject).to include(:runner, :pipeline) - expect(subject).to include(:raw_path, :merge_request) - expect(subject).to include(:new_issue_path) + it 'exposes correct details of the merge request' do + expect(subject[:merge_request][:iid]).to eq merge_request.iid + end + + it 'has a correct merge request path' do + expect(subject[:merge_request][:path]).to include project.full_path + end end - it 'exposes details of the merge request' do - expect(subject[:merge_request]).to include(:iid, :path) + context 'when merge request is from a fork' do + let(:fork_project) do + create(:empty_project, forked_from_project: project) + end + + let(:pipeline) { create(:ci_pipeline, project: fork_project) } + + before do + allow(build).to receive(:merge_request).and_return(merge_request) + end + + let(:merge_request) do + create(:merge_request, source_project: fork_project, + target_project: project, + source_branch: build.ref) + end + + it 'contains the needed key value pairs' do + expect(subject).to include(:merge_request) + expect(subject).to include(:new_issue_path) + end + + it 'exposes details of the merge request' do + expect(subject[:merge_request][:iid]).to eq merge_request.iid + end + + it 'has a merge request path to a target project' do + expect(subject[:merge_request][:path]) + .to include project.full_path + end end - context 'when the build has been erased' do - let!(:build) { create(:ci_build, :erasable, project: project) } + context 'when the build has not been erased' do + let(:build) { create(:ci_build, :erasable, project: project) } - it 'exposes the user whom erased the build' do + it 'exposes a build erase path' do expect(subject).to include(:erase_path) end end context 'when the build has been erased' do - let!(:build) { create(:ci_build, erased_at: Time.now, project: project, erased_by: user) } + let(:build) { create(:ci_build, :erased, project: project) } - it 'exposes the user whom erased the build' do + it 'exposes the user who erased the build' do expect(subject).to include(:erased_by) end end diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb index 9620f9665cf..8149de869f1 100644 --- a/spec/serializers/deploy_key_entity_spec.rb +++ b/spec/serializers/deploy_key_entity_spec.rb @@ -2,13 +2,15 @@ require 'spec_helper' describe DeployKeyEntity do include RequestAwareEntity - + let(:user) { create(:user) } let(:project) { create(:empty_project, :internal)} let(:project_private) { create(:empty_project, :private)} + let!(:project_pending_delete) { create(:empty_project, :internal, pending_delete: true) } let(:deploy_key) { create(:deploy_key) } let!(:deploy_key_internal) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) } let!(:deploy_key_private) { create(:deploy_keys_project, project: project_private, deploy_key: deploy_key) } + let!(:deploy_key_pending_delete) { create(:deploy_keys_project, project: project_pending_delete, deploy_key: deploy_key) } let(:entity) { described_class.new(deploy_key, user: user) } diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb index 5ca7bf2fcaf..026360e91a3 100644 --- a/spec/serializers/job_entity_spec.rb +++ b/spec/serializers/job_entity_spec.rb @@ -7,7 +7,9 @@ describe JobEntity do let(:request) { double('request') } before do + stub_not_protect_default_branch allow(request).to receive(:current_user).and_return(user) + project.add_developer(user) end @@ -77,7 +79,7 @@ describe JobEntity do project.add_developer(user) create(:protected_branch, :developers_can_merge, - name: 'master', project: project) + name: job.ref, project: job.project) end it 'contains path to play action' do @@ -90,6 +92,13 @@ describe JobEntity do end context 'when user is not allowed to trigger action' do + before do + allow(job.project).to receive(:empty_repo?).and_return(false) + + create(:protected_branch, :no_one_can_push, + name: job.ref, project: job.project) + end + it 'does not contain path to play action' do expect(subject).not_to include(:play_path) end diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb index d28dec9592a..b990370a271 100644 --- a/spec/serializers/pipeline_details_entity_spec.rb +++ b/spec/serializers/pipeline_details_entity_spec.rb @@ -9,6 +9,8 @@ describe PipelineDetailsEntity do end before do + stub_not_protect_default_branch + allow(request).to receive(:current_user).and_return(user) end @@ -52,7 +54,7 @@ describe PipelineDetailsEntity do context 'user has ability to retry pipeline' do before do - project.team << [user, :developer] + project.add_developer(user) end it 'retryable flag is true' do @@ -97,7 +99,7 @@ describe PipelineDetailsEntity do context 'when pipeline has commit statuses' do let(:pipeline) { create(:ci_empty_pipeline) } - + before do create(:generic_commit_status, pipeline: pipeline) end diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb index 46650f3a80d..5b01cc4fc9e 100644 --- a/spec/serializers/pipeline_entity_spec.rb +++ b/spec/serializers/pipeline_entity_spec.rb @@ -5,6 +5,8 @@ describe PipelineEntity do let(:request) { double('request') } before do + stub_not_protect_default_branch + allow(request).to receive(:current_user).and_return(user) end @@ -52,7 +54,7 @@ describe PipelineEntity do context 'user has ability to retry pipeline' do before do - project.team << [user, :developer] + project.add_developer(user) end it 'contains retry path' do diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 44813656aff..262bc4acb69 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -108,14 +108,35 @@ describe PipelineSerializer do end end - it 'verifies number of queries', :request_store do - recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.count).to be_within(1).of(57) - expect(recorded.cached_count).to eq(0) + shared_examples 'no N+1 queries' do + it 'verifies number of queries', :request_store do + recorded = ActiveRecord::QueryRecorder.new { subject } + expect(recorded.count).to be_within(1).of(59) + expect(recorded.cached_count).to eq(0) + end + end + + context 'with the same ref' do + let(:ref) { 'feature' } + + it_behaves_like 'no N+1 queries' + end + + context 'with different refs' do + def ref + @sequence ||= 0 + @sequence += 1 + "feature-#{@sequence}" + end + + it_behaves_like 'no N+1 queries' end def create_pipeline(status) - create(:ci_empty_pipeline, project: project, status: status).tap do |pipeline| + create(:ci_empty_pipeline, + project: project, + status: status, + ref: ref).tap do |pipeline| Ci::Build::AVAILABLE_STATUSES.each do |status| create_build(pipeline, status, status) end @@ -125,7 +146,7 @@ describe PipelineSerializer do def create_build(pipeline, stage, status) create(:ci_build, :tags, :triggered, :artifacts, pipeline: pipeline, stage: stage, - name: stage, status: status) + name: stage, status: status, ref: pipeline.ref) end end end diff --git a/spec/services/access_token_validation_service_spec.rb b/spec/services/access_token_validation_service_spec.rb index 11225fad18a..38a3f522504 100644 --- a/spec/services/access_token_validation_service_spec.rb +++ b/spec/services/access_token_validation_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AccessTokenValidationService, services: true do +describe AccessTokenValidationService do describe ".include_any_scope?" do let(:request) { double("request") } diff --git a/spec/services/after_branch_delete_service_spec.rb b/spec/services/after_branch_delete_service_spec.rb index 77ca17bc82c..bc9747d1413 100644 --- a/spec/services/after_branch_delete_service_spec.rb +++ b/spec/services/after_branch_delete_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AfterBranchDeleteService, services: true do +describe AfterBranchDeleteService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 60cb7a9440f..66a8a93b168 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Auth::ContainerRegistryAuthenticationService, services: true do +describe Auth::ContainerRegistryAuthenticationService do let(:current_project) { nil } let(:current_user) { nil } let(:current_params) { {} } diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb index 89615df1692..6e3227303fe 100644 --- a/spec/services/boards/create_service_spec.rb +++ b/spec/services/boards/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::CreateService, services: true do +describe Boards::CreateService do describe '#execute' do let(:project) { create(:empty_project) } diff --git a/spec/services/boards/issues/create_service_spec.rb b/spec/services/boards/issues/create_service_spec.rb index 360ee398f77..23ad66e454b 100644 --- a/spec/services/boards/issues/create_service_spec.rb +++ b/spec/services/boards/issues/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Issues::CreateService, services: true do +describe Boards::Issues::CreateService do describe '#execute' do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index a66cc2cd6e9..2c293088097 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Issues::ListService, services: true do +describe Boards::Issues::ListService do describe '#execute' do let(:user) { create(:user) } let(:project) { create(:empty_project) } diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb index 4ff7ac6bb2f..7dd1a601700 100644 --- a/spec/services/boards/issues/move_service_spec.rb +++ b/spec/services/boards/issues/move_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Issues::MoveService, services: true do +describe Boards::Issues::MoveService do describe '#execute' do let(:user) { create(:user) } let(:project) { create(:empty_project) } diff --git a/spec/services/boards/list_service_spec.rb b/spec/services/boards/list_service_spec.rb index dff33e4bcbb..a95b12eeaa3 100644 --- a/spec/services/boards/list_service_spec.rb +++ b/spec/services/boards/list_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::ListService, services: true do +describe Boards::ListService do describe '#execute' do let(:project) { create(:empty_project) } diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb index ebac38e68f1..86d3227a78d 100644 --- a/spec/services/boards/lists/create_service_spec.rb +++ b/spec/services/boards/lists/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Lists::CreateService, services: true do +describe Boards::Lists::CreateService do describe '#execute' do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb index af2d7c784bb..ad6ae83286d 100644 --- a/spec/services/boards/lists/destroy_service_spec.rb +++ b/spec/services/boards/lists/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Lists::DestroyService, services: true do +describe Boards::Lists::DestroyService do describe '#execute' do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } diff --git a/spec/services/boards/lists/generate_service_spec.rb b/spec/services/boards/lists/generate_service_spec.rb index ed0337662af..7dec9f7a4a5 100644 --- a/spec/services/boards/lists/generate_service_spec.rb +++ b/spec/services/boards/lists/generate_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Lists::GenerateService, services: true do +describe Boards::Lists::GenerateService do describe '#execute' do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb index 68140759600..c93788d4516 100644 --- a/spec/services/boards/lists/list_service_spec.rb +++ b/spec/services/boards/lists/list_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Lists::ListService, services: true do +describe Boards::Lists::ListService do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } let(:label) { create(:label, project: project) } diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb index 4b3bdd133f2..43e125a21df 100644 --- a/spec/services/boards/lists/move_service_spec.rb +++ b/spec/services/boards/lists/move_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Boards::Lists::MoveService, services: true do +describe Boards::Lists::MoveService do describe '#execute' do let(:project) { create(:empty_project) } let(:board) { create(:board, project: project) } diff --git a/spec/services/chat_names/authorize_user_service_spec.rb b/spec/services/chat_names/authorize_user_service_spec.rb index d50bfb0492c..d88b2504133 100644 --- a/spec/services/chat_names/authorize_user_service_spec.rb +++ b/spec/services/chat_names/authorize_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNames::AuthorizeUserService, services: true do +describe ChatNames::AuthorizeUserService do describe '#execute' do let(:service) { create(:service) } diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb index 0dc96521fa8..79aaac3aeb6 100644 --- a/spec/services/chat_names/find_user_service_spec.rb +++ b/spec/services/chat_names/find_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ChatNames::FindUserService, services: true do +describe ChatNames::FindUserService do describe '#execute' do let(:service) { create(:service) } diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index ba07c01d43f..4ec495f612e 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1,21 +1,28 @@ require 'spec_helper' -describe Ci::CreatePipelineService, :services do +describe Ci::CreatePipelineService do let(:project) { create(:project, :repository) } let(:user) { create(:admin) } + let(:ref_name) { 'refs/heads/master' } before do stub_ci_pipeline_to_return_yaml_file end describe '#execute' do - def execute_service(source: :push, after: project.commit.id, message: 'Message', ref: 'refs/heads/master') + def execute_service( + source: :push, + after: project.commit.id, + message: 'Message', + ref: ref_name, + trigger_request: nil) params = { ref: ref, before: '00000000', after: after, commits: [{ message: message }] } - described_class.new(project, user, params).execute(source) + described_class.new(project, user, params).execute( + source, trigger_request: trigger_request) end context 'valid params' do @@ -334,5 +341,209 @@ describe Ci::CreatePipelineService, :services do expect(pipeline.builds.find_by(name: 'rspec').retries_max).to eq 2 end end + + shared_examples 'when ref is protected' do + let(:user) { create(:user) } + + context 'when user is developer' do + before do + project.add_developer(user) + end + + it 'does not create a pipeline' do + expect(execute_service).not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when user is master' do + before do + project.add_master(user) + end + + it 'creates a pipeline' do + expect(execute_service).to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + + context 'when trigger belongs to no one' do + let(:user) {} + let(:trigger_request) { create(:ci_trigger_request) } + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when trigger belongs to a developer' do + let(:user) {} + + let(:trigger_request) do + create(:ci_trigger_request).tap do |request| + user = create(:user) + project.add_developer(user) + request.trigger.update(owner: user) + end + end + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .not_to be_persisted + expect(Ci::Pipeline.count).to eq(0) + end + end + + context 'when trigger belongs to a master' do + let(:user) {} + + let(:trigger_request) do + create(:ci_trigger_request).tap do |request| + user = create(:user) + project.add_master(user) + request.trigger.update(owner: user) + end + end + + it 'does not create a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + end + + context 'when ref is a protected branch' do + before do + create(:protected_branch, project: project, name: 'master') + end + + it_behaves_like 'when ref is protected' + end + + context 'when ref is a protected tag' do + let(:ref_name) { 'refs/tags/v1.0.0' } + + before do + create(:protected_tag, project: project, name: '*') + end + + it_behaves_like 'when ref is protected' + end + + context 'when ref is not protected' do + context 'when trigger belongs to no one' do + let(:user) {} + let(:trigger_request) { create(:ci_trigger_request) } + + it 'creates a pipeline' do + expect(execute_service(trigger_request: trigger_request)) + .to be_persisted + expect(Ci::Pipeline.count).to eq(1) + end + end + end + end + + describe '#allowed_to_create?' do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:ref) { 'master' } + + subject do + described_class.new(project, user, ref: ref) + .send(:allowed_to_create?, user) + end + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to merge' do + let!(:protected_branch) do + create(:protected_branch, + :developers_can_merge, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_falsey } + + context 'when developers are allowed to create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :developers_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_truthy } + end + end + end + + context 'when user is a master' do + before do + project.add_master(user) + end + + it { is_expected.to be_truthy } + + context 'when the branch is protected' do + let!(:protected_branch) do + create(:protected_branch, project: project, name: ref) + end + + it { is_expected.to be_truthy } + end + + context 'when the tag is protected' do + let(:ref) { 'v1.0.0' } + + let!(:protected_tag) do + create(:protected_tag, project: project, name: ref) + end + + it { is_expected.to be_truthy } + + context 'when no one can create the tag' do + let!(:protected_tag) do + create(:protected_tag, + :no_one_can_create, + project: project, + name: ref) + end + + it { is_expected.to be_falsey } + end + end + end + + context 'when owner cannot create pipeline' do + it { is_expected.to be_falsey } + end end end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index f2956262f4b..8295813a1ca 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -1,12 +1,15 @@ require 'spec_helper' -describe Ci::CreateTriggerRequestService, services: true do - let(:service) { described_class.new } +describe Ci::CreateTriggerRequestService do + let(:service) { described_class } let(:project) { create(:project, :repository) } - let(:trigger) { create(:ci_trigger, project: project) } + let(:trigger) { create(:ci_trigger, project: project, owner: owner) } + let(:owner) { create(:user) } before do stub_ci_pipeline_to_return_yaml_file + + project.add_developer(owner) end describe '#execute' do @@ -14,29 +17,26 @@ describe Ci::CreateTriggerRequestService, services: true do subject { service.execute(project, trigger, 'master') } context 'without owner' do - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request.builds.first).to be_kind_of(Ci::Build) } it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) } it { expect(subject.pipeline).to be_trigger } - it { expect(subject.builds.first).to be_kind_of(Ci::Build) } end context 'with owner' do - let(:owner) { create(:user) } - let(:trigger) { create(:ci_trigger, project: project, owner: owner) } - - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request).to be_kind_of(Ci::TriggerRequest) } + it { expect(subject.trigger_request.builds.first).to be_kind_of(Ci::Build) } + it { expect(subject.trigger_request.builds.first.user).to eq(owner) } it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) } it { expect(subject.pipeline).to be_trigger } it { expect(subject.pipeline.user).to eq(owner) } - it { expect(subject.builds.first).to be_kind_of(Ci::Build) } - it { expect(subject.builds.first.user).to eq(owner) } end end context 'no commit for ref' do subject { service.execute(project, trigger, 'other-branch') } - it { expect(subject).to be_nil } + it { expect(subject.pipeline).not_to be_persisted } end context 'no builds created' do @@ -46,7 +46,7 @@ describe Ci::CreateTriggerRequestService, services: true do stub_ci_pipeline_yaml_file('script: { only: [develop], script: hello World }') end - it { expect(subject).to be_nil } + it { expect(subject.pipeline).not_to be_persisted } end end end diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb new file mode 100644 index 00000000000..945a2fe1a6b --- /dev/null +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Ci::PipelineTriggerService, services: true do + let(:project) { create(:project, :repository) } + + before do + stub_ci_pipeline_to_return_yaml_file + end + + describe '#execute' do + let(:user) { create(:user) } + let(:trigger) { create(:ci_trigger, project: project, owner: user) } + let(:result) { described_class.new(project, user, params).execute } + + before do + project.add_developer(user) + end + + context 'when trigger belongs to a different project' do + let(:params) { { token: trigger.token, ref: 'master', variables: nil } } + let(:trigger) { create(:ci_trigger, project: create(:empty_project), owner: user) } + + it 'does nothing' do + expect { result }.not_to change { Ci::Pipeline.count } + end + end + + context 'when params have an existsed trigger token' do + context 'when params have an existsed ref' do + let(:params) { { token: trigger.token, ref: 'master', variables: nil } } + + it 'triggers a pipeline' do + expect { result }.to change { Ci::Pipeline.count }.by(1) + expect(result[:pipeline].ref).to eq('master') + expect(result[:pipeline].project).to eq(project) + expect(result[:pipeline].user).to eq(trigger.owner) + expect(result[:status]).to eq(:success) + end + + context 'when commit message has [ci skip]' do + before do + allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { '[ci skip]' } + end + + it 'ignores [ci skip] and create as general' do + expect { result }.to change { Ci::Pipeline.count }.by(1) + expect(result[:status]).to eq(:success) + end + end + + context 'when params have a variable' do + let(:params) { { token: trigger.token, ref: 'master', variables: variables } } + let(:variables) { { 'AAA' => 'AAA123' } } + + it 'has a variable' do + expect { result }.to change { Ci::PipelineVariable.count }.by(1) + .and change { Ci::TriggerRequest.count }.by(1) + expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables) + expect(result[:pipeline].trigger_requests.last.variables).to be_nil + end + end + end + + context 'when params have a non-existsed ref' do + let(:params) { { token: trigger.token, ref: 'invalid-ref', variables: nil } } + + it 'does not trigger a pipeline' do + expect { result }.not_to change { Ci::Pipeline.count } + expect(result[:http_status]).to eq(400) + end + end + end + + context 'when params have a non-existsed trigger token' do + let(:params) { { token: 'invalid-token', ref: nil, variables: nil } } + + it 'does not trigger a pipeline' do + expect { result }.not_to change { Ci::Pipeline.count } + expect(result).to be_nil + end + end + end +end diff --git a/spec/services/ci/play_build_service_spec.rb b/spec/services/ci/play_build_service_spec.rb index ea211de1f82..1ced26ff98d 100644 --- a/spec/services/ci/play_build_service_spec.rb +++ b/spec/services/ci/play_build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PlayBuildService, '#execute', :services do +describe Ci::PlayBuildService, '#execute' do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb index 0934833a4fa..3a702742681 100644 --- a/spec/services/ci/process_pipeline_service_spec.rb +++ b/spec/services/ci/process_pipeline_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::ProcessPipelineService, '#execute', :services do +describe Ci::ProcessPipelineService, '#execute' do let(:user) { create(:user) } let(:project) { create(:empty_project) } @@ -9,6 +9,8 @@ describe Ci::ProcessPipelineService, '#execute', :services do end before do + stub_not_protect_default_branch + project.add_developer(user) end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 62ba0b01339..23c0f715c3e 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Ci - describe RegisterJobService, services: true do + describe RegisterJobService do let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false } let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project } let!(:pending_build) { FactoryGirl.create :ci_build, pipeline: pipeline } diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index ef9927c5969..fd8a57d28f0 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::RetryBuildService, :services do +describe Ci::RetryBuildService do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:pipeline) { create(:ci_pipeline, project: project) } @@ -85,6 +85,8 @@ describe Ci::RetryBuildService, :services do context 'when user has ability to execute build' do before do + stub_not_protect_default_branch + project.add_developer(user) end @@ -131,6 +133,8 @@ describe Ci::RetryBuildService, :services do context 'when user has ability to execute build' do before do + stub_not_protect_default_branch + project.add_developer(user) end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 3e860203063..90b0c5a4dce 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::RetryPipelineService, '#execute', :services do +describe Ci::RetryPipelineService, '#execute' do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:pipeline) { create(:ci_pipeline, project: project) } @@ -244,13 +244,9 @@ describe Ci::RetryPipelineService, '#execute', :services do create_build('verify', :canceled, 1) end - it 'does not reprocess manual action' do - service.execute(pipeline) - - expect(build('test')).to be_pending - expect(build('deploy')).to be_failed - expect(build('verify')).to be_created - expect(pipeline.reload).to be_running + it 'raises an error' do + expect { service.execute(pipeline) } + .to raise_error Gitlab::Access::AccessDeniedError end end @@ -261,13 +257,9 @@ describe Ci::RetryPipelineService, '#execute', :services do create_build('verify', :canceled, 2) end - it 'does not reprocess manual action' do - service.execute(pipeline) - - expect(build('test')).to be_pending - expect(build('deploy')).to be_failed - expect(build('verify')).to be_created - expect(pipeline.reload).to be_running + it 'raises an error' do + expect { service.execute(pipeline) } + .to raise_error Gitlab::Access::AccessDeniedError end end end diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb index 98044ad232e..e2a9ed27e87 100644 --- a/spec/services/ci/stop_environments_service_spec.rb +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::StopEnvironmentsService, services: true do +describe Ci::StopEnvironmentsService do let(:project) { create(:project, :private, :repository) } let(:user) { create(:user) } diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index efefa8e8eca..0da0e57dbcd 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::UpdateBuildQueueService, :services do +describe Ci::UpdateBuildQueueService do let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } let(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb index e429fcfc72f..7cc04c92d27 100644 --- a/spec/services/ci/update_runner_service_spec.rb +++ b/spec/services/ci/update_runner_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::UpdateRunnerService, :services do +describe Ci::UpdateRunnerService do let(:runner) { create(:ci_runner) } describe '#update' do diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb index bea7c965233..9e15eae8c13 100644 --- a/spec/services/compare_service_spec.rb +++ b/spec/services/compare_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CompareService, services: true do +describe CompareService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:service) { described_class.new(project, 'feature') } diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb index 3f548688c20..38096a080a7 100644 --- a/spec/services/create_branch_service_spec.rb +++ b/spec/services/create_branch_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateBranchService, services: true do +describe CreateBranchService do let(:user) { create(:user) } let(:service) { described_class.new(project, user) } diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb index dfab6ebf372..049b082277a 100644 --- a/spec/services/create_deployment_service_spec.rb +++ b/spec/services/create_deployment_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateDeploymentService, services: true do +describe CreateDeploymentService do let(:user) { create(:user) } let(:options) { nil } @@ -244,6 +244,8 @@ describe CreateDeploymentService, services: true do context 'when job is retried' do it_behaves_like 'creates deployment' do before do + stub_not_protect_default_branch + project.add_developer(user) end diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb index 271ccfe7968..ac0a0458f56 100644 --- a/spec/services/create_release_service_spec.rb +++ b/spec/services/create_release_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe CreateReleaseService, services: true do +describe CreateReleaseService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } - let(:service) { CreateReleaseService.new(project, user) } + let(:service) { described_class.new(project, user) } it 'creates a new release' do result = service.execute(tag_name, description) diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb index d81d0fd76c9..b6ab6b8271c 100644 --- a/spec/services/create_snippet_service_spec.rb +++ b/spec/services/create_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateSnippetService, services: true do +describe CreateSnippetService do before do @user = create :user @admin = create :user, admin: true diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index c4685c9aa31..19855c9bee2 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeleteBranchService, services: true do +describe DeleteBranchService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } diff --git a/spec/services/delete_merged_branches_service_spec.rb b/spec/services/delete_merged_branches_service_spec.rb index fe21ca0b3cb..954c5d3ab73 100644 --- a/spec/services/delete_merged_branches_service_spec.rb +++ b/spec/services/delete_merged_branches_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DeleteMergedBranchesService, services: true do +describe DeleteMergedBranchesService do subject(:service) { described_class.new(project, project.owner) } let(:project) { create(:project, :repository) } diff --git a/spec/services/discussions/update_diff_position_service_spec.rb b/spec/services/discussions/update_diff_position_service_spec.rb index 177e32e13bd..c239494298b 100644 --- a/spec/services/discussions/update_diff_position_service_spec.rb +++ b/spec/services/discussions/update_diff_position_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Discussions::UpdateDiffPositionService, services: true do +describe Discussions::UpdateDiffPositionService do let(:project) { create(:project, :repository) } let(:current_user) { project.owner } let(:create_commit) { project.commit("913c66a37b4a45b9769037c55c2d238bd0942d2e") } diff --git a/spec/services/emails/create_service_spec.rb b/spec/services/emails/create_service_spec.rb index c1f477f551e..641d5538de8 100644 --- a/spec/services/emails/create_service_spec.rb +++ b/spec/services/emails/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Emails::CreateService, services: true do +describe Emails::CreateService do let(:user) { create(:user) } let(:opts) { { email: 'new@email.com' } } diff --git a/spec/services/emails/destroy_service_spec.rb b/spec/services/emails/destroy_service_spec.rb index 5e7ab4a40af..1f4294dd905 100644 --- a/spec/services/emails/destroy_service_spec.rb +++ b/spec/services/emails/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Emails::DestroyService, services: true do +describe Emails::DestroyService do let!(:user) { create(:user) } let!(:email) { create(:email, user: user) } diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index 8d067c194cc..00104ae1fd9 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe EventCreateService, services: true do +describe EventCreateService do include UserActivitiesHelpers - let(:service) { EventCreateService.new } + let(:service) { described_class.new } describe 'Issues' do describe '#open_issue' do diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index 213678c27f5..3ce01a995b4 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe GitHooksService, services: true do +describe GitHooksService do include RepoHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } - let(:service) { GitHooksService.new } + let(:service) { described_class.new } before do @blankrev = Gitlab::Git::BLANK_SHA diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index c493c08a7ae..4023c33aa59 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -3,27 +3,24 @@ require 'spec_helper' describe GitPushService, services: true do include RepoHelpers - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:blankrev) { Gitlab::Git::BLANK_SHA } + let(:oldrev) { sample_commit.parent_id } + let(:newrev) { sample_commit.id } + let(:ref) { 'refs/heads/master' } before do project.team << [user, :master] - @blankrev = Gitlab::Git::BLANK_SHA - @oldrev = sample_commit.parent_id - @newrev = sample_commit.id - @ref = 'refs/heads/master' end describe 'Push branches' do - let(:oldrev) { @oldrev } - let(:newrev) { @newrev } - subject do - execute_service(project, user, oldrev, newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end context 'new branch' do - let(:oldrev) { @blankrev } + let(:oldrev) { blankrev } it { is_expected.to be_truthy } @@ -51,7 +48,7 @@ describe GitPushService, services: true do end context 'rm branch' do - let(:newrev) { @blankrev } + let(:newrev) { blankrev } it { is_expected.to be_truthy } @@ -70,24 +67,20 @@ describe GitPushService, services: true do end describe "Git Push Data" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @push_data = service.push_data - @commit = project.commit(@newrev) - end + let(:commit) { project.commit(newrev) } - subject { @push_data } + subject { push_data_from_service(project, user, oldrev, newrev, ref) } it { is_expected.to include(object_kind: 'push') } - it { is_expected.to include(before: @oldrev) } - it { is_expected.to include(after: @newrev) } - it { is_expected.to include(ref: @ref) } + it { is_expected.to include(before: oldrev) } + it { is_expected.to include(after: newrev) } + it { is_expected.to include(ref: ref) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } it { is_expected.to include(project_id: project.id) } context "with repository data" do - subject { @push_data[:repository] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:repository] } it { is_expected.to include(name: project.name) } it { is_expected.to include(url: project.url_to_repo) } @@ -96,7 +89,7 @@ describe GitPushService, services: true do end context "with commits" do - subject { @push_data[:commits] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits] } it { is_expected.to be_an(Array) } it 'has 1 element' do @@ -104,11 +97,11 @@ describe GitPushService, services: true do end context "the commit" do - subject { @push_data[:commits].first } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first } - it { is_expected.to include(id: @commit.id) } - it { is_expected.to include(message: @commit.safe_message) } - it { expect(subject[:timestamp].in_time_zone).to eq(@commit.date.in_time_zone) } + it { is_expected.to include(id: commit.id) } + it { is_expected.to include(message: commit.safe_message) } + it { expect(subject[:timestamp].in_time_zone).to eq(commit.date.in_time_zone) } it do is_expected.to include( url: [ @@ -116,23 +109,23 @@ describe GitPushService, services: true do project.namespace.to_param, project.to_param, 'commit', - @commit.id + commit.id ].join('/') ) end context "with a author" do - subject { @push_data[:commits].first[:author] } + subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first[:author] } - it { is_expected.to include(name: @commit.author_name) } - it { is_expected.to include(email: @commit.author_email) } + it { is_expected.to include(name: commit.author_name) } + it { is_expected.to include(email: commit.author_email) } end end end end describe "Pipelines" do - subject { execute_service(project, user, @oldrev, @newrev, @ref) } + subject { execute_service(project, user, oldrev, newrev, ref) } before do stub_ci_pipeline_to_return_yaml_file @@ -145,29 +138,26 @@ describe GitPushService, services: true do end describe "Push Event" do - before do - service = execute_service(project, user, @oldrev, @newrev, @ref ) - @event = Event.find_by_action(Event::PUSHED) - @push_data = service.push_data - end + let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) } + let(:event) { Event.find_by_action(Event::PUSHED) } - it { expect(@event).not_to be_nil } - it { expect(@event.project).to eq(project) } - it { expect(@event.action).to eq(Event::PUSHED) } - it { expect(@event.data).to eq(@push_data) } + it { expect(event).not_to be_nil } + it { expect(event.project).to eq(project) } + it { expect(event.action).to eq(Event::PUSHED) } + it { expect(event.data).to eq(push_data) } context "Updates merge requests" do it "when pushing a new branch for the first time" do expect(UpdateMergeRequestsWorker).to receive(:perform_async) - .with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + .with(project.id, user.id, blankrev, 'newrev', ref) + execute_service(project, user, blankrev, 'newrev', ref ) end end context "Sends System Push data" do it "when pushing on a branch" do - expect(SystemHookPushWorker).to receive(:perform_async).with(@push_data, :push_hooks) - execute_service(project, user, @oldrev, @newrev, @ref ) + expect(SystemHookPushWorker).to receive(:perform_async).with(push_data, :push_hooks) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -177,13 +167,13 @@ describe GitPushService, services: true do it "calls the copy attributes method for the first push to the default branch" do expect(project.repository).to receive(:copy_gitattributes).with('master') - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master') + execute_service(project, user, blankrev, 'newrev', ref) end it "calls the copy attributes method for changes to the default branch" do - expect(project.repository).to receive(:copy_gitattributes).with('refs/heads/master') + expect(project.repository).to receive(:copy_gitattributes).with(ref) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master') + execute_service(project, user, 'oldrev', 'newrev', ref) end end @@ -196,7 +186,7 @@ describe GitPushService, services: true do it "does not call copy attributes method" do expect(project.repository).not_to receive(:copy_gitattributes) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end end @@ -206,7 +196,7 @@ describe GitPushService, services: true do it "when pushing a branch for the first time" do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) @@ -217,7 +207,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).to be_empty end @@ -227,7 +217,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -242,7 +232,7 @@ describe GitPushService, services: true do expect(project.default_branch).to eq("master") expect_any_instance_of(ProtectedBranches::CreateService).not_to receive(:execute) - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS]) @@ -254,7 +244,7 @@ describe GitPushService, services: true do expect(project).to receive(:execute_hooks) expect(project.default_branch).to eq("master") - execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' ) + execute_service(project, user, blankrev, 'newrev', ref) expect(project.protected_branches).not_to be_empty expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER]) expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER]) @@ -262,7 +252,7 @@ describe GitPushService, services: true do it "when pushing new commits to existing branch" do expect(project).to receive(:execute_hooks) - execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master' ) + execute_service(project, user, 'oldrev', 'newrev', ref) end end end @@ -292,7 +282,7 @@ describe GitPushService, services: true do it "creates a note if a pushed commit mentions an issue" do expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "only creates a cross-reference note if one doesn't already exist" do @@ -300,7 +290,7 @@ describe GitPushService, services: true do expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "defaults to the pushing user if the commit's author is not known" do @@ -310,16 +300,16 @@ describe GitPushService, services: true do ) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "finds references in the first push to a non-default branch" do - allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([]) - allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit]) + allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([]) + allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit]) expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author) - execute_service(project, user, @blankrev, @newrev, 'refs/heads/other' ) + execute_service(project, user, blankrev, newrev, 'refs/heads/other') end end @@ -349,14 +339,14 @@ describe GitPushService, services: true do context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do it 'sets the metric for referenced issues' do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time) end it 'does not set the metric for non-referenced issues' do non_referenced_issue = create(:issue, project: project) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil end @@ -388,18 +378,18 @@ describe GitPushService, services: true do context "to default branches" do it "closes issues" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_closed end it "adds a note indicating that the issue is now closed" do expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) end it "doesn't create additional cross-reference notes" do expect(SystemNoteService).not_to receive(:cross_reference) - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) end end @@ -411,11 +401,11 @@ describe GitPushService, services: true do it "creates cross-reference notes" do expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) end it "doesn't close issues" do - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, user, oldrev, newrev, ref) expect(Issue.find(issue.id)).to be_opened end end @@ -432,11 +422,12 @@ describe GitPushService, services: true do stub_jira_urls("JIRA-1") allow(closing_commit).to receive_messages({ - issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), - safe_message: message, - author_name: commit_author.name, - author_email: commit_author.email - }) + issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern), + safe_message: message, + author_name: commit_author.name, + author_email: commit_author.email + }) + allow(JIRA::Resource::Remotelink).to receive(:all).and_return([]) allow(project.repository).to receive_messages(commits_between: [closing_commit]) @@ -450,7 +441,7 @@ describe GitPushService, services: true do let(:message) { "this is some work.\n\nrelated to JIRA-1" } it "initiates one api call to jira server to mention the issue" do - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: /mentioned this issue in/ @@ -460,7 +451,11 @@ describe GitPushService, services: true do context "closing an issue" do let(:message) { "this is some work.\n\ncloses JIRA-1" } - let(:comment_body) { { body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json } + let(:comment_body) do + { + body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.path_with_namespace}/commit/#{closing_commit.id}]." + }.to_json + end before do open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" }) @@ -474,13 +469,13 @@ describe GitPushService, services: true do context "using right markdown" do it "initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once end it "initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, oldrev, newrev, ref) expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( body: comment_body @@ -488,21 +483,57 @@ describe GitPushService, services: true do end end - context "using wrong markdown" do - let(:message) { "this is some work.\n\ncloses #1" } + context "using internal issue reference" do + context 'when internal issues are disabled' do + before do + project.issues_enabled = false + project.save! + end + let(:message) { "this is some work.\n\ncloses #1" } - it "does not initiates one api call to jira server to close the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + it "does not initiates one api call to jira server to close the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) - expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1')) + end + + it "does not initiates one api call to jira server to comment on the issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end end - it "does not initiates one api call to jira server to comment on the issue" do - execute_service(project, commit_author, @oldrev, @newrev, @ref ) + context 'when internal issues are enabled' do + let(:issue) { create(:issue, project: project) } + let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" } - expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with( - body: comment_body - ).once + it "initiates one api call to jira server to close the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once + end + + it "initiates one api call to jira server to comment on the jira issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + + expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with( + body: comment_body + ).once + end + + it "closes the internal issue" do + execute_service(project, commit_author, oldrev, newrev, ref) + expect(issue.reload).to be_closed + end + + it "adds a note indicating that the issue is now closed" do + expect(SystemNoteService).to receive(:change_status) + .with(issue, project, commit_author, "closed", closing_commit) + execute_service(project, commit_author, oldrev, newrev, ref) + end end end end @@ -511,7 +542,7 @@ describe GitPushService, services: true do describe "empty project" do let(:project) { create(:project_empty_repo) } - let(:new_ref) { 'refs/heads/feature'} + let(:new_ref) { 'refs/heads/feature' } before do allow(project).to receive(:default_branch).and_return('feature') @@ -519,7 +550,7 @@ describe GitPushService, services: true do end it 'push to first branch updates HEAD' do - execute_service(project, user, @blankrev, @newrev, new_ref ) + execute_service(project, user, blankrev, newrev, new_ref) end end @@ -544,7 +575,7 @@ describe GitPushService, services: true do it 'does not perform housekeeping when not needed' do expect(housekeeping).not_to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end context 'when housekeeping is needed' do @@ -555,20 +586,20 @@ describe GitPushService, services: true do it 'performs housekeeping' do expect(housekeeping).to receive(:execute) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end it 'does not raise an exception' do allow(housekeeping).to receive(:try_obtain_lease).and_return(false) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end it 'increments the push counter' do expect(housekeeping).to receive(:increment!) - execute_service(project, user, @oldrev, @newrev, @ref) + execute_service(project, user, oldrev, newrev, ref) end end @@ -576,9 +607,9 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end context 'on the default branch' do @@ -621,9 +652,9 @@ describe GitPushService, services: true do let(:service) do described_class.new(project, user, - oldrev: sample_commit.parent_id, - newrev: sample_commit.id, - ref: 'refs/heads/master') + oldrev: oldrev, + newrev: newrev, + ref: ref) end it 'only schedules a limited number of commits' do @@ -645,9 +676,31 @@ describe GitPushService, services: true do end end + describe '#update_signatures' do + let(:service) do + described_class.new( + project, + user, + oldrev: oldrev, + newrev: newrev, + ref: 'refs/heads/master' + ) + end + + it 'calls CreateGpgSignatureWorker.perform_async for each commit' do + expect(CreateGpgSignatureWorker).to receive(:perform_async).with(sample_commit.id, project.id) + + execute_service(project, user, oldrev, newrev, ref) + end + end + def execute_service(project, user, oldrev, newrev, ref) - service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref ) + service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) service.execute service end + + def push_data_from_service(project, user, oldrev, newrev, ref) + execute_service(project, user, oldrev, newrev, ref).push_data + end end diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index 1fdcb420a8b..f877c145390 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe GitTagPushService, services: true do +describe GitTagPushService do include RepoHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } - let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } + let(:service) { described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } let(:oldrev) { Gitlab::Git::BLANK_SHA } let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0 @@ -184,7 +184,7 @@ describe GitTagPushService, services: true do describe "Webhooks" do context "execute webhooks" do - let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } + let(:service) { described_class.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } it "when pushing tags" do expect(project).to receive(:execute_hooks) diff --git a/spec/services/gravatar_service_spec.rb b/spec/services/gravatar_service_spec.rb index 8c4ad8c7a3e..d2cc53fe0ee 100644 --- a/spec/services/gravatar_service_spec.rb +++ b/spec/services/gravatar_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GravatarService, service: true do +describe GravatarService do describe '#execute' do let(:url) { 'http://example.com/avatar?hash=%{hash}&size=%{size}&email=%{email}&username=%{username}' } diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index fbd9026640c..b2175717a70 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Groups::CreateService, '#execute', services: true do +describe Groups::CreateService, '#execute' do let!(:user) { create(:user) } let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } } diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index d59b37bee36..c18870ea100 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Groups::DestroyService, services: true do +describe Groups::DestroyService do include DatabaseConnectionHelpers let!(:user) { create(:user) } @@ -35,6 +35,16 @@ describe Groups::DestroyService, services: true do it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) } end + context 'mattermost team' do + let!(:chat_team) { create(:chat_team, namespace: group) } + + it 'destroys the team too' do + expect_any_instance_of(Mattermost::Team).to receive(:destroy) + + destroy_group(group, user, async) + end + end + context 'file system' do context 'Sidekiq inline' do before do @@ -100,7 +110,7 @@ describe Groups::DestroyService, services: true do # Kick off the initial group destroy in a new thread, so that # it doesn't share this spec's database transaction. - Thread.new { Groups::DestroyService.new(group, user).async_execute }.join(5) + Thread.new { described_class.new(group, user).async_execute }.join(5) group_record = run_with_new_database_connection do |conn| conn.execute("SELECT * FROM namespaces WHERE id = #{group.id}").first diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index f6ad5cebd2c..9902e1aff8b 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Groups::UpdateService, services: true do +describe Groups::UpdateService do let!(:user) { create(:user) } let!(:private_group) { create(:group, :private) } let!(:internal_group) { create(:group, :internal) } diff --git a/spec/services/import_export_clean_up_service_spec.rb b/spec/services/import_export_clean_up_service_spec.rb index 81b1d327696..1875d0448cd 100644 --- a/spec/services/import_export_clean_up_service_spec.rb +++ b/spec/services/import_export_clean_up_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ImportExportCleanUpService, services: true do +describe ImportExportCleanUpService do describe '#execute' do let(:service) { described_class.new } diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb index eb9b1670c71..055aa6156cb 100644 --- a/spec/services/issuable/bulk_update_service_spec.rb +++ b/spec/services/issuable/bulk_update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issuable::BulkUpdateService, services: true do +describe Issuable::BulkUpdateService do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb index bed25fe7ccf..03f76bd428d 100644 --- a/spec/services/issues/build_service_spec.rb +++ b/spec/services/issues/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper.rb' -describe Issues::BuildService, services: true do +describe Issues::BuildService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index d6f4c694069..a03f68434de 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::CloseService, services: true do +describe Issues::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } @@ -98,13 +98,13 @@ describe Issues::CloseService, services: true do end end - context 'external issue tracker' do + context 'internal issues disabled' do before do - allow(project).to receive(:default_issues_tracker?).and_return(false) - described_class.new(project, user).close_issue(issue) + project.issues_enabled = false + project.save! end - it 'closes the issue' do + it 'does not close the issue' do expect(issue).to be_valid expect(issue).to be_opened expect(todo.reload).to be_pending diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index ae9d2b2855d..a48748e274d 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::CreateService, services: true do +describe Issues::CreateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/services/issues/duplicate_service_spec.rb b/spec/services/issues/duplicate_service_spec.rb new file mode 100644 index 00000000000..ed55664e382 --- /dev/null +++ b/spec/services/issues/duplicate_service_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Issues::DuplicateService do + let(:user) { create(:user) } + let(:canonical_project) { create(:empty_project) } + let(:duplicate_project) { create(:empty_project) } + + let(:canonical_issue) { create(:issue, project: canonical_project) } + let(:duplicate_issue) { create(:issue, project: duplicate_project) } + + subject { described_class.new(duplicate_project, user, {}) } + + describe '#execute' do + context 'when the issues passed are the same' do + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, duplicate_issue) + end + end + + context 'when the user cannot update the duplicate issue' do + before do + canonical_project.add_reporter(user) + end + + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, canonical_issue) + end + end + + context 'when the user cannot comment on the canonical issue' do + before do + duplicate_project.add_reporter(user) + end + + it 'does nothing' do + expect(subject).not_to receive(:close_service) + expect(SystemNoteService).not_to receive(:mark_duplicate_issue) + expect(SystemNoteService).not_to receive(:mark_canonical_issue_of_duplicate) + + subject.execute(duplicate_issue, canonical_issue) + end + end + + context 'when the user can mark the issue as a duplicate' do + before do + canonical_project.add_reporter(user) + duplicate_project.add_reporter(user) + end + + it 'closes the duplicate issue' do + subject.execute(duplicate_issue, canonical_issue) + + expect(duplicate_issue.reload).to be_closed + expect(canonical_issue.reload).to be_open + end + + it 'adds a system note to the duplicate issue' do + expect(SystemNoteService) + .to receive(:mark_duplicate_issue).with(duplicate_issue, duplicate_project, user, canonical_issue) + + subject.execute(duplicate_issue, canonical_issue) + end + + it 'adds a system note to the canonical issue' do + expect(SystemNoteService) + .to receive(:mark_canonical_issue_of_duplicate).with(canonical_issue, canonical_project, user, duplicate_issue) + + subject.execute(duplicate_issue, canonical_issue) + end + end + end +end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 36d5038fb95..171fc7334c4 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::MoveService, services: true do +describe Issues::MoveService do let(:user) { create(:user) } let(:author) { create(:user) } let(:title) { 'Some issue' } diff --git a/spec/services/issues/reopen_service_spec.rb b/spec/services/issues/reopen_service_spec.rb index 391ecad303a..1ff988e9b47 100644 --- a/spec/services/issues/reopen_service_spec.rb +++ b/spec/services/issues/reopen_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::ReopenService, services: true do +describe Issues::ReopenService do let(:project) { create(:empty_project) } let(:issue) { create(:issue, :closed, project: project) } diff --git a/spec/services/issues/resolve_discussions_spec.rb b/spec/services/issues/resolve_discussions_spec.rb index 86f218dec12..fac66791ffb 100644 --- a/spec/services/issues/resolve_discussions_spec.rb +++ b/spec/services/issues/resolve_discussions_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper.rb' -describe Issues::ResolveDiscussions, services: true do +describe Issues::ResolveDiscussions do class DummyService < Issues::BaseService include ::Issues::ResolveDiscussions diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index d0b991f19ab..eec2096fa34 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1,7 +1,7 @@ # coding: utf-8 require 'spec_helper' -describe Issues::UpdateService, services: true do +describe Issues::UpdateService do include EmailHelpers let(:user) { create(:user) } @@ -488,7 +488,28 @@ describe Issues::UpdateService, services: true do context 'updating mentions' do let(:mentionable) { issue } - include_examples 'updating mentions', Issues::UpdateService + include_examples 'updating mentions', described_class + end + + context 'duplicate issue' do + let(:canonical_issue) { create(:issue, project: project) } + + context 'invalid canonical_issue_id' do + it 'does not call the duplicate service' do + expect(Issues::DuplicateService).not_to receive(:new) + + update_issue(canonical_issue_id: 123456789) + end + end + + context 'valid canonical_issue_id' do + it 'calls the duplicate service with both issues' do + expect_any_instance_of(Issues::DuplicateService) + .to receive(:execute).with(issue, canonical_issue) + + update_issue(canonical_issue_id: canonical_issue.id) + end + end end include_examples 'issuable update service' do diff --git a/spec/services/labels/create_service_spec.rb b/spec/services/labels/create_service_spec.rb index 0ecab0c3475..ecb88653001 100644 --- a/spec/services/labels/create_service_spec.rb +++ b/spec/services/labels/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::CreateService, services: true do +describe Labels::CreateService do describe '#execute' do let(:project) { create(:project) } let(:group) { create(:group) } @@ -17,7 +17,7 @@ describe Labels::CreateService, services: true do context 'in a project' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(project: project) + label = described_class.new(params_with(hex_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -26,7 +26,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(project: project) + label = described_class.new(params_with(named_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -35,7 +35,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(project: project) + label = described_class.new(params_with(upcase_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -44,7 +44,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(project: project) + label = described_class.new(params_with(spaced_color)).execute(project: project) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -53,7 +53,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(project: project) + label = described_class.new(params_with(unknown_color)).execute(project: project) expect(label).not_to be_persisted end @@ -61,7 +61,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(project: project) + label = described_class.new(params_with(no_color)).execute(project: project) expect(label).not_to be_persisted end @@ -71,7 +71,7 @@ describe Labels::CreateService, services: true do context 'in a group' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(group: group) + label = described_class.new(params_with(hex_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -80,7 +80,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(group: group) + label = described_class.new(params_with(named_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -89,7 +89,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(group: group) + label = described_class.new(params_with(upcase_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -98,7 +98,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(group: group) + label = described_class.new(params_with(spaced_color)).execute(group: group) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -107,7 +107,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(group: group) + label = described_class.new(params_with(unknown_color)).execute(group: group) expect(label).not_to be_persisted end @@ -115,7 +115,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(group: group) + label = described_class.new(params_with(no_color)).execute(group: group) expect(label).not_to be_persisted end @@ -125,7 +125,7 @@ describe Labels::CreateService, services: true do context 'in admin area' do context 'with color in hex-code' do it 'creates a label' do - label = Labels::CreateService.new(params_with(hex_color)).execute(template: true) + label = described_class.new(params_with(hex_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -134,7 +134,7 @@ describe Labels::CreateService, services: true do context 'with color in allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(named_color)).execute(template: true) + label = described_class.new(params_with(named_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -143,7 +143,7 @@ describe Labels::CreateService, services: true do context 'with color in up-case allowed name' do it 'creates a label' do - label = Labels::CreateService.new(params_with(upcase_color)).execute(template: true) + label = described_class.new(params_with(upcase_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -152,7 +152,7 @@ describe Labels::CreateService, services: true do context 'with color surrounded by spaces' do it 'creates a label' do - label = Labels::CreateService.new(params_with(spaced_color)).execute(template: true) + label = described_class.new(params_with(spaced_color)).execute(template: true) expect(label).to be_persisted expect(label.color).to eq expected_saved_color @@ -161,7 +161,7 @@ describe Labels::CreateService, services: true do context 'with unknown color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(unknown_color)).execute(template: true) + label = described_class.new(params_with(unknown_color)).execute(template: true) expect(label).not_to be_persisted end @@ -169,7 +169,7 @@ describe Labels::CreateService, services: true do context 'with no color' do it 'doesn\'t create a label' do - label = Labels::CreateService.new(params_with(no_color)).execute(template: true) + label = described_class.new(params_with(no_color)).execute(template: true) expect(label).not_to be_persisted end diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb index de8f1827cce..2d3a15a1c11 100644 --- a/spec/services/labels/find_or_create_service_spec.rb +++ b/spec/services/labels/find_or_create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::FindOrCreateService, services: true do +describe Labels::FindOrCreateService do describe '#execute' do let(:group) { create(:group) } let(:project) { create(:empty_project, namespace: group) } diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb index 500afdfb916..7cea877ad88 100644 --- a/spec/services/labels/promote_service_spec.rb +++ b/spec/services/labels/promote_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::PromoteService, services: true do +describe Labels::PromoteService do describe '#execute' do let!(:user) { create(:user) } diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb index 11d5f1cad5e..f70edd3d16e 100644 --- a/spec/services/labels/transfer_service_spec.rb +++ b/spec/services/labels/transfer_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::TransferService, services: true do +describe Labels::TransferService do describe '#execute' do let(:user) { create(:admin) } let(:group_1) { create(:group) } diff --git a/spec/services/labels/update_service_spec.rb b/spec/services/labels/update_service_spec.rb index f2498ea6990..bb95fe20fbf 100644 --- a/spec/services/labels/update_service_spec.rb +++ b/spec/services/labels/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Labels::UpdateService, services: true do +describe Labels::UpdateService do describe '#execute' do let(:project) { create(:project) } @@ -20,7 +20,7 @@ describe Labels::UpdateService, services: true do context 'with color in hex-code' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(hex_color)).execute(@label) + label = described_class.new(params_with(hex_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -29,7 +29,7 @@ describe Labels::UpdateService, services: true do context 'with color in allowed name' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(named_color)).execute(@label) + label = described_class.new(params_with(named_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -38,7 +38,7 @@ describe Labels::UpdateService, services: true do context 'with color in up-case allowed name' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(upcase_color)).execute(@label) + label = described_class.new(params_with(upcase_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -47,7 +47,7 @@ describe Labels::UpdateService, services: true do context 'with color surrounded by spaces' do it 'updates the label' do - label = Labels::UpdateService.new(params_with(spaced_color)).execute(@label) + label = described_class.new(params_with(spaced_color)).execute(@label) expect(label).to be_valid expect(label.reload.color).to eq expected_saved_color @@ -56,7 +56,7 @@ describe Labels::UpdateService, services: true do context 'with unknown color' do it 'doesn\'t update the label' do - label = Labels::UpdateService.new(params_with(unknown_color)).execute(@label) + label = described_class.new(params_with(unknown_color)).execute(@label) expect(label).not_to be_valid end @@ -64,7 +64,7 @@ describe Labels::UpdateService, services: true do context 'with no color' do it 'doesn\'t update the label' do - label = Labels::UpdateService.new(params_with(no_color)).execute(@label) + label = described_class.new(params_with(no_color)).execute(@label) expect(label).not_to be_valid end diff --git a/spec/services/members/approve_access_request_service_spec.rb b/spec/services/members/approve_access_request_service_spec.rb index 7d5a66801db..ddba96b8d03 100644 --- a/spec/services/members/approve_access_request_service_spec.rb +++ b/spec/services/members/approve_access_request_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::ApproveAccessRequestService, services: true do +describe Members::ApproveAccessRequestService do let(:user) { create(:user) } let(:access_requester) { create(:user) } let(:project) { create(:empty_project, :public, :access_requestable) } diff --git a/spec/services/members/authorized_destroy_service_spec.rb b/spec/services/members/authorized_destroy_service_spec.rb index f99b11f208c..f0abbc46ca3 100644 --- a/spec/services/members/authorized_destroy_service_spec.rb +++ b/spec/services/members/authorized_destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::AuthorizedDestroyService, services: true do +describe Members::AuthorizedDestroyService do let(:member_user) { create(:user) } let(:project) { create(:empty_project, :public) } let(:group) { create(:group, :public) } diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb index 5a05ab3ea50..c73a229823d 100644 --- a/spec/services/members/create_service_spec.rb +++ b/spec/services/members/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::CreateService, services: true do +describe Members::CreateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:project_user) { create(:user) } diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 9ab7839430c..0e30b343eaf 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::DestroyService, services: true do +describe Members::DestroyService do let(:user) { create(:user) } let(:member_user) { create(:user) } let(:project) { create(:empty_project, :public) } diff --git a/spec/services/members/request_access_service_spec.rb b/spec/services/members/request_access_service_spec.rb index 814c17b9ad0..f39d4f47904 100644 --- a/spec/services/members/request_access_service_spec.rb +++ b/spec/services/members/request_access_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Members::RequestAccessService, services: true do +describe Members::RequestAccessService do let(:user) { create(:user) } shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb index d3556020d4d..fcbe0e5985f 100644 --- a/spec/services/merge_requests/assign_issues_service_spec.rb +++ b/spec/services/merge_requests/assign_issues_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::AssignIssuesService, services: true do +describe MergeRequests::AssignIssuesService do let(:user) { create(:user) } let(:project) { create(:project, :public, :repository) } let(:issue) { create(:issue, project: project) } diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb index 01ef52396d7..ea192e51f89 100644 --- a/spec/services/merge_requests/build_service_spec.rb +++ b/spec/services/merge_requests/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::BuildService, services: true do +describe MergeRequests::BuildService do include RepoHelpers let(:project) { create(:project, :repository) } @@ -19,7 +19,7 @@ describe MergeRequests::BuildService, services: true do let(:commits) { nil } let(:service) do - MergeRequests::BuildService.new(project, user, + described_class.new(project, user, description: description, source_branch: source_branch, target_branch: target_branch, @@ -207,7 +207,7 @@ describe MergeRequests::BuildService, services: true do let(:source_branch) { '12345-fix-issue' } before do - allow(project).to receive(:default_issues_tracker?).and_return(false) + allow(project).to receive(:external_issue_tracker).and_return(true) end it 'sets the title to: Resolves External Issue $issue-iid' do diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 074d4672b06..04bf267d167 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CloseService, services: true do +describe MergeRequests::CloseService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb index 1588d30c394..492b55cdece 100644 --- a/spec/services/merge_requests/create_from_issue_service_spec.rb +++ b/spec/services/merge_requests/create_from_issue_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CreateFromIssueService, services: true do +describe MergeRequests::CreateFromIssueService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:issue) { create(:issue, project: project) } diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index 36a2b672473..8fef480274d 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CreateService, services: true do +describe MergeRequests::CreateService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:assignee) { create(:user) } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 19d9e4049fe..e593bfeeaf7 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::MergeService, services: true do +describe MergeRequests::MergeService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) } @@ -13,7 +13,7 @@ describe MergeRequests::MergeService, services: true do describe '#execute' do context 'valid params' do - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } before do allow(service).to receive(:execute_hooks) @@ -112,7 +112,7 @@ describe MergeRequests::MergeService, services: true do context 'closes related todos' do let(:merge_request) { create(:merge_request, assignee: user, author: user) } let(:project) { merge_request.project } - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } let!(:todo) do create(:todo, :assigned, project: project, @@ -136,7 +136,7 @@ describe MergeRequests::MergeService, services: true do context 'source branch removal' do context 'when the source branch is protected' do let(:service) do - MergeRequests::MergeService.new(project, user, should_remove_source_branch: '1') + described_class.new(project, user, should_remove_source_branch: '1') end before do @@ -151,7 +151,7 @@ describe MergeRequests::MergeService, services: true do context 'when the source branch is the default branch' do let(:service) do - MergeRequests::MergeService.new(project, user, should_remove_source_branch: '1') + described_class.new(project, user, should_remove_source_branch: '1') end before do @@ -169,7 +169,7 @@ describe MergeRequests::MergeService, services: true do let(:service) do merge_request.merge_params['force_remove_source_branch'] = '1' merge_request.save! - MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') + described_class.new(project, user, commit_message: 'Awesome message') end it 'removes the source branch using the author user' do @@ -182,7 +182,7 @@ describe MergeRequests::MergeService, services: true do context 'when MR merger set the source branch to be removed' do let(:service) do - MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message', should_remove_source_branch: '1') + described_class.new(project, user, commit_message: 'Awesome message', should_remove_source_branch: '1') end it 'removes the source branch using the current user' do @@ -196,7 +196,7 @@ describe MergeRequests::MergeService, services: true do end context "error handling" do - let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } before do allow(Rails.logger).to receive(:error) diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb index a20b32eda5f..a37cdab8928 100644 --- a/spec/services/merge_requests/post_merge_service_spec.rb +++ b/spec/services/merge_requests/post_merge_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::PostMergeService, services: true do +describe MergeRequests::PostMergeService do let(:user) { create(:user) } let(:merge_request) { create(:merge_request, assignee: user) } let(:project) { merge_request.project } diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 74dcf152cb8..2af2485eeed 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe MergeRequests::RefreshService, services: true do +describe MergeRequests::RefreshService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } - let(:service) { MergeRequests::RefreshService } + let(:service) { described_class } describe '#execute' do before do diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index 6cc403bdb7f..ce1e9f2ff45 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::ReopenService, services: true do +describe MergeRequests::ReopenService do let(:user) { create(:user) } let(:user2) { create(:user) } let(:guest) { create(:user) } diff --git a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb index 7ddd812e513..e3fd906fe7b 100644 --- a/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb +++ b/spec/services/merge_requests/resolved_discussion_notification_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::ResolvedDiscussionNotificationService, services: true do +describe MergeRequests::ResolvedDiscussionNotificationService do let(:merge_request) { create(:merge_request) } let(:user) { create(:user) } let(:project) { merge_request.project } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index be62584ec0e..dd3ac9c4ac6 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::UpdateService, services: true do +describe MergeRequests::UpdateService do include EmailHelpers let(:project) { create(:project, :repository) } @@ -55,7 +55,7 @@ describe MergeRequests::UpdateService, services: true do } end - let(:service) { MergeRequests::UpdateService.new(project, user, opts) } + let(:service) { described_class.new(project, user, opts) } before do allow(service).to receive(:execute_hooks) @@ -145,7 +145,7 @@ describe MergeRequests::UpdateService, services: true do } end - let(:service) { MergeRequests::UpdateService.new(project, user, opts) } + let(:service) { described_class.new(project, user, opts) } context 'without pipeline' do before do @@ -205,7 +205,7 @@ describe MergeRequests::UpdateService, services: true do context 'with a non-authorised user' do let(:visitor) { create(:user) } - let(:service) { MergeRequests::UpdateService.new(project, visitor, opts) } + let(:service) { described_class.new(project, visitor, opts) } before do merge_request.update_attribute(:merge_error, 'Error') @@ -348,7 +348,7 @@ describe MergeRequests::UpdateService, services: true do opts = { label_ids: [label.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_email(subscriber) @@ -364,7 +364,7 @@ describe MergeRequests::UpdateService, services: true do opts = { label_ids: [label.id, label2.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_not_email(subscriber) @@ -375,7 +375,7 @@ describe MergeRequests::UpdateService, services: true do opts = { label_ids: [label2.id] } perform_enqueued_jobs do - @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request = described_class.new(project, user, opts).execute(merge_request) end should_not_email(subscriber) @@ -386,7 +386,7 @@ describe MergeRequests::UpdateService, services: true do context 'updating mentions' do let(:mentionable) { merge_request } - include_examples 'updating mentions', MergeRequests::UpdateService + include_examples 'updating mentions', described_class end context 'when MergeRequest has tasks' do diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb index d581b94ff43..fa0686d8061 100644 --- a/spec/services/milestones/close_service_spec.rb +++ b/spec/services/milestones/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestones::CloseService, services: true do +describe Milestones::CloseService do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) } @@ -11,7 +11,7 @@ describe Milestones::CloseService, services: true do describe '#execute' do before do - Milestones::CloseService.new(project, user, {}).execute(milestone) + described_class.new(project, user, {}).execute(milestone) end it { expect(milestone).to be_valid } diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb index 6d29edb449a..c6fe8e65912 100644 --- a/spec/services/milestones/create_service_spec.rb +++ b/spec/services/milestones/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestones::CreateService, services: true do +describe Milestones::CreateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } @@ -14,7 +14,7 @@ describe Milestones::CreateService, services: true do description: 'Patch release to fix security issue' } - @milestone = Milestones::CreateService.new(project, user, opts).execute + @milestone = described_class.new(project, user, opts).execute end it { expect(@milestone).to be_valid } diff --git a/spec/services/milestones/destroy_service_spec.rb b/spec/services/milestones/destroy_service_spec.rb index 8d1fe3ae2c1..5739386dd0d 100644 --- a/spec/services/milestones/destroy_service_spec.rb +++ b/spec/services/milestones/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestones::DestroyService, services: true do +describe Milestones::DestroyService do let(:user) { create(:user) } let(:project) { create(:project) } let(:milestone) { create(:milestone, title: 'Milestone v1.0', project: project) } diff --git a/spec/services/note_summary_spec.rb b/spec/services/note_summary_spec.rb index 39f06f8f8e7..dda579c7080 100644 --- a/spec/services/note_summary_spec.rb +++ b/spec/services/note_summary_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe NoteSummary, services: true do +describe NoteSummary do let(:project) { build(:empty_project) } let(:noteable) { build(:issue) } let(:user) { build(:user) } diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb index 133175769ca..6e1c1fe6c02 100644 --- a/spec/services/notes/build_service_spec.rb +++ b/spec/services/notes/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::BuildService, services: true do +describe Notes::BuildService do let(:note) { create(:discussion_note_on_issue) } let(:project) { note.project } let(:author) { note.author } diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 152c6d20daa..c08a5a940bb 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::CreateService, services: true do +describe Notes::CreateService do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:user) { create(:user) } diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb index f53f96e0c2b..4330190caaa 100644 --- a/spec/services/notes/destroy_service_spec.rb +++ b/spec/services/notes/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::DestroyService, services: true do +describe Notes::DestroyService do describe '#execute' do it 'deletes a note' do project = create(:empty_project) diff --git a/spec/services/notes/post_process_service_spec.rb b/spec/services/notes/post_process_service_spec.rb index e33a611929b..bf9320e5fce 100644 --- a/spec/services/notes/post_process_service_spec.rb +++ b/spec/services/notes/post_process_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::PostProcessService, services: true do +describe Notes::PostProcessService do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:user) { create(:user) } @@ -21,7 +21,7 @@ describe Notes::PostProcessService, services: true do expect(project).to receive(:execute_hooks) expect(project).to receive(:execute_services) - Notes::PostProcessService.new(@note).execute + described_class.new(@note).execute end end end diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb index 9a98499826f..fc4cd3dc2b7 100644 --- a/spec/services/notes/quick_actions_service_spec.rb +++ b/spec/services/notes/quick_actions_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::QuickActionsService, services: true do +describe Notes::QuickActionsService do shared_context 'note on noteable' do let(:project) { create(:empty_project) } let(:master) { create(:user).tap { |u| project.team << [u, :master] } } diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb index 905e2f46bde..dea2dc02d00 100644 --- a/spec/services/notes/update_service_spec.rb +++ b/spec/services/notes/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::UpdateService, services: true do +describe Notes::UpdateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb index dfe1ee7c41e..77233dc1b2f 100644 --- a/spec/services/notification_recipient_service_spec.rb +++ b/spec/services/notification_recipient_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe NotificationRecipientService, services: true do +describe NotificationRecipientService do set(:user) { create(:user) } set(:project) { create(:empty_project, :public) } set(:issue) { create(:issue, project: project) } diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 4fc5eb0a527..49d6fc7853f 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe NotificationService, services: true do +describe NotificationService do include EmailHelpers - let(:notification) { NotificationService.new } + let(:notification) { described_class.new } let(:assignee) { create(:user) } around(:each) do |example| @@ -93,6 +93,18 @@ describe NotificationService, services: true do end end + describe 'GpgKeys' do + describe '#new_gpg_key' do + let!(:key) { create(:gpg_key) } + + it { expect(notification.new_gpg_key(key)).to be_truthy } + + it 'sends email to key owner' do + expect{ notification.new_gpg_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + end + end + end + describe 'Email' do describe '#new_email' do let!(:email) { create(:email) } diff --git a/spec/services/pages_service_spec.rb b/spec/services/pages_service_spec.rb index cf38c7c75e5..f8db6900a0a 100644 --- a/spec/services/pages_service_spec.rb +++ b/spec/services/pages_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe PagesService, services: true do +describe PagesService do let(:build) { create(:ci_build) } let(:data) { Gitlab::DataBuilder::Build.build(build) } - let(:service) { PagesService.new(data) } + let(:service) { described_class.new(data) } before do allow(Gitlab.config.pages).to receive(:enabled).and_return(true) diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb index c198c3eedfc..fc7238862ab 100644 --- a/spec/services/projects/autocomplete_service_spec.rb +++ b/spec/services/projects/autocomplete_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::AutocompleteService, services: true do +describe Projects::AutocompleteService do describe '#issues' do describe 'confidential issues' do let(:author) { create(:user) } diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 40298dcb723..b0dc7488b5f 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::CreateService, '#execute', services: true do +describe Projects::CreateService, '#execute' do let(:user) { create :user } let(:opts) do { diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index b399d3402fd..85b05ef6d05 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::DestroyService, services: true do +describe Projects::DestroyService do let!(:user) { create(:user) } let!(:project) { create(:project, :repository, namespace: user.namespace) } let!(:path) { project.repository.path_to_repo } @@ -36,6 +36,27 @@ describe Projects::DestroyService, services: true do end end + shared_examples 'handles errors thrown during async destroy' do |error_message| + it 'does not allow the error to bubble up' do + expect do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + end.not_to raise_error + end + + it 'unmarks the project as "pending deletion"' do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + + expect(project.reload.pending_delete).to be(false) + end + + it 'stores an error message in `projects.delete_error`' do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + + expect(project.reload.delete_error).to be_present + expect(project.delete_error).to include(error_message) + end + end + context 'Sidekiq inline' do before do # Run sidekiq immediatly to check that renamed repository will be removed @@ -60,7 +81,7 @@ describe Projects::DestroyService, services: true do before do new_user = create(:user) project.team.add_user(new_user, Gitlab::Access::DEVELOPER) - allow_any_instance_of(Projects::DestroyService).to receive(:flush_caches).and_raise(::Redis::CannotConnectError) + allow_any_instance_of(described_class).to receive(:flush_caches).and_raise(::Redis::CannotConnectError) end it 'keeps project team intact upon an error' do @@ -89,10 +110,51 @@ describe Projects::DestroyService, services: true do end it_behaves_like 'deleting the project with pipeline and build' - end - context 'with execute' do - it_behaves_like 'deleting the project with pipeline and build' + context 'errors' do + context 'when `remove_legacy_registry_tags` fails' do + before do + expect_any_instance_of(described_class) + .to receive(:remove_legacy_registry_tags).and_return(false) + end + + it_behaves_like 'handles errors thrown during async destroy', "Failed to remove some tags" + end + + context 'when `remove_repository` fails' do + before do + expect_any_instance_of(described_class) + .to receive(:remove_repository).and_return(false) + end + + it_behaves_like 'handles errors thrown during async destroy', "Failed to remove project repository" + end + + context 'when `execute` raises expected error' do + before do + expect_any_instance_of(Project) + .to receive(:destroy!).and_raise(StandardError.new("Other error message")) + end + + it_behaves_like 'handles errors thrown during async destroy', "Other error message" + end + + context 'when `execute` raises unexpected error' do + before do + expect_any_instance_of(Project) + .to receive(:destroy!).and_raise(Exception.new("Other error message")) + end + + it 'allows error to bubble up and rolls back project deletion' do + expect do + Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + end.to raise_error + + expect(project.reload.pending_delete).to be(false) + expect(project.delete_error).to include("Other error message") + end + end + end end describe 'container registry' do @@ -119,8 +181,7 @@ describe Projects::DestroyService, services: true do expect_any_instance_of(ContainerRepository) .to receive(:delete_tags!).and_return(false) - expect{ destroy_project(project, user) } - .to raise_error(ActiveRecord::RecordNotDestroyed) + expect(destroy_project(project, user)).to be false end end end @@ -145,8 +206,7 @@ describe Projects::DestroyService, services: true do expect_any_instance_of(ContainerRepository) .to receive(:delete_tags!).and_return(false) - expect { destroy_project(project, user) } - .to raise_error(Projects::DestroyService::DestroyError) + expect(destroy_project(project, user)).to be false end end end diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index 33b267c069c..701f6cc8c6a 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::DownloadService, services: true do +describe Projects::DownloadService do describe 'File service' do before do @user = create(:user) diff --git a/spec/services/projects/enable_deploy_key_service_spec.rb b/spec/services/projects/enable_deploy_key_service_spec.rb index 78626fbad4b..9b8c24ba112 100644 --- a/spec/services/projects/enable_deploy_key_service_spec.rb +++ b/spec/services/projects/enable_deploy_key_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::EnableDeployKeyService, services: true do +describe Projects::EnableDeployKeyService do let(:deploy_key) { create(:deploy_key, public: true) } let(:project) { create(:empty_project) } let(:user) { project.creator} diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 0df81f3abcb..c90536ba346 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::ForkService, services: true do +describe Projects::ForkService do describe 'fork by user' do before do @from_user = create(:user) diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index e855de38037..67fe610f92a 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::ImportService, services: true do +describe Projects::ImportService do let!(:project) { create(:empty_project) } let(:user) { project.creator } diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb index 3688f6d4e23..8777e63a101 100644 --- a/spec/services/projects/participants_service_spec.rb +++ b/spec/services/projects/participants_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::ParticipantsService, services: true do +describe Projects::ParticipantsService do describe '#groups' do describe 'avatar_url' do let(:project) { create(:empty_project, :public) } diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb index a6d43c4f0f1..2763437f184 100644 --- a/spec/services/projects/propagate_service_template_spec.rb +++ b/spec/services/projects/propagate_service_template_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::PropagateServiceTemplate, services: true do +describe Projects::PropagateServiceTemplate do describe '.propagate' do let!(:service_template) do PushoverService.create( diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 441a5276c56..ae32e85b2a7 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::TransferService, services: true do +describe Projects::TransferService do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, :repository, namespace: user.namespace) } @@ -37,7 +37,7 @@ describe Projects::TransferService, services: true do end it 'executes system hooks' do - expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks) + expect_any_instance_of(described_class).to receive(:execute_system_hooks) transfer_project(project, user, group) end @@ -80,7 +80,7 @@ describe Projects::TransferService, services: true do end it "doesn't run system hooks" do - expect_any_instance_of(Projects::TransferService).not_to receive(:execute_system_hooks) + expect_any_instance_of(described_class).not_to receive(:execute_system_hooks) attempt_project_transfer end diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb index d34652bd7ac..2ae8d5f7c54 100644 --- a/spec/services/projects/unlink_fork_service_spec.rb +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -describe Projects::UnlinkForkService, services: true do - subject { Projects::UnlinkForkService.new(fork_project, user) } +describe Projects::UnlinkForkService do + subject { described_class.new(fork_project, user) } let(:fork_link) { create(:forked_project_link) } let(:fork_project) { fork_link.forked_to_project } diff --git a/spec/services/projects/update_pages_configuration_service_spec.rb b/spec/services/projects/update_pages_configuration_service_spec.rb index 8b329bc21c3..42925e73978 100644 --- a/spec/services/projects/update_pages_configuration_service_spec.rb +++ b/spec/services/projects/update_pages_configuration_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::UpdatePagesConfigurationService, services: true do +describe Projects::UpdatePagesConfigurationService do let(:project) { create(:empty_project) } subject { described_class.new(project) } diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index fc0a17296f3..aa6ad6340f5 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -96,6 +96,78 @@ describe Projects::UpdatePagesService do expect(execute).not_to eq(:success) end + describe 'maximum pages artifacts size' do + let(:metadata) { spy('metadata') } + + before do + file = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip') + metafile = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta') + + build.update_attributes(artifacts_file: file) + build.update_attributes(artifacts_metadata: metafile) + + allow(build).to receive(:artifacts_metadata_entry) + .and_return(metadata) + end + + shared_examples 'pages size limit exceeded' do + it 'limits the maximum size of gitlab pages' do + subject.execute + + expect(deploy_status.description) + .to match(/artifacts for pages are too large/) + end + end + + context 'when maximum pages size is set to zero' do + before do + stub_application_setting(max_pages_size: 0) + end + + context 'when page size does not exceed internal maximum' do + before do + allow(metadata).to receive(:total_size).and_return(200.megabytes) + end + + it 'updates pages correctly' do + subject.execute + + expect(deploy_status.description).not_to be_present + end + end + + context 'when pages size does exceed internal maximum' do + before do + allow(metadata).to receive(:total_size).and_return(2.terabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + end + + context 'when pages size is greater than max size setting' do + before do + stub_application_setting(max_pages_size: 200) + allow(metadata).to receive(:total_size).and_return(201.megabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + + context 'when max size setting is greater than internal max size' do + before do + stub_application_setting(max_pages_size: 3.terabytes / 1.megabyte) + allow(metadata).to receive(:total_size).and_return(2.terabytes) + end + + it_behaves_like 'pages size limit exceeded' + end + end + + def deploy_status + GenericCommitStatus.find_by(name: 'pages:deploy') + end + def execute subject.execute[:status] end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index fd4011ad606..d7b0df9a671 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::UpdateService, '#execute', :services do +describe Projects::UpdateService, '#execute' do let(:user) { create(:user) } let(:admin) { create(:admin) } @@ -103,7 +103,7 @@ describe Projects::UpdateService, '#execute', :services do end end - context 'when renaming project that contains container images' do + context 'when updating a project that contains container images' do before do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: /image/, tags: %w[rc1]) @@ -116,6 +116,13 @@ describe Projects::UpdateService, '#execute', :services do expect(result).to include(status: :error) expect(result[:message]).to match(/contains container registry tags/) end + + it 'allows to update other settings' do + result = update_project(project, admin, public_builds: true) + + expect(result[:status]).to eq :success + expect(project.reload.public_builds).to be true + end end context 'when passing invalid parameters' do diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb index 6ea8f309981..592f9b5929e 100644 --- a/spec/services/protected_branches/create_service_spec.rb +++ b/spec/services/protected_branches/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedBranches::CreateService, services: true do +describe ProtectedBranches::CreateService do let(:project) { create(:empty_project) } let(:user) { project.owner } let(:params) do diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb index 62bdd49a4d7..5698101af54 100644 --- a/spec/services/protected_branches/update_service_spec.rb +++ b/spec/services/protected_branches/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedBranches::UpdateService, services: true do +describe ProtectedBranches::UpdateService do let(:protected_branch) { create(:protected_branch) } let(:project) { protected_branch.project } let(:user) { project.owner } diff --git a/spec/services/protected_tags/create_service_spec.rb b/spec/services/protected_tags/create_service_spec.rb index d91a58e8de5..3a3cc9c1573 100644 --- a/spec/services/protected_tags/create_service_spec.rb +++ b/spec/services/protected_tags/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedTags::CreateService, services: true do +describe ProtectedTags::CreateService do let(:project) { create(:empty_project) } let(:user) { project.owner } let(:params) do diff --git a/spec/services/protected_tags/update_service_spec.rb b/spec/services/protected_tags/update_service_spec.rb index e78fde4c48d..d333430088d 100644 --- a/spec/services/protected_tags/update_service_spec.rb +++ b/spec/services/protected_tags/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ProtectedTags::UpdateService, services: true do +describe ProtectedTags::UpdateService do let(:protected_tag) { create(:protected_tag) } let(:project) { protected_tag.project } let(:user) { project.owner } diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index a2db3f68ff7..42a4a2591ea 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe QuickActions::InterpretService, services: true do +describe QuickActions::InterpretService do let(:project) { create(:empty_project, :public) } let(:developer) { create(:user) } let(:developer2) { create(:user) } @@ -261,6 +261,15 @@ describe QuickActions::InterpretService, services: true do end end + shared_examples 'duplicate command' do + it 'fetches issue and populates canonical_issue_id if content contains /duplicate issue_reference' do + issue_duplicate # populate the issue + _, updates = service.execute(content, issuable) + + expect(updates).to eq(canonical_issue_id: issue_duplicate.id) + end + end + it_behaves_like 'reopen command' do let(:content) { '/reopen' } let(:issuable) { issue } @@ -644,6 +653,41 @@ describe QuickActions::InterpretService, services: true do let(:issuable) { issue } end + context '/duplicate command' do + it_behaves_like 'duplicate command' do + let(:issue_duplicate) { create(:issue, project: project) } + let(:content) { "/duplicate #{issue_duplicate.to_reference}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { '/duplicate' } + let(:issuable) { issue } + end + + context 'cross project references' do + it_behaves_like 'duplicate command' do + let(:other_project) { create(:empty_project, :public) } + let(:issue_duplicate) { create(:issue, project: other_project) } + let(:content) { "/duplicate #{issue_duplicate.to_reference(project)}" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:content) { "/duplicate imaginary#1234" } + let(:issuable) { issue } + end + + it_behaves_like 'empty command' do + let(:other_project) { create(:empty_project, :private) } + let(:issue_duplicate) { create(:issue, project: other_project) } + + let(:content) { "/duplicate #{issue_duplicate.to_reference(project)}" } + let(:issuable) { issue } + end + end + end + context 'when current_user cannot :admin_issue' do let(:visitor) { create(:user) } let(:issue) { create(:issue, project: project, author: visitor) } @@ -693,6 +737,11 @@ describe QuickActions::InterpretService, services: true do let(:content) { '/remove_due_date' } let(:issuable) { issue } end + + it_behaves_like 'empty command' do + let(:content) { '/duplicate #{issue.to_reference}' } + let(:issuable) { issue } + end end context '/award command' do diff --git a/spec/services/repair_ldap_blocked_user_service_spec.rb b/spec/services/repair_ldap_blocked_user_service_spec.rb index 87192457298..bf79cfe74b7 100644 --- a/spec/services/repair_ldap_blocked_user_service_spec.rb +++ b/spec/services/repair_ldap_blocked_user_service_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe RepairLdapBlockedUserService, services: true do +describe RepairLdapBlockedUserService do let(:user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:identity) { user.ldap_identity } - subject(:service) { RepairLdapBlockedUserService.new(user) } + subject(:service) { described_class.new(user) } describe '#execute' do it 'changes to normal block after destroying last ldap identity' do diff --git a/spec/services/repository_archive_clean_up_service_spec.rb b/spec/services/repository_archive_clean_up_service_spec.rb index 842585f9e54..2d7fa3f80f7 100644 --- a/spec/services/repository_archive_clean_up_service_spec.rb +++ b/spec/services/repository_archive_clean_up_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe RepositoryArchiveCleanUpService, services: true do +describe RepositoryArchiveCleanUpService do describe '#execute' do subject(:service) { described_class.new } diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb index cbf4f56213d..de921573b1a 100644 --- a/spec/services/search/global_service_spec.rb +++ b/spec/services/search/global_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Search::GlobalService, services: true do +describe Search::GlobalService do let(:user) { create(:user) } let(:internal_user) { create(:user) } @@ -16,7 +16,7 @@ describe Search::GlobalService, services: true do describe '#execute' do context 'unauthenticated' do it 'returns public projects only' do - results = Search::GlobalService.new(nil, search: "searchable").execute + results = described_class.new(nil, search: "searchable").execute expect(results.objects('projects')).to match_array [public_project] end @@ -24,19 +24,19 @@ describe Search::GlobalService, services: true do context 'authenticated' do it 'returns public, internal and private projects' do - results = Search::GlobalService.new(user, search: "searchable").execute + results = described_class.new(user, search: "searchable").execute expect(results.objects('projects')).to match_array [public_project, found_project, internal_project] end it 'returns only public & internal projects' do - results = Search::GlobalService.new(internal_user, search: "searchable").execute + results = described_class.new(internal_user, search: "searchable").execute expect(results.objects('projects')).to match_array [internal_project, public_project] end it 'namespace name is searchable' do - results = Search::GlobalService.new(user, search: found_project.namespace.path).execute + results = described_class.new(user, search: found_project.namespace.path).execute expect(results.objects('projects')).to match_array [found_project] end diff --git a/spec/services/search/group_service_spec.rb b/spec/services/search/group_service_spec.rb index 38f264f6e7b..cb3f02d2883 100644 --- a/spec/services/search/group_service_spec.rb +++ b/spec/services/search/group_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Search::GroupService, services: true do +describe Search::GroupService do shared_examples_for 'group search' do context 'finding projects by name' do let(:user) { create(:user) } @@ -17,7 +17,7 @@ describe Search::GroupService, services: true do let!(:project2) { create(:empty_project, :internal, namespace: nested_group, name: "Inner #{term} 2") } let!(:project3) { create(:empty_project, :internal, namespace: nested_group.parent, name: "Outer #{term}") } - let(:results) { Search::GroupService.new(user, search_group, search: term).execute } + let(:results) { described_class.new(user, search_group, search: term).execute } subject { results.objects('projects') } context 'in parent group' do diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb index 14f3301d9f4..69438a3fa36 100644 --- a/spec/services/search/snippet_service_spec.rb +++ b/spec/services/search/snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Search::SnippetService, services: true do +describe Search::SnippetService do let(:author) { create(:author) } let(:project) { create(:empty_project) } diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index 5cf989105d0..a6ef7561bc8 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SearchService, services: true do +describe SearchService do let(:user) { create(:user) } let(:accessible_group) { create(:group, :private) } @@ -22,7 +22,7 @@ describe SearchService, services: true do describe '#project' do context 'when the project is accessible' do it 'returns the project' do - project = SearchService.new(user, project_id: accessible_project.id).project + project = described_class.new(user, project_id: accessible_project.id).project expect(project).to eq accessible_project end @@ -31,7 +31,7 @@ describe SearchService, services: true do search_project = create :empty_project search_project.add_guest(user) - project = SearchService.new(user, project_id: search_project.id).project + project = described_class.new(user, project_id: search_project.id).project expect(project).to eq search_project end @@ -39,7 +39,7 @@ describe SearchService, services: true do context 'when the project is not accessible' do it 'returns nil' do - project = SearchService.new(user, project_id: inaccessible_project.id).project + project = described_class.new(user, project_id: inaccessible_project.id).project expect(project).to be_nil end @@ -47,7 +47,7 @@ describe SearchService, services: true do context 'when there is no project_id' do it 'returns nil' do - project = SearchService.new(user).project + project = described_class.new(user).project expect(project).to be_nil end @@ -57,7 +57,7 @@ describe SearchService, services: true do describe '#group' do context 'when the group is accessible' do it 'returns the group' do - group = SearchService.new(user, group_id: accessible_group.id).group + group = described_class.new(user, group_id: accessible_group.id).group expect(group).to eq accessible_group end @@ -65,7 +65,7 @@ describe SearchService, services: true do context 'when the group is not accessible' do it 'returns nil' do - group = SearchService.new(user, group_id: inaccessible_group.id).group + group = described_class.new(user, group_id: inaccessible_group.id).group expect(group).to be_nil end @@ -73,7 +73,7 @@ describe SearchService, services: true do context 'when there is no group_id' do it 'returns nil' do - group = SearchService.new(user).group + group = described_class.new(user).group expect(group).to be_nil end @@ -83,7 +83,7 @@ describe SearchService, services: true do describe '#show_snippets?' do context 'when :snippets is \'true\'' do it 'returns true' do - show_snippets = SearchService.new(user, snippets: 'true').show_snippets? + show_snippets = described_class.new(user, snippets: 'true').show_snippets? expect(show_snippets).to be_truthy end @@ -91,7 +91,7 @@ describe SearchService, services: true do context 'when :snippets is not \'true\'' do it 'returns false' do - show_snippets = SearchService.new(user, snippets: 'tru').show_snippets? + show_snippets = described_class.new(user, snippets: 'tru').show_snippets? expect(show_snippets).to be_falsey end @@ -99,7 +99,7 @@ describe SearchService, services: true do context 'when :snippets is missing' do it 'returns false' do - show_snippets = SearchService.new(user).show_snippets? + show_snippets = described_class.new(user).show_snippets? expect(show_snippets).to be_falsey end @@ -110,7 +110,7 @@ describe SearchService, services: true do context 'with accessible project_id' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, project_id: accessible_project.id, scope: 'notes').scope + scope = described_class.new(user, project_id: accessible_project.id, scope: 'notes').scope expect(scope).to eq 'notes' end @@ -118,7 +118,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, project_id: accessible_project.id, scope: 'projects').scope + scope = described_class.new(user, project_id: accessible_project.id, scope: 'projects').scope expect(scope).to eq 'blobs' end @@ -126,7 +126,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user, project_id: accessible_project.id).scope + scope = described_class.new(user, project_id: accessible_project.id).scope expect(scope).to eq 'blobs' end @@ -136,7 +136,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, snippets: 'true', scope: 'snippet_titles').scope + scope = described_class.new(user, snippets: 'true', scope: 'snippet_titles').scope expect(scope).to eq 'snippet_titles' end @@ -144,7 +144,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, snippets: 'true', scope: 'projects').scope + scope = described_class.new(user, snippets: 'true', scope: 'projects').scope expect(scope).to eq 'snippet_blobs' end @@ -152,7 +152,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user, snippets: 'true').scope + scope = described_class.new(user, snippets: 'true').scope expect(scope).to eq 'snippet_blobs' end @@ -162,7 +162,7 @@ describe SearchService, services: true do context 'with no project_id, no snippets' do context 'and allowed scope' do it 'returns the specified scope' do - scope = SearchService.new(user, scope: 'issues').scope + scope = described_class.new(user, scope: 'issues').scope expect(scope).to eq 'issues' end @@ -170,7 +170,7 @@ describe SearchService, services: true do context 'and disallowed scope' do it 'returns the default scope' do - scope = SearchService.new(user, scope: 'blobs').scope + scope = described_class.new(user, scope: 'blobs').scope expect(scope).to eq 'projects' end @@ -178,7 +178,7 @@ describe SearchService, services: true do context 'and no scope' do it 'returns the default scope' do - scope = SearchService.new(user).scope + scope = described_class.new(user).scope expect(scope).to eq 'projects' end @@ -189,7 +189,7 @@ describe SearchService, services: true do describe '#search_results' do context 'with accessible project_id' do it 'returns an instance of Gitlab::ProjectSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, project_id: accessible_project.id, scope: 'notes', @@ -201,7 +201,7 @@ describe SearchService, services: true do context 'with accessible project_id and \'true\' snippets' do it 'returns an instance of Gitlab::ProjectSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, project_id: accessible_project.id, snippets: 'true', @@ -214,7 +214,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do it 'returns an instance of Gitlab::SnippetSearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, snippets: 'true', search: snippet.content).search_results @@ -225,7 +225,7 @@ describe SearchService, services: true do context 'with no project_id and no snippets' do it 'returns an instance of Gitlab::SearchResults' do - search_results = SearchService.new( + search_results = described_class.new( user, search: public_project.name).search_results @@ -237,7 +237,7 @@ describe SearchService, services: true do describe '#search_objects' do context 'with accessible project_id' do it 'returns objects in the project' do - search_objects = SearchService.new( + search_objects = described_class.new( user, project_id: accessible_project.id, scope: 'notes', @@ -249,7 +249,7 @@ describe SearchService, services: true do context 'with accessible project_id and \'true\' snippets' do it 'returns objects in the project' do - search_objects = SearchService.new( + search_objects = described_class.new( user, project_id: accessible_project.id, snippets: 'true', @@ -262,7 +262,7 @@ describe SearchService, services: true do context 'with \'true\' snippets' do it 'returns objects in snippets' do - search_objects = SearchService.new( + search_objects = described_class.new( user, snippets: 'true', search: snippet.content).search_objects @@ -273,7 +273,7 @@ describe SearchService, services: true do context 'with accessible group_id' do it 'returns objects in the group' do - search_objects = SearchService.new( + search_objects = described_class.new( user, group_id: accessible_group.id, search: group_project.name).search_objects @@ -284,7 +284,7 @@ describe SearchService, services: true do context 'with no project_id, group_id or snippets' do it 'returns objects in global' do - search_objects = SearchService.new( + search_objects = described_class.new( user, search: public_project.name).search_objects diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb index 5e6e43b7a90..46349c3e951 100644 --- a/spec/services/spam_service_spec.rb +++ b/spec/services/spam_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SpamService, services: true do +describe SpamService do describe '#when_recaptcha_verified' do def check_spam(issue, request, recaptcha_verified) described_class.new(issue, request).when_recaptcha_verified(recaptcha_verified) do diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 667059f230c..b2d9862e71e 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SystemHooksService, services: true do +describe SystemHooksService do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:project_member) { create(:project_member) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 60477b8e9ba..cb59493c343 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SystemNoteService, services: true do +describe SystemNoteService do include Gitlab::Routing let(:project) { create(:empty_project) } @@ -1101,4 +1101,54 @@ describe SystemNoteService, services: true do expect(subject.note).to include(diffs_project_merge_request_url(project, merge_request, diff_id: diff_id, anchor: line_code)) end end + + describe '.mark_duplicate_issue' do + subject { described_class.mark_duplicate_issue(noteable, project, author, canonical_issue) } + + context 'within the same project' do + let(:canonical_issue) { create(:issue, project: project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked this issue as a duplicate of #{canonical_issue.to_reference}" } + end + + context 'across different projects' do + let(:other_project) { create(:empty_project) } + let(:canonical_issue) { create(:issue, project: other_project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}" } + end + end + + describe '.mark_canonical_issue_of_duplicate' do + subject { described_class.mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue) } + + context 'within the same project' do + let(:duplicate_issue) { create(:issue, project: project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference} as a duplicate of this issue" } + end + + context 'across different projects' do + let(:other_project) { create(:empty_project) } + let(:duplicate_issue) { create(:issue, project: other_project) } + + it_behaves_like 'a system note' do + let(:action) { 'duplicate' } + end + + it { expect(subject.note).to eq "marked #{duplicate_issue.to_reference(project)} as a duplicate of this issue" } + end + end end diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb index 9f143cc5667..1b31ce29f7a 100644 --- a/spec/services/tags/create_service_spec.rb +++ b/spec/services/tags/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Tags::CreateService, services: true do +describe Tags::CreateService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb index 28396fc3658..7c8c1dd0d3a 100644 --- a/spec/services/tags/destroy_service_spec.rb +++ b/spec/services/tags/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Tags::DestroyService, services: true do +describe Tags::DestroyService do let(:project) { create(:project, :repository) } let(:repository) { project.repository } let(:user) { create(:user) } diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index de41cbab14c..230e40de9e0 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe TodoService, services: true do +describe TodoService do let(:author) { create(:user) } let(:assignee) { create(:user) } let(:non_member) { create(:user) } @@ -873,21 +873,21 @@ describe TodoService, services: true do create(:todo, :mentioned, user: john_doe, target: issue, project: project) todos = TodosFinder.new(john_doe, {}).execute - expect { TodoService.new.mark_todos_as_done(todos, john_doe) } + expect { described_class.new.mark_todos_as_done(todos, john_doe) } .to change { john_doe.todos.done.count }.from(0).to(1) end it 'marks an array of todos as done' do todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - expect { TodoService.new.mark_todos_as_done([todo], john_doe) } + expect { described_class.new.mark_todos_as_done([todo], john_doe) } .to change { todo.reload.state }.from('pending').to('done') end it 'returns the ids of updated todos' do # Needed on API todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - expect(TodoService.new.mark_todos_as_done([todo], john_doe)).to eq([todo.id]) + expect(described_class.new.mark_todos_as_done([todo], john_doe)).to eq([todo.id]) end context 'when some of the todos are done already' do @@ -895,23 +895,23 @@ describe TodoService, services: true do let!(:second_todo) { create(:todo, :mentioned, user: john_doe, target: another_issue, project: project) } it 'returns the ids of those still pending' do - TodoService.new.mark_pending_todos_as_done(issue, john_doe) + described_class.new.mark_pending_todos_as_done(issue, john_doe) - expect(TodoService.new.mark_todos_as_done(Todo.all, john_doe)).to eq([second_todo.id]) + expect(described_class.new.mark_todos_as_done(Todo.all, john_doe)).to eq([second_todo.id]) end it 'returns an empty array if all are done' do - TodoService.new.mark_pending_todos_as_done(issue, john_doe) - TodoService.new.mark_pending_todos_as_done(another_issue, john_doe) + described_class.new.mark_pending_todos_as_done(issue, john_doe) + described_class.new.mark_pending_todos_as_done(another_issue, john_doe) - expect(TodoService.new.mark_todos_as_done(Todo.all, john_doe)).to eq([]) + expect(described_class.new.mark_todos_as_done(Todo.all, john_doe)).to eq([]) end end it 'caches the number of todos of a user', :use_clean_rails_memory_store_caching do create(:todo, :mentioned, user: john_doe, target: issue, project: project) todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) - TodoService.new.mark_todos_as_done([todo], john_doe) + described_class.new.mark_todos_as_done([todo], john_doe) expect_any_instance_of(TodosFinder).not_to receive(:execute) diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb index 69ed8de9c31..dc2d0e2d47a 100644 --- a/spec/services/update_release_service_spec.rb +++ b/spec/services/update_release_service_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe UpdateReleaseService, services: true do +describe UpdateReleaseService do let(:project) { create(:project, :repository) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } let(:new_description) { 'The best release!' } - let(:service) { UpdateReleaseService.new(project, user) } + let(:service) { described_class.new(project, user) } context 'with an existing release' do let(:create_service) { CreateReleaseService.new(project, user) } diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index 37c2e861362..ef535c5cf1f 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UpdateSnippetService, services: true do +describe UpdateSnippetService do before do @user = create :user @admin = create :user, admin: true diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb index 95ba28dbecd..cf76a18f171 100644 --- a/spec/services/upload_service_spec.rb +++ b/spec/services/upload_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UploadService, services: true do +describe UploadService do describe 'File service' do before do @user = create(:user) diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb index e5330d1d3e4..fef4da0c76e 100644 --- a/spec/services/users/activity_service_spec.rb +++ b/spec/services/users/activity_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::ActivityService, services: true do +describe Users::ActivityService do include UserActivitiesHelpers let(:user) { create(:user) } diff --git a/spec/services/users/build_service_spec.rb b/spec/services/users/build_service_spec.rb index 2a6bfc1b3a0..677d4a622e1 100644 --- a/spec/services/users/build_service_spec.rb +++ b/spec/services/users/build_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::BuildService, services: true do +describe Users::BuildService do describe '#execute' do let(:params) do { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' } diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb index 75746278573..24dac569678 100644 --- a/spec/services/users/create_service_spec.rb +++ b/spec/services/users/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::CreateService, services: true do +describe Users::CreateService do describe '#execute' do let(:admin_user) { create(:admin) } diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 5409f67c091..786335120fd 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::DestroyService, services: true do +describe Users::DestroyService do describe "Deletes a user and all their personal projects" do let!(:user) { create(:user) } let!(:admin) { create(:admin) } diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb index e52ecd6d614..a0030ce8809 100644 --- a/spec/services/users/migrate_to_ghost_user_service_spec.rb +++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::MigrateToGhostUserService, services: true do +describe Users::MigrateToGhostUserService do let!(:user) { create(:user) } let!(:project) { create(:project) } let(:service) { described_class.new(user) } diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb index 0b2f840c462..343804e3de0 100644 --- a/spec/services/users/update_service_spec.rb +++ b/spec/services/users/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Users::UpdateService, services: true do +describe Users::UpdateService do let(:user) { create(:user) } describe '#execute' do diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb index 7ff37c22963..a5de86a0835 100644 --- a/spec/services/web_hook_service_spec.rb +++ b/spec/services/web_hook_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WebHookService, services: true do +describe WebHookService do let(:project) { create(:empty_project) } let(:project_hook) { create(:project_hook) } let(:headers) do @@ -12,7 +12,7 @@ describe WebHookService, services: true do let(:data) do { before: 'oldrev', after: 'newrev', ref: 'ref' } end - let(:service_instance) { WebHookService.new(project_hook, data, 'push_hooks') } + let(:service_instance) { described_class.new(project_hook, data, 'push_hooks') } describe '#execute' do before(:each) do @@ -114,7 +114,7 @@ describe WebHookService, services: true do context 'should not log ServiceHooks' do let(:service_hook) { create(:service_hook) } - let(:service_instance) { WebHookService.new(service_hook, data, 'service_hook') } + let(:service_instance) { described_class.new(service_hook, data, 'service_hook') } before do WebMock.stub_request(:post, service_hook.url).to_return(status: 200, body: 'Success') @@ -131,7 +131,7 @@ describe WebHookService, services: true do it 'enqueue WebHookWorker' do expect(Sidekiq::Client).to receive(:enqueue).with(WebHookWorker, project_hook.id, data, 'push_hooks') - WebHookService.new(project_hook, data, 'push_hooks').async_execute + described_class.new(project_hook, data, 'push_hooks').async_execute end end end diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb index 054e28ae7b0..fa3863e9b30 100644 --- a/spec/services/wiki_pages/create_service_spec.rb +++ b/spec/services/wiki_pages/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WikiPages::CreateService, services: true do +describe WikiPages::CreateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb index 920be4d4c8a..1668cd00ca8 100644 --- a/spec/services/wiki_pages/destroy_service_spec.rb +++ b/spec/services/wiki_pages/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WikiPages::DestroyService, services: true do +describe WikiPages::DestroyService do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:page) { create(:wiki_page) } diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb index 5e36ea4cf94..a672c84034b 100644 --- a/spec/services/wiki_pages/update_service_spec.rb +++ b/spec/services/wiki_pages/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WikiPages::UpdateService, services: true do +describe WikiPages::UpdateService do let(:project) { create(:empty_project) } let(:user) { create(:user) } let(:page) { create(:wiki_page) } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5d5715b10ff..e7329210896 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -148,3 +148,10 @@ FactoryGirl::SyntaxRunner.class_eval do end ActiveRecord::Migration.maintain_test_schema! + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end diff --git a/spec/requests/api/milestones_spec.rb b/spec/support/api/milestones_shared_examples.rb index ab5ea3e8f2c..480e7d5151f 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/support/api/milestones_shared_examples.rb @@ -1,21 +1,14 @@ -require 'spec_helper' - -describe API::Milestones do - let(:user) { create(:user) } - let!(:project) { create(:empty_project, namespace: user.namespace ) } - let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') } - let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') } +shared_examples_for 'group and project milestones' do |route_definition| + let(:resource_route) { "#{route}/#{milestone.id}" } let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) } let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) } let(:label_3) { create(:label, title: 'label_3', project: project) } + let(:merge_request) { create(:merge_request, source_project: project) } + let(:another_merge_request) { create(:merge_request, :simple, source_project: project) } - before do - project.team << [user, :developer] - end - - describe 'GET /projects/:id/milestones' do - it 'returns project milestones' do - get api("/projects/#{project.id}/milestones", user) + describe "GET #{route_definition}" do + it 'returns milestones list' do + get api(route, user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -24,13 +17,13 @@ describe API::Milestones do end it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones") + get api(route) expect(response).to have_http_status(401) end it 'returns an array of active milestones' do - get api("/projects/#{project.id}/milestones?state=active", user) + get api("#{route}/?state=active", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -40,7 +33,7 @@ describe API::Milestones do end it 'returns an array of closed milestones' do - get api("/projects/#{project.id}/milestones?state=closed", user) + get api("#{route}/?state=closed", user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -50,9 +43,9 @@ describe API::Milestones do end it 'returns an array of milestones specified by iids' do - other_milestone = create(:milestone, project: project) + other_milestone = create(:milestone, project: try(:project), group: try(:group)) - get api("/projects/#{project.id}/milestones", user), iids: [closed_milestone.iid, other_milestone.iid] + get api(route, user), iids: [closed_milestone.iid, other_milestone.iid] expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -61,25 +54,15 @@ describe API::Milestones do end it 'does not return any milestone if none found' do - get api("/projects/#{project.id}/milestones", user), iids: [Milestone.maximum(:iid).succ] + get api(route, user), iids: [Milestone.maximum(:iid).succ] expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end - end - - describe 'GET /projects/:id/milestones/:milestone_id' do - it 'returns a project milestone by id' do - get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - - expect(response).to have_http_status(200) - expect(json_response['title']).to eq(milestone.title) - expect(json_response['iid']).to eq(milestone.iid) - end - it 'returns a project milestone by iids array' do - get api("/projects/#{project.id}/milestones?iids=#{closed_milestone.iid}", user) + it 'returns a milestone by iids array' do + get api("#{route}?iids=#{closed_milestone.iid}", user) expect(response.status).to eq 200 expect(response).to include_pagination_headers @@ -89,8 +72,8 @@ describe API::Milestones do expect(json_response.first['id']).to eq closed_milestone.id end - it 'returns a project milestone by searching for title' do - get api("/projects/#{project.id}/milestones", user), search: 'version2' + it 'returns a milestone by searching for title' do + get api(route, user), search: 'version2' expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -99,8 +82,8 @@ describe API::Milestones do expect(json_response.first['id']).to eq milestone.id end - it 'returns a project milestones by searching for description' do - get api("/projects/#{project.id}/milestones", user), search: 'open' + it 'returns a milestones by searching for description' do + get api(route, user), search: 'open' expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -110,9 +93,17 @@ describe API::Milestones do end end - describe 'GET /projects/:id/milestones/:milestone_id' do - it 'returns a project milestone by id' do - get api("/projects/#{project.id}/milestones/#{milestone.id}", user) + describe "GET #{route_definition}/:milestone_id" do + it 'returns a milestone by id' do + get api(resource_route, user) + + expect(response).to have_http_status(200) + expect(json_response['title']).to eq(milestone.title) + expect(json_response['iid']).to eq(milestone.iid) + end + + it 'returns a milestone by id' do + get api(resource_route, user) expect(response).to have_http_status(200) expect(json_response['title']).to eq(milestone.title) @@ -120,29 +111,29 @@ describe API::Milestones do end it 'returns 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}") + get api(resource_route) expect(response).to have_http_status(401) end it 'returns a 404 error if milestone id not found' do - get api("/projects/#{project.id}/milestones/1234", user) + get api("#{route}/1234", user) expect(response).to have_http_status(404) end end - describe 'POST /projects/:id/milestones' do - it 'creates a new project milestone' do - post api("/projects/#{project.id}/milestones", user), title: 'new milestone' + describe "POST #{route_definition}" do + it 'creates a new milestone' do + post api(route, user), title: 'new milestone' expect(response).to have_http_status(201) expect(json_response['title']).to eq('new milestone') expect(json_response['description']).to be_nil end - it 'creates a new project milestone with description and dates' do - post api("/projects/#{project.id}/milestones", user), + it 'creates a new milestone with description and dates' do + post api(route, user), title: 'new milestone', description: 'release', due_date: '2013-03-02', start_date: '2013-02-02' expect(response).to have_http_status(201) @@ -152,20 +143,20 @@ describe API::Milestones do end it 'returns a 400 error if title is missing' do - post api("/projects/#{project.id}/milestones", user) + post api(route, user) expect(response).to have_http_status(400) end it 'returns a 400 error if params are invalid (duplicate title)' do - post api("/projects/#{project.id}/milestones", user), + post api(route, user), title: milestone.title, description: 'release', due_date: '2013-03-02' expect(response).to have_http_status(400) end - it 'creates a new project with reserved html characters' do - post api("/projects/#{project.id}/milestones", user), title: 'foo & bar 1.1 -> 2.2' + it 'creates a new milestone with reserved html characters' do + post api(route, user), title: 'foo & bar 1.1 -> 2.2' expect(response).to have_http_status(201) expect(json_response['title']).to eq('foo & bar 1.1 -> 2.2') @@ -173,9 +164,9 @@ describe API::Milestones do end end - describe 'PUT /projects/:id/milestones/:milestone_id' do - it 'updates a project milestone' do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), + describe "PUT #{route_definition}/:milestone_id" do + it 'updates a milestone' do + put api(resource_route, user), title: 'updated title' expect(response).to have_http_status(200) @@ -185,23 +176,21 @@ describe API::Milestones do it 'removes a due date if nil is passed' do milestone.update!(due_date: "2016-08-05") - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil + put api(resource_route, user), due_date: nil expect(response).to have_http_status(200) expect(json_response['due_date']).to be_nil end it 'returns a 404 error if milestone id not found' do - put api("/projects/#{project.id}/milestones/1234", user), + put api("#{route}/1234", user), title: 'updated title' expect(response).to have_http_status(404) end - end - describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do - it 'updates a project milestone' do - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), + it 'closes milestone' do + put api(resource_route, user), state_event: 'close' expect(response).to have_http_status(200) @@ -209,21 +198,14 @@ describe API::Milestones do end end - describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do - it 'creates an activity event when an milestone is closed' do - expect(Event).to receive(:create) - - put api("/projects/#{project.id}/milestones/#{milestone.id}", user), - state_event: 'close' - end - end + describe "GET #{route_definition}/:milestone_id/issues" do + let(:issues_route) { "#{route}/#{milestone.id}/issues" } - describe 'GET /projects/:id/milestones/:milestone_id/issues' do before do milestone.issues << create(:issue, project: project) end - it 'returns project issues for a particular milestone' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) + it 'returns issues for a particular milestone' do + get api(issues_route, user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -231,12 +213,12 @@ describe API::Milestones do expect(json_response.first['milestone']['title']).to eq(milestone.title) end - it 'returns project issues sorted by label priority' do + it 'returns issues sorted by label priority' do issue_1 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_3]) issue_2 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_1]) issue_3 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_2]) - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) + get api(issues_route, user) expect(json_response.first['id']).to eq(issue_2.id) expect(json_response.second['id']).to eq(issue_3.id) @@ -244,44 +226,58 @@ describe API::Milestones do end it 'matches V4 response schema for a list of issues' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) + get api(issues_route, user) expect(response).to have_http_status(200) expect(response).to match_response_schema('public_api/v4/issues') end it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") + get api(issues_route) expect(response).to have_http_status(401) end describe 'confidential issues' do - let(:public_project) { create(:empty_project, :public) } - let(:milestone) { create(:milestone, project: public_project) } - let(:issue) { create(:issue, project: public_project) } - let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } + let!(:public_project) { create(:empty_project, :public) } + let!(:context_group) { try(:group) } + let!(:milestone) do + context_group ? create(:milestone, group: context_group) : create(:milestone, project: public_project) + end + let!(:issue) { create(:issue, project: public_project) } + let!(:confidential_issue) { create(:issue, confidential: true, project: public_project) } + let!(:issues_route) do + if context_group + "#{route}/#{milestone.id}/issues" + else + "/projects/#{public_project.id}/milestones/#{milestone.id}/issues" + end + end before do + # Add public project to the group in context + setup_for_group if context_group + public_project.team << [user, :developer] milestone.issues << issue << confidential_issue end it 'returns confidential issues to team members' do - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + get api(issues_route, user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(2) + # 2 for projects, 3 for group(which has another project with an issue) + expect(json_response.size).to be_between(2, 3) expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) end it 'does not return confidential issues to team members with guest role' do member = create(:user) - project.team << [member, :guest] + public_project.team << [member, :guest] - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member) + get api(issues_route, member) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -291,7 +287,7 @@ describe API::Milestones do end it 'does not return confidential issues to regular users' do - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) + get api(issues_route, create(:user)) expect(response).to have_http_status(200) expect(response).to include_pagination_headers @@ -304,30 +300,30 @@ describe API::Milestones do issue.labels << label_2 confidential_issue.labels << label_1 - get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + get api(issues_route, user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(2) + # 2 for projects, 3 for group(which has another project with an issue) + expect(json_response.size).to be_between(2, 3) expect(json_response.first['id']).to eq(confidential_issue.id) expect(json_response.second['id']).to eq(issue.id) end end end - describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do - let(:merge_request) { create(:merge_request, source_project: project) } - let(:another_merge_request) { create(:merge_request, :simple, source_project: project) } + describe "GET #{route_definition}/:milestone_id/merge_requests" do + let(:merge_requests_route) { "#{route}/#{milestone.id}/merge_requests" } before do milestone.merge_requests << merge_request end - it 'returns project merge_requests for a particular milestone' do + it 'returns merge_requests for a particular milestone' do # eager-load another_merge_request another_merge_request - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) + get api(merge_requests_route, user) expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -336,12 +332,12 @@ describe API::Milestones do expect(json_response.first['milestone']['title']).to eq(milestone.title) end - it 'returns project merge_requests sorted by label priority' do + it 'returns merge_requests sorted by label priority' do merge_request_1 = create(:labeled_merge_request, source_branch: 'branch_1', source_project: project, milestone: milestone, labels: [label_2]) merge_request_2 = create(:labeled_merge_request, source_branch: 'branch_2', source_project: project, milestone: milestone, labels: [label_1]) merge_request_3 = create(:labeled_merge_request, source_branch: 'branch_3', source_project: project, milestone: milestone, labels: [label_3]) - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) + get api(merge_requests_route, user) expect(json_response.first['id']).to eq(merge_request_2.id) expect(json_response.second['id']).to eq(merge_request_1.id) @@ -349,20 +345,22 @@ describe API::Milestones do end it 'returns a 404 error if milestone id not found' do - get api("/projects/#{project.id}/milestones/1234/merge_requests", user) + not_found_route = "#{route}/1234/merge_requests" + + get api(not_found_route, user) expect(response).to have_http_status(404) end it 'returns a 404 if the user has no access to the milestone' do new_user = create :user - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", new_user) + get api(merge_requests_route, new_user) expect(response).to have_http_status(404) end it 'returns a 401 error if user not authenticated' do - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests") + get api(merge_requests_route) expect(response).to have_http_status(401) end @@ -372,7 +370,7 @@ describe API::Milestones do another_merge_request.labels << label_1 merge_request.labels << label_2 - get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user) + get api(merge_requests_route, user) expect(response).to have_http_status(200) expect(response).to include_pagination_headers diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb index dff0dfba675..67599f77adb 100644 --- a/spec/support/api/schema_matcher.rb +++ b/spec/support/api/schema_matcher.rb @@ -5,7 +5,14 @@ end RSpec::Matchers.define :match_response_schema do |schema, **options| match do |response| - JSON::Validator.validate!(schema_path(schema), response.body, options) + @errors = JSON::Validator.fully_validate(schema_path(schema), response.body, options) + + @errors.empty? + end + + failure_message do |response| + "didn't match the schema defined by #{schema_path(schema)}" \ + " The validation errors were:\n#{@errors.join("\n")}" end end diff --git a/spec/support/devise_helpers.rb b/spec/support/devise_helpers.rb new file mode 100644 index 00000000000..890a2d9d287 --- /dev/null +++ b/spec/support/devise_helpers.rb @@ -0,0 +1,14 @@ +module DeviseHelpers + # explicitly tells Devise which mapping to use + # this is needed when we are testing a Devise controller bypassing the router + def set_devise_mapping(context:) + env = + if context.respond_to?(:env_config) + context.env_config + elsif context.respond_to?(:env) + context.env + end + + env['devise.mapping'] = Devise.mappings[:user] if env + end +end diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb index 033e338fe61..035428a7d9b 100644 --- a/spec/support/features/issuable_slash_commands_shared_examples.rb +++ b/spec/support/features/issuable_slash_commands_shared_examples.rb @@ -5,8 +5,6 @@ shared_examples 'issuable record that supports quick actions in its description include QuickActionsHelpers let(:master) { create(:user) } - let(:assignee) { create(:user, username: 'bob') } - let(:guest) { create(:user) } let(:project) { create(:project, :public) } let!(:milestone) { create(:milestone, project: project, title: 'ASAP') } let!(:label_bug) { create(:label, project: project, title: 'bug') } @@ -15,8 +13,6 @@ shared_examples 'issuable record that supports quick actions in its description before do project.team << [master, :master] - project.team << [assignee, :developer] - project.team << [guest, :guest] sign_in(master) end @@ -57,6 +53,7 @@ shared_examples 'issuable record that supports quick actions in its description context 'with a note containing commands' do it 'creates a note without the commands and interpret the commands accordingly' do + assignee = create(:user, username: 'bob') write_note("Awesome!\n/assign @bob\n/label ~bug\n/milestone %\"ASAP\"") expect(page).to have_content 'Awesome!' @@ -77,6 +74,7 @@ shared_examples 'issuable record that supports quick actions in its description context 'with a note containing only commands' do it 'does not create a note but interpret the commands accordingly' do + assignee = create(:user, username: 'bob') write_note("/assign @bob\n/label ~bug\n/milestone %\"ASAP\"") expect(page).not_to have_content '/assign @bob' @@ -111,8 +109,12 @@ shared_examples 'issuable record that supports quick actions in its description context "when current user cannot close #{issuable_type}" do before do + guest = create(:user) + project.add_guest(guest) + sign_out(:user) sign_in(guest) + visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable) end @@ -146,8 +148,12 @@ shared_examples 'issuable record that supports quick actions in its description context "when current user cannot reopen #{issuable_type}" do before do + guest = create(:user) + project.add_guest(guest) + sign_out(:user) sign_in(guest) + visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable) end @@ -176,6 +182,9 @@ shared_examples 'issuable record that supports quick actions in its description context "when current user cannot change title of #{issuable_type}" do before do + guest = create(:user) + project.add_guest(guest) + sign_out(:user) sign_in(guest) visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable) @@ -267,6 +276,8 @@ shared_examples 'issuable record that supports quick actions in its description describe "preview of note on #{issuable_type}" do it 'removes quick actions from note and explains them' do + create(:user, username: 'bob') + visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable) page.within('.js-main-target-form') do diff --git a/spec/support/forgery_protection.rb b/spec/support/forgery_protection.rb new file mode 100644 index 00000000000..a5e7b761651 --- /dev/null +++ b/spec/support/forgery_protection.rb @@ -0,0 +1,11 @@ +RSpec.configure do |config| + config.around(:each, :allow_forgery_protection) do |example| + begin + ActionController::Base.allow_forgery_protection = true + + example.call + ensure + ActionController::Base.allow_forgery_protection = false + end + end +end diff --git a/spec/support/gpg_helpers.rb b/spec/support/gpg_helpers.rb new file mode 100644 index 00000000000..96ea6f28b30 --- /dev/null +++ b/spec/support/gpg_helpers.rb @@ -0,0 +1,202 @@ +module GpgHelpers + module User1 + extend self + + def signed_commit_signature + <<~SIGNATURE + -----BEGIN PGP SIGNATURE----- + Version: GnuPG v1 + + iJwEAAECAAYFAliu264ACgkQzPvhnwCsix1VXgP9F6zwAMb3OXKZzqGxJ4MQIBoL + OdiUSJpL/4sIA9uhFeIv3GIA+uhsG1BHHsG627+sDy7b8W9VWEd7tbcoz4Mvhf3P + 8g0AIt9/KJuStQZDrXwP1uP6Rrl759nDcNpoOKdSQ5EZ1zlRzeDROlZeDp7Ckfvw + GLmN/74Gl3pk0wfgHFY= + =wSgS + -----END PGP SIGNATURE----- + SIGNATURE + end + + def signed_commit_base_data + <<~SIGNEDDATA + tree ed60cfd202644fda1abaf684e7d965052db18c13 + parent caf6a0334a855e12f30205fff3d7333df1f65127 + author Nannie Bernhard <nannie.bernhard@example.com> 1487854510 +0100 + committer Nannie Bernhard <nannie.bernhard@example.com> 1487854510 +0100 + + signed commit, verified key/email + SIGNEDDATA + end + + def secret_key + <<~KEY.strip + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: GnuPG v1 + + lQHYBFiu1ScBBADUhWsrlWHp5e7ASlI5iMcA0XN43fivhVlGYJJy4Ii3Hr2i4f5s + VffHS8QyhgxxzSnPwe2OKnZWWL9cHzUFbiG3fHalEBTjpB+7pG4HBgU8R/tiDOu8 + vkAR+tfJbkuRs9XeG3dGKBX/8WRhIfRucYnM+04l2Myyo5zIx7thJmxXjwARAQAB + AAP/XUtcqrtfSnDYCK4Xvo4e3msUSAEZxOPDNzP51lhfbBQgp7qSGDj9Fw5ZyNwz + 5llse3nksT5OyMUY7HX+rq2UOs12a/piLqvhtX1okp/oTAETmKXNYkZLenv6t94P + NqLi0o2AnXAvL9ueXa7WUY3l4DkvuLcjT4+9Ut2Y71zIjeECAN7q9ohNL7E8tNkf + Elsbx+8KfyHRQXiSUYaQLlvDRq2lYCKIS7sogTqjZMEgbZx2mRX1fakRlvcmqOwB + QoX34zcCAPQPd+yTteNUV12uvDaj8V9DICktPPhbHdYYaUoHjF8RrIHCTRUPzk9E + KzCL9dUP8eXPPBV/ty+zjUwl69IgCmkB/3pnNZ0D4EJsNgu24UgI0N+c8H/PE1D6 + K+bGQ/jK83uYPMXJUsiojssCHLGNp7eBGHFn1PpEqZphgVI50ZMrZQWhJbQtTmFu + bmllIEJlcm5oYXJkIDxuYW5uaWUuYmVybmhhcmRAZXhhbXBsZS5jb20+iLgEEwEC + ACIFAliu1ScCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEMz74Z8ArIsd + p5ID/32hRalvTY+V+QAtzHlGdxugweSBzNgRT3A4UiC9chF6zBOEIw689lqmK6L4 + i3Il9XeKMl87wi9tsVy9TuOMYDTvcFvu1vMAQ5AsDXqZaAEtCUZpFZscNbi7AXG+ + QkoDQbMSxp0Rd6eIRJpk9zis5co87f78xJBZLZua+8awFMS6nQHYBFiu1ScBBADI + XkITf+kKCkD+n8tMsdTLInefu8KrJ8p7YRYCCabEXnWRsDb5zxUAG2VXCVUhYl6Q + XQybkNiBaduS+uxilz7gtYZUMFJvQ09+fV7D2N9B7u/1bGdIYz+cDFJnEJitLY4w + /nju2Sno5CL5Ead8sZuslKetSXPYHR/kbW462EOw5wARAQABAAP+IoZfU1XUdVbr + +RPWp3ny5SekviDPu8co9BZ4ANTh5+8wyfA3oNbGUxTlYthoU07MZYqq+/k63R28 + 6HgVGC3gdvCiRMGmryIQ6roLLRXkfzjXrI7Lgnhx4OtVjo62pAKDqdl45wEa1Q+M + v08CQF6XNpb5R9Xszz4aBC4eV0KjtjkCANlGSQHZ1B81g+iltj1FAhRHkyUFlrc1 + cqLVhNgxtHZ96+R57Uk2A7dIJBsE00eIYaHOfk5X5GD/95s1QvPcQskCAOwUk5xj + NeQ6VV/1+cI91TrWU6VnT2Yj8632fM/JlKKfaS15pp8t5Ha6pNFr3xD4KgQutchq + fPsEOjaU7nwQ/i8B/1rDPTYfNXFpRNt33WAB1XtpgOIHlpmOfaYYqf6lneTlZWBc + TgyO+j+ZsHAvP18ugIRkU8D192NflzgAGwXLryijyYifBBgBAgAJBQJYrtUnAhsM + AAoJEMz74Z8ArIsdlkUEALTl6QUutJsqwVF4ZXKmmw0IEk8PkqW4G+tYRDHJMs6Z + O0nzDS89BG2DL4/UlOs5wRvERnlJYz01TMTxq/ciKaBTEjygFIv9CgIEZh97VacZ + TIqcF40k9SbpJNnh3JLf94xsNxNRJTEhbVC3uruaeILue/IR7pBMEyCs49Gcguwy + =b6UD + -----END PGP PRIVATE KEY BLOCK----- + KEY + end + + def public_key + <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mI0EWK7VJwEEANSFayuVYenl7sBKUjmIxwDRc3jd+K+FWUZgknLgiLcevaLh/mxV + 98dLxDKGDHHNKc/B7Y4qdlZYv1wfNQVuIbd8dqUQFOOkH7ukbgcGBTxH+2IM67y+ + QBH618luS5Gz1d4bd0YoFf/xZGEh9G5xicz7TiXYzLKjnMjHu2EmbFePABEBAAG0 + LU5hbm5pZSBCZXJuaGFyZCA8bmFubmllLmJlcm5oYXJkQGV4YW1wbGUuY29tPoi4 + BBMBAgAiBQJYrtUnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDM++Gf + AKyLHaeSA/99oUWpb02PlfkALcx5RncboMHkgczYEU9wOFIgvXIReswThCMOvPZa + piui+ItyJfV3ijJfO8IvbbFcvU7jjGA073Bb7tbzAEOQLA16mWgBLQlGaRWbHDW4 + uwFxvkJKA0GzEsadEXeniESaZPc4rOXKPO3+/MSQWS2bmvvGsBTEuriNBFiu1ScB + BADIXkITf+kKCkD+n8tMsdTLInefu8KrJ8p7YRYCCabEXnWRsDb5zxUAG2VXCVUh + Yl6QXQybkNiBaduS+uxilz7gtYZUMFJvQ09+fV7D2N9B7u/1bGdIYz+cDFJnEJit + LY4w/nju2Sno5CL5Ead8sZuslKetSXPYHR/kbW462EOw5wARAQABiJ8EGAECAAkF + Aliu1ScCGwwACgkQzPvhnwCsix2WRQQAtOXpBS60myrBUXhlcqabDQgSTw+Spbgb + 61hEMckyzpk7SfMNLz0EbYMvj9SU6znBG8RGeUljPTVMxPGr9yIpoFMSPKAUi/0K + AgRmH3tVpxlMipwXjST1Jukk2eHckt/3jGw3E1ElMSFtULe6u5p4gu578hHukEwT + IKzj0ZyC7DI= + =Ug0r + -----END PGP PUBLIC KEY BLOCK----- + KEY + end + + def primary_keyid + fingerprint[-16..-1] + end + + def fingerprint + '5F7EA3981A5845B141ABD522CCFBE19F00AC8B1D' + end + + def names + ['Nannie Bernhard'] + end + + def emails + ['nannie.bernhard@example.com'] + end + end + + module User2 + extend self + + def private_key + <<~KEY.strip + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: GnuPG v1 + + lQHYBFiuqioBBADg46jkiATWMy9t1npxFWJ77xibPXdUo36LAZgZ6uGungSzcFL4 + 50bdEyMMGm5RJp6DCYkZlwQDlM//YEqwf0Cmq/AibC5m9bHr7hf5sMxl40ssJ4fj + dzT6odihO0vxD2ARSrtiwkESzFxjJ51mjOfdPvAGf0ucxzgeRfUlCrM3kwARAQAB + AAP8CJlDFnbywR9dWfqBxi19sFMOk/smCObNQanuTcx6CDcu4zHi0Yxx6BoNCQES + cDRCLX5HevnpZngzQB3qa7dga+yqxKzwO8v0P0hliL81B1ZVXUk9TWhBj3NS3m3v + +kf2XeTxuZFb9fj44/4HpfbQ2yazTs/Xa+/ZeMqFPCYSNEECAOtjIbwHdfjkpVWR + uiwphRkNimv5hdObufs63m9uqhpKPdPKmr2IXgahPZg5PooxqE0k9IXaX2pBsJUF + DyuL1dsCAPSVL+YAOviP8ecM1jvdKpkFDd67kR5C+7jEvOGl+c2aX3qLvKt62HPR + +DxvYE0Oy0xfoHT14zNSfqthmlhIPqkB/i4WyJaafQVvkkoA9+A5aXbyihOR+RTx + p+CMNYvaAplFAyey7nv8l5+la/N+Sv86utjaenLZmCf34nDQEZy7rFWny7QvQmV0 + dGUgQ2FydHdyaWdodCA8YmV0dGUuY2FydHdyaWdodEBleGFtcGxlLmNvbT6IuAQT + AQIAIgUCWK6qKgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQv52SX5Ee + /WVCGwP/QsOLTTyEJ6hl0Yy7DLY3kUxS6xiD9fW1FDoTQlxhiO+8TmghmhdtU3TI + ssP30/Su3pNKW3TkILtE9U8I2krEpsX5NkyMwmI6LXdeZjli2Lvtkx0Fm0Psd4HO + ORYJW5HqTx4jDLzeeIcYjqnobztDpfG8ONDvB0EI0GnCTOZNggG0L0JldHRlIENh + cnR3cmlnaHQgPGJldHRlLmNhcnR3cmlnaHRAZXhhbXBsZS5uZXQ+iLgEEwECACIF + AlivAsUCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEL+dkl+RHv1lXOwE + ANh7ce/vUjv6VMkO8o5OZXszhKE5+MSmYO8v/kkHcXNccC5wI6VF4K//r41p8Cyk + 9NzW7Kzjt2+14/BBqWugCx3xjWCuf88KH5PHbuSmfVYbzJmNSy6rfPmusZG5ePqD + xp5l2qQxMdRUX0Z36D/koM4N0ls6PAf6Xrdv9s6IBMMVnQHYBFiuqioBBADe5nUd + VOcbZlnxOjl0KBAT+A5bmyBLUT0BmLPsmA4PuXDSth7WvibPC8wcCdCYVk0IRMYn + eZUiWq/o5c4rthfLR4jg8kruvomQ4E4d4hyI6R0MLxXYZ3XMu67VuScFgbLURw1e + RZ16ANd3Nc1VuFW7ms0vCG0idB8iSZBoULaK8QARAQABAAP5AdCfUT/y2kmi75iF + ZX1ahSkax9LraEWW8TOCuolR6v2b7jFKrr2xX/P1A2DulID2Y1v4/5MJPHR/1G4D + l95Fkw+iGsTvKB5rPG5xye0vOYbbujRa6B9LL6s4Taf486shEegOrdjN9FIweM6f + vuVaDYzIk8Qwv5/sStEBxx8rxIkCAOBftFi56AY0gLniyEMAvVRjyVeOZPPJbS8i + v6L9asJB5wdsGJxJVyUZ/ylar5aCS7sroOcYTN2b1tOPoWuGqIkCAP5RlDRgm3Zg + xL6hXejqZp3G1/DXhKBSI/yUTR/D89H5/qNQe3W7dZqns9mSAJNtqOu+UMZ5UreY + Ond0/dmL5SkCAOO5r6gXM8ZDcNjydlQexCLnH70yVkCL6hG9Va1gOuFyUztRnCd+ + E35YRCEwZREZDr87BRr2Aak5t+lb1EFVqV+nvYifBBgBAgAJBQJYrqoqAhsMAAoJ + EL+dkl+RHv1lQggEANWwQwrlT2BFLWV8Fx+wlg31+mcjkTq0LaWu3oueAluoSl93 + 2B6ToruMh66JoxpSDU44x3JbCaZ/6poiYs5Aff8ZeyEVlfkVaQ7IWd5spjpXaS4i + oCOfkZepmbTuE7TPQWM4iBAtuIfiJGiwcpWWM+KIH281yhfCcbRzzFLsCVQx + =yEqv + -----END PGP PRIVATE KEY BLOCK----- + KEY + end + + def public_key + <<~KEY.strip + -----BEGIN PGP PUBLIC KEY BLOCK----- + Version: GnuPG v1 + + mI0EWK6qKgEEAODjqOSIBNYzL23WenEVYnvvGJs9d1SjfosBmBnq4a6eBLNwUvjn + Rt0TIwwablEmnoMJiRmXBAOUz/9gSrB/QKar8CJsLmb1sevuF/mwzGXjSywnh+N3 + NPqh2KE7S/EPYBFKu2LCQRLMXGMnnWaM590+8AZ/S5zHOB5F9SUKszeTABEBAAG0 + L0JldHRlIENhcnR3cmlnaHQgPGJldHRlLmNhcnR3cmlnaHRAZXhhbXBsZS5jb20+ + iLgEEwECACIFAliuqioCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEL+d + kl+RHv1lQhsD/0LDi008hCeoZdGMuwy2N5FMUusYg/X1tRQ6E0JcYYjvvE5oIZoX + bVN0yLLD99P0rt6TSlt05CC7RPVPCNpKxKbF+TZMjMJiOi13XmY5Yti77ZMdBZtD + 7HeBzjkWCVuR6k8eIwy83niHGI6p6G87Q6XxvDjQ7wdBCNBpwkzmTYIBtC9CZXR0 + ZSBDYXJ0d3JpZ2h0IDxiZXR0ZS5jYXJ0d3JpZ2h0QGV4YW1wbGUubmV0Poi4BBMB + AgAiBQJYrwLFAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRC/nZJfkR79 + ZVzsBADYe3Hv71I7+lTJDvKOTmV7M4ShOfjEpmDvL/5JB3FzXHAucCOlReCv/6+N + afAspPTc1uys47dvtePwQalroAsd8Y1grn/PCh+Tx27kpn1WG8yZjUsuq3z5rrGR + uXj6g8aeZdqkMTHUVF9Gd+g/5KDODdJbOjwH+l63b/bOiATDFbiNBFiuqioBBADe + 5nUdVOcbZlnxOjl0KBAT+A5bmyBLUT0BmLPsmA4PuXDSth7WvibPC8wcCdCYVk0I + RMYneZUiWq/o5c4rthfLR4jg8kruvomQ4E4d4hyI6R0MLxXYZ3XMu67VuScFgbLU + Rw1eRZ16ANd3Nc1VuFW7ms0vCG0idB8iSZBoULaK8QARAQABiJ8EGAECAAkFAliu + qioCGwwACgkQv52SX5Ee/WVCCAQA1bBDCuVPYEUtZXwXH7CWDfX6ZyOROrQtpa7e + i54CW6hKX3fYHpOiu4yHromjGlINTjjHclsJpn/qmiJizkB9/xl7IRWV+RVpDshZ + 3mymOldpLiKgI5+Rl6mZtO4TtM9BYziIEC24h+IkaLBylZYz4ogfbzXKF8JxtHPM + UuwJVDE= + =0vYo + -----END PGP PUBLIC KEY BLOCK----- + KEY + end + + def primary_keyid + fingerprint[-16..-1] + end + + def fingerprint + '6D494CA6FC90C0CAE0910E42BF9D925F911EFD65' + end + + def names + ['Bette Cartwright', 'Bette Cartwright'] + end + + def emails + ['bette.cartwright@example.com', 'bette.cartwright@example.net'] + end + end +end diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb index 97ae0b6afc5..0b5f66597fd 100644 --- a/spec/support/jira_service_helper.rb +++ b/spec/support/jira_service_helper.rb @@ -51,7 +51,7 @@ module JiraServiceHelper end def jira_project_url - JIRA_API + "/project/#{jira_tracker.project_key}" + JIRA_API + "/project" end def jira_api_comment_url(issue_id) diff --git a/spec/support/json_response_helpers.rb b/spec/support/json_response_helpers.rb index e8d2ef2d7f0..aa235529c56 100644 --- a/spec/support/json_response_helpers.rb +++ b/spec/support/json_response_helpers.rb @@ -3,7 +3,7 @@ shared_context 'JSON response' do end RSpec.configure do |config| - config.include_context 'JSON response', type: :controller + config.include_context 'JSON response' config.include_context 'JSON response', type: :request config.include_context 'JSON response', :api end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index b410a652126..c714d1b08a6 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -1,4 +1,6 @@ module LoginHelpers + include DeviseHelpers + # Internal: Log in as a specific user or a new user of a specific role # # user_or_role - User object, or a role to create (e.g., :admin, :user) @@ -62,7 +64,7 @@ module LoginHelpers visit new_user_session_path expect(page).to have_content('Sign in with') - check 'Remember Me' if remember_me + check 'remember_me' if remember_me click_link "oauth-login-#{provider}" end @@ -106,7 +108,7 @@ module LoginHelpers end def stub_omniauth_saml_config(messages) - Rails.application.env_config['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: Rails.application) Rails.application.routes.disable_clear_and_finalize = true Rails.application.routes.draw do post '/users/auth/saml' => 'omniauth_callbacks#saml' diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index bbbbaf4c5e8..7afa57fb76b 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -17,7 +17,7 @@ module MarkdownMatchers image = actual.at_css('img[alt="Relative Image"]') expect(link['href']).to end_with('master/doc/README.md') - expect(image['src']).to end_with('master/app/assets/images/touch-icon-ipad.png') + expect(image['data-src']).to end_with('master/app/assets/images/touch-icon-ipad.png') end end @@ -70,7 +70,7 @@ module MarkdownMatchers # GollumTagsFilter matcher :parse_gollum_tags do def have_image(src) - have_css("img[src$='#{src}']") + have_css("img[data-src$='#{src}']") end prefix = '/namespace1/gitlabhq/wikis' diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index 66e598e2691..d5bc12f3bc5 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -5,7 +5,7 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') - within('.new_protected_branch') do + within('.js-new-protected-branch') do allowed_to_push_button = find(".js-allowed-to-push") unless allowed_to_push_button.text == access_type_name @@ -50,7 +50,7 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') - within('.new_protected_branch') do + within('.js-new-protected-branch') do allowed_to_merge_button = find(".js-allowed-to-merge") unless allowed_to_merge_button.text == access_type_name diff --git a/spec/support/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb index 3481749a7f0..226277411d6 100644 --- a/spec/support/api/status_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb @@ -9,7 +9,7 @@ shared_examples_for '400 response' do end it 'returns 400' do - expect(response).to have_http_status(400) + expect(response).to have_gitlab_http_status(400) end end @@ -20,7 +20,7 @@ shared_examples_for '403 response' do end it 'returns 403' do - expect(response).to have_http_status(403) + expect(response).to have_gitlab_http_status(403) end end @@ -32,7 +32,7 @@ shared_examples_for '404 response' do end it 'returns 404' do - expect(response).to have_http_status(404) + expect(response).to have_gitlab_http_status(404) expect(json_response).to be_an Object if message.present? diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index 48f454c7187..516f8878679 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -4,29 +4,38 @@ module StubConfiguration # Stubbing both of these because we're not yet consistent with how we access # current application settings - allow_any_instance_of(ApplicationSetting).to receive_messages(messages) + allow_any_instance_of(ApplicationSetting).to receive_messages(to_settings(messages)) allow(Gitlab::CurrentSettings.current_application_settings) - .to receive_messages(messages) + .to receive_messages(to_settings(messages)) + end + + def stub_not_protect_default_branch + stub_application_setting( + default_branch_protection: Gitlab::Access::PROTECTION_NONE) end def stub_config_setting(messages) - allow(Gitlab.config.gitlab).to receive_messages(messages) + allow(Gitlab.config.gitlab).to receive_messages(to_settings(messages)) end def stub_gravatar_setting(messages) - allow(Gitlab.config.gravatar).to receive_messages(messages) + allow(Gitlab.config.gravatar).to receive_messages(to_settings(messages)) end def stub_incoming_email_setting(messages) - allow(Gitlab.config.incoming_email).to receive_messages(messages) + allow(Gitlab.config.incoming_email).to receive_messages(to_settings(messages)) end def stub_mattermost_setting(messages) - allow(Gitlab.config.mattermost).to receive_messages(messages) + allow(Gitlab.config.mattermost).to receive_messages(to_settings(messages)) end def stub_omniauth_setting(messages) - allow(Gitlab.config.omniauth).to receive_messages(messages) + allow(Gitlab.config.omniauth).to receive_messages(to_settings(messages)) + end + + def stub_backup_setting(messages) + allow(Gitlab.config.backup).to receive_messages(to_settings(messages)) end private @@ -49,4 +58,15 @@ module StubConfiguration messages[predicate.to_sym] = messages[key.to_sym] end end + + # Support nested hashes by converting all values into Settingslogic objects + def to_settings(hash) + hash.transform_values do |value| + if value.is_a? Hash + Settingslogic.new(value.deep_stringify_keys) + else + value + end + end + end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 0a194ca4c90..86f9568c12e 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -5,6 +5,7 @@ module TestEnv # When developing the seed repository, comment out the branch you will modify. BRANCH_SHA = { + 'signed-commits' => '5d4a1cb', 'not-merged-branch' => 'b83d6e3', 'branch-merged' => '498214d', 'empty-branch' => '7efb185', @@ -41,7 +42,8 @@ module TestEnv 'csv' => '3dd0896', 'v1.1.0' => 'b83d6e3', 'add-ipython-files' => '93ee732', - 'add-pdf-file' => 'e774ebd' + 'add-pdf-file' => 'e774ebd', + 'add-pdf-text-binary' => '79faa7b' }.freeze # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily @@ -145,7 +147,7 @@ module TestEnv gitaly_exec = File.join(gitaly_dir, 'gitaly') gitaly_config = File.join(gitaly_dir, 'config.toml') log_file = Rails.root.join('log/gitaly-test.log').to_s - @gitaly_pid = spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file) + @gitaly_pid = Bundler.with_original_env { spawn(gitaly_exec, gitaly_config, [:out, :err] => log_file) } end def stop_gitaly diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb index 91cc684d032..d34617be474 100644 --- a/spec/tasks/gitlab/task_helpers_spec.rb +++ b/spec/tasks/gitlab/task_helpers_spec.rb @@ -20,7 +20,6 @@ describe Gitlab::TaskHelpers do it 'checkout the version and reset to it' do expect(subject).to receive(:checkout_version).with(tag, clone_path) - expect(subject).to receive(:reset_to_version).with(tag, clone_path) subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end @@ -31,7 +30,6 @@ describe Gitlab::TaskHelpers do it 'checkout the version and reset to it with a branch name' do expect(subject).to receive(:checkout_version).with(branch, clone_path) - expect(subject).to receive(:reset_to_version).with(branch, clone_path) subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end @@ -70,20 +68,11 @@ describe Gitlab::TaskHelpers do describe '#checkout_version' do it 'clones the repo in the target dir' do expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet]) + .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet origin #{tag}]) expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}]) + .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout -f --quiet FETCH_HEAD --]) subject.checkout_version(tag, clone_path) end end - - describe '#reset_to_version' do - it 'resets --hard to the given version' do - expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}]) - - subject.reset_to_version(tag, clone_path) - end - end end diff --git a/spec/views/ci/status/_badge.html.haml_spec.rb b/spec/views/ci/status/_badge.html.haml_spec.rb index 6a4738ba443..de0b59f83f8 100644 --- a/spec/views/ci/status/_badge.html.haml_spec.rb +++ b/spec/views/ci/status/_badge.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'ci/status/_badge', :view do +describe 'ci/status/_badge' do let(:user) { create(:user) } let(:project) { create(:empty_project, :private) } let(:pipeline) { create(:ci_pipeline, project: project) } diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index f5381a48207..f8c6cb6b5c6 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/_home_panel', :view do +describe 'projects/_home_panel' do let(:project) { create(:empty_project, :public) } let(:notification_settings) do diff --git a/spec/views/projects/blob/_viewer.html.haml_spec.rb b/spec/views/projects/blob/_viewer.html.haml_spec.rb index bbd7f98fa8d..af833168bd9 100644 --- a/spec/views/projects/blob/_viewer.html.haml_spec.rb +++ b/spec/views/projects/blob/_viewer.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/blob/_viewer.html.haml', :view do +describe 'projects/blob/_viewer.html.haml' do include FakeBlobHelpers let(:project) { build(:empty_project) } diff --git a/spec/views/projects/commit/_commit_box.html.haml_spec.rb b/spec/views/projects/commit/_commit_box.html.haml_spec.rb index ab120929c6c..448b925cf34 100644 --- a/spec/views/projects/commit/_commit_box.html.haml_spec.rb +++ b/spec/views/projects/commit/_commit_box.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/commit/_commit_box.html.haml', :view do +describe 'projects/commit/_commit_box.html.haml' do let(:user) { create(:user) } let(:project) { create(:project, :repository) } diff --git a/spec/views/projects/commit/show.html.haml_spec.rb b/spec/views/projects/commit/show.html.haml_spec.rb index 92b4aa12d49..32c95c6bb0d 100644 --- a/spec/views/projects/commit/show.html.haml_spec.rb +++ b/spec/views/projects/commit/show.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/commit/show.html.haml', :view do +describe 'projects/commit/show.html.haml' do let(:project) { create(:project, :repository) } before do diff --git a/spec/views/projects/diffs/_viewer.html.haml_spec.rb b/spec/views/projects/diffs/_viewer.html.haml_spec.rb index 32469202508..8ac32db5585 100644 --- a/spec/views/projects/diffs/_viewer.html.haml_spec.rb +++ b/spec/views/projects/diffs/_viewer.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/diffs/_viewer.html.haml', :view do +describe 'projects/diffs/_viewer.html.haml' do include FakeBlobHelpers let(:project) { create(:project, :repository) } diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb index d9a7ba265f8..117f48450e2 100644 --- a/spec/views/projects/jobs/show.html.haml_spec.rb +++ b/spec/views/projects/jobs/show.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/jobs/show', :view do +describe 'projects/jobs/show' do let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } diff --git a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb index 1e9bdf9108f..5770cf92b4e 100644 --- a/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/merge_requests/creations/_new_submit.html.haml', :view do +describe 'projects/merge_requests/creations/_new_submit.html.haml' do let(:merge_request) { create(:merge_request) } let!(:pipeline) { create(:ci_empty_pipeline) } diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb index e56c0f6be03..37ce7121ccb 100644 --- a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb +++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/notes/_more_actions_dropdown', :view do +describe 'projects/notes/_more_actions_dropdown' do let(:author_user) { create(:user) } let(:not_author_user) { create(:user) } diff --git a/spec/views/projects/pipelines/_stage.html.haml_spec.rb b/spec/views/projects/pipelines/_stage.html.haml_spec.rb index 9c91c4e0fbd..e40e16e742b 100644 --- a/spec/views/projects/pipelines/_stage.html.haml_spec.rb +++ b/spec/views/projects/pipelines/_stage.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/pipelines/_stage', :view do +describe 'projects/pipelines/_stage' do let(:project) { create(:project, :repository) } let(:pipeline) { create(:ci_pipeline, project: project) } let(:stage) { build(:ci_stage, pipeline: pipeline) } diff --git a/spec/views/projects/registry/repositories/index.html.haml_spec.rb b/spec/views/projects/registry/repositories/index.html.haml_spec.rb index ceeace3dc8d..f13b657d474 100644 --- a/spec/views/projects/registry/repositories/index.html.haml_spec.rb +++ b/spec/views/projects/registry/repositories/index.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/registry/repositories/index', :view do +describe 'projects/registry/repositories/index' do let(:group) { create(:group, path: 'group') } let(:project) { create(:empty_project, group: group, path: 'test') } diff --git a/spec/views/projects/tags/index.html.haml_spec.rb b/spec/views/projects/tags/index.html.haml_spec.rb index 33122365e9a..f65cd9f398f 100644 --- a/spec/views/projects/tags/index.html.haml_spec.rb +++ b/spec/views/projects/tags/index.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'projects/tags/index', :view do +describe 'projects/tags/index' do let(:project) { create(:project) } before do diff --git a/spec/workers/create_gpg_signature_worker_spec.rb b/spec/workers/create_gpg_signature_worker_spec.rb new file mode 100644 index 00000000000..c6a17d77d73 --- /dev/null +++ b/spec/workers/create_gpg_signature_worker_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe CreateGpgSignatureWorker do + context 'when GpgKey is found' do + it 'calls Commit#signature' do + commit_sha = '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' + project = create :project + commit = instance_double(Commit) + + allow(Project).to receive(:find_by).with(id: project.id).and_return(project) + allow(project).to receive(:commit).with(commit_sha).and_return(commit) + + expect(commit).to receive(:signature) + + described_class.new.perform(commit_sha, project.id) + end + end + + context 'when Commit is not found' do + let(:nonexisting_commit_sha) { 'bogus' } + let(:project) { create :project } + + it 'does not raise errors' do + expect { described_class.new.perform(nonexisting_commit_sha, project.id) }.not_to raise_error + end + + it 'does not call Commit#signature' do + expect_any_instance_of(Commit).not_to receive(:signature) + + described_class.new.perform(nonexisting_commit_sha, project.id) + end + end + + context 'when Project is not found' do + let(:nonexisting_project_id) { -1 } + + it 'does not raise errors' do + expect { described_class.new.perform(anything, nonexisting_project_id) }.not_to raise_error + end + + it 'does not call Commit#signature' do + expect_any_instance_of(Commit).not_to receive(:signature) + + described_class.new.perform(anything, nonexisting_project_id) + end + end +end diff --git a/spec/workers/invalid_gpg_signature_update_worker_spec.rb b/spec/workers/invalid_gpg_signature_update_worker_spec.rb new file mode 100644 index 00000000000..5972696515b --- /dev/null +++ b/spec/workers/invalid_gpg_signature_update_worker_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe InvalidGpgSignatureUpdateWorker do + context 'when GpgKey is found' do + it 'calls NotificationService.new.run' do + gpg_key = create(:gpg_key) + invalid_signature_updater = double(:invalid_signature_updater) + + expect(Gitlab::Gpg::InvalidGpgSignatureUpdater).to receive(:new).with(gpg_key).and_return(invalid_signature_updater) + expect(invalid_signature_updater).to receive(:run) + + described_class.new.perform(gpg_key.id) + end + end + + context 'when GpgKey is not found' do + let(:nonexisting_gpg_key_id) { -1 } + + it 'does not raise errors' do + expect { described_class.new.perform(nonexisting_gpg_key_id) }.not_to raise_error + end + + it 'does not call NotificationService.new.run' do + expect(Gitlab::Gpg::InvalidGpgSignatureUpdater).not_to receive(:new) + + described_class.new.perform(nonexisting_gpg_key_id) + end + end +end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index a8f4bb72acf..74a9f90195c 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -74,6 +74,7 @@ describe PostReceive do OpenStruct.new(id: '123456') end allow_any_instance_of(Ci::CreatePipelineService).to receive(:branch?).and_return(true) + allow_any_instance_of(Repository).to receive(:ref_exists?).and_return(true) stub_ci_pipeline_to_return_yaml_file end diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb index 3d135f40c1f..f19c9dff941 100644 --- a/spec/workers/project_destroy_worker_spec.rb +++ b/spec/workers/project_destroy_worker_spec.rb @@ -1,24 +1,36 @@ require 'spec_helper' describe ProjectDestroyWorker do - let(:project) { create(:project, :repository) } + let(:project) { create(:project, :repository, pending_delete: true) } let(:path) { project.repository.path_to_repo } subject { described_class.new } - describe "#perform" do - it "deletes the project" do + describe '#perform' do + it 'deletes the project' do subject.perform(project.id, project.owner.id, {}) expect(Project.all).not_to include(project) expect(Dir.exist?(path)).to be_falsey end - it "deletes the project but skips repo deletion" do + it 'deletes the project but skips repo deletion' do subject.perform(project.id, project.owner.id, { "skip_repo" => true }) expect(Project.all).not_to include(project) expect(Dir.exist?(path)).to be_truthy end + + it 'does not raise error when project could not be found' do + expect do + subject.perform(-1, project.owner.id, {}) + end.not_to raise_error + end + + it 'does not raise error when user could not be found' do + expect do + subject.perform(project.id, -1, {}) + end.not_to raise_error + end end end |