diff options
author | Jarka Kadlecova <jarka@gitlab.com> | 2017-10-03 09:37:48 +0200 |
---|---|---|
committer | Jarka Kadlecova <jarka@gitlab.com> | 2017-10-03 09:37:48 +0200 |
commit | c488bcd13e6e0405d990608c8695ffabe17ffcae (patch) | |
tree | 7c1e07081e8ccf88fef05a368a8c5bc4d87ceb55 | |
parent | 59a6f15cd22621826116446d5f28753c973ba2d1 (diff) | |
parent | bdc50ed779cb0c7d266c0f80f3e66a25da8b1964 (diff) | |
download | gitlab-ce-c488bcd13e6e0405d990608c8695ffabe17ffcae.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 18608-lock-issues-v2
# Conflicts:
# db/schema.rb
621 files changed, 11866 insertions, 7092 deletions
diff --git a/.eslintignore b/.eslintignore index d6ce39636bd..1623b996213 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,4 +8,4 @@ karma.config.js webpack.config.js svg.config.js -/app/assets/javascripts/locale/**/*.js +/app/assets/javascripts/locale/**/app.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed993abae73..8501911fde4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -404,6 +404,7 @@ docs lint: before_script: [] script: - scripts/lint-doc.sh + - scripts/lint-changelog-yaml - mv doc/ /nanoc/content/ - cd /nanoc # Build HTML from Markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c671f8d53a..a31817e2b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.0.2 (2017-09-27) + +- [FIXED] Notes will not show an empty bubble when the author isn't a member. !14450 +- [FIXED] Some checks in `rake gitlab:check` were failling with 'undefined method `run_command`'. !14469 +- [FIXED] Make locked setting of Runner to not affect jobs scheduling. !14483 +- [FIXED] Re-allow `name` attribute on user-provided anchor HTML. + ## 10.0.1 (2017-09-23) - [FIXED] Fix duplicate key errors in PostDeployMigrateUserExternalMailData migration. @@ -78,6 +85,8 @@ entry. - [FIXED] Fixed merge request changes bar jumping. - [FIXED] Improve migrations using triggers. - [FIXED] Fix ConvDev Index nav item and Monitoring submenu regression. +- [FIXED] disabling notifications globally now properly turns off group/project added + emails !13325 - [DEPRECATED] Deprecate custom SSH client configuration for the git user. !13930 - [CHANGED] allow all users to delete their account. !13636 (Jacopo Beschi @jacopo-beschi) - [CHANGED] Use full path of project's avatar in webhooks. !13649 (Vitaliy @blackst0ne Klachkov) @@ -186,6 +195,13 @@ entry. - Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi) - [BUGIFX] Improves subgroup creation permissions. !13418 +## 9.5.6 (2017-09-29) + +- [FIXED] Fix MR ready to merge buttons/controls at mobile breakpoint. !14242 +- [FIXED] Fix errors thrown in merge request widget with external CI service/integration. +- [FIXED] Update x/x discussions resolved checkmark icon to be green when all discussions resolved. +- [FIXED] Fix 500 error on merged merge requests when GitLab is restored from a backup. + ## 9.5.5 (2017-09-18) - [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfb2ce0099a..6b261bd5938 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._ Thank you for your interest in contributing to GitLab. This guide details how to contribute to GitLab in a way that is efficient for everyone. -Looking for something to work on? Look for the label [Accepting Merge Requests](#i-want-to-contribute). +Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute). GitLab comes into two flavors, GitLab Community Edition (CE) our free and open source edition, and GitLab Enterprise Edition (EE) which is our commercial @@ -101,7 +101,7 @@ the remaining issues on the GitHub issue tracker. ## I want to contribute! If you want to contribute to GitLab, but are not sure where to start, -look for [issues with the label `Accepting Merge Requests` and weight < 5][accepting-mrs-weight]. +look for [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight]. These issues will be of reasonable size and challenge, for anyone to start contributing to GitLab. @@ -209,8 +209,7 @@ We add the ~"Accepting Merge Requests" label to: - Low priority ~bug issues (i.e. we do not add it to the bugs that we want to solve in the ~"Next Patch Release") -- Small ~"feature proposal" that do not need ~UX / ~"Product work", or for which -the ~UX / ~"Product work" is already done +- Small ~"feature proposal" - Small ~"technical debt" issues After adding the ~"Accepting Merge Requests" label, we try to estimate the @@ -223,6 +222,17 @@ know how difficult the issue is. Additionally: - We encourage people that have never contributed to any open source project to look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers] +If you've decided that you would like to work on an issue, please @-mention +the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what) +as soon as possible. The product manager will then pull in appropriate GitLab team +members to further discuss scope, design, and technical considerations. This will +ensure that that your contribution is aligned with the GitLab product and minimize +any rework and delay in getting it merged into master. + +GitLab team members who apply the ~"Accepting Merge Requests" label to an issue +should update the issue description with a responsible product manager, inviting +any potential community contributor to @-mention per above. + [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened [firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 9b0025a7850..8298bb08b2d 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.40.0 +0.43.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index b3d91f9cfc0..5508e17c23b 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1,2 @@ -5.9.0 +5.9.3 + @@ -26,7 +26,7 @@ gem 'doorkeeper', '~> 4.2.0' gem 'doorkeeper-openid_connect', '~> 1.1.0' gem 'omniauth', '~> 1.4.2' gem 'omniauth-auth0', '~> 1.4.1' -gem 'omniauth-azure-oauth2', '~> 0.0.6' +gem 'omniauth-azure-oauth2', '~> 0.0.9' gem 'omniauth-cas3', '~> 1.1.4' gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-github', '~> 1.1.1' @@ -398,7 +398,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.33.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.38.0', require: 'gitaly' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 4b2f86fe48d..03ffb880fc9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -194,7 +194,7 @@ GEM factory_girl_rails (4.7.0) factory_girl (~> 4.7.0) railties (>= 3.0.0) - faraday (0.12.1) + faraday (0.12.2) multipart-post (>= 1.2, < 3) faraday_middleware (0.11.0.1) faraday (>= 0.7.4, < 1.0) @@ -275,7 +275,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.33.0) + gitaly-proto (0.38.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -516,10 +516,10 @@ GEM omniauth-oauth2 (~> 1.1) omniauth-authentiq (0.3.1) omniauth-oauth2 (~> 1.3, >= 1.3.1) - omniauth-azure-oauth2 (0.0.6) + omniauth-azure-oauth2 (0.0.9) jwt (~> 1.0) omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) + omniauth-oauth2 (~> 1.4) omniauth-cas3 (1.1.4) addressable (~> 2.3) nokogiri (~> 1.7, >= 1.7.1) @@ -545,7 +545,7 @@ GEM omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.3.1) + omniauth-oauth2 (1.4.0) oauth2 (~> 1.0) omniauth (~> 1.2) omniauth-oauth2-generic (0.2.2) @@ -1025,7 +1025,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.33.0) + gitaly-proto (~> 0.38.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) @@ -1075,7 +1075,7 @@ DEPENDENCIES omniauth (~> 1.4.2) omniauth-auth0 (~> 1.4.1) omniauth-authentiq (~> 0.3.1) - omniauth-azure-oauth2 (~> 0.0.6) + omniauth-azure-oauth2 (~> 0.0.9) omniauth-cas3 (~> 1.1.4) omniauth-facebook (~> 4.0.0) omniauth-github (~> 1.1.1) diff --git a/app/assets/images/favicon-blue.ico b/app/assets/images/favicon-blue.ico Binary files differindex 156fcf07588..156fcf07588 100755..100644 --- a/app/assets/images/favicon-blue.ico +++ b/app/assets/images/favicon-blue.ico diff --git a/app/assets/images/icons.json b/app/assets/images/icons.json index e5da75faf38..6b8f85e37fd 100644 --- a/app/assets/images/icons.json +++ b/app/assets/images/icons.json @@ -1 +1 @@ -{"iconCount":134,"icons":["abuse","account","admin","angle-double-left","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","calendar","cancel","chevron-down","chevron-left","chevron-right","chevron-up","clock","code","comment-dots","comment-next","comment","comments","commit","credit-card","disk","doc_code","doc_image","doc_text","download","duplicate","earth","eye-slash","eye","file-additions","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","merge-request-close-m","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","star-o","star","stop","talic","task-done","template","thump-down","thump-up","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file +{"iconCount":135,"spriteSize":58718,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","calendar","cancel","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","comment-dots","comment-next","comment","comments","commit","credit-card","disk","doc_code","doc_image","doc_text","download","duplicate","earth","eye-slash","eye","file-additions","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","star-o","star","stop","talic","task-done","template","thump-down","thump-up","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file diff --git a/app/assets/images/icons.svg b/app/assets/images/icons.svg index 5c3a9962bd3..30cb2109ec2 100644 --- a/app/assets/images/icons.svg +++ b/app/assets/images/icons.svg @@ -1 +1 @@ -<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 15V1a1 1 0 0 1 1-1h4.604c.93 0 1.762.088 2.495.264.733.176 1.353.445 1.863.807.509.363.897.82 1.164 1.369.268.549.401 1.197.401 1.945 0 .366-.045.718-.137 1.055-.091.337-.23.652-.417.945a3.453 3.453 0 0 1-.71.796 3.645 3.645 0 0 1-1.021.588c.469.117.87.295 1.203.533.333.238.608.515.824.83.216.315.374.657.473 1.027.099.37.148.75.148 1.138 0 1.553-.5 2.725-1.5 3.516-1 .791-2.423 1.187-4.27 1.187H3a1 1 0 0 1-1-1zm3.297-5.967v4.319H8.12c.425 0 .791-.053 1.099-.16.307-.106.564-.252.769-.44.205-.186.357-.406.456-.659.099-.252.148-.529.148-.83a3.04 3.04 0 0 0-.131-.928 1.78 1.78 0 0 0-.413-.703 1.8 1.8 0 0 0-.73-.445c-.3-.103-.66-.154-1.077-.154H5.297zm0-2.33h2.44c.842-.014 1.468-.192 1.878-.533.41-.34.616-.826.616-1.456 0-.725-.21-1.247-.632-1.566-.421-.318-1.086-.478-1.995-.478H5.297v4.033z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-additions" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M7.228 5l-.475-1.335A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H7.228zM13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177a.505.505 0 0 1-.038.044l.038-.044zm-.787 0l.038.043a.5.5 0 0 1-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close-m" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V1a1 1 0 1 1 2 0v6h6a1 1 0 0 1 0 2H9v6a1 1 0 0 1-2 0V9H1a1 1 0 1 1 0-2h6z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.666 4.423a5 5 0 1 1-.203 6.944 1 1 0 1 0-1.478 1.347 7 7 0 1 0 .12-9.556L1.842 2.137a.5.5 0 0 0-.815.385L1 7.26a.5.5 0 0 0 .607.492l4.629-1.013a.5.5 0 0 0 .207-.877L4.666 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.494 4.423a5 5 0 1 0 .203 6.944 1 1 0 1 1 1.478 1.347 7 7 0 1 1-.12-9.556l1.262-1.021a.5.5 0 0 1 .815.385l.028 4.738a.5.5 0 0 1-.607.492L9.924 6.739a.5.5 0 0 1-.207-.877l1.777-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.009 6.958a4 4 0 0 0 5.283 4.775 1 1 0 0 1 .712 1.87A6 6 0 0 1 2.077 6.44l-.741-.2a.5.5 0 0 1-.12-.915L3.41 4.058a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.711-1.87 6 6 0 0 1 7.927 7.162l.74.2a.5.5 0 0 1 .121.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v7.186a3 3 0 0 1-1.426 2.554l-4 2.465a3 3 0 0 1-3.148 0l-4-2.465A3 3 0 0 1 1 10.186V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v7.186a1 1 0 0 0 .475.852l4 2.464a1 1 0 0 0 1.05 0l4-2.464a1 1 0 0 0 .475-.852V3a1 1 0 0 0-1-1H4zm0 1.5a.5.5 0 0 1 .5-.5h4v8.837a.5.5 0 0 1-.753.431l-3.5-2.052A.5.5 0 0 1 4 9.785V3.5z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="talic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 0h7a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm2 2h3L8 14H5L8 2zM3 14h7a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="thump-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thump-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.34 10.479A3 3 0 0 1 12.756 15h-9.51A3 3 0 0 1 .66 10.479l4.755-8.083a3 3 0 0 1 5.172 0l4.755 8.083zm-6.478-7.07a1 1 0 0 0-1.724 0l-4.755 8.084A1 1 0 0 0 3.245 13h9.51a1 1 0 0 0 .862-1.507L8.862 3.41zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol><symbol viewBox="0 0 16 16" id="angle-double-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.535 7.95L1.292 3.707a1 1 0 0 1-.281-.695 1 1 0 1 1 1.695-.719l4.95 4.95a.998.998 0 0 1 0 1.414l-4.95 4.95a1.002 1.002 0 0 1-.707.293c-.549 0-1-.452-1-1 0-.266.106-.52.293-.708L5.535 7.95zm7 0L8.292 3.707a1 1 0 0 1-.281-.695 1 1 0 1 1 1.695-.719l4.95 4.95a.998.998 0 0 1 0 1.414l-4.95 4.95a1.002 1.002 0 0 1-.707.293c-.549 0-1-.452-1-1.001 0-.265.106-.519.293-.707l4.243-4.242z"/></symbol><symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol><symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol><symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol><symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol><symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 15V1a1 1 0 0 1 1-1h4.604c.93 0 1.762.088 2.495.264.733.176 1.353.445 1.863.807.509.363.897.82 1.164 1.369.268.549.401 1.197.401 1.945 0 .366-.045.718-.137 1.055-.091.337-.23.652-.417.945a3.453 3.453 0 0 1-.71.796 3.645 3.645 0 0 1-1.021.588c.469.117.87.295 1.203.533.333.238.608.515.824.83.216.315.374.657.473 1.027.099.37.148.75.148 1.138 0 1.553-.5 2.725-1.5 3.516-1 .791-2.423 1.187-4.27 1.187H3a1 1 0 0 1-1-1zm3.297-5.967v4.319H8.12c.425 0 .791-.053 1.099-.16.307-.106.564-.252.769-.44.205-.186.357-.406.456-.659.099-.252.148-.529.148-.83a3.04 3.04 0 0 0-.131-.928 1.78 1.78 0 0 0-.413-.703 1.8 1.8 0 0 0-.73-.445c-.3-.103-.66-.154-1.077-.154H5.297zm0-2.33h2.44c.842-.014 1.468-.192 1.878-.533.41-.34.616-.826.616-1.456 0-.725-.21-1.247-.632-1.566-.421-.318-1.086-.478-1.995-.478H5.297v4.033z"/></symbol><symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol><symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol><symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol><symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol><symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol><symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol><symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol><symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol><symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol><symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol><symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol><symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol><symbol viewBox="0 0 16 16" id="file-additions" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol><symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol><symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol><symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M7.228 5l-.475-1.335A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H7.228zM13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol><symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="geo-nodes" xmlns="http://www.w3.org/2000/svg"><path d="M9.7 13.1l-.2.2c-.7.8-2 .9-2.8.1-.1 0-.1-.1-.1-.1l-.2-.2c-2 .2-3.4.7-3.4 1.4 0 .8 2.2 1.5 5 1.5s5-.7 5-1.5c0-.7-1.4-1.2-3.3-1.4M7.3 12.7c.4.4 1 .3 1.4-.1C11.6 9.5 13 7 13 5.3 13 2.4 10.8 0 8 0S3 2.4 3 5.3C3 7 4.4 9.5 7.3 12.7M8 2c1.6 0 3 1.4 3 3.3 0 1-1 2.8-3 5.2-2-2.4-3-4.2-3-5.2C5 3.4 6.4 2 8 2"/><circle cx="8" cy="5" r="1"/></symbol><symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol><symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol><symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177a.505.505 0 0 1-.038.044l.038-.044zm-.787 0l.038.043a.5.5 0 0 1-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol><symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol><symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol><symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol><symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol><symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol><symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol><symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol><symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol><symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol><symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol><symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol><symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol><symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol><symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol><symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol><symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol><symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol><symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol><symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol><symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol><symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol><symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V1a1 1 0 1 1 2 0v6h6a1 1 0 0 1 0 2H9v6a1 1 0 0 1-2 0V9H1a1 1 0 1 1 0-2h6z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol><symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol><symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol><symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol><symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol><symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol><symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.666 4.423a5 5 0 1 1-.203 6.944 1 1 0 1 0-1.478 1.347 7 7 0 1 0 .12-9.556L1.842 2.137a.5.5 0 0 0-.815.385L1 7.26a.5.5 0 0 0 .607.492l4.629-1.013a.5.5 0 0 0 .207-.877L4.666 4.423z"/></symbol><symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.494 4.423a5 5 0 1 0 .203 6.944 1 1 0 1 1 1.478 1.347 7 7 0 1 1-.12-9.556l1.262-1.021a.5.5 0 0 1 .815.385l.028 4.738a.5.5 0 0 1-.607.492L9.924 6.739a.5.5 0 0 1-.207-.877l1.777-1.439z"/></symbol><symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.009 6.958a4 4 0 0 0 5.283 4.775 1 1 0 0 1 .712 1.87A6 6 0 0 1 2.077 6.44l-.741-.2a.5.5 0 0 1-.12-.915L3.41 4.058a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.711-1.87 6 6 0 0 1 7.927 7.162l.74.2a.5.5 0 0 1 .121.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol><symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol><symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol><symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol><symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v7.186a3 3 0 0 1-1.426 2.554l-4 2.465a3 3 0 0 1-3.148 0l-4-2.465A3 3 0 0 1 1 10.186V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v7.186a1 1 0 0 0 .475.852l4 2.464a1 1 0 0 0 1.05 0l4-2.464a1 1 0 0 0 .475-.852V3a1 1 0 0 0-1-1H4zm0 1.5a.5.5 0 0 1 .5-.5h4v8.837a.5.5 0 0 1-.753.431l-3.5-2.052A.5.5 0 0 1 4 9.785V3.5z"/></symbol><symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol><symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol><symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol><symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol><symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol><symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol><symbol viewBox="0 0 16 16" id="talic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 0h7a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm2 2h3L8 14H5L8 2zM3 14h7a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol><symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol><symbol viewBox="0 0 16 16" id="thump-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol><symbol viewBox="0 0 16 16" id="thump-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol><symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol><symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol><symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol><symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol><symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol><symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.34 10.479A3 3 0 0 1 12.756 15h-9.51A3 3 0 0 1 .66 10.479l4.755-8.083a3 3 0 0 1 5.172 0l4.755 8.083zm-6.478-7.07a1 1 0 0 0-1.724 0l-4.755 8.084A1 1 0 0 0 3.245 13h9.51a1 1 0 0 0 .862-1.507L8.862 3.41zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol><symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol></svg>
\ No newline at end of file diff --git a/app/assets/images/sprite.symbol.html b/app/assets/images/sprite.symbol.html deleted file mode 100644 index d928d3f73b8..00000000000 --- a/app/assets/images/sprite.symbol.html +++ /dev/null @@ -1,3297 +0,0 @@ -<!DOCTYPE html> -<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta charset="utf-8"/> - <meta http-equiv="X-UA-Compatible" content="IE=Edge"/> - <script src="https://rawgit.com/jonathantneal/svg4everybody/master/dist/svg4everybody.js"></script> - <script>svg4everybody();</script> - <title>SVG <symbol> sprite preview | svg-sprite</title> - <style>@charset "UTF-8";body{padding:0;margin:0;color:#666;background:#fafafa;font-family:Arial,Helvetica,sans-serif;font-size:1em;line-height:1.4}header{display:block;padding:3em 3em 2em 3em;background-color:#fff}header p{margin:2em 0 0 0}section{border-top:1px solid #eee;padding:2em 3em 0 3em}section ul{margin:0;padding:0}section li{display:inline;display:inline-block;background-color:#fff;position:relative;margin:0 2em 2em 0;vertical-align:top;border:1px solid #ccc;padding:1em 1em 3em 1em;cursor:default}.icon-box{margin:0;width:144px;height:144px;position:relative;background:#ccc url("data:image/gif;base64,R0lGODlhDAAMAIAAAMzMzP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjEgNjQuMTQwOTQ5LCAyMDEwLzEyLzA3LTEwOjU3OjAxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgV2luZG93cyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozQjk4OTI0MUY5NTIxMUUyQkJDMEI5NEFEM0Y1QTYwQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozQjk4OTI0MkY5NTIxMUUyQkJDMEI5NEFEM0Y1QTYwQyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjNCOTg5MjNGRjk1MjExRTJCQkMwQjk0QUQzRjVBNjBDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjNCOTg5MjQwRjk1MjExRTJCQkMwQjk0QUQzRjVBNjBDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAAAAAAAsAAAAAAwADAAAAhaEH6mHmmzcgzJAUG/NVGrfOZ8YLlABADs=") top left repeat;border:1px solid #ccc;display:table-cell;vertical-align:middle;text-align:center}.icon{display:inline;display:inline-block}h1{margin-top:0}h2{margin:0;padding:0;font-size:1em;font-weight:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;position:absolute;left:1em;right:1em;bottom:1em}footer{display:block;margin:0;padding:0 3em 3em 3em}footer p{margin:0;font-size:.7em}footer a{color:#0f7595;margin-left:0}</style> - -<!-- - -Sprite shape dimensions -==================================================================================================== -You will need to set the sprite shape dimensions via CSS when you use them as inline SVG, otherwise -they would become a huge 100% in size. You may use the following dimension classes for doing so. -They might well be outsourced to an external stylesheet of course. - ---> - -<style type="text/css"> - .svg-abuse-dims { width: 16px; height: 16px; } - .svg-account-dims { width: 16px; height: 16px; } - .svg-admin-dims { width: 16px; height: 16px; } - .svg-angle-double-left-dims { width: 16px; height: 16px; } - .svg-angle-down-dims { width: 16px; height: 16px; } - .svg-angle-left-dims { width: 16px; height: 16px; } - .svg-angle-right-dims { width: 16px; height: 16px; } - .svg-angle-up-dims { width: 16px; height: 16px; } - .svg-appearance-dims { width: 16px; height: 16px; } - .svg-applications-dims { width: 16px; height: 16px; } - .svg-approval-dims { width: 16px; height: 16px; } - .svg-arrow-right-dims { width: 16px; height: 16px; } - .svg-assignee-dims { width: 16px; height: 16px; } - .svg-bold-dims { width: 16px; height: 16px; } - .svg-book-dims { width: 16px; height: 16px; } - .svg-branch-dims { width: 16px; height: 16px; } - .svg-calendar-dims { width: 16px; height: 16px; } - .svg-cancel-dims { width: 16px; height: 16px; } - .svg-chevron-down-dims { width: 16px; height: 16px; } - .svg-chevron-left-dims { width: 16px; height: 16px; } - .svg-chevron-right-dims { width: 16px; height: 16px; } - .svg-chevron-up-dims { width: 16px; height: 16px; } - .svg-clock-dims { width: 16px; height: 16px; } - .svg-code-dims { width: 16px; height: 16px; } - .svg-comment-dims { width: 16px; height: 16px; } - .svg-comment-dots-dims { width: 16px; height: 16px; } - .svg-comment-next-dims { width: 16px; height: 16px; } - .svg-comments-dims { width: 16px; height: 16px; } - .svg-commit-dims { width: 16px; height: 16px; } - .svg-credit-card-dims { width: 16px; height: 16px; } - .svg-disk-dims { width: 16px; height: 16px; } - .svg-doc_code-dims { width: 16px; height: 16px; } - .svg-doc_image-dims { width: 16px; height: 16px; } - .svg-doc_text-dims { width: 16px; height: 16px; } - .svg-download-dims { width: 16px; height: 16px; } - .svg-duplicate-dims { width: 16px; height: 16px; } - .svg-earth-dims { width: 16px; height: 16px; } - .svg-eye-dims { width: 16px; height: 16px; } - .svg-eye-slash-dims { width: 16px; height: 16px; } - .svg-file-additions-dims { width: 16px; height: 16px; } - .svg-file-deletion-dims { width: 16px; height: 16px; } - .svg-file-modified-dims { width: 16px; height: 16px; } - .svg-filter-dims { width: 16px; height: 16px; } - .svg-folder-dims { width: 16px; height: 16px; } - .svg-fork-dims { width: 16px; height: 16px; } - .svg-git-merge-dims { width: 16px; height: 16px; } - .svg-group-dims { width: 16px; height: 16px; } - .svg-history-dims { width: 16px; height: 16px; } - .svg-home-dims { width: 16px; height: 16px; } - .svg-hook-dims { width: 16px; height: 16px; } - .svg-issue-block-dims { width: 16px; height: 16px; } - .svg-issue-child-dims { width: 16px; height: 16px; } - .svg-issue-close-dims { width: 16px; height: 16px; } - .svg-issue-duplicate-dims { width: 16px; height: 16px; } - .svg-issue-new-dims { width: 16px; height: 16px; } - .svg-issue-open-dims { width: 16px; height: 16px; } - .svg-issue-open-m-dims { width: 16px; height: 16px; } - .svg-issue-parent-dims { width: 16px; height: 16px; } - .svg-issues-dims { width: 16px; height: 16px; } - .svg-key-dims { width: 16px; height: 16px; } - .svg-key-2-dims { width: 16px; height: 16px; } - .svg-label-dims { width: 16px; height: 16px; } - .svg-labels-dims { width: 16px; height: 16px; } - .svg-leave-dims { width: 16px; height: 16px; } - .svg-level-up-dims { width: 16px; height: 16px; } - .svg-license-dims { width: 16px; height: 16px; } - .svg-link-dims { width: 16px; height: 16px; } - .svg-list-bulleted-dims { width: 16px; height: 16px; } - .svg-list-numbered-dims { width: 16px; height: 16px; } - .svg-location-dims { width: 16px; height: 16px; } - .svg-location-dot-dims { width: 16px; height: 16px; } - .svg-lock-dims { width: 16px; height: 16px; } - .svg-lock-open-dims { width: 16px; height: 16px; } - .svg-log-dims { width: 16px; height: 16px; } - .svg-mail-dims { width: 16px; height: 16px; } - .svg-merge-request-close-dims { width: 16px; height: 16px; } - .svg-merge-request-close-m-dims { width: 16px; height: 16px; } - .svg-messages-dims { width: 16px; height: 16px; } - .svg-mobile-issue-close-dims { width: 16px; height: 16px; } - .svg-monitor-dims { width: 16px; height: 16px; } - .svg-more-dims { width: 16px; height: 16px; } - .svg-notifications-dims { width: 16px; height: 16px; } - .svg-notifications-off-dims { width: 16px; height: 16px; } - .svg-overview-dims { width: 16px; height: 16px; } - .svg-pencil-dims { width: 16px; height: 16px; } - .svg-pipeline-dims { width: 16px; height: 16px; } - .svg-play-dims { width: 16px; height: 16px; } - .svg-plus-dims { width: 16px; height: 16px; } - .svg-plus-square-dims { width: 16px; height: 16px; } - .svg-plus-square-o-dims { width: 16px; height: 16px; } - .svg-preferences-dims { width: 16px; height: 16px; } - .svg-profile-dims { width: 16px; height: 16px; } - .svg-project-dims { width: 16px; height: 16px; } - .svg-push-rules-dims { width: 16px; height: 16px; } - .svg-question-dims { width: 16px; height: 16px; } - .svg-question-o-dims { width: 16px; height: 16px; } - .svg-quote-dims { width: 16px; height: 16px; } - .svg-redo-dims { width: 16px; height: 16px; } - .svg-remove-dims { width: 16px; height: 16px; } - .svg-repeat-dims { width: 16px; height: 16px; } - .svg-retry-dims { width: 16px; height: 16px; } - .svg-scale-dims { width: 16px; height: 16px; } - .svg-screen-full-dims { width: 16px; height: 16px; } - .svg-screen-normal-dims { width: 16px; height: 16px; } - .svg-search-dims { width: 16px; height: 16px; } - .svg-settings-dims { width: 16px; height: 16px; } - .svg-shield-dims { width: 16px; height: 16px; } - .svg-slight-frown-dims { width: 16px; height: 16px; } - .svg-slight-smile-dims { width: 16px; height: 16px; } - .svg-smile-dims { width: 16px; height: 16px; } - .svg-smiley-dims { width: 16px; height: 16px; } - .svg-snippet-dims { width: 16px; height: 16px; } - .svg-spam-dims { width: 16px; height: 16px; } - .svg-star-dims { width: 16px; height: 16px; } - .svg-star-o-dims { width: 16px; height: 16px; } - .svg-stop-dims { width: 16px; height: 16px; } - .svg-talic-dims { width: 16px; height: 16px; } - .svg-task-done-dims { width: 16px; height: 16px; } - .svg-template-dims { width: 16px; height: 16px; } - .svg-thump-down-dims { width: 16px; height: 16px; } - .svg-thump-up-dims { width: 16px; height: 16px; } - .svg-timer-dims { width: 16px; height: 16px; } - .svg-todo-add-dims { width: 16px; height: 16px; } - .svg-todo-done-dims { width: 16px; height: 16px; } - .svg-token-dims { width: 16px; height: 16px; } - .svg-unapproval-dims { width: 16px; height: 16px; } - .svg-unassignee-dims { width: 16px; height: 16px; } - .svg-unlink-dims { width: 16px; height: 16px; } - .svg-user-dims { width: 16px; height: 16px; } - .svg-users-dims { width: 16px; height: 16px; } - .svg-volume-up-dims { width: 16px; height: 16px; } - .svg-warning-dims { width: 16px; height: 16px; } - .svg-work-dims { width: 16px; height: 16px; } -</style> -<!-- -==================================================================================================== ---> - - </head> - <body> - -<!-- - -Inline <symbol> SVG sprite -==================================================================================================== -This is an inlined version of the generated SVG sprite. The single images may be <use>d everywhere -below within this document. Please see - - https://github.com/jkphl/svg-sprite/blob/master/docs/configuration.md#defs--symbol-mode - -for further details on how to create this embeddable sprite variant. - ---> - -<svg width="0" height="0" style="position:absolute"> - <symbol viewBox="0 0 16 16" id="abuse" xmlns="http://www.w3.org/2000/svg"><path d="M11.408.328l4.029 3.222A1.5 1.5 0 0 1 16 4.72v6.555a1.5 1.5 0 0 1-.563 1.171l-4.026 3.224a1.5 1.5 0 0 1-.937.329H5.529a1.5 1.5 0 0 1-.937-.328L.563 12.45A1.5 1.5 0 0 1 0 11.28V4.724a1.5 1.5 0 0 1 .563-1.171L4.589.329A1.5 1.5 0 0 1 5.526 0h4.945c.34 0 .67.116.937.328zM10.296 2H5.702L2 4.964v6.074L5.704 14h4.594L14 11.036V4.962L10.296 2zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="account" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.195 9.965l-.568-.875a.25.25 0 0 1 .015-.294l.405-.5a.25.25 0 0 1 .283-.075l.938.36c.257-.183.543-.325.851-.42l.322-.988A.25.25 0 0 1 11.679 7h.642a.25.25 0 0 1 .238.173l.322.988c.308.095.594.237.851.42l.938-.36a.25.25 0 0 1 .283.076l.405.5a.25.25 0 0 1 .015.293l-.568.875c.113.297.18.616.193.95l.898.54a.25.25 0 0 1 .115.27l-.144.626a.25.25 0 0 1-.222.193l-1.115.098a3.015 3.015 0 0 1-.512.608l.165 1.18a.25.25 0 0 1-.138.259l-.577.281a.25.25 0 0 1-.29-.05l-.874-.905a3.035 3.035 0 0 1-.608 0l-.875.904a.25.25 0 0 1-.289.051l-.577-.281a.25.25 0 0 1-.138-.26l.165-1.18a3.015 3.015 0 0 1-.512-.607l-1.115-.098a.25.25 0 0 1-.222-.193l-.144-.626a.25.25 0 0 1 .115-.27l.898-.54c.013-.334.08-.653.193-.95zM6.789 8.023A12.845 12.845 0 0 0 6 8c-5.036 0-6 2.74-6 4.48C0 14.22.076 15 6 15c.553 0 1.055-.006 1.51-.02A5.977 5.977 0 0 1 6 11c0-1.083.287-2.1.79-2.977zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM12 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="admin" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.162 2.5a3.5 3.5 0 0 1-3.163 5.479L6.08 14.766a1.5 1.5 0 0 1-2.598-1.5L7.4 6.479A3.5 3.5 0 0 1 10.564 1L8.9 3.88l2.599 1.5 1.663-2.88zm-8.63 11.949a.5.5 0 1 0 .5-.866.5.5 0 0 0-.5.866z"/></symbol> - <symbol viewBox="0 0 16 16" id="angle-double-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.414 7.95l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 1 0 1.414-1.415L10.414 7.95zm-7 0l4.243-4.243a1 1 0 0 0-1.414-1.414l-4.95 4.95a.997.997 0 0 0 0 1.414l4.95 4.95a1 1 0 0 0 1.414-1.415L3.414 7.95z"/></symbol> - <symbol viewBox="0 0 16 16" id="angle-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 10.243l-4.95-4.95a1 1 0 0 0-1.414 1.414l5.657 5.657a.997.997 0 0 0 1.414 0l5.657-5.657a1 1 0 0 0-1.414-1.414L8 10.243z"/></symbol> - <symbol viewBox="0 0 16 16" id="angle-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.757 8l4.95-4.95a1 1 0 1 0-1.414-1.414L3.636 7.293a.997.997 0 0 0 0 1.414l5.657 5.657a1 1 0 0 0 1.414-1.414L5.757 8z"/></symbol> - <symbol viewBox="0 0 16 16" id="angle-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.243 8l-4.95-4.95a1 1 0 0 1 1.414-1.414l5.657 5.657a.997.997 0 0 1 0 1.414l-5.657 5.657a1 1 0 0 1-1.414-1.414L10.243 8z"/></symbol> - <symbol viewBox="0 0 16 16" id="angle-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 6.757l-4.95 4.95a1 1 0 1 1-1.414-1.414l5.657-5.657a.997.997 0 0 1 1.414 0l5.657 5.657a1 1 0 0 1-1.414 1.414L8 6.757z"/></symbol> - <symbol viewBox="0 0 16 16" id="appearance" xmlns="http://www.w3.org/2000/svg"><path d="M11.161 12.456l.232.121c.1.053.175.094.249.137.53.318.844.75.857 1.402.012 1.397-1.116 1.756-3.12 1.858a23.85 23.85 0 0 1-1.38.026A8 8 0 0 1 0 8a8 8 0 0 1 8-8c4.417 0 7.998 3.582 7.998 7.977.06 2.621-1.312 3.586-4.48 3.648-.602.008-1.068.043-1.4.104.228.192.598.47 1.043.727zm-3.287-.943c-.019-1.495 1.228-1.856 3.611-1.888C13.67 9.582 14.028 9.33 13.998 8A6 6 0 1 0 8 14c.603 0 .91-.004 1.277-.023a9.7 9.7 0 0 0 .478-.035c-1.172-.738-1.868-1.47-1.88-2.43zM6 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-2-3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM4 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="applications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 1v2h2V1H7zm0 5h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm6-6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zm0 6h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm0 1v2h2V7h-2zM1 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0 1v2h2v-2H1zm6-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm6 0h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="approval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.536 10.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 1 1 9.12 9.243l1.415 1.414zM7.632 8.109A2 2 0 0 0 7 11.364l2.121 2.121a1.996 1.996 0 0 0 2.807.021C11.686 14.554 10.627 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.6 0 1.142.038 1.632.109zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol> - <symbol viewBox="0 0 16 16" id="arrow-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 6H2a2 2 0 1 0 0 4h7v2.586a1 1 0 0 0 1.707.707l4.586-4.586a1 1 0 0 0 0-1.414l-4.586-4.586A1 1 0 0 0 9 3.414V6z"/></symbol> - <symbol viewBox="0 0 16 16" id="assignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 5V4a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V7h-1a1 1 0 0 1 0-2h1zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol> - <symbol viewBox="0 0 16 16" id="bold" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 15V1a1 1 0 0 1 1-1h4.604c.93 0 1.762.088 2.495.264.733.176 1.353.445 1.863.807.509.363.897.82 1.164 1.369.268.549.401 1.197.401 1.945 0 .366-.045.718-.137 1.055-.091.337-.23.652-.417.945a3.453 3.453 0 0 1-.71.796 3.645 3.645 0 0 1-1.021.588c.469.117.87.295 1.203.533.333.238.608.515.824.83.216.315.374.657.473 1.027.099.37.148.75.148 1.138 0 1.553-.5 2.725-1.5 3.516-1 .791-2.423 1.187-4.27 1.187H3a1 1 0 0 1-1-1zm3.297-5.967v4.319H8.12c.425 0 .791-.053 1.099-.16.307-.106.564-.252.769-.44.205-.186.357-.406.456-.659.099-.252.148-.529.148-.83a3.04 3.04 0 0 0-.131-.928 1.78 1.78 0 0 0-.413-.703 1.8 1.8 0 0 0-.73-.445c-.3-.103-.66-.154-1.077-.154H5.297zm0-2.33h2.44c.842-.014 1.468-.192 1.878-.533.41-.34.616-.826.616-1.456 0-.725-.21-1.247-.632-1.566-.421-.318-1.086-.478-1.995-.478H5.297v4.033z"/></symbol> - <symbol viewBox="0 0 16 16" id="book" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2v4.191a.5.5 0 0 1-.724.447l-1.052-.526a.5.5 0 0 0-.448 0l-1.052.526A.5.5 0 0 1 7 6.191V2zM5 0h6a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol> - <symbol viewBox="0 0 16 16" id="branch" xmlns="http://www.w3.org/2000/svg"><path d="M6 11.978v.29a2 2 0 1 1-2 0V3.732a2 2 0 1 1 2 0v3.849c.592-.491 1.31-.854 2.15-1.081 1.308-.353 1.875-.882 1.893-1.743a2 2 0 1 1 2.002-.051C12.053 6.54 10.857 7.84 8.67 8.43 7.056 8.867 6.195 9.98 6 11.978zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 15a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="calendar" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12 2h2a2 2 0 0 1 2 2H0a2 2 0 0 1 2-2h2V1a1 1 0 1 1 2 0v1h4V1a1 1 0 1 1 2 0v1zM0 4h16v9a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4zm2 2.5V13a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5zM5 8h2a1 1 0 1 1 0 2H5a1 1 0 1 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="cancel" xmlns="http://www.w3.org/2000/svg"><path d="M3.11 4.523a6 6 0 0 0 8.367 8.367L3.109 4.524zM4.522 3.11l8.368 8.368A6 6 0 0 0 4.524 3.11zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol> - <symbol viewBox="0 0 16 16" id="chevron-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.078 8.2l3.535-3.536a2 2 0 0 1 2.828 2.828l-4.949 4.95c-.39.39-.902.586-1.414.586a1.994 1.994 0 0 1-1.414-.586l-4.95-4.95a2 2 0 1 1 2.828-2.828l3.536 3.535z"/></symbol> - <symbol viewBox="0 0 16 16" id="chevron-left" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.977 7.998l3.535-3.535a2 2 0 1 0-2.828-2.828l-4.95 4.949c-.39.39-.586.902-.586 1.414 0 .512.196 1.024.586 1.414l4.95 4.95a2 2 0 1 0 2.828-2.828L7.977 7.998z"/></symbol> - <symbol viewBox="0 0 16 16" id="chevron-right" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.22 7.998L4.683 4.463a2 2 0 0 1 2.828-2.828l4.95 4.949c.39.39.586.902.586 1.414a1.99 1.99 0 0 1-.586 1.414l-4.95 4.95a2 2 0 0 1-2.828-2.828l3.535-3.536z"/></symbol> - <symbol viewBox="0 0 16 16" id="chevron-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.778 8.957l3.535 3.535a2 2 0 1 0 2.828-2.828l-4.949-4.95a1.994 1.994 0 0 0-1.414-.586c-.512 0-1.024.196-1.414.586l-4.95 4.95a2 2 0 1 0 2.828 2.828l3.536-3.535z"/></symbol> - <symbol viewBox="0 0 16 16" id="clock" xmlns="http://www.w3.org/2000/svg"><path d="M9 7h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V5a1 1 0 1 1 2 0v2zm-1 9A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol> - <symbol viewBox="0 0 16 16" id="code" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.871 8.243a.997.997 0 0 0-.293-.707L12.75 4.707a1 1 0 0 0-1.414 1.414l2.12 2.122-2.12 2.121a1 1 0 0 0 1.414 1.414l2.828-2.828a.997.997 0 0 0 .293-.707zm-13.243 0L4.75 6.12a1 1 0 1 0-1.414-1.414L.507 7.536a.997.997 0 0 0 0 1.414l2.829 2.828a1 1 0 1 0 1.414-1.414L2.628 8.243zm6.407-4.107a1 1 0 0 1 .707 1.225L8.19 11.157a1 1 0 1 1-1.931-.518L7.81 4.843a1 1 0 0 1 1.224-.707z"/></symbol> - <symbol viewBox="0 0 16 16" id="comment" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol> - <symbol viewBox="0 0 16 16" id="comment-dots" xmlns="http://www.w3.org/2000/svg"><path d="M1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586zM5 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="comment-next" xmlns="http://www.w3.org/2000/svg"><path d="M8 5V4a.5.5 0 0 1 .8-.4l2.667 2a.5.5 0 0 1 0 .8L8.8 8.4A.5.5 0 0 1 8 8V7H6a1 1 0 1 1 0-2h2zM1.707 15.707C1.077 16.337 0 15.891 0 15V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5.414l-3.707 3.707zM2 12.586l2.293-2.293A1 1 0 0 1 5 10h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v9.586z"/></symbol> - <symbol viewBox="0 0 16 16" id="comments" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.75 10L0 13V3a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2H3.75zM13 5h1a2 2 0 0 1 2 2v8l-2.667-2H8a2 2 0 0 1-2-2h4a3 3 0 0 0 3-3V5z"/></symbol> - <symbol viewBox="0 0 16 16" id="commit" xmlns="http://www.w3.org/2000/svg"><path d="M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3.876-1.008a4.002 4.002 0 0 1-7.752 0A1.01 1.01 0 0 1 4 9H1a1 1 0 1 1 0-2h3c.042 0 .083.003.124.008a4.002 4.002 0 0 1 7.752 0A1.01 1.01 0 0 1 12 7h3a1 1 0 0 1 0 2h-3a1.01 1.01 0 0 1-.124-.008z"/></symbol> - <symbol viewBox="0 0 16 16" id="credit-card" xmlns="http://www.w3.org/2000/svg"><path d="M14 5a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1h12zm0 3H2v3a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V8zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm6.5 8h3a.5.5 0 1 1 0 1h-3a.5.5 0 1 1 0-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="disk" xmlns="http://www.w3.org/2000/svg"><path d="M16 11.764V3a3 3 0 0 0-3-3H3a3 3 0 0 0-3 3v8.764A2.989 2.989 0 0 1 2 11V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v8c.768 0 1.47.289 2 .764zM2 12h12a2 2 0 1 1 0 4H2a2 2 0 1 1 0-4zm10 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="doc_code" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zm1.036 7.607a.498.498 0 0 1-.147.354l-1.414 1.414a.5.5 0 0 1-.707-.707l1.06-1.06-1.06-1.061a.5.5 0 0 1 .707-.707l1.414 1.414a.498.498 0 0 1 .147.353zm-4.822 0l1.06 1.061a.5.5 0 0 1-.706.707l-1.414-1.414a.498.498 0 0 1 0-.707l1.414-1.414a.5.5 0 1 1 .707.707l-1.06 1.06zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"/></symbol> - <symbol viewBox="0 0 16 16" id="doc_image" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM7.333 9.667l1.313-1.313a.5.5 0 0 1 .708 0L12 11H4l2.188-1.75a.5.5 0 0 1 .624 0l.521.417zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 8a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM4 11h8v.7a.3.3 0 0 1-.3.3H4.3a.3.3 0 0 1-.3-.3V11z"/></symbol> - <symbol viewBox="0 0 16 16" id="doc_text" xmlns="http://www.w3.org/2000/svg"><path d="M8 2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7h-3a2 2 0 0 1-2-2V2zm2 .414V5h2.586L10 2.414zM5 0h4.586A2 2 0 0 1 11 .586L14.414 4A2 2 0 0 1 15 5.414V12a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4zm.5 11h5a.5.5 0 1 1 0 1h-5a.5.5 0 1 1 0-1zm0-2h5a.5.5 0 1 1 0 1h-5a.5.5 0 0 1 0-1zm0-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="download" xmlns="http://www.w3.org/2000/svg"><path d="M9 12h1a.5.5 0 0 1 .4.8l-2 2.667a.5.5 0 0 1-.8 0l-2-2.667A.5.5 0 0 1 6 12h1V8a1 1 0 1 1 2 0v4zM4 9a1 1 0 1 1 0 2 4 4 0 0 1-1.971-7.481 4 4 0 0 1 6.633-2.505 3.999 3.999 0 0 1 3.82 2.014A4 4 0 0 1 12 11a1 1 0 0 1 0-2 2 2 0 1 0 0-4h-1a2 2 0 0 0-3.112-1.662A2 2 0 1 0 4.268 5H4a2 2 0 1 0 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-3a1 1 0 0 1-1-1V6H8.527A.527.527 0 0 0 8 6.527V13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-3zm-4-7H8.527c-.18 0-.355.013-.527.04V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2v2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h4a3 3 0 0 1 3 3zM8.527 4h2.323a.5.5 0 0 1 .35.143l4.65 4.551a.5.5 0 0 1 .15.357V13a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V6.527A2.527 2.527 0 0 1 8.527 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="earth" xmlns="http://www.w3.org/2000/svg"><path d="M8.7 2.04l-.082.177c.283.223.422.413.417.571-.008.237-.311.057-.444.274-.133.218.038.542-.112.637-.15.096-.398-.386-.479-.46-.054-.049-.166-.257-.336-.625l-.216-.225a.844.844 0 0 0-.418-.035c-.177.038-.075.1-.035.132.04.032.32.037.452.2.132.164.03.224-.05.298-.054.05-.157.062-.31.035H5.952l-.402.398.03.325.229.455.324-.463c.008-.206.058-.342.15-.41.14-.1.342-.15.534-.085.191.066-.057.218.011.271.068.053.204-.098.313-.02.11.08.07.155.104.322.036.167.254.114.398.328.144.215.19.29.147.483-.043.195-.168.26-.305.232-.138-.028-.107-.246-.275-.348-.168-.102-.266-.114-.386-.054-.12.06-.016.129.023.235.04.106.274.321.224.43-.05.107-.108.116-.42 0-.21-.077-.414-.007-.615.212l-.76.722c-.153.715-.3 1.13-.44 1.243-.211.17-.177-.483-.483-.656-.306-.174-.494-.047-.8-.07-.307-.023-.42.65-.38.873a.434.434 0 0 0 .221.321c.236-.141.39-.184.465-.128.11.084-.144.267-.074.425.07.158.314.069.386.283.073.213.084.48-.05.706-.135.227-.275.178-.4.053-.127-.126-.033-.375-.255-.704-.223-.329-.381-.337-.63-.787-.158-.287-.35-.743-.575-1.366a6 6 0 0 0 3.21 7.198l.001-.075c0-.577-.004-.944-.012-1.102-.011-.236-.95-.945-1.104-1.2-.154-.256-.34-.595-.355-.746-.016-.151.185-.232.344-.325.16-.093-.11-.367.028-.626.137-.258.395-.438.496-.356.101.081.058.228.267.333.209.104.077-.213.456-.178.38.035.143.201.252.216.11.016.113-.127.299-.143.186-.015.282.445.471.622.19.178.452.008.611.043.159.034.267.09.402.255.136.166-.03.352.073.557.103.205 1.07.22 1.433.255.364.034.371.011.371.324s-.166.314-.453.507c-.286.193-.166.462-.38.762-.212.3-.316.062-.622.14-.306.077-.413.382-.452.568-.039.186-.386.094-.877.232-.29.082-.429.144-.569.204a6.002 6.002 0 0 0 7.682-4.3c-.094-.384-.18-.63-.258-.74-.213-.297-.36.21-.924.49-.564.278-.57-.288-.81-.49-.16-.133-.212-.44-.158-.92-.005-.478.02-.828.077-1.049.057-.221.126-.543.207-.965.351-.373.606-.572.764-.595.237-.034.336.374.658.3a.315.315 0 0 0 .035-.01 5.993 5.993 0 0 0-.475-.824l-.309-.043a.646.646 0 0 0-.332-.117c-.205-.02-.025.128-.089.24-.064.112-.235.724-.437.685-.201-.039-.204-.374-.17-.668.036-.294-.077-.35-.2-.412-.124-.062-.325-.213-.556-.295-.232-.082-.123-.175-.093-.274.03-.1.208-.015.193-.058-.014-.044-.313-.135-.266-.167.03-.02.2-.02.506.003l.216-.012.293-.163a.58.58 0 0 0-.376-.22c-.233-.036-.513-.034-.73-.142-.205-.103-.458-.36-.643-.638A5.965 5.965 0 0 0 8.7 2.04zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/></symbol> - <symbol viewBox="0 0 16 16" id="eye" xmlns="http://www.w3.org/2000/svg"><path d="M8 14C4.816 14 2.253 12.284.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2s5.747 1.716 7.607 5.019a2 2 0 0 1 0 1.962C13.747 12.284 11.184 14 8 14zm0-2c2.41 0 4.338-1.29 5.864-4C12.338 5.29 10.411 4 8 4 5.59 4 3.662 5.29 2.136 8 3.662 10.71 5.589 12 8 12zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm1-3a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="eye-slash" xmlns="http://www.w3.org/2000/svg"><path d="M13.618 2.62L1.62 14.619a1 1 0 0 1-.985-1.668l1.525-1.526C1.516 10.742.926 9.927.393 8.981a2 2 0 0 1 0-1.962C2.253 3.716 4.816 2 8 2c1.074 0 2.076.195 3.006.58l.944-.944a1 1 0 0 1 1.668.985zM8.068 11a3 3 0 0 0 2.931-2.932l-2.931 2.931zm-3.02-2.462a3 3 0 0 1 3.49-3.49l.884-.884A6.044 6.044 0 0 0 8 4C5.59 4 3.662 5.29 2.136 8c.445.79.924 1.46 1.439 2.011l1.473-1.473zm.421 5.06l1.658-1.658c.283.04.575.06.873.06 2.41 0 4.338-1.29 5.864-4a11.023 11.023 0 0 0-1.133-1.664l1.418-1.418a12.799 12.799 0 0 1 1.458 2.1 2 2 0 0 1 0 1.963C13.747 12.284 11.184 14 8 14a7.883 7.883 0 0 1-2.53-.402z"/></symbol> - <symbol viewBox="0 0 16 16" id="file-additions" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3z"/></symbol> - <symbol viewBox="0 0 16 16" id="file-deletion" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm2 6h6a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="file-modified" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm5 4a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/></symbol> - <symbol viewBox="0 0 16 16" id="filter" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 6v9l-3.724-1.862A.5.5 0 0 1 6 12.691V6L1.854 1.854A.5.5 0 0 1 2.207 1h11.586a.5.5 0 0 1 .353.854L10 6z"/></symbol> - <symbol viewBox="0 0 16 16" id="folder" xmlns="http://www.w3.org/2000/svg"><path d="M7.228 5l-.475-1.335A1 1 0 0 0 5.81 3H2v9a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H7.228zM13 3a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a2 2 0 0 1 2-2h3.81a3 3 0 0 1 2.827 1.995L13 3z"/></symbol> - <symbol viewBox="0 0 16 16" id="fork" xmlns="http://www.w3.org/2000/svg"><path d="M9 12.268a2 2 0 1 1-2 0V8.874A4.002 4.002 0 0 1 4 5V3.732a2 2 0 1 1 2 0V5a2 2 0 1 0 4 0V3.732a2 2 0 1 1 2 0V5a4.002 4.002 0 0 1-3 3.874v3.394zM11 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM5 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm3 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="git-merge" xmlns="http://www.w3.org/2000/svg"><path d="M11 12.268V5a1 1 0 0 0-1-1v1a.5.5 0 0 1-.8.4l-2.667-2a.5.5 0 0 1 0-.8L9.2.6a.5.5 0 0 1 .8.4v1a3 3 0 0 1 3 3v7.268a2 2 0 1 1-2 0zm-6 0a2 2 0 1 1-2 0V4.732a2 2 0 1 1 2 0v7.536zM4 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm8 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="group" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3.048 11.997C-.377 11.975.013 11.782.013 10.56.013 9.235.653 8 4 8c.444 0 .84.022 1.194.062.164.435.426.82.76 1.132-1.786.389-2.721 1.353-2.906 2.803zm2.94-7.222a2.993 2.993 0 0 0-.976 1.95 2 2 0 1 1 .975-1.95zm6.964 7.222c-.185-1.45-1.12-2.414-2.906-2.803.334-.311.596-.697.76-1.132C11.16 8.022 11.556 8 12 8c3.346 0 3.987 1.235 3.987 2.56 0 1.222.39 1.415-3.035 1.437zm-1.964-5.272a2.993 2.993 0 0 0-.976-1.95 2 2 0 1 1 .976 1.95zM8 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 5c-2.177 0-3.987-.115-3.987-1.44S4.653 10 8 10c3.346 0 3.987 1.235 3.987 2.56S10.177 14 8 14z"/></symbol> - <symbol viewBox="0 0 16 16" id="history" xmlns="http://www.w3.org/2000/svg"><path d="M2.868 3.24a7 7 0 1 1-.043 9.475 1 1 0 0 1 1.478-1.348 5 5 0 1 0 .124-6.865l.796.645a.5.5 0 0 1-.193.873l-3.232.814a.5.5 0 0 1-.622-.504L1.3 3a.5.5 0 0 1 .814-.37l.754.61zM9 8h1a1 1 0 0 1 0 2H8a.997.997 0 0 1-1-1V6a1 1 0 1 1 2 0v2z"/></symbol> - <symbol viewBox="0 0 16 16" id="home" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177a.505.505 0 0 1-.038.044l.038-.044zm-.787 0l.038.043a.5.5 0 0 1-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol> - <symbol viewBox="0 0 16 16" id="hook" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1h4zm0 1H6v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V4zM7 8a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3v4a2 2 0 1 0 4 0h-.44a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H15a4 4 0 0 1-7 2.646A4 4 0 0 1 1 12H.56a.3.3 0 0 1-.25-.466l1.44-2.16a.3.3 0 0 1 .5 0l1.44 2.16a.3.3 0 0 1-.25.466H3a2 2 0 1 0 4 0V8z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-block" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.803 8a5.97 5.97 0 0 0-.462 1H4.5a.5.5 0 0 1 0-1h1.303zM4.5 5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm7.5.083a6.04 6.04 0 0 0-2 0V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.083a5.96 5.96 0 0 0 .72 2H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v2.083zm1.121 3.796zM11 16a5 5 0 1 1 0-10 5 5 0 0 1 0 10zm-1.293-2.292a3 3 0 0 0 4.001-4.001l-4.001 4zm-1.415-1.415l4.001-4a3 3 0 0 0-4.001 4.001z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-child" xmlns="http://www.w3.org/2000/svg"><path d="M11 8H5v1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V7a.997.997 0 0 1 1-1h3V4H4.5a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9v2h3a.997.997 0 0 1 1 1v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8zm-9 3v2h3v-2H2zm9 0v2h3v-2h-3z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-close" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-duplicate" xmlns="http://www.w3.org/2000/svg"><path d="M10.874 2H12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-2c-.918 0-1.74-.413-2.29-1.063a3.987 3.987 0 0 0 1.988-.984A1 1 0 0 0 10 14h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-1V3c0-.345-.044-.68-.126-1zM4 0h3a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-new" xmlns="http://www.w3.org/2000/svg"><path d="M10 2V1a1 1 0 0 1 2 0v1h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V4H9a1 1 0 1 1 0-2h1zm0 6a1 1 0 0 1 2 0v5a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h1a1 1 0 1 1 0 2H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V8z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-open" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-2a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-open-m" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol> - <symbol viewBox="0 0 16 16" id="issue-parent" xmlns="http://www.w3.org/2000/svg"><path d="M11 11H5v1h1.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H3v-2a.997.997 0 0 1 1-1h3V7H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9v2h3a.997.997 0 0 1 1 1v2h2.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5H11v-1zM6 3v2h4V3H6z"/></symbol> - <symbol viewBox="0 0 16 16" id="issues" xmlns="http://www.w3.org/2000/svg"><path d="M10.458 15.012l.311.055a3 3 0 0 0 3.476-2.433l1.389-7.879A3 3 0 0 0 13.2 1.28L11.23.933a3.002 3.002 0 0 0-.824-.031c.364.59.58 1.28.593 2.02l1.854.328a1 1 0 0 1 .811 1.158l-1.389 7.879a1 1 0 0 1-1.158.81l-.118-.02a3.98 3.98 0 0 1-.541 1.935zM3 0h4a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol> - <symbol viewBox="0 0 16 16" id="key" xmlns="http://www.w3.org/2000/svg"><path d="M7.575 6.689a4.002 4.002 0 0 1 6.274-4.86 4 4 0 0 1-4.86 6.274l-2.21 2.21.706.708a1 1 0 1 1-1.414 1.414l-.707-.707-.707.707.707.707a1 1 0 1 1-1.414 1.414l-.707-.707a1 1 0 0 1-1.414-1.414l5.746-5.746zm2.032-.618a2 2 0 1 0 2.828-2.828A2 2 0 0 0 9.607 6.07z"/></symbol> - <symbol viewBox="0 0 16 16" id="key-2" xmlns="http://www.w3.org/2000/svg"><path d="M5.172 14.157l-.344.344-2.485.133a.462.462 0 0 1-.497-.503l.14-2.24a.599.599 0 0 1 .177-.382l5.155-5.155a4 4 0 1 1 2.828 2.828l-1.439 1.44-1.06-.354-.708.707.354 1.06-.707.708-1.06-.354-.708.707.354 1.06zm6.01-8.839a1 1 0 1 0 1.414-1.414 1 1 0 0 0-1.414 1.414z"/></symbol> - <symbol viewBox="0 0 16 16" id="label" xmlns="http://www.w3.org/2000/svg"><path d="M11.782 14.718a3 3 0 0 1-4.242 0L1.652 8.829a2 2 0 0 1-.565-1.702l.54-3.703a2 2 0 0 1 1.69-1.69l3.703-.54a2 2 0 0 1 1.703.564l5.888 5.888a3 3 0 0 1 0 4.243l-2.829 2.829zm1.415-5.657L7.309 3.173l-3.703.54-.54 3.702 5.888 5.888a1 1 0 0 0 1.414 0l2.829-2.828a1 1 0 0 0 0-1.414zM5.732 5.525A1 1 0 1 1 7.146 6.94a1 1 0 0 1-1.414-1.414z"/></symbol> - <symbol viewBox="0 0 16 16" id="labels" xmlns="http://www.w3.org/2000/svg"><path d="M9.424 2.254l2.08-.905a1 1 0 0 1 1.206.326l3.013 4.12a1 1 0 0 1 .16.849l-1.947 7.264a3 3 0 0 1-3.675 2.122l-.5-.135a3.999 3.999 0 0 0 1.082-1.782 1 1 0 0 0 1.16-.722l1.823-6.802-2.258-3.087-.687.299a2 2 0 0 0-.628-.88l-.829-.667zM.377 3.7L4.4.498a1 1 0 0 1 1.25.003L9.627 3.7a1 1 0 0 1 .373.78V13a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V4.482A1 1 0 0 1 .377 3.7zM2 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1V4.958L5.02 2.561 2 4.964V13zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="leave" xmlns="http://www.w3.org/2000/svg"><path d="M11 7V5.883a.5.5 0 0 1 .757-.429l3.528 2.117a.5.5 0 0 1 0 .858l-3.528 2.117a.5.5 0 0 1-.757-.43V9H7a1 1 0 1 1 0-2h4zm-2 6.256a1 1 0 0 1 2 0A2.744 2.744 0 0 1 8.256 16H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h5.19A2.81 2.81 0 0 1 11 2.81a1 1 0 0 1-2 0A.81.81 0 0 0 8.19 2H3a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h5.256c.41 0 .744-.333.744-.744z"/></symbol> - <symbol viewBox="0 0 16 16" id="level-up" xmlns="http://www.w3.org/2000/svg"><path fill="#2E2E2E" fill-rule="evenodd" d="M7 6h3.489a.5.5 0 0 0 .373-.832L6.374.117a.5.5 0 0 0-.748 0l-4.488 5.05A.5.5 0 0 0 1.51 6H5v7a3 3 0 0 0 3 3h6a1 1 0 0 0 0-2H8a1 1 0 0 1-1-1V6z"/></symbol> - <symbol viewBox="0 0 16 16" id="license" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.56 8.9l2.66 4.606a.3.3 0 0 1-.243.45l-1.678.094a.1.1 0 0 0-.078.044l-.953 1.432a.3.3 0 0 1-.51-.016L9.097 10.9a5.994 5.994 0 0 0 3.464-2zm-5.23 2.063L4.707 15.51a.3.3 0 0 1-.51.016l-.953-1.432a.1.1 0 0 0-.078-.044l-1.678-.094a.3.3 0 0 1-.243-.45l2.48-4.297a5.983 5.983 0 0 0 3.607 1.754zM8 10A5 5 0 1 1 8 0a5 5 0 0 1 0 10zm0-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="link" xmlns="http://www.w3.org/2000/svg"><path d="M6.986 3.35l2.12-2.122a4 4 0 0 1 5.657 5.657l-2.828 2.829a4 4 0 0 1-5.657 0 1 1 0 0 1 1.414-1.415 2 2 0 0 0 2.829 0l2.828-2.828a2 2 0 1 0-2.828-2.828l-1.001 1a5.018 5.018 0 0 0-2.534-.294zm2.12 9.192l-2.12 2.121a4 4 0 1 1-5.658-5.656l2.829-2.829a4 4 0 0 1 5.657 0 1 1 0 1 1-1.415 1.414 2 2 0 0 0-2.828 0l-2.828 2.829a2 2 0 1 0 2.828 2.828l1.001-1.001a5.018 5.018 0 0 0 2.534.294z"/></symbol> - <symbol viewBox="0 0 16 16" id="list-bulleted" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-7h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm0 5h10a1 1 0 0 1 0 2H5a1 1 0 1 1 0-2zm-4 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4-2h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="list-numbered" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 2h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm0 5h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2zM1.156 5v-.828h.816V2.204h-.72v-.636c.432-.084.708-.192.996-.372h.756v2.976h.684V5H1.156zm-.18 5v-.588c.9-.828 1.596-1.464 1.596-1.98 0-.342-.192-.504-.468-.504-.252 0-.444.18-.624.36l-.552-.552c.396-.42.756-.612 1.32-.612.768 0 1.308.492 1.308 1.248 0 .612-.576 1.284-1.092 1.812.192-.024.468-.048.636-.048h.636V10H.976zm1.26 5.072c-.618 0-1.068-.204-1.356-.54l.468-.648c.234.216.51.36.78.36.336 0 .552-.12.552-.36 0-.288-.15-.456-.948-.456v-.72c.636 0 .828-.168.828-.432 0-.228-.138-.348-.396-.348-.252 0-.432.108-.672.312l-.516-.624c.372-.312.768-.492 1.236-.492.84 0 1.38.384 1.38 1.074 0 .366-.204.642-.612.822v.024c.432.132.732.432.732.912 0 .72-.684 1.116-1.476 1.116z"/></symbol> - <symbol viewBox="0 0 16 16" id="location" xmlns="http://www.w3.org/2000/svg"><path d="M8.755 15.144a1 1 0 0 1-1.51 0C3.748 11.114 2 8.065 2 6a6 6 0 1 1 12 0c0 2.065-1.748 5.113-5.245 9.144zM12 6a4 4 0 1 0-8 0c0 1.314 1.312 3.71 4 6.944C10.688 9.71 12 7.314 12 6zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="location-dot" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6.314 13.087C4.382 13.295 3 13.85 3 14.5c0 .828 2.239 1.5 5 1.5s5-.672 5-1.5c0-.65-1.382-1.205-3.314-1.413l-.202.225a2 2 0 0 1-2.968 0l-.202-.225zm2.428-.445a1 1 0 0 1-1.484 0C4.419 9.5 3 7.037 3 5.252 3 2.353 5.239 0 8 0s5 2.352 5 5.253c0 1.784-1.42 4.247-4.258 7.389zM11 5.252C11 3.436 9.634 2 8 2S5 3.435 5 5.253c0 1.027.974 2.824 3 5.203 2.026-2.38 3-4.176 3-5.203zM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="lock" xmlns="http://www.w3.org/2000/svg"><path d="M10 5V4h2v1a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3V4h2v1h4zM4 7a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H4zm0-3a4 4 0 1 1 8 0h-2a2 2 0 1 0-4 0H4z"/></symbol> - <symbol viewBox="0 0 16 16" id="lock-open" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 4a4 4 0 0 1 6.99-2.658 1 1 0 1 1-1.495 1.33A2 2 0 0 0 6.044 4a.998.998 0 0 1-.07.367v.701H12a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3v-5a3 3 0 0 1 2.974-3V4h.07zM4 7.07a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H4z"/></symbol> - <symbol viewBox="0 0 16 16" id="log" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H4zm1 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 3a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-5h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm0 3h3a1 1 0 0 1 0 2H8a1 1 0 1 1 0-2zm-3 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm3-2h3a1 1 0 0 1 0 2H8a1 1 0 0 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="mail" xmlns="http://www.w3.org/2000/svg"><path d="M14 5.6L9.338 9.796a2 2 0 0 1-2.676 0L2 5.6V11a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5.6zM3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm.212 2L8 8.31 12.788 4H3.212z"/></symbol> - <symbol viewBox="0 0 16 16" id="merge-request-close" xmlns="http://www.w3.org/2000/svg"><path d="M9.414 8l1.414 1.414a1 1 0 1 1-1.414 1.414L8 9.414l-1.414 1.414a1 1 0 1 1-1.414-1.414L6.586 8 5.172 6.586a1 1 0 1 1 1.414-1.414L8 6.586l1.414-1.414a1 1 0 1 1 1.414 1.414L9.414 8zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/></symbol> - <symbol viewBox="0 0 16 16" id="merge-request-close-m" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9.414 8l4.95-4.95a1 1 0 0 0-1.414-1.414L8 6.586l-4.95-4.95A1 1 0 0 0 1.636 3.05L6.586 8l-4.95 4.95a1 1 0 1 0 1.414 1.414L8 9.414l4.95 4.95a1 1 0 1 0 1.414-1.414L9.414 8z"/></symbol> - <symbol viewBox="0 0 16 16" id="messages" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.588 8.942l1.173 5.862A1 1 0 0 1 8.78 16H7.22a1 1 0 0 1-.98-1.196l1.172-5.862a3.014 3.014 0 0 0 1.176 0zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM4.464 2.464L5.88 3.88a3 3 0 0 0 0 4.242L4.464 9.536a5 5 0 0 1 0-7.072zm7.072 7.072L10.12 8.12a3 3 0 0 0 0-4.242l1.415-1.415a5 5 0 0 1 0 7.072zM2.343.343l1.414 1.414a6 6 0 0 0 0 8.486l-1.414 1.414a8 8 0 0 1 0-11.314zm11.314 11.314l-1.414-1.414a6 6 0 0 0 0-8.486L13.657.343a8 8 0 0 1 0 11.314z"/></symbol> - <symbol viewBox="0 0 16 16" id="mobile-issue-close" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5.657 10.728L2.12 7.192A1 1 0 1 0 .707 8.607l4.243 4.242a.997.997 0 0 0 1.414 0l8.485-8.485a1 1 0 1 0-1.414-1.414l-7.778 7.778z"/></symbol> - <symbol viewBox="0 0 16 16" id="monitor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 13v1h3a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2h3v-1H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3h-3zM3 2a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm5.723 6.416l-2.66-1.773-1.71 1.71a.5.5 0 1 1-.707-.707l2-2a.5.5 0 0 1 .631-.062l2.66 1.773 2.71-2.71a.5.5 0 0 1 .707.707l-3 3a.5.5 0 0 1-.631.062z"/></symbol> - <symbol viewBox="0 0 16 16" id="more" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="notifications" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 14H2.435a2 2 0 0 1-1.761-2.947c.962-1.788 1.521-3.065 1.68-3.832.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024c3.755.528 4.375 4.27 4.761 6.043.188.86.742 2.188 1.661 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0zm5.805-6.468c-.325-1.492-.37-1.674-.61-2.288C10.6 3.716 9.742 3 8.07 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.208 1.012-.827 2.424-1.877 4.375H13.64c-.993-1.937-1.6-3.396-1.835-4.468z"/></symbol> - <symbol viewBox="0 0 16 16" id="notifications-off" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.26 5.089c.243.757.382 1.478.5 2.017.187.86.74 2.188 1.66 3.982A2 2 0 0 1 13.64 14H10a2 2 0 1 1-4 0H4.35l2-2h7.29c-.993-1.937-1.6-3.396-1.835-4.468-.07-.326-.129-.59-.178-.81l1.634-1.633zM10.943 1.75l-1.48 1.48C9.07 3.076 8.612 3 8.069 3c-1.608 0-2.49.718-3.103 2.197-.28.676-.356.982-.654 2.428-.065.317-.17.673-.317 1.073L.45 12.242a1.99 1.99 0 0 1 .224-1.19c.962-1.787 1.521-3.064 1.68-3.831.322-1.566.947-5.501 4.65-6.134a1 1 0 1 1 1.994-.024 4.867 4.867 0 0 1 1.944.688zm2.932-.105a1 1 0 0 1 0 1.415L2.561 14.374a1 1 0 1 1-1.415-1.414L12.46 1.646a1 1 0 0 1 1.414 0z"/></symbol> - <symbol viewBox="0 0 16 16" id="overview" xmlns="http://www.w3.org/2000/svg"><path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2zm0 2v3h3V2h-3zM2 9h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3H2zm9-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2zm0 2v3h3v-3h-3z"/></symbol> - <symbol viewBox="0 0 16 16" id="pencil" xmlns="http://www.w3.org/2000/svg"><path d="M13.02 1.293l1.414 1.414a1 1 0 0 1 0 1.414L4.119 14.436a1 1 0 0 1-.704.293l-2.407.008L1 12.316a1 1 0 0 1 .293-.71L11.605 1.292a1 1 0 0 1 1.414 0zm-1.416 1.415l-.707.707L12.31 4.83l.707-.707-1.414-1.415zM3.411 13.73l1.123-1.122H3.12v-1.415L2 12.312l.005 1.422 1.406-.005z"/></symbol> - <symbol viewBox="0 0 16 16" id="pipeline" xmlns="http://www.w3.org/2000/svg"><path d="M8.969 7.25a2 2 0 1 1-1.938 0A1.002 1.002 0 0 1 7 7V5.083a.2.2 0 0 1 .06-.142l.877-.87a.1.1 0 0 1 .141 0l.864.87A.2.2 0 0 1 9 5.083V7c0 .086-.01.17-.031.25zM8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm4.5-4a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-5 9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-9a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm-2 6a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0-3a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zM8 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="play" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.765 15.835c-.545.321-1.258.159-1.593-.363A1.075 1.075 0 0 1 1 14.89V1.11C1 .496 1.518 0 2.158 0c.214 0 .424.057.607.165l11.684 6.89c.544.321.714 1.005.38 1.526a1.135 1.135 0 0 1-.38.364l-11.684 6.89z"/></symbol> - <symbol viewBox="0 0 16 16" id="plus" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V1a1 1 0 1 1 2 0v6h6a1 1 0 0 1 0 2H9v6a1 1 0 0 1-2 0V9H1a1 1 0 1 1 0-2h6z"/></symbol> - <symbol viewBox="0 0 16 16" id="plus-square" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M9 7V4a1 1 0 1 0-2 0v3H4a1 1 0 1 0 0 2h3v3a1 1 0 0 0 2 0V9h3a1 1 0 0 0 0-2H9zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3z"/></symbol> - <symbol viewBox="0 0 16 16" id="plus-square-o" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7 7V5a1 1 0 1 1 2 0v2h2a1 1 0 0 1 0 2H9v2a1 1 0 0 1-2 0V9H5a1 1 0 1 1 0-2h2zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol> - <symbol viewBox="0 0 16 16" id="preferences" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 12h10a1 1 0 0 1 0 2H5a1 1 0 0 1-2 0v-2a1 1 0 0 1 2 0zm-3 0H1a1 1 0 0 0 0 2h1v-2zm11-5h2a1 1 0 0 1 0 2h-2a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0zm-3 0H1a1 1 0 1 0 0 2h9V7zM6 2h9a1 1 0 0 1 0 2H6a1 1 0 1 1-2 0V2a1 1 0 1 1 2 0zM3 2H1a1 1 0 1 0 0 2h2V2z"/></symbol> - <symbol viewBox="0 0 16 16" id="profile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-4.274-3.404C4.412 9.709 5.694 9 8 9c2.313 0 3.595.7 4.28 1.586A4.997 4.997 0 0 1 8 13a4.997 4.997 0 0 1-4.274-2.404zM8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></symbol> - <symbol viewBox="0 0 16 16" id="project" xmlns="http://www.w3.org/2000/svg"><path d="M8.462 2.177l-.038.044a.505.505 0 0 0 .038-.044zm-.787 0a.5.5 0 0 0 .038.043l-.038-.043zM3.706 7h8.725L8.069 2.585 3.706 7zM7 13.369V12a1 1 0 0 1 2 0v1.369h3V9H4v4.369h3zM14 9v4.836c0 .833-.657 1.533-1.5 1.533h-9c-.843 0-1.5-.7-1.5-1.533V9h-.448a1.1 1.1 0 0 1-.783-1.873L6.934.887a1.5 1.5 0 0 1 2.269 0l6.165 6.24A1.1 1.1 0 0 1 14.585 9H14z"/></symbol> - <symbol viewBox="0 0 16 16" id="push-rules" xmlns="http://www.w3.org/2000/svg"><path d="M6.268 9a2 2 0 0 1 3.464 0H11a1 1 0 0 1 0 2H9.732a2 2 0 0 1-3.464 0H5a1 1 0 0 1 0-2h1.268zM7 2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1v3.515a.3.3 0 0 1-.434.268l-1.432-.716a.3.3 0 0 0-.268 0l-1.432.716A.3.3 0 0 1 7 5.515V2zM4 0h8a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm4 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="question" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm-1.46-5.602h2.233a3.97 3.97 0 0 1 .051-.558c.029-.17.073-.326.133-.469.06-.143.14-.28.242-.41.102-.13.228-.263.38-.399.26-.24.504-.467.733-.683a5.03 5.03 0 0 0 .598-.668c.17-.23.302-.477.399-.742a2.66 2.66 0 0 0 .144-.907c0-.505-.083-.95-.25-1.335a2.55 2.55 0 0 0-.723-.97 3.2 3.2 0 0 0-1.152-.589 5.441 5.441 0 0 0-1.531-.2c-.516 0-.998.063-1.445.188a3.19 3.19 0 0 0-1.168.59c-.331.268-.594.61-.79 1.027-.195.417-.295.917-.3 1.5h2.64c.006-.224.04-.416.102-.578.062-.161.142-.293.238-.394a.921.921 0 0 1 .332-.227 1.04 1.04 0 0 1 .39-.074c.34 0 .593.095.763.285.169.19.254.488.254.895 0 .328-.106.63-.317.906-.21.276-.499.565-.863.867-.214.182-.39.374-.531.574-.141.2-.253.42-.336.657a3.656 3.656 0 0 0-.176.777 7.89 7.89 0 0 0-.05.937zm-.321 2.375c0 .188.035.362.105.524.07.161.17.3.301.418.13.117.284.21.46.277.178.068.376.102.595.102.218 0 .416-.034.593-.102.178-.068.331-.16.461-.277a1.2 1.2 0 0 0 .301-.418c.07-.162.106-.336.106-.524a1.3 1.3 0 0 0-.106-.523 1.2 1.2 0 0 0-.3-.418 1.461 1.461 0 0 0-.462-.277 1.651 1.651 0 0 0-.593-.102c-.22 0-.417.034-.594.102a1.46 1.46 0 0 0-.461.277 1.2 1.2 0 0 0-.3.418 1.284 1.284 0 0 0-.106.523z"/></symbol> - <symbol viewBox="0 0 16 16" id="question-o" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-.778-4.151c0-.301.014-.575.044-.82a3.2 3.2 0 0 1 .154-.68c.073-.208.17-.4.294-.575.123-.176.278-.343.465-.503a4.81 4.81 0 0 0 .755-.758c.185-.242.277-.506.277-.793 0-.356-.074-.617-.222-.783-.148-.166-.37-.25-.667-.25a.92.92 0 0 0-.342.065.806.806 0 0 0-.29.199 1.04 1.04 0 0 0-.209.345 1.5 1.5 0 0 0-.088.506H5.082c.005-.51.092-.948.263-1.313.171-.364.401-.664.69-.899.29-.234.63-.406 1.023-.516a4.66 4.66 0 0 1 1.264-.164c.497 0 .944.058 1.34.174.397.117.733.289 1.008.517.276.227.487.51.633.847.146.337.218.727.218 1.17 0 .295-.042.56-.126.792a2.52 2.52 0 0 1-.349.65 4.4 4.4 0 0 1-.523.584c-.2.19-.414.389-.642.598a2.73 2.73 0 0 0-.332.349c-.089.114-.16.233-.212.359a1.868 1.868 0 0 0-.116.41 3.39 3.39 0 0 0-.044.489H7.222zm-.28 2.078c0-.164.03-.317.092-.458a1.05 1.05 0 0 1 .263-.366c.114-.103.248-.183.403-.243a1.45 1.45 0 0 1 .52-.089c.191 0 .364.03.52.09.154.059.289.14.403.242.114.103.201.224.263.366.061.141.092.294.092.458 0 .164-.03.316-.092.458a1.05 1.05 0 0 1-.263.365 1.278 1.278 0 0 1-.404.243 1.43 1.43 0 0 1-.52.089c-.19 0-.364-.03-.519-.089-.155-.06-.29-.14-.403-.243a1.05 1.05 0 0 1-.263-.365 1.135 1.135 0 0 1-.093-.458z"/></symbol> - <symbol viewBox="0 0 16 16" id="quote" xmlns="http://www.w3.org/2000/svg"><path d="M15 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9h-2a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1zM7 3v8a3 3 0 0 1-3 3 1 1 0 0 1 0-2 1 1 0 0 0 1-1V9H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h3a1 1 0 0 1 1 1z"/></symbol> - <symbol viewBox="0 0 16 16" id="redo" xmlns="http://www.w3.org/2000/svg"><path d="M4.666 4.423a5 5 0 1 1-.203 6.944 1 1 0 1 0-1.478 1.347 7 7 0 1 0 .12-9.556L1.842 2.137a.5.5 0 0 0-.815.385L1 7.26a.5.5 0 0 0 .607.492l4.629-1.013a.5.5 0 0 0 .207-.877L4.666 4.423z"/></symbol> - <symbol viewBox="0 0 16 16" id="remove" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 3a1 1 0 1 1 0-2h12a1 1 0 0 1 0 2v10a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V3zm3-2a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1H5zM4 3v10a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3H4zm2.5 2a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm3 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol> - <symbol viewBox="0 0 16 16" id="repeat" xmlns="http://www.w3.org/2000/svg"><path d="M11.494 4.423a5 5 0 1 0 .203 6.944 1 1 0 1 1 1.478 1.347 7 7 0 1 1-.12-9.556l1.262-1.021a.5.5 0 0 1 .815.385l.028 4.738a.5.5 0 0 1-.607.492L9.924 6.739a.5.5 0 0 1-.207-.877l1.777-1.439z"/></symbol> - <symbol viewBox="0 0 16 16" id="retry" xmlns="http://www.w3.org/2000/svg"><path d="M4.009 6.958a4 4 0 0 0 5.283 4.775 1 1 0 0 1 .712 1.87A6 6 0 0 1 2.077 6.44l-.741-.2a.5.5 0 0 1-.12-.915L3.41 4.058a.5.5 0 0 1 .683.183l1.268 2.196a.5.5 0 0 1-.563.733l-.79-.212zm7.777 2.084a4 4 0 0 0-5.284-4.775 1 1 0 0 1-.711-1.87 6 6 0 0 1 7.927 7.162l.74.2a.5.5 0 0 1 .121.915l-2.196 1.268a.5.5 0 0 1-.683-.183l-1.267-2.196a.5.5 0 0 1 .562-.733l.79.212z"/></symbol> - <symbol viewBox="0 0 16 16" id="scale" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.99 9a.792.792 0 0 0-.078-.231L13 7l-.912 1.769a.791.791 0 0 0-.077.231h1.978zm-10 0a.792.792 0 0 0-.078-.231L3 7l-.912 1.769A.791.791 0 0 0 2.011 9h1.978zM2 0h12a1 1 0 0 1 0 2H2a1 1 0 1 1 0-2zm3 14h6a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2zM8 4a1 1 0 0 1 1 1v9H7V5a1 1 0 0 1 1-1zm-4.53-.714l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 3 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158L2.53 3.286a.53.53 0 0 1 .94 0zm10 0l2.265 4.735c.68 1.42.006 3.091-1.504 3.73A3.161 3.161 0 0 1 13 12c-1.657 0-3-1.263-3-2.821 0-.4.09-.794.264-1.158l2.266-4.735a.53.53 0 0 1 .94 0z"/></symbol> - <symbol viewBox="0 0 16 16" id="screen-full" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14 14v-2a1 1 0 0 1 2 0v3a.997.997 0 0 1-1 1h-3a1 1 0 0 1 0-2h2zM2 14v-2a1 1 0 0 0-2 0v3a1 1 0 0 0 1 1h3a1 1 0 0 0 0-2H2zM15.707.293A.997.997 0 0 1 16 1v3a1 1 0 0 1-2 0V2h-2a1 1 0 0 1 0-2h3c.276 0 .526.112.707.293zM2 2v2a1 1 0 1 1-2 0V1a.997.997 0 0 1 1-1h3a1 1 0 1 1 0 2H2zm4 4h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="screen-normal" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 3V1a1 1 0 1 1 2 0v3a.997.997 0 0 1-1 1H1a1 1 0 1 1 0-2h2zm10 0h2a1 1 0 0 1 0 2h-3a.997.997 0 0 1-1-1V1a1 1 0 0 1 2 0v2zM3 13H1a1 1 0 0 1 0-2h3a.997.997 0 0 1 1 1v3a1 1 0 0 1-2 0v-2zm10 0v2a1 1 0 0 1-2 0v-3a.997.997 0 0 1 1-1h3a1 1 0 0 1 0 2h-2zM6.5 7h3a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5z"/></symbol> - <symbol viewBox="0 0 16 16" id="search" xmlns="http://www.w3.org/2000/svg"><path d="M8.853 8.854a3.5 3.5 0 1 0-4.95-4.95 3.5 3.5 0 0 0 4.95 4.95zm.207 2.328a5.5 5.5 0 1 1 2.121-2.121l3.329 3.328a1.5 1.5 0 0 1-2.121 2.121L9.06 11.182z"/></symbol> - <symbol viewBox="0 0 16 16" id="settings" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2.415 5.803L1.317 4.084A.5.5 0 0 1 1.35 3.5l.805-.994a.5.5 0 0 1 .564-.153l1.878.704a5.975 5.975 0 0 1 1.65-.797L6.885.342A.5.5 0 0 1 7.36 0h1.28a.5.5 0 0 1 .474.342l.639 1.918a5.97 5.97 0 0 1 1.65.797l1.877-.704a.5.5 0 0 1 .565.153l.805.994a.5.5 0 0 1 .032.584l-1.097 1.719c.217.551.354 1.143.399 1.76l1.731 1.058a.5.5 0 0 1 .227.54l-.288 1.246a.5.5 0 0 1-.44.385l-2.008.19a6.026 6.026 0 0 1-1.142 1.431l.265 1.995a.5.5 0 0 1-.277.516l-1.15.56a.5.5 0 0 1-.576-.1l-1.424-1.452a6.047 6.047 0 0 1-1.804 0l-1.425 1.453a.5.5 0 0 1-.576.1l-1.15-.561a.5.5 0 0 1-.276-.516l.265-1.995a6.026 6.026 0 0 1-1.143-1.43l-2.008-.191a.5.5 0 0 1-.44-.385L.058 9.16a.5.5 0 0 1 .226-.539l1.732-1.058a5.968 5.968 0 0 1 .399-1.76zM8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/></symbol> - <symbol viewBox="0 0 16 16" id="shield" xmlns="http://www.w3.org/2000/svg"><path d="M4 0h8a3 3 0 0 1 3 3v7.186a3 3 0 0 1-1.426 2.554l-4 2.465a3 3 0 0 1-3.148 0l-4-2.465A3 3 0 0 1 1 10.186V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v7.186a1 1 0 0 0 .475.852l4 2.464a1 1 0 0 0 1.05 0l4-2.464a1 1 0 0 0 .475-.852V3a1 1 0 0 0-1-1H4zm0 1.5a.5.5 0 0 1 .5-.5h4v8.837a.5.5 0 0 1-.753.431l-3.5-2.052A.5.5 0 0 1 4 9.785V3.5z"/></symbol> - <symbol viewBox="0 0 16 16" id="slight-frown" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm-2.163-3.275a2.499 2.499 0 0 1 4.343.03.5.5 0 0 1-.871.49 1.5 1.5 0 0 0-2.607-.018.5.5 0 1 1-.865-.502zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="slight-smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-5.163 2.254a.5.5 0 1 1 .865-.502 1.499 1.499 0 0 0 2.607-.018.5.5 0 1 1 .871.49 2.499 2.499 0 0 1-4.343.03z"/></symbol> - <symbol viewBox="0 0 16 16" id="smile" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM6.18 6.27a.5.5 0 0 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zm6 0a.5.5 0 1 1-.873.487.5.5 0 0 0-.872-.003.5.5 0 1 1-.87-.495 1.5 1.5 0 0 1 2.616.012zM5 9a3 3 0 0 0 6 0H5z"/></symbol> - <symbol viewBox="0 0 16 16" id="smiley" xmlns="http://www.w3.org/2000/svg"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm0-2A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM5 9h6a3 3 0 0 1-6 0z"/></symbol> - <symbol viewBox="0 0 16 16" id="snippet" xmlns="http://www.w3.org/2000/svg"><path d="M10.67 9.31a3.001 3.001 0 0 1 2.062 5.546 3 3 0 0 1-3.771-4.559 1.007 1.007 0 0 1-.095-.137l-4.5-7.794a1 1 0 0 1 1.732-1l4.5 7.794c.028.05.052.1.071.15zm-3.283.35l-.289.5c-.028.05-.06.095-.095.137a3.001 3.001 0 0 1-3.77 4.56A3 3 0 0 1 5.294 9.31c.02-.051.043-.102.071-.15l.866-1.5 1.155 2zm2.31-4l-1.156-2 1.325-2.294a1 1 0 0 1 1.732 1L9.696 5.66zm-5.465 7.464a1 1 0 1 0 1-1.732 1 1 0 0 0-1 1.732zm7.5 0a1 1 0 1 0-1-1.732 1 1 0 0 0 1 1.732z"/></symbol> - <symbol viewBox="0 0 16 16" id="spam" xmlns="http://www.w3.org/2000/svg"><path d="M8.75.433l5.428 3.134a1.5 1.5 0 0 1 .75 1.299v6.268a1.5 1.5 0 0 1-.75 1.299L8.75 15.567a1.5 1.5 0 0 1-1.5 0l-5.428-3.134a1.5 1.5 0 0 1-.75-1.299V4.866a1.5 1.5 0 0 1 .75-1.299L7.25.433a1.5 1.5 0 0 1 1.5 0zM3.072 5.155v5.69L8 13.691l4.928-2.846v-5.69L8 2.309 3.072 5.155zM8 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="star" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.609 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol> - <symbol viewBox="0 0 16 16" id="star-o" xmlns="http://www.w3.org/2000/svg"><path d="M10.975 10.99a3 3 0 0 1 .655-2.083l1.54-1.916-2.219-.576a3 3 0 0 1-1.825-1.37L8 3.15 6.874 5.044a3 3 0 0 1-1.825 1.371l-2.218.576 1.54 1.916a3 3 0 0 1 .654 2.083l-.165 2.4 1.965-.836a3 3 0 0 1 2.348 0l1.965.836-.164-2.399zM7.61 14.394l-3.465 1.473a1 1 0 0 1-1.39-.989l.276-4.024a1 1 0 0 0-.219-.694L.303 7.037A1 1 0 0 1 .83 5.443l3.715-.964a1 1 0 0 0 .609-.457L7.14.682a1 1 0 0 1 1.72 0l1.985 3.34a1 1 0 0 0 .609.457l3.715.964a1 1 0 0 1 .528 1.594L13.19 10.16a1 1 0 0 0-.219.694l.275 4.024a1 1 0 0 1-1.389.989l-3.465-1.473a1 1 0 0 0-.782 0z"/></symbol> - <symbol viewBox="0 0 16 16" id="stop" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M2 0h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="talic" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M6 0h7a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2zm2 2h3L8 14H5L8 2zM3 14h7a1 1 0 0 1 0 2H3a1 1 0 0 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="task-done" xmlns="http://www.w3.org/2000/svg"><path d="M7.536 8.657l2.828-2.829a1 1 0 0 1 1.414 1.415l-3.535 3.535a.997.997 0 0 1-1.415 0l-2.12-2.121A1 1 0 0 1 6.12 7.243l1.415 1.414zM3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3z"/></symbol> - <symbol viewBox="0 0 16 16" id="template" xmlns="http://www.w3.org/2000/svg"><path d="M3 0h10a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H3zm.8 2h2.4a.8.8 0 0 1 .8.8v1.4a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8V4.8a.8.8 0 0 1 .8-.8zm4.7 0h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm0 2h4a.5.5 0 1 1 0 1h-4a.5.5 0 0 1 0-1zm-5 3h9a.5.5 0 1 1 0 1h-9a.5.5 0 0 1 0-1zm0 2h9a.5.5 0 1 1 0 1h-9a.5.5 0 1 1 0-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="thump-down" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 11h5.282a2 2 0 0 0 1.963-2.38l-.563-2.905a3 3 0 0 0-.243-.732l-1.103-2.286A3 3 0 0 0 10.964 1H7a3 3 0 0 0-3 3v6.3a2 2 0 0 0 .436 1.247l3.11 3.9a.632.632 0 0 0 .941.053l.137-.137a1 1 0 0 0 .28-.87L8.329 11zM1 10h2V3H1a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1z"/></symbol> - <symbol viewBox="0 0 16 16" id="thump-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.103 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.487.5l.137.137a1 1 0 0 1 .28.87L8.329 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/></symbol> - <symbol viewBox="0 0 16 16" id="timer" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 3.27l.77-.77a1 1 0 0 1 1.415 1.414l-.728.729a7 7 0 1 1-1.456-1.372zM8 14A5 5 0 1 0 8 4a5 5 0 0 0 0 10zm0-9a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zM6 0h4a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="todo-add" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 4V2a1 1 0 0 1 2 0v2h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0V6H8a1 1 0 1 1 0-2h2zm2 7a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="todo-done" xmlns="http://www.w3.org/2000/svg"><path d="M8.243 7.485l4.95-4.95a1 1 0 1 1 1.414 1.415L8.95 9.607a.997.997 0 0 1-1.414 0L4.707 6.778a1 1 0 0 1 1.414-1.414l2.122 2.121zM12 11a1 1 0 0 1 2 0v2a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h2a1 1 0 1 1 0 2H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-2z"/></symbol> - <symbol viewBox="0 0 16 16" id="token" xmlns="http://www.w3.org/2000/svg"><path d="M3 2h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3zm0 2a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H3zm1 5a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="unapproval" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.95 8.536l1.06-1.061a1 1 0 0 1 1.415 1.414l-1.061 1.06 1.06 1.061a1 1 0 0 1-1.414 1.415l-1.06-1.061-1.06 1.06a1 1 0 1 1-1.415-1.414l1.06-1.06-1.06-1.06a1 1 0 0 1 1.414-1.415l1.06 1.06zm-3.768-.33c.006.503.201 1.006.586 1.39l.353.354-.353.353a2 2 0 1 0 2.828 2.829l.354-.354.047.048C11.964 14.363 11.527 15 6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8c.834 0 1.557.074 2.182.205zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></symbol> - <symbol viewBox="0 0 16 16" id="unassignee" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11 5h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol> - <symbol viewBox="0 0 16 16" id="unlink" xmlns="http://www.w3.org/2000/svg"><path d="M11.295 8.845l-.659-1.664a1.78 1.78 0 0 0 .04-.04l1.415-1.414c.586-.586.654-1.468.152-1.97s-1.384-.434-1.97.152L8.859 5.323a1.781 1.781 0 0 0-.04.04l-1.664-.658c.141-.208.305-.408.491-.594l1.415-1.414c1.366-1.367 3.424-1.525 4.596-.354 1.171 1.172 1.013 3.23-.354 4.596L11.89 8.354c-.186.186-.386.35-.594.491zm-2.45 2.45a4.075 4.075 0 0 1-.491.594l-1.415 1.414c-1.366 1.367-3.424 1.525-4.596.354-1.171-1.172-1.013-3.23.354-4.596L4.11 7.646c.186-.186.386-.35.594-.491l.659 1.664a1.781 1.781 0 0 0-.04.04l-1.415 1.414c-.586.586-.654 1.468-.152 1.97s1.384.434 1.97-.152l1.414-1.414a1.78 1.78 0 0 0 .04-.04l1.664.658zm3.812-2.088h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-.05a.5.5 0 0 1 .5-.5zm-.384 2.116l1.415 1.414a.5.5 0 0 1 0 .708l-.037.036a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 0-.707l.036-.037a.5.5 0 0 1 .707 0zm-2.823 1.09a.5.5 0 0 1 .5-.5h.052a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5H9.95a.5.5 0 0 1-.5-.5v-2zm-2.748-9.16a.5.5 0 0 1-.5.5h-.05a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h.05a.5.5 0 0 1 .5.5v2zm-2.116.383a.5.5 0 0 1 0 .707l-.036.036a.5.5 0 0 1-.707 0L2.428 2.965a.5.5 0 0 1 0-.707l.037-.036a.5.5 0 0 1 .707 0l1.414 1.414zm-1.09 2.823h-2a.5.5 0 0 1-.5-.5v-.051a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v.05a.5.5 0 0 1-.5.5z"/></symbol> - <symbol viewBox="0 0 16 16" id="user" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 8c-6.888 0-6.976-.78-6.976-2.52S2.144 8 8 8s6.976 2.692 6.976 4.48c0 1.788-.088 2.52-6.976 2.52z"/></symbol> - <symbol viewBox="0 0 16 16" id="users" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.521 8.01C15.103 8.19 16 10.755 16 12.48c0 1.533-.056 2.29-3.808 2.475.609-.54.808-1.331.808-2.475 0-1.911-.804-3.503-2.479-4.47zm-1.67-1.228A3.987 3.987 0 0 0 9.976 4a3.987 3.987 0 0 0-1.125-2.782 3 3 0 1 1 0 5.563zM5.976 7a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM6 15c-5.924 0-6-.78-6-2.52S.964 8 6 8s6 2.692 6 4.48c0 1.788-.076 2.52-6 2.52z"/></symbol> - <symbol viewBox="0 0 16 16" id="volume-up" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M1 5h1v6H1a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1zm2 0l4.445-2.964A1 1 0 0 1 9 2.87v10.26a1 1 0 0 1-1.555.833L3 11V5zm10.283 7.89a.5.5 0 0 1-.66-.752A5.485 5.485 0 0 0 14.5 8c0-1.601-.687-3.09-1.865-4.128a.5.5 0 0 1 .661-.75A6.484 6.484 0 0 1 15.5 8a6.485 6.485 0 0 1-2.217 4.89zm-2.002-2.236a.5.5 0 1 1-.652-.758c.55-.472.871-1.157.871-1.896 0-.732-.315-1.411-.856-1.883a.5.5 0 0 1 .658-.753A3.492 3.492 0 0 1 12.5 8c0 1.033-.45 1.994-1.219 2.654z"/></symbol> - <symbol viewBox="0 0 16 16" id="warning" xmlns="http://www.w3.org/2000/svg"><path d="M15.34 10.479A3 3 0 0 1 12.756 15h-9.51A3 3 0 0 1 .66 10.479l4.755-8.083a3 3 0 0 1 5.172 0l4.755 8.083zm-6.478-7.07a1 1 0 0 0-1.724 0l-4.755 8.084A1 1 0 0 0 3.245 13h9.51a1 1 0 0 0 .862-1.507L8.862 3.41zM8 5a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V6a1 1 0 0 1 1-1zm0 7a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></symbol> - <symbol viewBox="0 0 16 16" id="work" xmlns="http://www.w3.org/2000/svg"><path d="M12 3h1a3 3 0 0 1 3 3v7a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h1V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zM6 2v1h4V2H6zM3 5a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H3zm1.5 1a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5zm7 0a.5.5 0 0 1 .5.5v6a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .5-.5z"/></symbol> -</svg> - -<!-- -==================================================================================================== ---> - - <header> - <h1>SVG <code><symbol></code> sprite preview</h1> - <p>This preview features two methods of using the generated sprite in conjunction with inline SVG. Please have a look at the HTML source for further details and be aware of the following constraints:</p> - <ul> - <li>Your browser has to <a href="http://caniuse.com/#feat=svg-html5" target="_blank">support inline SVG</a> for these techniques to work.</li> - <li>The embedded sprite (A) slightly differs from the generated external one. Please <a href="https://github.com/jkphl/svg-sprite/blob/master/docs/configuration.md#defs--symbol-mode" target="_blank">see the documentation</a> for details on how to create such an embeddable sprite.</li> - <li>Internet Explorer up to version 11 doesn't support external sprites for use with inline SVG. For IE 9-11, you may polyfill this functionality with <a href="https://github.com/jonathantneal/svg4everybody" target="_blank">SVG for Everybody</a>.</li> - </ul> - </header> - <section> - -<!-- - -A) Inline SVG with embedded sprite -==================================================================================================== -These SVG images make use of fragment identifiers (IDs) and are extracted out of the inline sprite -embedded above. They may be styled via CSS. - ---> - - <h3>A) Inline SVG with embedded sprite</h3> - <ul> - - <li title="abuse"> - <div class="icon-box"> - - <!-- abuse --> - <svg class="svg-abuse-dims"> - <use xlink:href="#abuse"></use> - </svg> - - </div> - <h2>abuse</h2> - </li> - <li title="account"> - <div class="icon-box"> - - <!-- account --> - <svg class="svg-account-dims"> - <use xlink:href="#account"></use> - </svg> - - </div> - <h2>account</h2> - </li> - <li title="admin"> - <div class="icon-box"> - - <!-- admin --> - <svg class="svg-admin-dims"> - <use xlink:href="#admin"></use> - </svg> - - </div> - <h2>admin</h2> - </li> - <li title="angle-double-left"> - <div class="icon-box"> - - <!-- angle-double-left --> - <svg class="svg-angle-double-left-dims"> - <use xlink:href="#angle-double-left"></use> - </svg> - - </div> - <h2>angle-double-left</h2> - </li> - <li title="angle-down"> - <div class="icon-box"> - - <!-- angle-down --> - <svg class="svg-angle-down-dims"> - <use xlink:href="#angle-down"></use> - </svg> - - </div> - <h2>angle-down</h2> - </li> - <li title="angle-left"> - <div class="icon-box"> - - <!-- angle-left --> - <svg class="svg-angle-left-dims"> - <use xlink:href="#angle-left"></use> - </svg> - - </div> - <h2>angle-left</h2> - </li> - <li title="angle-right"> - <div class="icon-box"> - - <!-- angle-right --> - <svg class="svg-angle-right-dims"> - <use xlink:href="#angle-right"></use> - </svg> - - </div> - <h2>angle-right</h2> - </li> - <li title="angle-up"> - <div class="icon-box"> - - <!-- angle-up --> - <svg class="svg-angle-up-dims"> - <use xlink:href="#angle-up"></use> - </svg> - - </div> - <h2>angle-up</h2> - </li> - <li title="appearance"> - <div class="icon-box"> - - <!-- appearance --> - <svg class="svg-appearance-dims"> - <use xlink:href="#appearance"></use> - </svg> - - </div> - <h2>appearance</h2> - </li> - <li title="applications"> - <div class="icon-box"> - - <!-- applications --> - <svg class="svg-applications-dims"> - <use xlink:href="#applications"></use> - </svg> - - </div> - <h2>applications</h2> - </li> - <li title="approval"> - <div class="icon-box"> - - <!-- approval --> - <svg class="svg-approval-dims"> - <use xlink:href="#approval"></use> - </svg> - - </div> - <h2>approval</h2> - </li> - <li title="arrow-right"> - <div class="icon-box"> - - <!-- arrow-right --> - <svg class="svg-arrow-right-dims"> - <use xlink:href="#arrow-right"></use> - </svg> - - </div> - <h2>arrow-right</h2> - </li> - <li title="assignee"> - <div class="icon-box"> - - <!-- assignee --> - <svg class="svg-assignee-dims"> - <use xlink:href="#assignee"></use> - </svg> - - </div> - <h2>assignee</h2> - </li> - <li title="bold"> - <div class="icon-box"> - - <!-- bold --> - <svg class="svg-bold-dims"> - <use xlink:href="#bold"></use> - </svg> - - </div> - <h2>bold</h2> - </li> - <li title="book"> - <div class="icon-box"> - - <!-- book --> - <svg class="svg-book-dims"> - <use xlink:href="#book"></use> - </svg> - - </div> - <h2>book</h2> - </li> - <li title="branch"> - <div class="icon-box"> - - <!-- branch --> - <svg class="svg-branch-dims"> - <use xlink:href="#branch"></use> - </svg> - - </div> - <h2>branch</h2> - </li> - <li title="calendar"> - <div class="icon-box"> - - <!-- calendar --> - <svg class="svg-calendar-dims"> - <use xlink:href="#calendar"></use> - </svg> - - </div> - <h2>calendar</h2> - </li> - <li title="cancel"> - <div class="icon-box"> - - <!-- cancel --> - <svg class="svg-cancel-dims"> - <use xlink:href="#cancel"></use> - </svg> - - </div> - <h2>cancel</h2> - </li> - <li title="chevron-down"> - <div class="icon-box"> - - <!-- chevron-down --> - <svg class="svg-chevron-down-dims"> - <use xlink:href="#chevron-down"></use> - </svg> - - </div> - <h2>chevron-down</h2> - </li> - <li title="chevron-left"> - <div class="icon-box"> - - <!-- chevron-left --> - <svg class="svg-chevron-left-dims"> - <use xlink:href="#chevron-left"></use> - </svg> - - </div> - <h2>chevron-left</h2> - </li> - <li title="chevron-right"> - <div class="icon-box"> - - <!-- chevron-right --> - <svg class="svg-chevron-right-dims"> - <use xlink:href="#chevron-right"></use> - </svg> - - </div> - <h2>chevron-right</h2> - </li> - <li title="chevron-up"> - <div class="icon-box"> - - <!-- chevron-up --> - <svg class="svg-chevron-up-dims"> - <use xlink:href="#chevron-up"></use> - </svg> - - </div> - <h2>chevron-up</h2> - </li> - <li title="clock"> - <div class="icon-box"> - - <!-- clock --> - <svg class="svg-clock-dims"> - <use xlink:href="#clock"></use> - </svg> - - </div> - <h2>clock</h2> - </li> - <li title="code"> - <div class="icon-box"> - - <!-- code --> - <svg class="svg-code-dims"> - <use xlink:href="#code"></use> - </svg> - - </div> - <h2>code</h2> - </li> - <li title="comment"> - <div class="icon-box"> - - <!-- comment --> - <svg class="svg-comment-dims"> - <use xlink:href="#comment"></use> - </svg> - - </div> - <h2>comment</h2> - </li> - <li title="comment-dots"> - <div class="icon-box"> - - <!-- comment-dots --> - <svg class="svg-comment-dots-dims"> - <use xlink:href="#comment-dots"></use> - </svg> - - </div> - <h2>comment-dots</h2> - </li> - <li title="comment-next"> - <div class="icon-box"> - - <!-- comment-next --> - <svg class="svg-comment-next-dims"> - <use xlink:href="#comment-next"></use> - </svg> - - </div> - <h2>comment-next</h2> - </li> - <li title="comments"> - <div class="icon-box"> - - <!-- comments --> - <svg class="svg-comments-dims"> - <use xlink:href="#comments"></use> - </svg> - - </div> - <h2>comments</h2> - </li> - <li title="commit"> - <div class="icon-box"> - - <!-- commit --> - <svg class="svg-commit-dims"> - <use xlink:href="#commit"></use> - </svg> - - </div> - <h2>commit</h2> - </li> - <li title="credit-card"> - <div class="icon-box"> - - <!-- credit-card --> - <svg class="svg-credit-card-dims"> - <use xlink:href="#credit-card"></use> - </svg> - - </div> - <h2>credit-card</h2> - </li> - <li title="disk"> - <div class="icon-box"> - - <!-- disk --> - <svg class="svg-disk-dims"> - <use xlink:href="#disk"></use> - </svg> - - </div> - <h2>disk</h2> - </li> - <li title="doc_code"> - <div class="icon-box"> - - <!-- doc_code --> - <svg class="svg-doc_code-dims"> - <use xlink:href="#doc_code"></use> - </svg> - - </div> - <h2>doc_code</h2> - </li> - <li title="doc_image"> - <div class="icon-box"> - - <!-- doc_image --> - <svg class="svg-doc_image-dims"> - <use xlink:href="#doc_image"></use> - </svg> - - </div> - <h2>doc_image</h2> - </li> - <li title="doc_text"> - <div class="icon-box"> - - <!-- doc_text --> - <svg class="svg-doc_text-dims"> - <use xlink:href="#doc_text"></use> - </svg> - - </div> - <h2>doc_text</h2> - </li> - <li title="download"> - <div class="icon-box"> - - <!-- download --> - <svg class="svg-download-dims"> - <use xlink:href="#download"></use> - </svg> - - </div> - <h2>download</h2> - </li> - <li title="duplicate"> - <div class="icon-box"> - - <!-- duplicate --> - <svg class="svg-duplicate-dims"> - <use xlink:href="#duplicate"></use> - </svg> - - </div> - <h2>duplicate</h2> - </li> - <li title="earth"> - <div class="icon-box"> - - <!-- earth --> - <svg class="svg-earth-dims"> - <use xlink:href="#earth"></use> - </svg> - - </div> - <h2>earth</h2> - </li> - <li title="eye"> - <div class="icon-box"> - - <!-- eye --> - <svg class="svg-eye-dims"> - <use xlink:href="#eye"></use> - </svg> - - </div> - <h2>eye</h2> - </li> - <li title="eye-slash"> - <div class="icon-box"> - - <!-- eye-slash --> - <svg class="svg-eye-slash-dims"> - <use xlink:href="#eye-slash"></use> - </svg> - - </div> - <h2>eye-slash</h2> - </li> - <li title="file-additions"> - <div class="icon-box"> - - <!-- file-additions --> - <svg class="svg-file-additions-dims"> - <use xlink:href="#file-additions"></use> - </svg> - - </div> - <h2>file-additions</h2> - </li> - <li title="file-deletion"> - <div class="icon-box"> - - <!-- file-deletion --> - <svg class="svg-file-deletion-dims"> - <use xlink:href="#file-deletion"></use> - </svg> - - </div> - <h2>file-deletion</h2> - </li> - <li title="file-modified"> - <div class="icon-box"> - - <!-- file-modified --> - <svg class="svg-file-modified-dims"> - <use xlink:href="#file-modified"></use> - </svg> - - </div> - <h2>file-modified</h2> - </li> - <li title="filter"> - <div class="icon-box"> - - <!-- filter --> - <svg class="svg-filter-dims"> - <use xlink:href="#filter"></use> - </svg> - - </div> - <h2>filter</h2> - </li> - <li title="folder"> - <div class="icon-box"> - - <!-- folder --> - <svg class="svg-folder-dims"> - <use xlink:href="#folder"></use> - </svg> - - </div> - <h2>folder</h2> - </li> - <li title="fork"> - <div class="icon-box"> - - <!-- fork --> - <svg class="svg-fork-dims"> - <use xlink:href="#fork"></use> - </svg> - - </div> - <h2>fork</h2> - </li> - <li title="git-merge"> - <div class="icon-box"> - - <!-- git-merge --> - <svg class="svg-git-merge-dims"> - <use xlink:href="#git-merge"></use> - </svg> - - </div> - <h2>git-merge</h2> - </li> - <li title="group"> - <div class="icon-box"> - - <!-- group --> - <svg class="svg-group-dims"> - <use xlink:href="#group"></use> - </svg> - - </div> - <h2>group</h2> - </li> - <li title="history"> - <div class="icon-box"> - - <!-- history --> - <svg class="svg-history-dims"> - <use xlink:href="#history"></use> - </svg> - - </div> - <h2>history</h2> - </li> - <li title="home"> - <div class="icon-box"> - - <!-- home --> - <svg class="svg-home-dims"> - <use xlink:href="#home"></use> - </svg> - - </div> - <h2>home</h2> - </li> - <li title="hook"> - <div class="icon-box"> - - <!-- hook --> - <svg class="svg-hook-dims"> - <use xlink:href="#hook"></use> - </svg> - - </div> - <h2>hook</h2> - </li> - <li title="issue-block"> - <div class="icon-box"> - - <!-- issue-block --> - <svg class="svg-issue-block-dims"> - <use xlink:href="#issue-block"></use> - </svg> - - </div> - <h2>issue-block</h2> - </li> - <li title="issue-child"> - <div class="icon-box"> - - <!-- issue-child --> - <svg class="svg-issue-child-dims"> - <use xlink:href="#issue-child"></use> - </svg> - - </div> - <h2>issue-child</h2> - </li> - <li title="issue-close"> - <div class="icon-box"> - - <!-- issue-close --> - <svg class="svg-issue-close-dims"> - <use xlink:href="#issue-close"></use> - </svg> - - </div> - <h2>issue-close</h2> - </li> - <li title="issue-duplicate"> - <div class="icon-box"> - - <!-- issue-duplicate --> - <svg class="svg-issue-duplicate-dims"> - <use xlink:href="#issue-duplicate"></use> - </svg> - - </div> - <h2>issue-duplicate</h2> - </li> - <li title="issue-new"> - <div class="icon-box"> - - <!-- issue-new --> - <svg class="svg-issue-new-dims"> - <use xlink:href="#issue-new"></use> - </svg> - - </div> - <h2>issue-new</h2> - </li> - <li title="issue-open"> - <div class="icon-box"> - - <!-- issue-open --> - <svg class="svg-issue-open-dims"> - <use xlink:href="#issue-open"></use> - </svg> - - </div> - <h2>issue-open</h2> - </li> - <li title="issue-open-m"> - <div class="icon-box"> - - <!-- issue-open-m --> - <svg class="svg-issue-open-m-dims"> - <use xlink:href="#issue-open-m"></use> - </svg> - - </div> - <h2>issue-open-m</h2> - </li> - <li title="issue-parent"> - <div class="icon-box"> - - <!-- issue-parent --> - <svg class="svg-issue-parent-dims"> - <use xlink:href="#issue-parent"></use> - </svg> - - </div> - <h2>issue-parent</h2> - </li> - <li title="issues"> - <div class="icon-box"> - - <!-- issues --> - <svg class="svg-issues-dims"> - <use xlink:href="#issues"></use> - </svg> - - </div> - <h2>issues</h2> - </li> - <li title="key"> - <div class="icon-box"> - - <!-- key --> - <svg class="svg-key-dims"> - <use xlink:href="#key"></use> - </svg> - - </div> - <h2>key</h2> - </li> - <li title="key-2"> - <div class="icon-box"> - - <!-- key-2 --> - <svg class="svg-key-2-dims"> - <use xlink:href="#key-2"></use> - </svg> - - </div> - <h2>key-2</h2> - </li> - <li title="label"> - <div class="icon-box"> - - <!-- label --> - <svg class="svg-label-dims"> - <use xlink:href="#label"></use> - </svg> - - </div> - <h2>label</h2> - </li> - <li title="labels"> - <div class="icon-box"> - - <!-- labels --> - <svg class="svg-labels-dims"> - <use xlink:href="#labels"></use> - </svg> - - </div> - <h2>labels</h2> - </li> - <li title="leave"> - <div class="icon-box"> - - <!-- leave --> - <svg class="svg-leave-dims"> - <use xlink:href="#leave"></use> - </svg> - - </div> - <h2>leave</h2> - </li> - <li title="level-up"> - <div class="icon-box"> - - <!-- level-up --> - <svg class="svg-level-up-dims"> - <use xlink:href="#level-up"></use> - </svg> - - </div> - <h2>level-up</h2> - </li> - <li title="license"> - <div class="icon-box"> - - <!-- license --> - <svg class="svg-license-dims"> - <use xlink:href="#license"></use> - </svg> - - </div> - <h2>license</h2> - </li> - <li title="link"> - <div class="icon-box"> - - <!-- link --> - <svg class="svg-link-dims"> - <use xlink:href="#link"></use> - </svg> - - </div> - <h2>link</h2> - </li> - <li title="list-bulleted"> - <div class="icon-box"> - - <!-- list-bulleted --> - <svg class="svg-list-bulleted-dims"> - <use xlink:href="#list-bulleted"></use> - </svg> - - </div> - <h2>list-bulleted</h2> - </li> - <li title="list-numbered"> - <div class="icon-box"> - - <!-- list-numbered --> - <svg class="svg-list-numbered-dims"> - <use xlink:href="#list-numbered"></use> - </svg> - - </div> - <h2>list-numbered</h2> - </li> - <li title="location"> - <div class="icon-box"> - - <!-- location --> - <svg class="svg-location-dims"> - <use xlink:href="#location"></use> - </svg> - - </div> - <h2>location</h2> - </li> - <li title="location-dot"> - <div class="icon-box"> - - <!-- location-dot --> - <svg class="svg-location-dot-dims"> - <use xlink:href="#location-dot"></use> - </svg> - - </div> - <h2>location-dot</h2> - </li> - <li title="lock"> - <div class="icon-box"> - - <!-- lock --> - <svg class="svg-lock-dims"> - <use xlink:href="#lock"></use> - </svg> - - </div> - <h2>lock</h2> - </li> - <li title="lock-open"> - <div class="icon-box"> - - <!-- lock-open --> - <svg class="svg-lock-open-dims"> - <use xlink:href="#lock-open"></use> - </svg> - - </div> - <h2>lock-open</h2> - </li> - <li title="log"> - <div class="icon-box"> - - <!-- log --> - <svg class="svg-log-dims"> - <use xlink:href="#log"></use> - </svg> - - </div> - <h2>log</h2> - </li> - <li title="mail"> - <div class="icon-box"> - - <!-- mail --> - <svg class="svg-mail-dims"> - <use xlink:href="#mail"></use> - </svg> - - </div> - <h2>mail</h2> - </li> - <li title="merge-request-close"> - <div class="icon-box"> - - <!-- merge-request-close --> - <svg class="svg-merge-request-close-dims"> - <use xlink:href="#merge-request-close"></use> - </svg> - - </div> - <h2>merge-request-close</h2> - </li> - <li title="merge-request-close-m"> - <div class="icon-box"> - - <!-- merge-request-close-m --> - <svg class="svg-merge-request-close-m-dims"> - <use xlink:href="#merge-request-close-m"></use> - </svg> - - </div> - <h2>merge-request-close-m</h2> - </li> - <li title="messages"> - <div class="icon-box"> - - <!-- messages --> - <svg class="svg-messages-dims"> - <use xlink:href="#messages"></use> - </svg> - - </div> - <h2>messages</h2> - </li> - <li title="mobile-issue-close"> - <div class="icon-box"> - - <!-- mobile-issue-close --> - <svg class="svg-mobile-issue-close-dims"> - <use xlink:href="#mobile-issue-close"></use> - </svg> - - </div> - <h2>mobile-issue-close</h2> - </li> - <li title="monitor"> - <div class="icon-box"> - - <!-- monitor --> - <svg class="svg-monitor-dims"> - <use xlink:href="#monitor"></use> - </svg> - - </div> - <h2>monitor</h2> - </li> - <li title="more"> - <div class="icon-box"> - - <!-- more --> - <svg class="svg-more-dims"> - <use xlink:href="#more"></use> - </svg> - - </div> - <h2>more</h2> - </li> - <li title="notifications"> - <div class="icon-box"> - - <!-- notifications --> - <svg class="svg-notifications-dims"> - <use xlink:href="#notifications"></use> - </svg> - - </div> - <h2>notifications</h2> - </li> - <li title="notifications-off"> - <div class="icon-box"> - - <!-- notifications-off --> - <svg class="svg-notifications-off-dims"> - <use xlink:href="#notifications-off"></use> - </svg> - - </div> - <h2>notifications-off</h2> - </li> - <li title="overview"> - <div class="icon-box"> - - <!-- overview --> - <svg class="svg-overview-dims"> - <use xlink:href="#overview"></use> - </svg> - - </div> - <h2>overview</h2> - </li> - <li title="pencil"> - <div class="icon-box"> - - <!-- pencil --> - <svg class="svg-pencil-dims"> - <use xlink:href="#pencil"></use> - </svg> - - </div> - <h2>pencil</h2> - </li> - <li title="pipeline"> - <div class="icon-box"> - - <!-- pipeline --> - <svg class="svg-pipeline-dims"> - <use xlink:href="#pipeline"></use> - </svg> - - </div> - <h2>pipeline</h2> - </li> - <li title="play"> - <div class="icon-box"> - - <!-- play --> - <svg class="svg-play-dims"> - <use xlink:href="#play"></use> - </svg> - - </div> - <h2>play</h2> - </li> - <li title="plus"> - <div class="icon-box"> - - <!-- plus --> - <svg class="svg-plus-dims"> - <use xlink:href="#plus"></use> - </svg> - - </div> - <h2>plus</h2> - </li> - <li title="plus-square"> - <div class="icon-box"> - - <!-- plus-square --> - <svg class="svg-plus-square-dims"> - <use xlink:href="#plus-square"></use> - </svg> - - </div> - <h2>plus-square</h2> - </li> - <li title="plus-square-o"> - <div class="icon-box"> - - <!-- plus-square-o --> - <svg class="svg-plus-square-o-dims"> - <use xlink:href="#plus-square-o"></use> - </svg> - - </div> - <h2>plus-square-o</h2> - </li> - <li title="preferences"> - <div class="icon-box"> - - <!-- preferences --> - <svg class="svg-preferences-dims"> - <use xlink:href="#preferences"></use> - </svg> - - </div> - <h2>preferences</h2> - </li> - <li title="profile"> - <div class="icon-box"> - - <!-- profile --> - <svg class="svg-profile-dims"> - <use xlink:href="#profile"></use> - </svg> - - </div> - <h2>profile</h2> - </li> - <li title="project"> - <div class="icon-box"> - - <!-- project --> - <svg class="svg-project-dims"> - <use xlink:href="#project"></use> - </svg> - - </div> - <h2>project</h2> - </li> - <li title="push-rules"> - <div class="icon-box"> - - <!-- push-rules --> - <svg class="svg-push-rules-dims"> - <use xlink:href="#push-rules"></use> - </svg> - - </div> - <h2>push-rules</h2> - </li> - <li title="question"> - <div class="icon-box"> - - <!-- question --> - <svg class="svg-question-dims"> - <use xlink:href="#question"></use> - </svg> - - </div> - <h2>question</h2> - </li> - <li title="question-o"> - <div class="icon-box"> - - <!-- question-o --> - <svg class="svg-question-o-dims"> - <use xlink:href="#question-o"></use> - </svg> - - </div> - <h2>question-o</h2> - </li> - <li title="quote"> - <div class="icon-box"> - - <!-- quote --> - <svg class="svg-quote-dims"> - <use xlink:href="#quote"></use> - </svg> - - </div> - <h2>quote</h2> - </li> - <li title="redo"> - <div class="icon-box"> - - <!-- redo --> - <svg class="svg-redo-dims"> - <use xlink:href="#redo"></use> - </svg> - - </div> - <h2>redo</h2> - </li> - <li title="remove"> - <div class="icon-box"> - - <!-- remove --> - <svg class="svg-remove-dims"> - <use xlink:href="#remove"></use> - </svg> - - </div> - <h2>remove</h2> - </li> - <li title="repeat"> - <div class="icon-box"> - - <!-- repeat --> - <svg class="svg-repeat-dims"> - <use xlink:href="#repeat"></use> - </svg> - - </div> - <h2>repeat</h2> - </li> - <li title="retry"> - <div class="icon-box"> - - <!-- retry --> - <svg class="svg-retry-dims"> - <use xlink:href="#retry"></use> - </svg> - - </div> - <h2>retry</h2> - </li> - <li title="scale"> - <div class="icon-box"> - - <!-- scale --> - <svg class="svg-scale-dims"> - <use xlink:href="#scale"></use> - </svg> - - </div> - <h2>scale</h2> - </li> - <li title="screen-full"> - <div class="icon-box"> - - <!-- screen-full --> - <svg class="svg-screen-full-dims"> - <use xlink:href="#screen-full"></use> - </svg> - - </div> - <h2>screen-full</h2> - </li> - <li title="screen-normal"> - <div class="icon-box"> - - <!-- screen-normal --> - <svg class="svg-screen-normal-dims"> - <use xlink:href="#screen-normal"></use> - </svg> - - </div> - <h2>screen-normal</h2> - </li> - <li title="search"> - <div class="icon-box"> - - <!-- search --> - <svg class="svg-search-dims"> - <use xlink:href="#search"></use> - </svg> - - </div> - <h2>search</h2> - </li> - <li title="settings"> - <div class="icon-box"> - - <!-- settings --> - <svg class="svg-settings-dims"> - <use xlink:href="#settings"></use> - </svg> - - </div> - <h2>settings</h2> - </li> - <li title="shield"> - <div class="icon-box"> - - <!-- shield --> - <svg class="svg-shield-dims"> - <use xlink:href="#shield"></use> - </svg> - - </div> - <h2>shield</h2> - </li> - <li title="slight-frown"> - <div class="icon-box"> - - <!-- slight-frown --> - <svg class="svg-slight-frown-dims"> - <use xlink:href="#slight-frown"></use> - </svg> - - </div> - <h2>slight-frown</h2> - </li> - <li title="slight-smile"> - <div class="icon-box"> - - <!-- slight-smile --> - <svg class="svg-slight-smile-dims"> - <use xlink:href="#slight-smile"></use> - </svg> - - </div> - <h2>slight-smile</h2> - </li> - <li title="smile"> - <div class="icon-box"> - - <!-- smile --> - <svg class="svg-smile-dims"> - <use xlink:href="#smile"></use> - </svg> - - </div> - <h2>smile</h2> - </li> - <li title="smiley"> - <div class="icon-box"> - - <!-- smiley --> - <svg class="svg-smiley-dims"> - <use xlink:href="#smiley"></use> - </svg> - - </div> - <h2>smiley</h2> - </li> - <li title="snippet"> - <div class="icon-box"> - - <!-- snippet --> - <svg class="svg-snippet-dims"> - <use xlink:href="#snippet"></use> - </svg> - - </div> - <h2>snippet</h2> - </li> - <li title="spam"> - <div class="icon-box"> - - <!-- spam --> - <svg class="svg-spam-dims"> - <use xlink:href="#spam"></use> - </svg> - - </div> - <h2>spam</h2> - </li> - <li title="star"> - <div class="icon-box"> - - <!-- star --> - <svg class="svg-star-dims"> - <use xlink:href="#star"></use> - </svg> - - </div> - <h2>star</h2> - </li> - <li title="star-o"> - <div class="icon-box"> - - <!-- star-o --> - <svg class="svg-star-o-dims"> - <use xlink:href="#star-o"></use> - </svg> - - </div> - <h2>star-o</h2> - </li> - <li title="stop"> - <div class="icon-box"> - - <!-- stop --> - <svg class="svg-stop-dims"> - <use xlink:href="#stop"></use> - </svg> - - </div> - <h2>stop</h2> - </li> - <li title="talic"> - <div class="icon-box"> - - <!-- talic --> - <svg class="svg-talic-dims"> - <use xlink:href="#talic"></use> - </svg> - - </div> - <h2>talic</h2> - </li> - <li title="task-done"> - <div class="icon-box"> - - <!-- task-done --> - <svg class="svg-task-done-dims"> - <use xlink:href="#task-done"></use> - </svg> - - </div> - <h2>task-done</h2> - </li> - <li title="template"> - <div class="icon-box"> - - <!-- template --> - <svg class="svg-template-dims"> - <use xlink:href="#template"></use> - </svg> - - </div> - <h2>template</h2> - </li> - <li title="thump-down"> - <div class="icon-box"> - - <!-- thump-down --> - <svg class="svg-thump-down-dims"> - <use xlink:href="#thump-down"></use> - </svg> - - </div> - <h2>thump-down</h2> - </li> - <li title="thump-up"> - <div class="icon-box"> - - <!-- thump-up --> - <svg class="svg-thump-up-dims"> - <use xlink:href="#thump-up"></use> - </svg> - - </div> - <h2>thump-up</h2> - </li> - <li title="timer"> - <div class="icon-box"> - - <!-- timer --> - <svg class="svg-timer-dims"> - <use xlink:href="#timer"></use> - </svg> - - </div> - <h2>timer</h2> - </li> - <li title="todo-add"> - <div class="icon-box"> - - <!-- todo-add --> - <svg class="svg-todo-add-dims"> - <use xlink:href="#todo-add"></use> - </svg> - - </div> - <h2>todo-add</h2> - </li> - <li title="todo-done"> - <div class="icon-box"> - - <!-- todo-done --> - <svg class="svg-todo-done-dims"> - <use xlink:href="#todo-done"></use> - </svg> - - </div> - <h2>todo-done</h2> - </li> - <li title="token"> - <div class="icon-box"> - - <!-- token --> - <svg class="svg-token-dims"> - <use xlink:href="#token"></use> - </svg> - - </div> - <h2>token</h2> - </li> - <li title="unapproval"> - <div class="icon-box"> - - <!-- unapproval --> - <svg class="svg-unapproval-dims"> - <use xlink:href="#unapproval"></use> - </svg> - - </div> - <h2>unapproval</h2> - </li> - <li title="unassignee"> - <div class="icon-box"> - - <!-- unassignee --> - <svg class="svg-unassignee-dims"> - <use xlink:href="#unassignee"></use> - </svg> - - </div> - <h2>unassignee</h2> - </li> - <li title="unlink"> - <div class="icon-box"> - - <!-- unlink --> - <svg class="svg-unlink-dims"> - <use xlink:href="#unlink"></use> - </svg> - - </div> - <h2>unlink</h2> - </li> - <li title="user"> - <div class="icon-box"> - - <!-- user --> - <svg class="svg-user-dims"> - <use xlink:href="#user"></use> - </svg> - - </div> - <h2>user</h2> - </li> - <li title="users"> - <div class="icon-box"> - - <!-- users --> - <svg class="svg-users-dims"> - <use xlink:href="#users"></use> - </svg> - - </div> - <h2>users</h2> - </li> - <li title="volume-up"> - <div class="icon-box"> - - <!-- volume-up --> - <svg class="svg-volume-up-dims"> - <use xlink:href="#volume-up"></use> - </svg> - - </div> - <h2>volume-up</h2> - </li> - <li title="warning"> - <div class="icon-box"> - - <!-- warning --> - <svg class="svg-warning-dims"> - <use xlink:href="#warning"></use> - </svg> - - </div> - <h2>warning</h2> - </li> - <li title="work"> - <div class="icon-box"> - - <!-- work --> - <svg class="svg-work-dims"> - <use xlink:href="#work"></use> - </svg> - - </div> - <h2>work</h2> - </li> - </ul> - -<!-- -==================================================================================================== ---> - - </section> - <section> - -<!-- - -B) Inline SVG with external sprite (IE 9-11 with polyfill only) -==================================================================================================== -These SVG images make use of an URL + fragment identifiers (IDs) and refer to the regular external -SVG sprite. They may be styled via CSS. (IE 9-11 with polyfill only) - ---> - - <h3>B) Inline SVG with external sprite (IE 9-11 with polyfill only)</h3> - <ul> - - <li title="abuse"> - <div class="icon-box"> - - <!-- abuse --> - <svg class="svg-abuse-dims"> - <use xlink:href="icons.svg#abuse"></use> - </svg> - - </div> - <h2>abuse</h2> - </li> - <li title="account"> - <div class="icon-box"> - - <!-- account --> - <svg class="svg-account-dims"> - <use xlink:href="icons.svg#account"></use> - </svg> - - </div> - <h2>account</h2> - </li> - <li title="admin"> - <div class="icon-box"> - - <!-- admin --> - <svg class="svg-admin-dims"> - <use xlink:href="icons.svg#admin"></use> - </svg> - - </div> - <h2>admin</h2> - </li> - <li title="angle-double-left"> - <div class="icon-box"> - - <!-- angle-double-left --> - <svg class="svg-angle-double-left-dims"> - <use xlink:href="icons.svg#angle-double-left"></use> - </svg> - - </div> - <h2>angle-double-left</h2> - </li> - <li title="angle-down"> - <div class="icon-box"> - - <!-- angle-down --> - <svg class="svg-angle-down-dims"> - <use xlink:href="icons.svg#angle-down"></use> - </svg> - - </div> - <h2>angle-down</h2> - </li> - <li title="angle-left"> - <div class="icon-box"> - - <!-- angle-left --> - <svg class="svg-angle-left-dims"> - <use xlink:href="icons.svg#angle-left"></use> - </svg> - - </div> - <h2>angle-left</h2> - </li> - <li title="angle-right"> - <div class="icon-box"> - - <!-- angle-right --> - <svg class="svg-angle-right-dims"> - <use xlink:href="icons.svg#angle-right"></use> - </svg> - - </div> - <h2>angle-right</h2> - </li> - <li title="angle-up"> - <div class="icon-box"> - - <!-- angle-up --> - <svg class="svg-angle-up-dims"> - <use xlink:href="icons.svg#angle-up"></use> - </svg> - - </div> - <h2>angle-up</h2> - </li> - <li title="appearance"> - <div class="icon-box"> - - <!-- appearance --> - <svg class="svg-appearance-dims"> - <use xlink:href="icons.svg#appearance"></use> - </svg> - - </div> - <h2>appearance</h2> - </li> - <li title="applications"> - <div class="icon-box"> - - <!-- applications --> - <svg class="svg-applications-dims"> - <use xlink:href="icons.svg#applications"></use> - </svg> - - </div> - <h2>applications</h2> - </li> - <li title="approval"> - <div class="icon-box"> - - <!-- approval --> - <svg class="svg-approval-dims"> - <use xlink:href="icons.svg#approval"></use> - </svg> - - </div> - <h2>approval</h2> - </li> - <li title="arrow-right"> - <div class="icon-box"> - - <!-- arrow-right --> - <svg class="svg-arrow-right-dims"> - <use xlink:href="icons.svg#arrow-right"></use> - </svg> - - </div> - <h2>arrow-right</h2> - </li> - <li title="assignee"> - <div class="icon-box"> - - <!-- assignee --> - <svg class="svg-assignee-dims"> - <use xlink:href="icons.svg#assignee"></use> - </svg> - - </div> - <h2>assignee</h2> - </li> - <li title="bold"> - <div class="icon-box"> - - <!-- bold --> - <svg class="svg-bold-dims"> - <use xlink:href="icons.svg#bold"></use> - </svg> - - </div> - <h2>bold</h2> - </li> - <li title="book"> - <div class="icon-box"> - - <!-- book --> - <svg class="svg-book-dims"> - <use xlink:href="icons.svg#book"></use> - </svg> - - </div> - <h2>book</h2> - </li> - <li title="branch"> - <div class="icon-box"> - - <!-- branch --> - <svg class="svg-branch-dims"> - <use xlink:href="icons.svg#branch"></use> - </svg> - - </div> - <h2>branch</h2> - </li> - <li title="calendar"> - <div class="icon-box"> - - <!-- calendar --> - <svg class="svg-calendar-dims"> - <use xlink:href="icons.svg#calendar"></use> - </svg> - - </div> - <h2>calendar</h2> - </li> - <li title="cancel"> - <div class="icon-box"> - - <!-- cancel --> - <svg class="svg-cancel-dims"> - <use xlink:href="icons.svg#cancel"></use> - </svg> - - </div> - <h2>cancel</h2> - </li> - <li title="chevron-down"> - <div class="icon-box"> - - <!-- chevron-down --> - <svg class="svg-chevron-down-dims"> - <use xlink:href="icons.svg#chevron-down"></use> - </svg> - - </div> - <h2>chevron-down</h2> - </li> - <li title="chevron-left"> - <div class="icon-box"> - - <!-- chevron-left --> - <svg class="svg-chevron-left-dims"> - <use xlink:href="icons.svg#chevron-left"></use> - </svg> - - </div> - <h2>chevron-left</h2> - </li> - <li title="chevron-right"> - <div class="icon-box"> - - <!-- chevron-right --> - <svg class="svg-chevron-right-dims"> - <use xlink:href="icons.svg#chevron-right"></use> - </svg> - - </div> - <h2>chevron-right</h2> - </li> - <li title="chevron-up"> - <div class="icon-box"> - - <!-- chevron-up --> - <svg class="svg-chevron-up-dims"> - <use xlink:href="icons.svg#chevron-up"></use> - </svg> - - </div> - <h2>chevron-up</h2> - </li> - <li title="clock"> - <div class="icon-box"> - - <!-- clock --> - <svg class="svg-clock-dims"> - <use xlink:href="icons.svg#clock"></use> - </svg> - - </div> - <h2>clock</h2> - </li> - <li title="code"> - <div class="icon-box"> - - <!-- code --> - <svg class="svg-code-dims"> - <use xlink:href="icons.svg#code"></use> - </svg> - - </div> - <h2>code</h2> - </li> - <li title="comment"> - <div class="icon-box"> - - <!-- comment --> - <svg class="svg-comment-dims"> - <use xlink:href="icons.svg#comment"></use> - </svg> - - </div> - <h2>comment</h2> - </li> - <li title="comment-dots"> - <div class="icon-box"> - - <!-- comment-dots --> - <svg class="svg-comment-dots-dims"> - <use xlink:href="icons.svg#comment-dots"></use> - </svg> - - </div> - <h2>comment-dots</h2> - </li> - <li title="comment-next"> - <div class="icon-box"> - - <!-- comment-next --> - <svg class="svg-comment-next-dims"> - <use xlink:href="icons.svg#comment-next"></use> - </svg> - - </div> - <h2>comment-next</h2> - </li> - <li title="comments"> - <div class="icon-box"> - - <!-- comments --> - <svg class="svg-comments-dims"> - <use xlink:href="icons.svg#comments"></use> - </svg> - - </div> - <h2>comments</h2> - </li> - <li title="commit"> - <div class="icon-box"> - - <!-- commit --> - <svg class="svg-commit-dims"> - <use xlink:href="icons.svg#commit"></use> - </svg> - - </div> - <h2>commit</h2> - </li> - <li title="credit-card"> - <div class="icon-box"> - - <!-- credit-card --> - <svg class="svg-credit-card-dims"> - <use xlink:href="icons.svg#credit-card"></use> - </svg> - - </div> - <h2>credit-card</h2> - </li> - <li title="disk"> - <div class="icon-box"> - - <!-- disk --> - <svg class="svg-disk-dims"> - <use xlink:href="icons.svg#disk"></use> - </svg> - - </div> - <h2>disk</h2> - </li> - <li title="doc_code"> - <div class="icon-box"> - - <!-- doc_code --> - <svg class="svg-doc_code-dims"> - <use xlink:href="icons.svg#doc_code"></use> - </svg> - - </div> - <h2>doc_code</h2> - </li> - <li title="doc_image"> - <div class="icon-box"> - - <!-- doc_image --> - <svg class="svg-doc_image-dims"> - <use xlink:href="icons.svg#doc_image"></use> - </svg> - - </div> - <h2>doc_image</h2> - </li> - <li title="doc_text"> - <div class="icon-box"> - - <!-- doc_text --> - <svg class="svg-doc_text-dims"> - <use xlink:href="icons.svg#doc_text"></use> - </svg> - - </div> - <h2>doc_text</h2> - </li> - <li title="download"> - <div class="icon-box"> - - <!-- download --> - <svg class="svg-download-dims"> - <use xlink:href="icons.svg#download"></use> - </svg> - - </div> - <h2>download</h2> - </li> - <li title="duplicate"> - <div class="icon-box"> - - <!-- duplicate --> - <svg class="svg-duplicate-dims"> - <use xlink:href="icons.svg#duplicate"></use> - </svg> - - </div> - <h2>duplicate</h2> - </li> - <li title="earth"> - <div class="icon-box"> - - <!-- earth --> - <svg class="svg-earth-dims"> - <use xlink:href="icons.svg#earth"></use> - </svg> - - </div> - <h2>earth</h2> - </li> - <li title="eye"> - <div class="icon-box"> - - <!-- eye --> - <svg class="svg-eye-dims"> - <use xlink:href="icons.svg#eye"></use> - </svg> - - </div> - <h2>eye</h2> - </li> - <li title="eye-slash"> - <div class="icon-box"> - - <!-- eye-slash --> - <svg class="svg-eye-slash-dims"> - <use xlink:href="icons.svg#eye-slash"></use> - </svg> - - </div> - <h2>eye-slash</h2> - </li> - <li title="file-additions"> - <div class="icon-box"> - - <!-- file-additions --> - <svg class="svg-file-additions-dims"> - <use xlink:href="icons.svg#file-additions"></use> - </svg> - - </div> - <h2>file-additions</h2> - </li> - <li title="file-deletion"> - <div class="icon-box"> - - <!-- file-deletion --> - <svg class="svg-file-deletion-dims"> - <use xlink:href="icons.svg#file-deletion"></use> - </svg> - - </div> - <h2>file-deletion</h2> - </li> - <li title="file-modified"> - <div class="icon-box"> - - <!-- file-modified --> - <svg class="svg-file-modified-dims"> - <use xlink:href="icons.svg#file-modified"></use> - </svg> - - </div> - <h2>file-modified</h2> - </li> - <li title="filter"> - <div class="icon-box"> - - <!-- filter --> - <svg class="svg-filter-dims"> - <use xlink:href="icons.svg#filter"></use> - </svg> - - </div> - <h2>filter</h2> - </li> - <li title="folder"> - <div class="icon-box"> - - <!-- folder --> - <svg class="svg-folder-dims"> - <use xlink:href="icons.svg#folder"></use> - </svg> - - </div> - <h2>folder</h2> - </li> - <li title="fork"> - <div class="icon-box"> - - <!-- fork --> - <svg class="svg-fork-dims"> - <use xlink:href="icons.svg#fork"></use> - </svg> - - </div> - <h2>fork</h2> - </li> - <li title="git-merge"> - <div class="icon-box"> - - <!-- git-merge --> - <svg class="svg-git-merge-dims"> - <use xlink:href="icons.svg#git-merge"></use> - </svg> - - </div> - <h2>git-merge</h2> - </li> - <li title="group"> - <div class="icon-box"> - - <!-- group --> - <svg class="svg-group-dims"> - <use xlink:href="icons.svg#group"></use> - </svg> - - </div> - <h2>group</h2> - </li> - <li title="history"> - <div class="icon-box"> - - <!-- history --> - <svg class="svg-history-dims"> - <use xlink:href="icons.svg#history"></use> - </svg> - - </div> - <h2>history</h2> - </li> - <li title="home"> - <div class="icon-box"> - - <!-- home --> - <svg class="svg-home-dims"> - <use xlink:href="icons.svg#home"></use> - </svg> - - </div> - <h2>home</h2> - </li> - <li title="hook"> - <div class="icon-box"> - - <!-- hook --> - <svg class="svg-hook-dims"> - <use xlink:href="icons.svg#hook"></use> - </svg> - - </div> - <h2>hook</h2> - </li> - <li title="issue-block"> - <div class="icon-box"> - - <!-- issue-block --> - <svg class="svg-issue-block-dims"> - <use xlink:href="icons.svg#issue-block"></use> - </svg> - - </div> - <h2>issue-block</h2> - </li> - <li title="issue-child"> - <div class="icon-box"> - - <!-- issue-child --> - <svg class="svg-issue-child-dims"> - <use xlink:href="icons.svg#issue-child"></use> - </svg> - - </div> - <h2>issue-child</h2> - </li> - <li title="issue-close"> - <div class="icon-box"> - - <!-- issue-close --> - <svg class="svg-issue-close-dims"> - <use xlink:href="icons.svg#issue-close"></use> - </svg> - - </div> - <h2>issue-close</h2> - </li> - <li title="issue-duplicate"> - <div class="icon-box"> - - <!-- issue-duplicate --> - <svg class="svg-issue-duplicate-dims"> - <use xlink:href="icons.svg#issue-duplicate"></use> - </svg> - - </div> - <h2>issue-duplicate</h2> - </li> - <li title="issue-new"> - <div class="icon-box"> - - <!-- issue-new --> - <svg class="svg-issue-new-dims"> - <use xlink:href="icons.svg#issue-new"></use> - </svg> - - </div> - <h2>issue-new</h2> - </li> - <li title="issue-open"> - <div class="icon-box"> - - <!-- issue-open --> - <svg class="svg-issue-open-dims"> - <use xlink:href="icons.svg#issue-open"></use> - </svg> - - </div> - <h2>issue-open</h2> - </li> - <li title="issue-open-m"> - <div class="icon-box"> - - <!-- issue-open-m --> - <svg class="svg-issue-open-m-dims"> - <use xlink:href="icons.svg#issue-open-m"></use> - </svg> - - </div> - <h2>issue-open-m</h2> - </li> - <li title="issue-parent"> - <div class="icon-box"> - - <!-- issue-parent --> - <svg class="svg-issue-parent-dims"> - <use xlink:href="icons.svg#issue-parent"></use> - </svg> - - </div> - <h2>issue-parent</h2> - </li> - <li title="issues"> - <div class="icon-box"> - - <!-- issues --> - <svg class="svg-issues-dims"> - <use xlink:href="icons.svg#issues"></use> - </svg> - - </div> - <h2>issues</h2> - </li> - <li title="key"> - <div class="icon-box"> - - <!-- key --> - <svg class="svg-key-dims"> - <use xlink:href="icons.svg#key"></use> - </svg> - - </div> - <h2>key</h2> - </li> - <li title="key-2"> - <div class="icon-box"> - - <!-- key-2 --> - <svg class="svg-key-2-dims"> - <use xlink:href="icons.svg#key-2"></use> - </svg> - - </div> - <h2>key-2</h2> - </li> - <li title="label"> - <div class="icon-box"> - - <!-- label --> - <svg class="svg-label-dims"> - <use xlink:href="icons.svg#label"></use> - </svg> - - </div> - <h2>label</h2> - </li> - <li title="labels"> - <div class="icon-box"> - - <!-- labels --> - <svg class="svg-labels-dims"> - <use xlink:href="icons.svg#labels"></use> - </svg> - - </div> - <h2>labels</h2> - </li> - <li title="leave"> - <div class="icon-box"> - - <!-- leave --> - <svg class="svg-leave-dims"> - <use xlink:href="icons.svg#leave"></use> - </svg> - - </div> - <h2>leave</h2> - </li> - <li title="level-up"> - <div class="icon-box"> - - <!-- level-up --> - <svg class="svg-level-up-dims"> - <use xlink:href="icons.svg#level-up"></use> - </svg> - - </div> - <h2>level-up</h2> - </li> - <li title="license"> - <div class="icon-box"> - - <!-- license --> - <svg class="svg-license-dims"> - <use xlink:href="icons.svg#license"></use> - </svg> - - </div> - <h2>license</h2> - </li> - <li title="link"> - <div class="icon-box"> - - <!-- link --> - <svg class="svg-link-dims"> - <use xlink:href="icons.svg#link"></use> - </svg> - - </div> - <h2>link</h2> - </li> - <li title="list-bulleted"> - <div class="icon-box"> - - <!-- list-bulleted --> - <svg class="svg-list-bulleted-dims"> - <use xlink:href="icons.svg#list-bulleted"></use> - </svg> - - </div> - <h2>list-bulleted</h2> - </li> - <li title="list-numbered"> - <div class="icon-box"> - - <!-- list-numbered --> - <svg class="svg-list-numbered-dims"> - <use xlink:href="icons.svg#list-numbered"></use> - </svg> - - </div> - <h2>list-numbered</h2> - </li> - <li title="location"> - <div class="icon-box"> - - <!-- location --> - <svg class="svg-location-dims"> - <use xlink:href="icons.svg#location"></use> - </svg> - - </div> - <h2>location</h2> - </li> - <li title="location-dot"> - <div class="icon-box"> - - <!-- location-dot --> - <svg class="svg-location-dot-dims"> - <use xlink:href="icons.svg#location-dot"></use> - </svg> - - </div> - <h2>location-dot</h2> - </li> - <li title="lock"> - <div class="icon-box"> - - <!-- lock --> - <svg class="svg-lock-dims"> - <use xlink:href="icons.svg#lock"></use> - </svg> - - </div> - <h2>lock</h2> - </li> - <li title="lock-open"> - <div class="icon-box"> - - <!-- lock-open --> - <svg class="svg-lock-open-dims"> - <use xlink:href="icons.svg#lock-open"></use> - </svg> - - </div> - <h2>lock-open</h2> - </li> - <li title="log"> - <div class="icon-box"> - - <!-- log --> - <svg class="svg-log-dims"> - <use xlink:href="icons.svg#log"></use> - </svg> - - </div> - <h2>log</h2> - </li> - <li title="mail"> - <div class="icon-box"> - - <!-- mail --> - <svg class="svg-mail-dims"> - <use xlink:href="icons.svg#mail"></use> - </svg> - - </div> - <h2>mail</h2> - </li> - <li title="merge-request-close"> - <div class="icon-box"> - - <!-- merge-request-close --> - <svg class="svg-merge-request-close-dims"> - <use xlink:href="icons.svg#merge-request-close"></use> - </svg> - - </div> - <h2>merge-request-close</h2> - </li> - <li title="merge-request-close-m"> - <div class="icon-box"> - - <!-- merge-request-close-m --> - <svg class="svg-merge-request-close-m-dims"> - <use xlink:href="icons.svg#merge-request-close-m"></use> - </svg> - - </div> - <h2>merge-request-close-m</h2> - </li> - <li title="messages"> - <div class="icon-box"> - - <!-- messages --> - <svg class="svg-messages-dims"> - <use xlink:href="icons.svg#messages"></use> - </svg> - - </div> - <h2>messages</h2> - </li> - <li title="mobile-issue-close"> - <div class="icon-box"> - - <!-- mobile-issue-close --> - <svg class="svg-mobile-issue-close-dims"> - <use xlink:href="icons.svg#mobile-issue-close"></use> - </svg> - - </div> - <h2>mobile-issue-close</h2> - </li> - <li title="monitor"> - <div class="icon-box"> - - <!-- monitor --> - <svg class="svg-monitor-dims"> - <use xlink:href="icons.svg#monitor"></use> - </svg> - - </div> - <h2>monitor</h2> - </li> - <li title="more"> - <div class="icon-box"> - - <!-- more --> - <svg class="svg-more-dims"> - <use xlink:href="icons.svg#more"></use> - </svg> - - </div> - <h2>more</h2> - </li> - <li title="notifications"> - <div class="icon-box"> - - <!-- notifications --> - <svg class="svg-notifications-dims"> - <use xlink:href="icons.svg#notifications"></use> - </svg> - - </div> - <h2>notifications</h2> - </li> - <li title="notifications-off"> - <div class="icon-box"> - - <!-- notifications-off --> - <svg class="svg-notifications-off-dims"> - <use xlink:href="icons.svg#notifications-off"></use> - </svg> - - </div> - <h2>notifications-off</h2> - </li> - <li title="overview"> - <div class="icon-box"> - - <!-- overview --> - <svg class="svg-overview-dims"> - <use xlink:href="icons.svg#overview"></use> - </svg> - - </div> - <h2>overview</h2> - </li> - <li title="pencil"> - <div class="icon-box"> - - <!-- pencil --> - <svg class="svg-pencil-dims"> - <use xlink:href="icons.svg#pencil"></use> - </svg> - - </div> - <h2>pencil</h2> - </li> - <li title="pipeline"> - <div class="icon-box"> - - <!-- pipeline --> - <svg class="svg-pipeline-dims"> - <use xlink:href="icons.svg#pipeline"></use> - </svg> - - </div> - <h2>pipeline</h2> - </li> - <li title="play"> - <div class="icon-box"> - - <!-- play --> - <svg class="svg-play-dims"> - <use xlink:href="icons.svg#play"></use> - </svg> - - </div> - <h2>play</h2> - </li> - <li title="plus"> - <div class="icon-box"> - - <!-- plus --> - <svg class="svg-plus-dims"> - <use xlink:href="icons.svg#plus"></use> - </svg> - - </div> - <h2>plus</h2> - </li> - <li title="plus-square"> - <div class="icon-box"> - - <!-- plus-square --> - <svg class="svg-plus-square-dims"> - <use xlink:href="icons.svg#plus-square"></use> - </svg> - - </div> - <h2>plus-square</h2> - </li> - <li title="plus-square-o"> - <div class="icon-box"> - - <!-- plus-square-o --> - <svg class="svg-plus-square-o-dims"> - <use xlink:href="icons.svg#plus-square-o"></use> - </svg> - - </div> - <h2>plus-square-o</h2> - </li> - <li title="preferences"> - <div class="icon-box"> - - <!-- preferences --> - <svg class="svg-preferences-dims"> - <use xlink:href="icons.svg#preferences"></use> - </svg> - - </div> - <h2>preferences</h2> - </li> - <li title="profile"> - <div class="icon-box"> - - <!-- profile --> - <svg class="svg-profile-dims"> - <use xlink:href="icons.svg#profile"></use> - </svg> - - </div> - <h2>profile</h2> - </li> - <li title="project"> - <div class="icon-box"> - - <!-- project --> - <svg class="svg-project-dims"> - <use xlink:href="icons.svg#project"></use> - </svg> - - </div> - <h2>project</h2> - </li> - <li title="push-rules"> - <div class="icon-box"> - - <!-- push-rules --> - <svg class="svg-push-rules-dims"> - <use xlink:href="icons.svg#push-rules"></use> - </svg> - - </div> - <h2>push-rules</h2> - </li> - <li title="question"> - <div class="icon-box"> - - <!-- question --> - <svg class="svg-question-dims"> - <use xlink:href="icons.svg#question"></use> - </svg> - - </div> - <h2>question</h2> - </li> - <li title="question-o"> - <div class="icon-box"> - - <!-- question-o --> - <svg class="svg-question-o-dims"> - <use xlink:href="icons.svg#question-o"></use> - </svg> - - </div> - <h2>question-o</h2> - </li> - <li title="quote"> - <div class="icon-box"> - - <!-- quote --> - <svg class="svg-quote-dims"> - <use xlink:href="icons.svg#quote"></use> - </svg> - - </div> - <h2>quote</h2> - </li> - <li title="redo"> - <div class="icon-box"> - - <!-- redo --> - <svg class="svg-redo-dims"> - <use xlink:href="icons.svg#redo"></use> - </svg> - - </div> - <h2>redo</h2> - </li> - <li title="remove"> - <div class="icon-box"> - - <!-- remove --> - <svg class="svg-remove-dims"> - <use xlink:href="icons.svg#remove"></use> - </svg> - - </div> - <h2>remove</h2> - </li> - <li title="repeat"> - <div class="icon-box"> - - <!-- repeat --> - <svg class="svg-repeat-dims"> - <use xlink:href="icons.svg#repeat"></use> - </svg> - - </div> - <h2>repeat</h2> - </li> - <li title="retry"> - <div class="icon-box"> - - <!-- retry --> - <svg class="svg-retry-dims"> - <use xlink:href="icons.svg#retry"></use> - </svg> - - </div> - <h2>retry</h2> - </li> - <li title="scale"> - <div class="icon-box"> - - <!-- scale --> - <svg class="svg-scale-dims"> - <use xlink:href="icons.svg#scale"></use> - </svg> - - </div> - <h2>scale</h2> - </li> - <li title="screen-full"> - <div class="icon-box"> - - <!-- screen-full --> - <svg class="svg-screen-full-dims"> - <use xlink:href="icons.svg#screen-full"></use> - </svg> - - </div> - <h2>screen-full</h2> - </li> - <li title="screen-normal"> - <div class="icon-box"> - - <!-- screen-normal --> - <svg class="svg-screen-normal-dims"> - <use xlink:href="icons.svg#screen-normal"></use> - </svg> - - </div> - <h2>screen-normal</h2> - </li> - <li title="search"> - <div class="icon-box"> - - <!-- search --> - <svg class="svg-search-dims"> - <use xlink:href="icons.svg#search"></use> - </svg> - - </div> - <h2>search</h2> - </li> - <li title="settings"> - <div class="icon-box"> - - <!-- settings --> - <svg class="svg-settings-dims"> - <use xlink:href="icons.svg#settings"></use> - </svg> - - </div> - <h2>settings</h2> - </li> - <li title="shield"> - <div class="icon-box"> - - <!-- shield --> - <svg class="svg-shield-dims"> - <use xlink:href="icons.svg#shield"></use> - </svg> - - </div> - <h2>shield</h2> - </li> - <li title="slight-frown"> - <div class="icon-box"> - - <!-- slight-frown --> - <svg class="svg-slight-frown-dims"> - <use xlink:href="icons.svg#slight-frown"></use> - </svg> - - </div> - <h2>slight-frown</h2> - </li> - <li title="slight-smile"> - <div class="icon-box"> - - <!-- slight-smile --> - <svg class="svg-slight-smile-dims"> - <use xlink:href="icons.svg#slight-smile"></use> - </svg> - - </div> - <h2>slight-smile</h2> - </li> - <li title="smile"> - <div class="icon-box"> - - <!-- smile --> - <svg class="svg-smile-dims"> - <use xlink:href="icons.svg#smile"></use> - </svg> - - </div> - <h2>smile</h2> - </li> - <li title="smiley"> - <div class="icon-box"> - - <!-- smiley --> - <svg class="svg-smiley-dims"> - <use xlink:href="icons.svg#smiley"></use> - </svg> - - </div> - <h2>smiley</h2> - </li> - <li title="snippet"> - <div class="icon-box"> - - <!-- snippet --> - <svg class="svg-snippet-dims"> - <use xlink:href="icons.svg#snippet"></use> - </svg> - - </div> - <h2>snippet</h2> - </li> - <li title="spam"> - <div class="icon-box"> - - <!-- spam --> - <svg class="svg-spam-dims"> - <use xlink:href="icons.svg#spam"></use> - </svg> - - </div> - <h2>spam</h2> - </li> - <li title="star"> - <div class="icon-box"> - - <!-- star --> - <svg class="svg-star-dims"> - <use xlink:href="icons.svg#star"></use> - </svg> - - </div> - <h2>star</h2> - </li> - <li title="star-o"> - <div class="icon-box"> - - <!-- star-o --> - <svg class="svg-star-o-dims"> - <use xlink:href="icons.svg#star-o"></use> - </svg> - - </div> - <h2>star-o</h2> - </li> - <li title="stop"> - <div class="icon-box"> - - <!-- stop --> - <svg class="svg-stop-dims"> - <use xlink:href="icons.svg#stop"></use> - </svg> - - </div> - <h2>stop</h2> - </li> - <li title="talic"> - <div class="icon-box"> - - <!-- talic --> - <svg class="svg-talic-dims"> - <use xlink:href="icons.svg#talic"></use> - </svg> - - </div> - <h2>talic</h2> - </li> - <li title="task-done"> - <div class="icon-box"> - - <!-- task-done --> - <svg class="svg-task-done-dims"> - <use xlink:href="icons.svg#task-done"></use> - </svg> - - </div> - <h2>task-done</h2> - </li> - <li title="template"> - <div class="icon-box"> - - <!-- template --> - <svg class="svg-template-dims"> - <use xlink:href="icons.svg#template"></use> - </svg> - - </div> - <h2>template</h2> - </li> - <li title="thump-down"> - <div class="icon-box"> - - <!-- thump-down --> - <svg class="svg-thump-down-dims"> - <use xlink:href="icons.svg#thump-down"></use> - </svg> - - </div> - <h2>thump-down</h2> - </li> - <li title="thump-up"> - <div class="icon-box"> - - <!-- thump-up --> - <svg class="svg-thump-up-dims"> - <use xlink:href="icons.svg#thump-up"></use> - </svg> - - </div> - <h2>thump-up</h2> - </li> - <li title="timer"> - <div class="icon-box"> - - <!-- timer --> - <svg class="svg-timer-dims"> - <use xlink:href="icons.svg#timer"></use> - </svg> - - </div> - <h2>timer</h2> - </li> - <li title="todo-add"> - <div class="icon-box"> - - <!-- todo-add --> - <svg class="svg-todo-add-dims"> - <use xlink:href="icons.svg#todo-add"></use> - </svg> - - </div> - <h2>todo-add</h2> - </li> - <li title="todo-done"> - <div class="icon-box"> - - <!-- todo-done --> - <svg class="svg-todo-done-dims"> - <use xlink:href="icons.svg#todo-done"></use> - </svg> - - </div> - <h2>todo-done</h2> - </li> - <li title="token"> - <div class="icon-box"> - - <!-- token --> - <svg class="svg-token-dims"> - <use xlink:href="icons.svg#token"></use> - </svg> - - </div> - <h2>token</h2> - </li> - <li title="unapproval"> - <div class="icon-box"> - - <!-- unapproval --> - <svg class="svg-unapproval-dims"> - <use xlink:href="icons.svg#unapproval"></use> - </svg> - - </div> - <h2>unapproval</h2> - </li> - <li title="unassignee"> - <div class="icon-box"> - - <!-- unassignee --> - <svg class="svg-unassignee-dims"> - <use xlink:href="icons.svg#unassignee"></use> - </svg> - - </div> - <h2>unassignee</h2> - </li> - <li title="unlink"> - <div class="icon-box"> - - <!-- unlink --> - <svg class="svg-unlink-dims"> - <use xlink:href="icons.svg#unlink"></use> - </svg> - - </div> - <h2>unlink</h2> - </li> - <li title="user"> - <div class="icon-box"> - - <!-- user --> - <svg class="svg-user-dims"> - <use xlink:href="icons.svg#user"></use> - </svg> - - </div> - <h2>user</h2> - </li> - <li title="users"> - <div class="icon-box"> - - <!-- users --> - <svg class="svg-users-dims"> - <use xlink:href="icons.svg#users"></use> - </svg> - - </div> - <h2>users</h2> - </li> - <li title="volume-up"> - <div class="icon-box"> - - <!-- volume-up --> - <svg class="svg-volume-up-dims"> - <use xlink:href="icons.svg#volume-up"></use> - </svg> - - </div> - <h2>volume-up</h2> - </li> - <li title="warning"> - <div class="icon-box"> - - <!-- warning --> - <svg class="svg-warning-dims"> - <use xlink:href="icons.svg#warning"></use> - </svg> - - </div> - <h2>warning</h2> - </li> - <li title="work"> - <div class="icon-box"> - - <!-- work --> - <svg class="svg-work-dims"> - <use xlink:href="icons.svg#work"></use> - </svg> - - </div> - <h2>work</h2> - </li> - </ul> - -<!-- -==================================================================================================== ---> - - </section> - <footer> - <p>Generated at Tue, 12 Sep 2017 09:08:46 GMT by <a href="https://github.com/jkphl/svg-sprite" target="_blank">svg-sprite</a>.</p> - </footer> - </body> -</html> diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js index 27312d718b0..c858a6bb7b4 100644 --- a/app/assets/javascripts/blob/notebook/index.js +++ b/app/assets/javascripts/blob/notebook/index.js @@ -40,10 +40,10 @@ export default () => { class="text-center" v-if="error"> <span v-if="loadError"> - An error occured whilst loading the file. Please try again later. + An error occurred whilst loading the file. Please try again later. </span> <span v-else> - An error occured whilst parsing the file. + An error occurred whilst parsing the file. </span> </p> </div> diff --git a/app/assets/javascripts/blob/pdf/index.js b/app/assets/javascripts/blob/pdf/index.js index 0ed915c1ac9..7109f356540 100644 --- a/app/assets/javascripts/blob/pdf/index.js +++ b/app/assets/javascripts/blob/pdf/index.js @@ -48,10 +48,10 @@ export default () => { class="text-center" v-if="error"> <span v-if="loadError"> - An error occured whilst loading the file. Please try again later. + An error occurred whilst loading the file. Please try again later. </span> <span v-else> - An error occured whilst decoding the file. + An error occurred whilst decoding the file. </span> </p> </div> diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index ea00efe4b46..815248f38ee 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -77,9 +77,6 @@ $(() => { }); Store.rootPath = this.boardsEndpoint; - this.filterManager = new FilteredSearchBoards(Store.filter, true); - this.filterManager.setup(); - // Listen for updateTokens event eventHub.$on('updateTokens', this.updateTokens); }, @@ -87,6 +84,9 @@ $(() => { eventHub.$off('updateTokens', this.updateTokens); }, mounted () { + this.filterManager = new FilteredSearchBoards(Store.filter, true); + this.filterManager.setup(); + Store.disabled = this.disabled; gl.boardService.all() .then(response => response.json()) diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.js index 541b8049855..bc28f7f45f4 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js +++ b/app/assets/javascripts/boards/components/board_new_issue.js @@ -68,7 +68,7 @@ export default { <div class="flash-container" v-if="error"> <div class="flash-alert"> - An error occured. Please try again. + An error occurred. Please try again. </div> </div> <label class="label-light" diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 286a758b8a9..3d27a3544eb 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -167,7 +167,7 @@ window.Build = (function () { Build.prototype.getBuildTrace = function () { return $.ajax({ url: `${this.pageUrl}/trace.json`, - data: this.state, + data: { state: this.state }, }) .done((log) => { setCiStatusFavicon(`${this.pageUrl}/status.json`); diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js deleted file mode 100644 index 8d3d34f836f..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js +++ /dev/null @@ -1,17 +0,0 @@ -export default { - props: { - count: { - type: Number, - required: true, - }, - }, - template: ` - <span v-if="count === 50" class="events-info pull-right"> - <i class="fa fa-warning has-tooltip" - aria-hidden="true" - :title="n__('Limited to showing %d event at most', 'Limited to showing %d events at most', 50)" - data-placement="top"></i> - {{ n__('Showing %d event', 'Showing %d events', 50) }} - </span> - `, -}; diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue new file mode 100644 index 00000000000..6e94ba929b2 --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue @@ -0,0 +1,26 @@ +<script> + import tooltip from '../../vue_shared/directives/tooltip'; + + export default { + props: { + count: { + type: Number, + required: true, + }, + }, + directives: { + tooltip, + }, + }; +</script> +<template> + <span v-if="count === 50" class="events-info pull-right"> + <i + class="fa fa-warning" + v-tooltip + aria-hidden="true" + :title="n__('Limited to showing %d event at most', 'Limited to showing %d events at most', 50)" + data-placement="top"></i> + {{ n__('Showing %d event', 'Showing %d events', 50) }} + </span> +</template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js deleted file mode 100644 index 7c32a38fbe7..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable no-param-reassign */ - -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageCodeComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="mergeRequest in items" class="stage-event-item"> - <div class="item-details"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/> - <h5 class="item-title merge-merquest-title"> - <a :href="mergeRequest.url"> - {{ mergeRequest.title }} - </a> - </h5> - <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a> - · - <span> - {{ s__('OpenedNDaysAgo|Opened') }} - <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a> - </span> - <span> - {{ s__('ByAuthor|by') }} - <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a> - </span> - </div> - <div class="item-time"> - <total-time :time="mergeRequest.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue new file mode 100644 index 00000000000..45930145b0a --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue @@ -0,0 +1,51 @@ +<script> + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + userAvatarImage, + limitWarning, + totalTime, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li v-for="mergeRequest in items" class="stage-event-item"> + <div class="item-details"> + <!-- FIXME: Pass an alt attribute here for accessibility --> + <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/> + <h5 class="item-title merge-merquest-title"> + <a :href="mergeRequest.url"> + {{ mergeRequest.title }} + </a> + </h5> + <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a> + · + <span> + {{ s__('OpenedNDaysAgo|Opened') }} + <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a> + </span> + <span> + {{ s__('ByAuthor|by') }} + <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a> + </span> + </div> + <div class="item-time"> + <total-time :time="mergeRequest.totalTime"></total-time> + </div> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue new file mode 100644 index 00000000000..8c98bd249a1 --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue @@ -0,0 +1,57 @@ +<script> + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + userAvatarImage, + limitWarning, + totalTime, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li + v-for="(issue, i) in items" + :key="i" + class="stage-event-item"> + <div class="item-details"> + <!-- FIXME: Pass an alt attribute here for accessibility --> + <user-avatar-image :img-src="issue.author.avatarUrl"/> + <h5 class="item-title issue-title"> + <a class="issue-title" :href="issue.url"> + {{ issue.title }} + </a> + </h5> + <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a> + · + <span> + {{ s__('OpenedNDaysAgo|Opened') }} + <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a> + </span> + <span> + {{ s__('ByAuthor|by') }} + <a :href="issue.author.webUrl" class="issue-author-link"> + {{ issue.author.name }} + </a> + </span> + </div> + <div class="item-time"> + <total-time :time="issue.totalTime"/> + </div> + </li> + </ul> + </div> +</template> + diff --git a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js deleted file mode 100644 index 5f4a0ac8590..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageIssueComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="issue in items" class="stage-event-item"> - <div class="item-details"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="issue.author.avatarUrl"/> - <h5 class="item-title issue-title"> - <a class="issue-title" :href="issue.url"> - {{ issue.title }} - </a> - </h5> - <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a> - · - <span> - {{ s__('OpenedNDaysAgo|Opened') }} - <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a> - </span> - <span> - {{ s__('ByAuthor|by') }} - <a :href="issue.author.webUrl" class="issue-author-link"> - {{ issue.author.name }} - </a> - </span> - </div> - <div class="item-time"> - <total-time :time="issue.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js deleted file mode 100644 index 11fee5410d9..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; -import iconCommit from '../svg/icon_commit.svg'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StagePlanComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - data() { - return { iconCommit }; - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="commit in items" class="stage-event-item"> - <div class="item-details item-conmmit-component"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="commit.author.avatarUrl"/> - <h5 class="item-title commit-title"> - <a :href="commit.commitUrl"> - {{ commit.title }} - </a> - </h5> - <span> - {{ s__('FirstPushedBy|First') }} - <span class="commit-icon">${iconCommit}</span> - <a :href="commit.commitUrl" class="commit-hash-link commit-sha">{{ commit.shortSha }}</a> - {{ s__('FirstPushedBy|pushed by') }} - <a :href="commit.author.webUrl" class="commit-author-link"> - {{ commit.author.name }} - </a> - </span> - </div> - <div class="item-time"> - <total-time :time="commit.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue new file mode 100644 index 00000000000..75d2f1fd70c --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue @@ -0,0 +1,60 @@ +<script> + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import iconCommit from '../svg/icon_commit.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + userAvatarImage, + totalTime, + limitWarning, + }, + computed: { + iconCommit() { + return iconCommit; + }, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li + v-for="(commit, i) in items" + :key="i" + class="stage-event-item"> + <div class="item-details item-conmmit-component"> + <!-- FIXME: Pass an alt attribute here for accessibility --> + <user-avatar-image :img-src="commit.author.avatarUrl"/> + <h5 class="item-title commit-title"> + <a :href="commit.commitUrl"> + {{ commit.title }} + </a> + </h5> + <span> + {{ s__('FirstPushedBy|First') }} + <span class="commit-icon" v-html="iconCommit"></span> + <a :href="commit.commitUrl" class="commit-hash-link commit-sha">{{ commit.shortSha }}</a> + {{ s__('FirstPushedBy|pushed by') }} + <a :href="commit.author.webUrl" class="commit-author-link"> + {{ commit.author.name }} + </a> + </span> + </div> + <div class="item-time"> + <total-time :time="commit.totalTime" /> + </div> + </li> + </ul> + </div> +</template> + diff --git a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js deleted file mode 100644 index b7ba9360f70..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageProductionComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="issue in items" class="stage-event-item"> - <div class="item-details"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="issue.author.avatarUrl"/> - <h5 class="item-title issue-title"> - <a class="issue-title" :href="issue.url"> - {{ issue.title }} - </a> - </h5> - <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a> - · - <span> - {{ s__('OpenedNDaysAgo|Opened') }} - <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a> - </span> - <span> - {{ s__('ByAuthor|by') }} - <a :href="issue.author.webUrl" class="issue-author-link"> - {{ issue.author.name }} - </a> - </span> - </div> - <div class="item-time"> - <total-time :time="issue.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js deleted file mode 100644 index f41a0d0e4ff..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js +++ /dev/null @@ -1,62 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageReviewComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="mergeRequest in items" class="stage-event-item"> - <div class="item-details"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/> - <h5 class="item-title merge-merquest-title"> - <a :href="mergeRequest.url"> - {{ mergeRequest.title }} - </a> - </h5> - <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a> - · - <span> - {{ s__('OpenedNDaysAgo|Opened') }} - <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a> - </span> - <span> - {{ s__('ByAuthor|by') }} - <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a> - </span> - <template v-if="mergeRequest.state === 'closed'"> - <span class="merge-request-state"> - <i class="fa fa-ban"></i> - {{ mergeRequest.state.toUpperCase() }} - </span> - </template> - <template v-else> - <span class="merge-request-branch" v-if="mergeRequest.branch"> - <i class= "fa fa-code-fork"></i> - <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a> - </span> - </template> - </div> - <div class="item-time"> - <total-time :time="mergeRequest.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue new file mode 100644 index 00000000000..f54ea7df522 --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -0,0 +1,66 @@ +<script> + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + userAvatarImage, + totalTime, + limitWarning, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li + v-for="(mergeRequest, i) in items" + :key="i" + class="stage-event-item"> + <div class="item-details"> + <!-- FIXME: Pass an alt attribute here for accessibility --> + <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/> + <h5 class="item-title merge-merquest-title"> + <a :href="mergeRequest.url"> + {{ mergeRequest.title }} + </a> + </h5> + <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a> + · + <span> + {{ s__('OpenedNDaysAgo|Opened') }} + <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a> + </span> + <span> + {{ s__('ByAuthor|by') }} + <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a> + </span> + <template v-if="mergeRequest.state === 'closed'"> + <span class="merge-request-state"> + <i class="fa fa-ban"></i> + {{ mergeRequest.state.toUpperCase() }} + </span> + </template> + <template v-else> + <span class="merge-request-branch" v-if="mergeRequest.branch"> + <i class= "fa fa-code-fork"></i> + <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a> + </span> + </template> + </div> + <div class="item-time"> + <total-time :time="mergeRequest.totalTime"/> + </div> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js deleted file mode 100644 index d7c906c9d39..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; -import iconBranch from '../svg/icon_branch.svg'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageStagingComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - data() { - return { iconBranch }; - }, - components: { - userAvatarImage, - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="build in items" class="stage-event-item item-build-component"> - <div class="item-details"> - <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="build.author.avatarUrl"/> - <h5 class="item-title"> - <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> - <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> - <span class="icon-branch">${iconBranch}</span> - <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> - </h5> - <span> - <a :href="build.url" class="build-date">{{ build.date }}</a> - {{ s__('ByAuthor|by') }} - <a :href="build.author.webUrl" class="issue-author-link"> - {{ build.author.name }} - </a> - </span> - </div> - <div class="item-time"> - <total-time :time="build.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue new file mode 100644 index 00000000000..5d95ddcd90e --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue @@ -0,0 +1,59 @@ +<script> + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import iconBranch from '../svg/icon_branch.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + userAvatarImage, + totalTime, + limitWarning, + }, + computed: { + iconBranch() { + return iconBranch; + }, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li + v-for="(build, i) in items" + class="stage-event-item item-build-component" + :key="i"> + <div class="item-details"> + <!-- FIXME: Pass an alt attribute here for accessibility --> + <user-avatar-image :img-src="build.author.avatarUrl"/> + <h5 class="item-title"> + <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> + <i class="fa fa-code-fork"></i> + <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> + <span class="icon-branch" v-html="iconBranch"></span> + <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> + </h5> + <span> + <a :href="build.url" class="build-date">{{ build.date }}</a> + {{ s__('ByAuthor|by') }} + <a :href="build.author.webUrl" class="issue-author-link"> + {{ build.author.name }} + </a> + </span> + </div> + <div class="item-time"> + <total-time :time="build.totalTime"/> + </div> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js deleted file mode 100644 index 78cc97eea0b..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js +++ /dev/null @@ -1,49 +0,0 @@ -/* eslint-disable no-param-reassign */ -import Vue from 'vue'; -import iconBuildStatus from '../svg/icon_build_status.svg'; -import iconBranch from '../svg/icon_branch.svg'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.StageTestComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - data() { - return { iconBuildStatus, iconBranch }; - }, - template: ` - <div> - <div class="events-description"> - {{ stage.description }} - <limit-warning :count="items.length" /> - </div> - <ul class="stage-event-list"> - <li v-for="build in items" class="stage-event-item item-build-component"> - <div class="item-details"> - <h5 class="item-title"> - <span class="icon-build-status">${iconBuildStatus}</span> - <a :href="build.url" class="item-build-name">{{ build.name }}</a> - · - <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> - <i class="fa fa-code-fork"></i> - <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> - <span class="icon-branch">${iconBranch}</span> - <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> - </h5> - <span> - <a :href="build.url" class="issue-date"> - {{ build.date }} - </a> - </span> - </div> - <div class="item-time"> - <total-time :time="build.totalTime"></total-time> - </div> - </li> - </ul> - </div> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue new file mode 100644 index 00000000000..04d5440b77b --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue @@ -0,0 +1,60 @@ +<script> + import iconBuildStatus from '../svg/icon_build_status.svg'; + import iconBranch from '../svg/icon_branch.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; + + export default { + props: { + items: Array, + stage: Object, + }, + components: { + totalTime, + limitWarning, + }, + computed: { + iconBuildStatus() { + return iconBuildStatus; + }, + iconBranch() { + return iconBranch; + }, + }, + }; +</script> +<template> + <div> + <div class="events-description"> + {{ stage.description }} + <limit-warning :count="items.length" /> + </div> + <ul class="stage-event-list"> + <li + v-for="(build, i) in items" + :key="i" + class="stage-event-item item-build-component"> + <div class="item-details"> + <h5 class="item-title"> + <span class="icon-build-status" v-html="iconBuildStatus"></span> + <a :href="build.url" class="item-build-name">{{ build.name }}</a> + · + <a :href="build.url" class="pipeline-id">#{{ build.id }}</a> + <i class="fa fa-code-fork"></i> + <a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a> + <span class="icon-branch" v-html="iconBranch"></span> + <a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a> + </h5> + <span> + <a :href="build.url" class="issue-date"> + {{ build.date }} + </a> + </span> + </div> + <div class="item-time"> + <total-time :time="build.totalTime"/> + </div> + </li> + </ul> + </div> +</template> diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js b/app/assets/javascripts/cycle_analytics/components/total_time_component.js deleted file mode 100644 index d5e6167b2a8..00000000000 --- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable no-param-reassign */ - -import Vue from 'vue'; - -const global = window.gl || (window.gl = {}); -global.cycleAnalytics = global.cycleAnalytics || {}; - -global.cycleAnalytics.TotalTimeComponent = Vue.extend({ - props: { - time: Object, - }, - template: ` - <span class="total-time"> - <template v-if="Object.keys(time).length"> - <template v-if="time.days">{{ time.days }} <span>{{ n__('day', 'days', time.days) }}</span></template> - <template v-if="time.hours">{{ time.hours }} <span>{{ n__('Time|hr', 'Time|hrs', time.hours) }}</span></template> - <template v-if="time.mins && !time.days">{{ time.mins }} <span>{{ n__('Time|min', 'Time|mins', time.mins) }}</span></template> - <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>{{ s__('Time|s') }}</span></template> - </template> - <template v-else> - -- - </template> - </span> - `, -}); diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.vue b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue new file mode 100644 index 00000000000..9941b997b3f --- /dev/null +++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue @@ -0,0 +1,29 @@ +<script> + export default { + props: { + time: { + type: Object, + required: false, + default: () => ({}), + }, + }, + computed: { + hasData() { + return Object.keys(this.time).length; + }, + }, + }; +</script> +<template> + <span class="total-time"> + <template v-if="hasData"> + <template v-if="time.days">{{ time.days }} <span>{{ n__('day', 'days', time.days) }}</span></template> + <template v-if="time.hours">{{ time.hours }} <span>{{ n__('Time|hr', 'Time|hrs', time.hours) }}</span></template> + <template v-if="time.mins && !time.days">{{ time.mins }} <span>{{ n__('Time|min', 'Time|mins', time.mins) }}</span></template> + <template v-if="time.seconds && hasDa === 1 || time.seconds === 0">{{ time.seconds }} <span>{{ s__('Time|s') }}</span></template> + </template> + <template v-else> + -- + </template> + </span> +</template> diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index 5f1221c4c49..991fcf114da 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -3,15 +3,12 @@ import Vue from 'vue'; import Cookies from 'js-cookie'; import Translate from '../vue_shared/translate'; -import LimitWarningComponent from './components/limit_warning_component'; -import './components/stage_code_component'; -import './components/stage_issue_component'; -import './components/stage_plan_component'; -import './components/stage_production_component'; -import './components/stage_review_component'; -import './components/stage_staging_component'; -import './components/stage_test_component'; -import './components/total_time_component'; +import stageCodeComponent from './components/stage_code_component.vue'; +import stagePlanComponent from './components/stage_plan_component.vue'; +import stageComponent from './components/stage_component.vue'; +import stageReviewComponent from './components/stage_review_component.vue'; +import stageStagingComponent from './components/stage_staging_component.vue'; +import stageTestComponent from './components/stage_test_component.vue'; import CycleAnalyticsService from './cycle_analytics_service'; import CycleAnalyticsStore from './cycle_analytics_store'; @@ -47,13 +44,13 @@ $(() => { }, }, components: { - 'stage-issue-component': gl.cycleAnalytics.StageIssueComponent, - 'stage-plan-component': gl.cycleAnalytics.StagePlanComponent, - 'stage-code-component': gl.cycleAnalytics.StageCodeComponent, - 'stage-test-component': gl.cycleAnalytics.StageTestComponent, - 'stage-review-component': gl.cycleAnalytics.StageReviewComponent, - 'stage-staging-component': gl.cycleAnalytics.StageStagingComponent, - 'stage-production-component': gl.cycleAnalytics.StageProductionComponent, + 'stage-issue-component': stageComponent, + 'stage-plan-component': stagePlanComponent, + 'stage-code-component': stageCodeComponent, + 'stage-test-component': stageTestComponent, + 'stage-review-component': stageReviewComponent, + 'stage-staging-component': stageStagingComponent, + 'stage-production-component': stageComponent, }, created() { this.fetchCycleAnalyticsData(); @@ -134,8 +131,4 @@ $(() => { }, }, }); - - // Register global components - Vue.component('limit-warning', LimitWarningComponent); - Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent); }); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 31214818496..bbaa4e4d91e 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -14,7 +14,6 @@ /* global NotificationsDropdown */ /* global GroupAvatar */ /* global LineHighlighter */ -/* global ProjectFork */ /* global BuildArtifacts */ /* global GroupsSelect */ /* global Search */ @@ -476,7 +475,9 @@ import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils'; shortcut_handler = true; break; case 'projects:forks:new': - new ProjectFork(); + import(/* webpackChunkName: 'project_fork' */ './project_fork') + .then(fork => fork.default()) + .catch(() => {}); break; case 'projects:artifacts:browse': new ShortcutsNavigation(); diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue index 14fde1afb16..ce5f6219a3e 100644 --- a/app/assets/javascripts/environments/components/environment.vue +++ b/app/assets/javascripts/environments/components/environment.vue @@ -163,7 +163,7 @@ export default { this.service.postAction(endpoint) .then(() => this.fetchEnvironments()) - .catch(() => new Flash('An error occured while making the request.')); + .catch(() => new Flash('An error occurred while making the request.')); } }, diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue index 35891240239..01e70c0bbb7 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.vue +++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue @@ -158,7 +158,7 @@ export default { this.service.postAction(endpoint) .then(() => this.fetchEnvironments()) - .catch(() => new Flash('An error occured while making the request.')); + .catch(() => new Flash('An error occurred while making the request.')); } }, }, diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index d02e4cd5876..a00d29a845a 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -7,6 +7,8 @@ * causes reflows, visit https://gist.github.com/paulirish/5d52fb081b3570c81e3a */ +import Cookies from 'js-cookie'; + const LINE_NUMBER_CLASS = 'diff-line-num'; const UNFOLDABLE_LINE_CLASS = 'js-unfold'; const NO_COMMENT_CLASS = 'no-comment-btn'; @@ -27,9 +29,7 @@ export default { this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === ''; } - if (typeof notes !== 'undefined' && !this.isParallelView) { - this.isParallelView = notes.isParallelView && notes.isParallelView(); - } + this.isParallelView = Cookies.get('diff_view') === 'parallel'; if (this.userCanCreateNote) { $diffFile.on('mouseover', LINE_COLUMN_CLASSES, e => this.showButton(this.isParallelView, e)) diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js index f9bbbf0cbc1..ada14d2053c 100644 --- a/app/assets/javascripts/filtered_search/dropdown_emoji.js +++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js @@ -14,7 +14,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { loadingTemplate: this.loadingTemplate, onError() { /* eslint-disable no-new */ - new Flash('An error occured fetching the dropdown data.'); + new Flash('An error occurred fetching the dropdown data.'); /* eslint-enable no-new */ }, }, diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js index 0bc4b6f22a9..b32d589481d 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js @@ -17,7 +17,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { preprocessing, onError() { /* eslint-disable no-new */ - new Flash('An error occured fetching the dropdown data.'); + new Flash('An error occurred fetching the dropdown data.'); /* eslint-enable no-new */ }, }, diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index 720fbc87ea0..ce8817b1b2e 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -26,7 +26,7 @@ class DropdownUser extends gl.FilteredSearchDropdown { }, onError() { /* eslint-disable no-new */ - new Flash('An error occured fetching the dropdown data.'); + new Flash('An error occurred fetching the dropdown data.'); /* eslint-enable no-new */ }, }, diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 9178fec085a..a44dc279a6f 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -36,7 +36,7 @@ class FilteredSearchManager { .catch((error) => { if (error.name === 'RecentSearchesServiceError') return undefined; // eslint-disable-next-line no-new - new window.Flash('An error occured while parsing recent searches'); + new window.Flash('An error occurred while parsing recent searches'); // Gracefully fail to empty array return []; }) diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index 70c364e51fe..1d305f1eb2f 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -67,10 +67,13 @@ const PARTICIPANTS_ROW_COUNT = 7; originalText = $(this).data("original-text"); if (currentText === originalText) { $(this).text(lessText); + + if (gl.lazyLoader) gl.lazyLoader.loadCheck(); } else { $(this).text(originalText); } - return $(".js-participants-hidden").toggle(); + + $(".js-participants-hidden").toggle(); }; return IssuableContext; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index ea2d61af9be..423a25fbdfa 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -71,6 +71,7 @@ export const handleLocationHash = () => { // This is required to handle non-unicode characters in hash hash = decodeURIComponent(hash); + const target = document.getElementById(hash) || document.getElementById(`user-content-${hash}`); const fixedTabs = document.querySelector('.js-tabs-affix'); const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck'); const fixedNav = document.querySelector('.navbar-gitlab'); @@ -78,25 +79,19 @@ export const handleLocationHash = () => { let adjustment = 0; if (fixedNav) adjustment -= fixedNav.offsetHeight; - // scroll to user-generated markdown anchor if we cannot find a match - if (document.getElementById(hash) === null) { - const target = document.getElementById(`user-content-${hash}`); - if (target && target.scrollIntoView) { - target.scrollIntoView(true); - window.scrollBy(0, adjustment); - } - } else { - // only adjust for fixedTabs when not targeting user-generated content - if (fixedTabs) { - adjustment -= fixedTabs.offsetHeight; - } + if (target && target.scrollIntoView) { + target.scrollIntoView(true); + } - if (fixedDiffStats) { - adjustment -= fixedDiffStats.offsetHeight; - } + if (fixedTabs) { + adjustment -= fixedTabs.offsetHeight; + } - window.scrollBy(0, adjustment); + if (fixedDiffStats) { + adjustment -= fixedDiffStats.offsetHeight; } + + window.scrollBy(0, adjustment); }; // Check if element scrolled into viewport from above or below diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 1d1763c3963..29fc91733b3 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -55,7 +55,7 @@ window.dateFormat = dateFormat; if (!timeagoInstance) { const localeRemaining = function(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], @@ -73,7 +73,7 @@ window.dateFormat = dateFormat; }; locale = function(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js index 283c0ec0410..64db42701ce 100644 --- a/app/assets/javascripts/lib/utils/sticky.js +++ b/app/assets/javascripts/lib/utils/sticky.js @@ -1,14 +1,34 @@ -export const isSticky = (el, scrollY, stickyTop) => { +export const createPlaceholder = () => { + const placeholder = document.createElement('div'); + placeholder.classList.add('sticky-placeholder'); + + return placeholder; +}; + +export const isSticky = (el, scrollY, stickyTop, insertPlaceholder) => { const top = Math.floor(el.offsetTop - scrollY); - if (top <= stickyTop) { + if (top <= stickyTop && !el.classList.contains('is-stuck')) { + const placeholder = insertPlaceholder ? createPlaceholder() : null; + const heightBefore = el.offsetHeight; + el.classList.add('is-stuck'); - } else { + + if (insertPlaceholder) { + el.parentNode.insertBefore(placeholder, el.nextElementSibling); + + placeholder.style.height = `${heightBefore - el.offsetHeight}px`; + } + } else if (top > stickyTop && el.classList.contains('is-stuck')) { el.classList.remove('is-stuck'); + + if (insertPlaceholder && el.nextElementSibling && el.nextElementSibling.classList.contains('sticky-placeholder')) { + el.nextElementSibling.remove(); + } } }; -export default (el) => { +export default (el, insertPlaceholder = true) => { if (!el) return; const computedStyle = window.getComputedStyle(el); @@ -17,7 +37,7 @@ export default (el) => { const stickyTop = parseInt(computedStyle.top, 10); - document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop), { + document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop, insertPlaceholder), { passive: true, }); }; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index 7400c22543f..a16d00b5cef 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -28,148 +28,149 @@ // </div> // </div> // -(function() { - this.LineHighlighter = (function() { - // CSS class applied to highlighted lines - LineHighlighter.prototype.highlightClass = 'hll'; - - // Internal copy of location.hash so we're not dependent on `location` in tests - LineHighlighter.prototype._hash = ''; - - function LineHighlighter(hash) { - if (hash == null) { - // Initialize a LineHighlighter object - // - // hash - String URL hash for dependency injection in tests - hash = location.hash; - } - this.setHash = this.setHash.bind(this); - this.highlightLine = this.highlightLine.bind(this); - this.clickHandler = this.clickHandler.bind(this); - this.highlightHash = this.highlightHash.bind(this); - this._hash = hash; - this.bindEvents(); - this.highlightHash(); - } - LineHighlighter.prototype.bindEvents = function() { - const $fileHolder = $('.file-holder'); - $fileHolder.on('click', 'a[data-line-number]', this.clickHandler); - $fileHolder.on('highlight:line', this.highlightHash); - }; - - LineHighlighter.prototype.highlightHash = function() { - var range; - if (this._hash !== '') { - range = this.hashToRange(this._hash); - if (range[0]) { - this.highlightRange(range); - $.scrollTo("#L" + range[0], { - // Scroll to the first highlighted line on initial load - // Offset -50 for the sticky top bar, and another -100 for some context - offset: -150 - }); - } - } - }; - - LineHighlighter.prototype.clickHandler = function(event) { - var current, lineNumber, range; - event.preventDefault(); - this.clearHighlight(); - lineNumber = $(event.target).closest('a').data('line-number'); - current = this.hashToRange(this._hash); - if (!(current[0] && event.shiftKey)) { - // If there's no current selection, or there is but Shift wasn't held, - // treat this like a single-line selection. - this.setHash(lineNumber); - return this.highlightLine(lineNumber); - } else if (event.shiftKey) { - if (lineNumber < current[0]) { - range = [lineNumber, current[0]]; - } else { - range = [current[0], lineNumber]; - } - this.setHash(range[0], range[1]); - return this.highlightRange(range); - } - }; - - LineHighlighter.prototype.clearHighlight = function() { - return $("." + this.highlightClass).removeClass(this.highlightClass); - // Unhighlight previously highlighted lines - }; - - // Convert a URL hash String into line numbers - // - // hash - Hash String - // - // Examples: - // - // hashToRange('#L5') # => [5, null] - // hashToRange('#L5-15') # => [5, 15] - // hashToRange('#foo') # => [null, null] - // - // Returns an Array - LineHighlighter.prototype.hashToRange = function(hash) { - var first, last, matches; - // ?L(\d+)(?:-(\d+))?$/) - matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/); - if (matches && matches.length) { - first = parseInt(matches[1], 10); - last = matches[2] ? parseInt(matches[2], 10) : null; - return [first, last]; - } else { - return [null, null]; - } - }; - - // Highlight a single line - // - // lineNumber - Line number to highlight - LineHighlighter.prototype.highlightLine = function(lineNumber) { - return $("#LC" + lineNumber).addClass(this.highlightClass); - }; - - // Highlight all lines within a range - // - // range - Array containing the starting and ending line numbers - LineHighlighter.prototype.highlightRange = function(range) { - var i, lineNumber, ref, ref1, results; - if (range[1]) { - results = []; - for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) { - results.push(this.highlightLine(lineNumber)); - } - return results; - } else { - return this.highlightLine(range[0]); - } - }; +const LineHighlighter = function(options = {}) { + options.highlightLineClass = options.highlightLineClass || 'hll'; + options.fileHolderSelector = options.fileHolderSelector || '.file-holder'; + options.scrollFileHolder = options.scrollFileHolder || false; + options.hash = options.hash || location.hash; - // Set the URL hash string - LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) { - var hash; - if (lastLineNumber) { - hash = "#L" + firstLineNumber + "-" + lastLineNumber; + this.options = options; + this._hash = options.hash; + this.highlightLineClass = options.highlightLineClass; + this.setHash = this.setHash.bind(this); + this.highlightLine = this.highlightLine.bind(this); + this.clickHandler = this.clickHandler.bind(this); + this.highlightHash = this.highlightHash.bind(this); + + this.bindEvents(); + this.highlightHash(); +}; + +LineHighlighter.prototype.bindEvents = function() { + const $fileHolder = $(this.options.fileHolderSelector); + + $fileHolder.on('click', 'a[data-line-number]', this.clickHandler); + $fileHolder.on('highlight:line', this.highlightHash); +}; + +LineHighlighter.prototype.highlightHash = function() { + var range; + + if (this._hash !== '') { + range = this.hashToRange(this._hash); + + if (range[0]) { + this.highlightRange(range); + const lineSelector = `#L${range[0]}`; + const scrollOptions = { + // Scroll to the first highlighted line on initial load + // Offset -50 for the sticky top bar, and another -100 for some context + offset: -150 + }; + if (this.options.scrollFileHolder) { + $(this.options.fileHolderSelector).scrollTo(lineSelector, scrollOptions); } else { - hash = "#L" + firstLineNumber; + $.scrollTo(lineSelector, scrollOptions); } - this._hash = hash; - return this.__setLocationHash__(hash); - }; - - // Make the actual hash change in the browser - // - // This method is stubbed in tests. - LineHighlighter.prototype.__setLocationHash__ = function(value) { - return history.pushState({ - url: value - // We're using pushState instead of assigning location.hash directly to - // prevent the page from scrolling on the hashchange event - }, document.title, value); - }; - - return LineHighlighter; - })(); -}).call(window); + } + } +}; + +LineHighlighter.prototype.clickHandler = function(event) { + var current, lineNumber, range; + event.preventDefault(); + this.clearHighlight(); + lineNumber = $(event.target).closest('a').data('line-number'); + current = this.hashToRange(this._hash); + if (!(current[0] && event.shiftKey)) { + // If there's no current selection, or there is but Shift wasn't held, + // treat this like a single-line selection. + this.setHash(lineNumber); + return this.highlightLine(lineNumber); + } else if (event.shiftKey) { + if (lineNumber < current[0]) { + range = [lineNumber, current[0]]; + } else { + range = [current[0], lineNumber]; + } + this.setHash(range[0], range[1]); + return this.highlightRange(range); + } +}; + +LineHighlighter.prototype.clearHighlight = function() { + return $("." + this.highlightLineClass).removeClass(this.highlightLineClass); +}; + +// Convert a URL hash String into line numbers +// +// hash - Hash String +// +// Examples: +// +// hashToRange('#L5') # => [5, null] +// hashToRange('#L5-15') # => [5, 15] +// hashToRange('#foo') # => [null, null] +// +// Returns an Array +LineHighlighter.prototype.hashToRange = function(hash) { + var first, last, matches; + // ?L(\d+)(?:-(\d+))?$/) + matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/); + if (matches && matches.length) { + first = parseInt(matches[1], 10); + last = matches[2] ? parseInt(matches[2], 10) : null; + return [first, last]; + } else { + return [null, null]; + } +}; + +// Highlight a single line +// +// lineNumber - Line number to highlight +LineHighlighter.prototype.highlightLine = function(lineNumber) { + return $("#LC" + lineNumber).addClass(this.highlightLineClass); +}; + +// Highlight all lines within a range +// +// range - Array containing the starting and ending line numbers +LineHighlighter.prototype.highlightRange = function(range) { + var i, lineNumber, ref, ref1, results; + if (range[1]) { + results = []; + for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) { + results.push(this.highlightLine(lineNumber)); + } + return results; + } else { + return this.highlightLine(range[0]); + } +}; + +// Set the URL hash string +LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) { + var hash; + if (lastLineNumber) { + hash = "#L" + firstLineNumber + "-" + lastLineNumber; + } else { + hash = "#L" + firstLineNumber; + } + this._hash = hash; + return this.__setLocationHash__(hash); +}; + +// Make the actual hash change in the browser +// +// This method is stubbed in tests. +LineHighlighter.prototype.__setLocationHash__ = function(value) { + return history.pushState({ + url: value + // We're using pushState instead of assigning location.hash directly to + // prevent the page from scrolling on the hashchange event + }, document.title, value); +}; + +window.LineHighlighter = LineHighlighter; diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js index 7ba676d6d20..6a5084efeb8 100644 --- a/app/assets/javascripts/locale/index.js +++ b/app/assets/javascripts/locale/index.js @@ -16,9 +16,8 @@ const locales = allLocales.reduce((d, obj) => { return data; }, {}); -let lang = document.querySelector('html').getAttribute('lang') || 'en'; -lang = lang.replace(/-/g, '_'); - +const langAttribute = document.querySelector('html').getAttribute('lang'); +const lang = (langAttribute || 'en').replace(/-/g, '_'); const locale = new Jed(locales[lang]); /** diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index ec001b9b31c..24abc5c5c9e 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -124,7 +124,6 @@ import './preview_markdown'; import './project'; import './project_avatar'; import './project_find_file'; -import './project_fork'; import './project_import'; import './project_label_subscription'; import './project_new'; @@ -302,7 +301,10 @@ $(function () { return $container.remove(); // Commit show suppressed diff }); - $('.navbar-toggle').on('click', () => $('.header-content').toggleClass('menu-expanded')); + $('.navbar-toggle').on('click', () => { + $('.header-content').toggleClass('menu-expanded'); + gl.lazyLoader.loadCheck(); + }); // Show/hide comments on diff $body.on('click', '.js-toggle-diff-comments', function (e) { var $this = $(this); diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 8ae127776e8..d3299c15720 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -352,7 +352,7 @@ import { } expandViewContainer() { - const $wrapper = $('.content-wrapper .container-fluid'); + const $wrapper = $('.content-wrapper .container-fluid').not('.breadcrumbs'); if (this.fixedLayoutPref === null) { this.fixedLayoutPref = $wrapper.hasClass('container-limited'); } diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/issue_note.vue index 3483f6c7538..1f43b8a16ad 100644 --- a/app/assets/javascripts/notes/components/issue_note.vue +++ b/app/assets/javascripts/notes/components/issue_note.vue @@ -62,7 +62,7 @@ }, deleteHandler() { // eslint-disable-next-line no-alert - if (confirm('Are you sure you want to delete this list?')) { + if (confirm('Are you sure you want to delete this comment?')) { this.isDeleting = true; this.deleteNote(this.note) diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index f0b44dfa6d8..76b97af39f1 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -28,8 +28,7 @@ popoverOptions() { return { html: true, - delay: { hide: 600 }, - trigger: 'hover', + trigger: 'focus', placement: 'top', title: '<div class="autodevops-title">This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b></div>', content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow">Learn more about Auto DevOps</a>`, @@ -75,6 +74,7 @@ </span> <a v-if="pipeline.flags.auto_devops" + tabindex="0" class="js-pipeline-url-autodevops label label-info autodevops-badge" v-popover="popoverOptions" role="button"> diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js index 9adc15e6266..e97f5632dc8 100644 --- a/app/assets/javascripts/pipelines/mixins/pipelines.js +++ b/app/assets/javascripts/pipelines/mixins/pipelines.js @@ -97,7 +97,7 @@ export default { postAction(endpoint) { this.service.postAction(endpoint) .then(() => eventHub.$emit('refreshPipelines')) - .catch(() => new Flash('An error occured while making the request.')); + .catch(() => new Flash('An error occurred while making the request.')); }, }, }; diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index 291ae24aa68..4bdda611cfc 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -73,7 +73,8 @@ import _ from 'underscore'; aspectRatio: 1, modal: true, scalable: false, - rotatable: false, + rotatable: true, + checkOrientation: true, zoomable: true, dragMode: 'move', guides: false, diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js index 47197db39d3..68cf47fd54e 100644 --- a/app/assets/javascripts/project_fork.js +++ b/app/assets/javascripts/project_fork.js @@ -1,13 +1,8 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */ -(function() { - this.ProjectFork = (function() { - function ProjectFork() { - $('.fork-thumbnail a').on('click', function() { - $('.fork-namespaces').hide(); - return $('.save-project-loader').show(); - }); - } +export default () => { + $('.fork-thumbnail a').on('click', function forkThumbnailClicked() { + if ($(this).hasClass('disabled')) return false; - return ProjectFork; - })(); -}).call(window); + $('.fork-namespaces').hide(); + return $('.save-project-loader').show(); + }); +}; diff --git a/app/assets/javascripts/projects_dropdown/service/projects_service.js b/app/assets/javascripts/projects_dropdown/service/projects_service.js index fad956b4c26..9cbd8f21f2a 100644 --- a/app/assets/javascripts/projects_dropdown/service/projects_service.js +++ b/app/assets/javascripts/projects_dropdown/service/projects_service.js @@ -19,7 +19,7 @@ export default class ProjectsService { getSearchedProjects(searchQuery) { return this.projectsPath.get({ - simple: false, + simple: true, per_page: 20, membership: !!gon.current_user_id, order_by: 'last_activity_at', diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue index 1282828b504..119e38c583d 100644 --- a/app/assets/javascripts/repo/components/repo_commit_section.vue +++ b/app/assets/javascripts/repo/components/repo_commit_section.vue @@ -37,14 +37,14 @@ export default { content: f.newContent, })); const payload = { - branch: Store.targetBranch, + branch: Store.currentBranch, commit_message: commitMessage, actions, }; Store.submitCommitsLoading = true; Service.commitFiles(payload) .then(this.resetCommitState) - .catch(() => Flash('An error occured while committing your changes')); + .catch(() => Flash('An error occurred while committing your changes')); }, resetCommitState() { @@ -105,7 +105,7 @@ export default { </label> <div class="col-md-6"> <span class="help-block"> - {{targetBranch}} + {{currentBranch}} </span> </div> </div> diff --git a/app/assets/javascripts/repo/components/repo_edit_button.vue b/app/assets/javascripts/repo/components/repo_edit_button.vue index 29b76975561..353142edeb7 100644 --- a/app/assets/javascripts/repo/components/repo_edit_button.vue +++ b/app/assets/javascripts/repo/components/repo_edit_button.vue @@ -26,16 +26,6 @@ export default { this.editMode = !this.editMode; Store.toggleBlobView(); }, - toggleProjectRefsForm() { - $('.project-refs-form').toggleClass('disabled', this.editMode); - $('.js-tree-ref-target-holder').toggle(this.editMode); - }, - }, - - watch: { - editMode() { - this.toggleProjectRefsForm(); - }, }, }; </script> diff --git a/app/assets/javascripts/repo/components/repo_file.vue b/app/assets/javascripts/repo/components/repo_file.vue index 20ebf840774..8b9cbd23456 100644 --- a/app/assets/javascripts/repo/components/repo_file.vue +++ b/app/assets/javascripts/repo/components/repo_file.vue @@ -95,7 +95,7 @@ export default RepoFile; </div> </td> - <td class="hidden-xs"> + <td class="hidden-xs text-right"> <span class="commit-update" :title="tooltipTitle(file.lastCommitUpdate)"> diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/repo/components/repo_preview.vue index 2200754cbef..2fe369a4693 100644 --- a/app/assets/javascripts/repo/components/repo_preview.vue +++ b/app/assets/javascripts/repo/components/repo_preview.vue @@ -1,23 +1,27 @@ <script> +/* global LineHighlighter */ + import Store from '../stores/repo_store'; export default { data: () => Store, - mounted() { - this.highlightFile(); - }, computed: { html() { return this.activeFile.html; }, }, - methods: { highlightFile() { $(this.$el).find('.file-content').syntaxHighlight(); }, }, - + mounted() { + this.highlightFile(); + this.lineHighlighter = new LineHighlighter({ + fileHolderSelector: '.blob-viewer-container', + scrollFileHolder: true, + }); + }, watch: { html() { this.$nextTick(() => { @@ -45,7 +49,7 @@ export default { v-else class="vertical-center render-error"> <p class="text-center"> - The source could not be displayed because a rendering error occured. You can <a :href="activeFile.raw_path">download</a> it instead. + The source could not be displayed because a rendering error occurred. You can <a :href="activeFile.raw_path">download</a> it instead. </p> </div> </div> diff --git a/app/assets/javascripts/repo/components/repo_sidebar.vue b/app/assets/javascripts/repo/components/repo_sidebar.vue index 3414128526d..1e40814b95f 100644 --- a/app/assets/javascripts/repo/components/repo_sidebar.vue +++ b/app/assets/javascripts/repo/components/repo_sidebar.vue @@ -37,17 +37,24 @@ export default { let file = clickedFile; if (file.loading) return; file.loading = true; + if (file.type === 'tree' && file.opened) { file = Store.removeChildFilesOfTree(file); file.loading = false; } else { - Service.url = file.url; - Helper.getContent(file) - .then(() => { - file.loading = false; - Helper.scrollTabsRight(); - }) - .catch(Helper.loadingError); + const openFile = Helper.getFileFromPath(file.url); + if (openFile) { + file.loading = false; + Store.setActiveFiles(openFile); + } else { + Service.url = file.url; + Helper.getContent(file) + .then(() => { + file.loading = false; + Helper.scrollTabsRight(); + }) + .catch(Helper.loadingError); + } } }, @@ -68,7 +75,7 @@ export default { <tr> <th class="name">Name</th> <th class="hidden-sm hidden-xs last-commit">Last Commit</th> - <th class="hidden-xs last-update">Last Update</th> + <th class="hidden-xs last-update text-right">Last Update</th> </tr> </thead> <tbody> diff --git a/app/assets/javascripts/repo/helpers/repo_helper.js b/app/assets/javascripts/repo/helpers/repo_helper.js index 2bd8d7eea65..ac59a2bed23 100644 --- a/app/assets/javascripts/repo/helpers/repo_helper.js +++ b/app/assets/javascripts/repo/helpers/repo_helper.js @@ -58,13 +58,13 @@ const RepoHelper = { return langs.find(lang => lang.extensions && lang.extensions.indexOf(`.${ext}`) > -1); }, - setDirectoryOpen(tree) { + setDirectoryOpen(tree, title) { const file = tree; if (!file) return undefined; file.opened = true; file.icon = 'fa-folder-open'; - RepoHelper.updateHistoryEntry(file.url, file.name); + RepoHelper.updateHistoryEntry(file.url, title); return file; }, @@ -135,6 +135,8 @@ const RepoHelper = { return Service.getContent() .then((response) => { const data = response.data; + if (response.headers && response.headers['page-title']) data.pageTitle = response.headers['page-title']; + Store.isTree = RepoHelper.isTree(data); if (!Store.isTree) { if (!file) file = data; @@ -168,7 +170,7 @@ const RepoHelper = { } else { // it's a tree if (!file) Store.isRoot = RepoHelper.isRoot(Service.url); - file = RepoHelper.setDirectoryOpen(file); + file = RepoHelper.setDirectoryOpen(file, data.pageTitle || data.name); const newDirectory = RepoHelper.dataToListOfFiles(data); Store.addFilesToDirectory(file, Store.files, newDirectory); Store.prevURL = Service.blobURLtoParentTree(Service.url); @@ -178,8 +180,8 @@ const RepoHelper = { setFile(data, file) { const newFile = data; + newFile.url = file.url || Service.url; // Grab the URL from service, happens on page refresh. - newFile.url = file.url; if (newFile.render_error === 'too_large' || newFile.render_error === 'collapsed') { newFile.tooLarge = true; } @@ -255,7 +257,7 @@ const RepoHelper = { history.pushState({ key: RepoHelper.key }, '', url); if (title) { - document.title = `${title} · GitLab`; + document.title = title; } }, @@ -263,6 +265,10 @@ const RepoHelper = { return Store.openedFiles.find(openedFile => Store.activeFile.url === openedFile.url); }, + getFileFromPath(path) { + return Store.openedFiles.find(file => file.url === path); + }, + loadingError() { Flash('Unable to load this content at this time.'); }, diff --git a/app/assets/javascripts/repo/index.js b/app/assets/javascripts/repo/index.js index 6c1d468e937..7d0123e3d3a 100644 --- a/app/assets/javascripts/repo/index.js +++ b/app/assets/javascripts/repo/index.js @@ -11,10 +11,6 @@ function initDropdowns() { } function addEventsForNonVueEls() { - $(document).on('change', '.dropdown', () => { - Store.targetBranch = $('.project-refs-target-form input[name="ref"]').val(); - }); - window.onbeforeunload = function confirmUnload(e) { const hasChanged = Store.openedFiles .some(file => file.changed); diff --git a/app/assets/javascripts/repo/stores/repo_store.js b/app/assets/javascripts/repo/stores/repo_store.js index 1c0df528aea..9a4fc40bc69 100644 --- a/app/assets/javascripts/repo/stores/repo_store.js +++ b/app/assets/javascripts/repo/stores/repo_store.js @@ -32,7 +32,6 @@ const RepoStore = { isCommitable: false, binary: false, currentBranch: '', - targetBranch: 'new-branch', commitMessage: '', binaryTypes: { png: false, @@ -84,7 +83,7 @@ const RepoStore = { }).catch(Helper.loadingError); } - if (!file.loading) Helper.updateHistoryEntry(file.url, file.name); + if (!file.loading) Helper.updateHistoryEntry(file.url, file.pageTitle || file.name); RepoStore.binary = file.binary; }, diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index a4eae135403..a41548bd694 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -29,28 +29,32 @@ import Cookies from 'js-cookie'; $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); - $document.on('click', '.js-sidebar-toggle', function(e, triggered) { - var $allGutterToggleIcons, $this, $thisIcon; - e.preventDefault(); - $this = $(this); - $thisIcon = $this.find('i'); - $allGutterToggleIcons = $('.js-sidebar-toggle i'); - if ($thisIcon.hasClass('fa-angle-double-right')) { - $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); - $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - } else { - $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); - $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - } - if (!triggered) { - return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); - } - }); + $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); }; + Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { + var $allGutterToggleIcons, $this, $thisIcon; + e.preventDefault(); + $this = $(this); + $thisIcon = $this.find('i'); + $allGutterToggleIcons = $('.js-sidebar-toggle i'); + if ($thisIcon.hasClass('fa-angle-double-right')) { + $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); + $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + } else { + $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); + $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + + if (gl.lazyLoader) gl.lazyLoader.loadCheck(); + } + if (!triggered) { + Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); + } + }; + Sidebar.prototype.toggleTodo = function(e) { var $btnText, $this, $todoLoading, ajaxType, url; $this = $(e.currentTarget); diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 38c9a71dd20..f15452ec683 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -287,6 +287,7 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '. onClearInputClick(e) { e.preventDefault(); + this.wrap.toggleClass('has-value', !!e.target.value); return this.searchInput.val('').focus(); } diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js index 1c15a1b877a..3c9de02407e 100644 --- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js +++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js @@ -38,7 +38,7 @@ class SidebarMoveIssue { data: (searchTerm, callback) => { this.mediator.fetchAutocompleteProjects(searchTerm) .then(callback) - .catch(() => new Flash('An error occured while fetching projects autocomplete.')); + .catch(() => new Flash('An error occurred while fetching projects autocomplete.')); }, renderRow: project => ` <li> @@ -73,7 +73,7 @@ class SidebarMoveIssue { this.mediator.moveIssue() .catch(() => { - Flash('An error occured while moving the issue.'); + Flash('An error occurred while moving the issue.'); this.$confirmButton .enable() .removeClass('is-loading'); diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index e38a8db4cc5..2fe6e5b31f0 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -41,7 +41,7 @@ export default class SidebarMediator { this.store.setAssigneeData(data); this.store.setTimeTrackingData(data); }) - .catch(() => new Flash('Error occured when fetching sidebar data')); + .catch(() => new Flash('Error occurred when fetching sidebar data')); } fetchAutocompleteProjects(searchTerm) { diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 923d14f2c3d..74b846217bb 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -31,10 +31,12 @@ @import "framework/mobile"; @import "framework/modal"; @import "framework/nav"; +@import "framework/new-nav"; @import "framework/pagination"; @import "framework/panels"; @import "framework/selects"; @import "framework/sidebar"; +@import "framework/new-sidebar"; @import "framework/tables"; @import "framework/notes"; @import "framework/timeline"; diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 5c68059f485..1d72a70f0f5 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -260,7 +260,7 @@ position: relative; border: 1px solid $blue-300; border-radius: $border-radius-default; - background-color: $blue-25; + background-color: $blue-50; justify-content: center; .dismiss-button { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 2bcd23a15e6..c0d8e6c328c 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -779,6 +779,14 @@ white-space: normal; width: 100%; + &.dropdown-menu-user-link { + white-space: nowrap; + + .dropdown-menu-user-username { + display: block; + } + } + // make sure the text color is not overriden &.text-danger { color: $brand-danger; diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 7fa58b11d9d..6b69e8018be 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -126,7 +126,7 @@ .search-input-wrap { .search-icon, .clear-icon { - color: rgba($color-200, .8); + fill: rgba($color-200, .8); } } @@ -141,7 +141,7 @@ .search-input-wrap { .search-icon { - color: rgba($color-200, .8); + fill: rgba($color-200, .8); } } } @@ -242,17 +242,21 @@ body { &:hover { background-color: $white-light; - box-shadow: inset 0 0 0 1px $blue-100; + box-shadow: inset 0 0 0 1px $blue-200; .location-badge { - box-shadow: inset 0 0 0 1px $blue-100; + box-shadow: inset 0 0 0 1px $blue-200; } } } .search-input-wrap { .search-icon { - color: $theme-gray-200; + fill: $theme-gray-200; + } + + .search-input { + color: $gl-text-color; } } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index ab3c34df1fb..d932ea8794f 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -109,8 +109,7 @@ header { .user-counter { svg { - height: 16px; - width: 23px; + margin-right: 3px; } } @@ -133,16 +132,16 @@ header { } &.navbar-gitlab-new { - .fa-times { + .close-icon { display: none; } .menu-expanded { - .fa-ellipsis-v { + .more-icon { display: none; } - .fa-times { + .close-icon { display: block; } } diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index 60d61c68d63..59bfc5a8d77 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -27,7 +27,9 @@ } svg { + &.s8 { @include svg-size(8px); } &.s16 { @include svg-size(16px); } + &.s18 { @include svg-size(18px); } &.s24 { @include svg-size(24px); } &.s32 { @include svg-size(32px); } &.s48 { @include svg-size(48px); } diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index d40b65bb2cc..2fee2164190 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -142,5 +142,41 @@ } @mixin green-status-color { - @include status-color($green-50, $green-500, $green-700); + @include status-color($green-100, $green-500, $green-700); +} + +@mixin fade($gradient-direction, $gradient-color) { + visibility: hidden; + opacity: 0; + z-index: 2; + position: absolute; + bottom: 12px; + width: 43px; + height: 30px; + transition-duration: .3s; + -webkit-transform: translateZ(0); + background: linear-gradient(to $gradient-direction, $gradient-color 45%, rgba($gradient-color, 0.4)); + + &.scrolling { + visibility: visible; + opacity: 1; + transition-duration: .3s; + } + + .fa { + position: relative; + top: 5px; + font-size: 18px; + } +} + +@mixin scrolling-links() { + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + + &::-webkit-scrollbar { + display: none; + } } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 2f7717760ec..f8777d1fd9d 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -1,38 +1,4 @@ -@mixin fade($gradient-direction, $gradient-color) { - visibility: hidden; - opacity: 0; - z-index: 2; - position: absolute; - bottom: 12px; - width: 43px; - height: 30px; - transition-duration: .3s; - -webkit-transform: translateZ(0); - background: linear-gradient(to $gradient-direction, $gradient-color 45%, rgba($gradient-color, 0.4)); - - &.scrolling { - visibility: visible; - opacity: 1; - transition-duration: .3s; - } - - .fa { - position: relative; - top: 5px; - font-size: 18px; - } -} -@mixin scrolling-links() { - overflow-x: auto; - overflow-y: hidden; - -webkit-overflow-scrolling: touch; - display: flex; - - &::-webkit-scrollbar { - display: none; - } -} .nav-links { display: flex; diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/framework/new-nav.scss index 8c5bafac637..3abf3e4ac7d 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/framework/new-nav.scss @@ -120,17 +120,24 @@ header.navbar-gitlab-new { .container-fluid { .navbar-toggle { min-width: 45px; - padding: 4px $gl-padding; + padding: 0 $gl-padding; margin-right: -7px; - font-size: 14px; text-align: center; color: currentColor; + svg { + fill: currentColor; + } + &:hover, &:focus, &.active { color: currentColor; background-color: transparent; + + svg { + fill: currentColor; + } } } @@ -279,10 +286,6 @@ header.navbar-gitlab-new { } } -.admin-icon i { - font-size: 18px; -} - .caret-down { height: 11px; width: 11px; @@ -295,75 +298,6 @@ header.navbar-gitlab-new { margin-top: 4px; } -.search { - margin: 4px 8px 0; - - form { - height: 32px; - border: 0; - border-radius: $border-radius-default; - transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s; - - &:hover { - box-shadow: none; - } - } - - &.search-active form { - box-shadow: none; - - .search-input { - color: $gl-text-color; - transition: color ease-in-out 0.15s; - } - - .search-input::placeholder { - color: $gl-text-color-tertiary; - } - - .search-input-wrap { - .search-icon, - .clear-icon { - color: $gl-text-color-tertiary; - transition: color ease-in-out 0.15s; - } - } - } - - .search-input { - color: $white-light; - background: none; - transition: color ease-in-out 0.15s; - } - - .search-input::placeholder { - transition: color ease-in-out 0.15s; - } - - .location-badge { - font-size: 12px; - margin: -4px 4px -4px -4px; - line-height: 25px; - padding: 4px 8px; - border-radius: 2px 0 0 2px; - height: 32px; - transition: border-color ease-in-out 0.15s; - } - - &.search-active { - .location-badge { - background-color: $nav-badge-bg; - border-color: $border-color; - } - - .search-input-wrap { - .clear-icon { - color: $white-light; - } - } - } -} - .breadcrumbs { display: flex; min-height: 48px; @@ -375,6 +309,8 @@ header.navbar-gitlab-new { display: flex; width: 100%; position: relative; + padding-top: $gl-padding / 2; + padding-bottom: $gl-padding / 2; align-items: center; border-bottom: 1px solid $border-color; } @@ -386,11 +322,6 @@ header.navbar-gitlab-new { align-self: center; color: $gl-text-color-secondary; - @media (max-width: $screen-xs-max) { - padding-left: 17px; - border-left: 1px solid $gl-text-color-quaternary; - } - .avatar-tile { margin-right: 4px; border: 1px solid $border-color; @@ -420,6 +351,7 @@ header.navbar-gitlab-new { display: flex; align-items: center; position: relative; + padding: 2px 0; &:not(:last-child) { margin-right: 20px; @@ -455,7 +387,7 @@ header.navbar-gitlab-new { margin: 0; font-size: 12px; font-weight: 600; - line-height: 1; + line-height: 16px; a { color: $gl-text-color; diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/framework/new-sidebar.scss index 9c404b7e542..8332cec2962 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/framework/new-sidebar.scss @@ -56,8 +56,8 @@ $new-sidebar-collapsed-width: 50px; color: $hover-color; .settings-avatar { - i { - color: $hover-color; + svg { + fill: $hover-color; } } } @@ -76,12 +76,9 @@ $new-sidebar-collapsed-width: 50px; .settings-avatar { background-color: $white-light; - i { - font-size: 20px; - width: 100%; - color: $gl-text-color-secondary; - text-align: center; - align-self: center; + svg { + fill: $gl-text-color-secondary; + margin: auto; } } @@ -177,16 +174,16 @@ $new-sidebar-collapsed-width: 50px; .nav-icon-container { display: flex; margin-right: 8px; - - svg { - height: 16px; - width: 16px; - } } .fly-out-top-item { display: none; } + + svg { + height: 16px; + width: 16px; + } } .nav-sidebar-inner-scroll { @@ -354,18 +351,22 @@ $new-sidebar-collapsed-width: 50px; display: flex; align-items: center; - i { - font-size: 20px; + svg { + fill: $gl-text-color-secondary; margin-right: 8px; } - .fa-angle-double-right { + .icon-angle-double-right { display: none; } &:hover { background-color: $border-color; color: $gl-text-color; + + svg { + fill: $gl-text-color; + } } } @@ -407,15 +408,16 @@ $new-sidebar-collapsed-width: 50px; .toggle-sidebar-button { width: $new-sidebar-collapsed-width - 2px; - padding: 16px 18px; + padding: 16px; .collapse-text, - .fa-angle-double-left { + .icon-angle-double-left { display: none; } - .fa-angle-double-right { + .icon-angle-double-right { display: block; + margin: 0; } } } @@ -461,6 +463,13 @@ $new-sidebar-collapsed-width: 50px; font-size: 18px; } } + + @media (max-width: $screen-xs-max) { + + .breadcrumbs-links { + padding-left: 17px; + border-left: 1px solid $gl-text-color-quaternary; + } + } } @media (max-width: $screen-xs-max) { diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index 4c35e3a9c3c..3ea77eb7a43 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -137,7 +137,7 @@ $well-border: #eee; //## $code-color: $red-600; -$code-bg: lighten($red-50, 2%); +$code-bg: lighten($red-100, 2%); $kbd-color: $white-light; $kbd-bg: #333; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 75cb9e009bb..d7a8bfab08a 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -6,6 +6,8 @@ $gutter_width: 290px; $gutter_inner_width: 250px; $sidebar-transition-duration: .15s; $sidebar-breakpoint: 1024px; +$default-transition-duration: .15s; +$right-sidebar-transition-duration: .3s; /* * Color schema @@ -27,46 +29,45 @@ $gray-dark: darken($gray-light, $darken-dark-factor); $gray-darker: #eee; $gray-darkest: #c4c4c4; -$green-25: #f6fcf8; -$green-50: #e4f5eb; -$green-100: #bae6cc; -$green-200: #8dd5aa; -$green-300: #5fc488; -$green-400: #3cb76f; +$green-50: #f1fdf6; +$green-100: #dcf5e7; +$green-200: #b3e6c8; +$green-300: #75d09b; +$green-400: #37b96d; $green-500: #1aaa55; $green-600: #168f48; $green-700: #12753a; $green-800: #0e5a2d; $green-900: #0a4020; +$green-950: #072b15; -$blue-25: #f6fafd; -$blue-50: #e4eff9; -$blue-100: #bcd7f1; -$blue-200: #8fbce8; -$blue-300: #62a1df; -$blue-400: #418cd8; +$blue-50: #f6fafe; +$blue-100: #e4f0fb; +$blue-200: #b8d6f4; +$blue-300: #73afea; +$blue-400: #2e87e0; $blue-500: #1f78d1; $blue-600: #1b69b6; $blue-700: #17599c; $blue-800: #134a81; $blue-900: #0f3b66; +$blue-950: #0a2744; -$orange-25: #fffcf8; -$orange-50: #fff2e1; -$orange-100: #fedfb3; -$orange-200: #feca81; -$orange-300: #fdb44f; -$orange-400: #fca429; +$orange-50: #fffaf4; +$orange-100: #fff1de; +$orange-200: #fed69f; +$orange-300: #fdbc60; +$orange-400: #fca121; $orange-500: #fc9403; $orange-600: #de7e00; $orange-700: #c26700; -$orange-800: #a35100; -$orange-900: #853b00; +$orange-800: #a35200; +$orange-900: #853c00; +$orange-950: #592800; -$red-25: #fef7f6; -$red-50: #fbe7e4; -$red-100: #f4c4bc; -$red-200: #ed9d90; +$red-50: #fef6f5; +$red-100: #fbe5e1; +$red-200: #f2b4a9; $red-300: #e67664; $red-400: #e05842; $red-500: #db3b21; @@ -74,6 +75,7 @@ $red-600: #c0341d; $red-700: #a62d19; $red-800: #8b2615; $red-900: #711e11; +$red-950: #4b140b; // GitLab themes @@ -184,8 +186,8 @@ $list-text-disabled-color: $gl-text-color-tertiary; $list-border-light: #eee; $list-border: rgba(0, 0, 0, 0.05); $list-text-height: 42px; -$list-warning-row-bg: $orange-50; -$list-warning-row-border: $orange-100; +$list-warning-row-bg: $orange-100; +$list-warning-row-border: $orange-200; $list-warning-row-color: $orange-700; /* @@ -214,8 +216,8 @@ $gl-sidebar-padding: 22px; /* * Misc */ -$row-hover: $blue-25; -$row-hover-border: $blue-100; +$row-hover: $blue-50; +$row-hover-border: $blue-200; $progress-color: #c0392b; $header-height: 50px; $new-navbar-height: 40px; @@ -265,8 +267,8 @@ $time-color: #999; $project-member-show-color: #aaa; $gl-promo-color: #aaa; $error-bg: $red-400; -$warning-message-bg: $orange-50; -$warning-message-border: $orange-100; +$warning-message-bg: $orange-100; +$warning-message-border: $orange-200; $warning-message-color: $orange-700; $control-group-descr-color: #666; $table-permission-x-bg: #d9edf7; @@ -451,17 +453,17 @@ $builds-trace-bg: #111; /* * Callout */ -$callout-danger-bg: $red-50; -$callout-danger-border: $red-100; +$callout-danger-bg: $red-100; +$callout-danger-border: $red-200; $callout-danger-color: $red-700; -$callout-warning-bg: $orange-50; -$callout-warning-border: $orange-100; +$callout-warning-bg: $orange-100; +$callout-warning-border: $orange-200; $callout-warning-color: $orange-700; -$callout-info-bg: $blue-50; -$callout-info-border: $blue-100; +$callout-info-bg: $blue-100; +$callout-info-border: $blue-200; $callout-info-color: $blue-700; -$callout-success-bg: $green-50; -$callout-success-border: $green-100; +$callout-success-bg: $green-100; +$callout-success-border: $green-200; $callout-success-color: $green-700; /* diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 700be173039..3305a482a0d 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -55,6 +55,15 @@ .boards-app { position: relative; + + @media (min-width: $screen-sm-min) { + transition: width $right-sidebar-transition-duration; + width: 100%; + + &.is-compact { + width: calc(100% - #{$gutter_width}); + } + } } .boards-app-loading { @@ -78,11 +87,6 @@ height: calc(100vh - 222px); // scss-lint:enable DuplicateProperty min-height: 475px; - transition: width .2s; - - &.is-compact { - width: calc(100% - 290px); - } } } @@ -412,14 +416,6 @@ .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; - height: 100%; - } - .issuable-sidebar-header { position: relative; } @@ -457,8 +453,8 @@ .right-sidebar.right-sidebar-expanded { &.boards-sidebar-slide-enter-active, &.boards-sidebar-slide-leave-active { - transition: width .2s, - padding .2s; + transition: width $right-sidebar-transition-duration, + padding $right-sidebar-transition-duration; } &.boards-sidebar-slide-enter, diff --git a/app/assets/stylesheets/pages/convdev_index.scss b/app/assets/stylesheets/pages/convdev_index.scss index 16702442f50..fb1899284fd 100644 --- a/app/assets/stylesheets/pages/convdev_index.scss +++ b/app/assets/stylesheets/pages/convdev_index.scss @@ -83,7 +83,7 @@ $space-between-cards: 8px; border-top-color: $color-low-score; .card-score-big { - background-color: $red-25; + background-color: $red-50; } } @@ -91,7 +91,7 @@ $space-between-cards: 8px; border-top-color: $color-average-score; .card-score-big { - background-color: $orange-25; + background-color: $orange-50; } } @@ -99,7 +99,7 @@ $space-between-cards: 8px; border-top-color: $color-high-score; .card-score-big { - background-color: $green-25; + background-color: $green-50; } } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 951580ea1fe..e4bd783c8bc 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -451,7 +451,7 @@ } .files { - margin-top: -1px; + margin-top: 1px; .diff-file:last-child { margin-bottom: 0; @@ -535,7 +535,6 @@ } .diff-notes-collapse { - position: relative; width: 19px; height: 19px; padding: 0; @@ -543,11 +542,7 @@ z-index: 100; svg { - position: absolute; - left: 50%; - top: 50%; - margin-left: -5.5px; - margin-top: -5.5px; + vertical-align: text-top; } path { @@ -586,11 +581,6 @@ top: 76px; } - + .files, - + .alert { - margin-top: 1px; - } - &:not(.is-stuck) .diff-stats-additions-deletions-collapsed { display: none; } @@ -605,11 +595,6 @@ .inline-parallel-buttons { display: none; } - - + .files, - + .alert { - margin-top: 32px; - } } } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index a88484d1a9e..e8d57214cb7 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -7,7 +7,7 @@ .issuable-warning-icon { color: $orange-600; - background-color: $orange-50; + background-color: $orange-100; border-radius: $border-radius-default; padding: 5px; margin: 0 $btn-side-margin 0 0; @@ -221,14 +221,14 @@ top: $new-navbar-height; bottom: 0; right: 0; - transition: width .3s; + transition: width $right-sidebar-transition-duration; background: $gray-light; z-index: 200; overflow: hidden; .issuable-sidebar { width: calc(100% + 100px); - height: calc(100% - #{$new-navbar-height}); + height: 100%; overflow-y: scroll; overflow-x: hidden; -webkit-overflow-scrolling: touch; diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss index 35cefd449f1..dbf3e2b763c 100644 --- a/app/assets/stylesheets/pages/merge_conflicts.scss +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -255,7 +255,7 @@ $colors: ( &.saved { .editor { - border-top: solid 2px $green-200; + border-top: solid 2px $green-300; } } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 5b93ff5ce14..14ce8b97c13 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -103,7 +103,7 @@ .issuable-note-warning { color: $orange-600; - background-color: $orange-50; + background-color: $orange-100; border-radius: $border-radius-default $border-radius-default 0 0; border: 1px solid $border-gray-normal; border-bottom: none; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 9d03a042aa3..086dd528579 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -644,20 +644,20 @@ button.mini-pipeline-graph-dropdown-toggle { // Dropdown button animation in mini pipeline graph &.ci-status-icon-success { - @include mini-pipeline-graph-color($green-50, $green-500, $green-600); + @include mini-pipeline-graph-color($green-100, $green-500, $green-600); } &.ci-status-icon-failed { - @include mini-pipeline-graph-color($red-50, $red-500, $red-600); + @include mini-pipeline-graph-color($red-100, $red-500, $red-600); } &.ci-status-icon-pending, &.ci-status-icon-success_with_warnings { - @include mini-pipeline-graph-color($orange-50, $orange-500, $orange-600); + @include mini-pipeline-graph-color($orange-100, $orange-500, $orange-600); } &.ci-status-icon-running { - @include mini-pipeline-graph-color($blue-50, $blue-400, $blue-600); + @include mini-pipeline-graph-color($blue-100, $blue-400, $blue-600); } &.ci-status-icon-canceled, diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index c5d6ff66dd6..67abe6e88ed 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -291,7 +291,7 @@ table.u2f-registrations { .bordered-box { border: 1px solid $blue-300; border-radius: $border-radius-default; - background-color: $blue-25; + background-color: $blue-50; position: relative; display: flex; justify-content: center; @@ -379,7 +379,7 @@ table.u2f-registrations { .nav-wip { border: 1px solid $blue-500; - background: $blue-25; + background: $blue-50; padding: $gl-padding; margin-bottom: $gl-padding; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 6400b72742c..1f7b6703909 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -516,7 +516,7 @@ a.deploy-project-label { text-align: center; width: 169px; - &:hover, + &:hover:not(.disabled), &.forked { background-color: $row-hover; border-color: $row-hover-border; @@ -543,6 +543,15 @@ a.deploy-project-label { padding-top: $gl-padding; color: $gl-text-color; + &.disabled { + opacity: .3; + cursor: not-allowed; + + &:hover { + text-decoration: none; + } + } + .caption { min-height: 30px; padding: $gl-padding 0; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 4d4d92f9494..c36fe25f74d 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -54,6 +54,10 @@ border-radius: $border-radius-default; color: $almost-black; + .code.white pre .hll { + background-color: $well-light-border !important; + } + .tree-content-holder { display: flex; min-height: 300px; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 13dd7b5a780..89ebe3f9917 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -28,9 +28,7 @@ input[type="checkbox"]:hover { } .search { - margin-right: 10px; - margin-left: 10px; - margin-top: ($header-height - 35) / 2; + margin: 4px 8px 0; form { @extend .form-control; @@ -38,15 +36,23 @@ input[type="checkbox"]:hover { padding: 4px; width: $search-input-width; line-height: 24px; + height: 32px; + border: 0; + border-radius: $border-radius-default; + transition: border-color ease-in-out $default-transition-duration, background-color ease-in-out $default-transition-duration; &:hover { - border-color: lighten($dropdown-input-focus-border, 20%); - box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%); + box-shadow: none; } } - .location-text { - font-style: normal; + .location-badge { + font-size: 12px; + margin: -4px 4px -4px -4px; + line-height: 25px; + padding: 4px 8px; + border-radius: $border-radius-default 0 0 $border-radius-default; + transition: border-color ease-in-out $default-transition-duration; } .search-input { @@ -56,59 +62,36 @@ input[type="checkbox"]:hover { margin-left: 5px; line-height: 25px; width: 98%; + color: $white-light; + background: none; + transition: color ease-in-out $default-transition-duration; } - .location-badge { - line-height: 25px; - padding: 0 5px; - border-radius: $border-radius-default; - font-size: 14px; - font-style: normal; - color: $note-disabled-comment-color; - display: inline-block; - background-color: $gray-normal; - vertical-align: top; - cursor: default; + .search-input::placeholder { + transition: color ease-in-out $default-transition-duration; } .search-input-container { - display: -webkit-flex; display: flex; position: relative; } .search-input-wrap { - // Fallback if flexbox is not supported - display: inline-block; - } - - .search-input-wrap { - width: 100%; - .search-icon, .clear-icon { position: absolute; right: 5px; - top: 0; - color: $location-icon-color; - - &::before { - font-family: FontAwesome; - font-weight: $gl-font-weight-normal; - font-style: normal; - } + top: 4px; } .search-icon { - @extend .fa-search; - transition: color 0.15s; + transition: color $default-transition-duration; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; } .clear-icon { - @extend .fa-times; display: none; } @@ -148,21 +131,32 @@ input[type="checkbox"]:hover { form { @extend .form-control:focus; border-color: $dropdown-input-focus-border; - box-shadow: 0 0 4px $search-input-focus-shadow-color; - } + box-shadow: none; + + .search-input-wrap { + .search-icon, + .clear-icon { + color: $gl-text-color-tertiary; + transition: color ease-in-out $default-transition-duration; + } + } - .location-badge { - transition: all 0.15s; - background-color: $location-badge-active-bg; - color: $white-light; - } + .search-input { + color: $gl-text-color; + transition: color ease-in-out $default-transition-duration; + } - .search-input-wrap { - i { - color: $layout-link-gray; + .search-input::placeholder { + color: $gl-text-color-tertiary; } } + .location-badge { + transition: all $default-transition-duration; + background-color: $nav-badge-bg; + border-color: $border-color; + } + .dropdown-menu { transition-duration: 100ms, 75ms; transition-delay: 75ms, 100ms; diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 36f622db136..25c80e1f950 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -18,7 +18,7 @@ } &.ci-failed { - @include status-color($red-50, $red-500, $red-600); + @include status-color($red-100, $red-500, $red-600); } &.ci-success { @@ -39,12 +39,12 @@ &.ci-pending, &.ci-failed_with_warnings, &.ci-success_with_warnings { - @include status-color($orange-50, $orange-500, $orange-700); + @include status-color($orange-100, $orange-500, $orange-700); } &.ci-info, &.ci-running { - @include status-color($blue-50, $blue-500, $blue-600); + @include status-color($blue-100, $blue-500, $blue-600); } &.ci-created, diff --git a/app/assets/stylesheets/test.scss b/app/assets/stylesheets/test.scss index 7d9f3da79c5..06733b7f1a9 100644 --- a/app/assets/stylesheets/test.scss +++ b/app/assets/stylesheets/test.scss @@ -15,3 +15,9 @@ -ms-animation: none !important; animation: none !important; } + +// Disable sticky changes bar for tests +.diff-files-changed { + position: relative !important; + top: 0 !important; +} diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index 16590e66d61..fb6d8c0bb81 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -22,8 +22,7 @@ class Admin::ApplicationsController < Admin::ApplicationController @application = Doorkeeper::Application.new(application_params) if @application.save - flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - redirect_to admin_application_url(@application) + redirect_to_admin_page else render :new end @@ -42,6 +41,13 @@ class Admin::ApplicationsController < Admin::ApplicationController redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.' end + protected + + def redirect_to_admin_page + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) + redirect_to admin_application_url(@application) + end + private def set_application diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index cbcef70e957..676a7203c7d 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -128,7 +128,7 @@ class Admin::UsersController < Admin::ApplicationController end respond_to do |format| - result = Users::UpdateService.new(user, user_params_with_pass).execute do |user| + result = Users::UpdateService.new(current_user, user_params_with_pass.merge(user: user)).execute do |user| user.skip_reconfirmation! end @@ -155,7 +155,7 @@ class Admin::UsersController < Admin::ApplicationController def remove_email email = user.emails.find(params[:email_id]) - success = Emails::DestroyService.new(user, email: email.email).execute + success = Emails::DestroyService.new(current_user, user: user, email: email.email).execute respond_to do |format| if success @@ -219,7 +219,7 @@ class Admin::UsersController < Admin::ApplicationController end def update_user(&block) - result = Users::UpdateService.new(user).execute(&block) + result = Users::UpdateService.new(current_user, user: user).execute(&block) result[:status] == :success end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 97922e39ba8..771c6f3034a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,6 +25,8 @@ class ApplicationController < ActionController::Base around_action :set_locale + after_action :set_page_title_header, if: -> { request.format == :json } + protect_from_forgery with: :exception helper_method :can?, :current_application_settings @@ -335,4 +337,9 @@ class ApplicationController < ActionController::Base sign_in user, store: false end end + + def set_page_title_header + # Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8 + response.headers['Page-Title'] = page_title('GitLab').encode('ISO-8859-1') + end end diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index b75e401a8df..db8c362f125 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -59,6 +59,7 @@ module AuthenticatesWithTwoFactor sign_in(user) else user.increment_failed_attempts! + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") flash.now[:alert] = 'Invalid two-factor code.' prompt_for_two_factor(user) end @@ -75,6 +76,7 @@ module AuthenticatesWithTwoFactor sign_in(user) else user.increment_failed_attempts! + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F") flash.now[:alert] = 'Authentication via U2F device failed.' prompt_for_two_factor(user) end diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 8921d55c3d0..3181f517087 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -106,7 +106,7 @@ module IssuableCollections # @filter_params[:authorized_only] = true end - @filter_params + @filter_params.permit(IssuableFinder::VALID_PARAMS) end def set_default_state diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index 306afb65f10..0c2646d7bf0 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -12,10 +12,15 @@ class ConfirmationsController < Devise::ConfirmationsController def after_confirmation_path_for(resource_name, resource) if signed_in?(resource_name) - after_sign_in_path_for(resource) + after_sign_in(resource) else + Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}") flash[:notice] += " Please sign in." new_session_path(resource_name) end end + + def after_sign_in(resource) + after_sign_in_path_for(resource) + end end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 2ae4785b12c..b02e64a132b 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -21,14 +21,20 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController @application.owner = current_user if @application.save - flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - redirect_to oauth_application_url(@application) + redirect_to_oauth_application_page else set_index_vars render :index end end + protected + + def redirect_to_oauth_application_page + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) + redirect_to oauth_application_url(@application) + end + private def verify_user_oauth_applications_enabled diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb index 408650aac54..39b9f8a84d1 100644 --- a/app/controllers/profiles/avatars_controller.rb +++ b/app/controllers/profiles/avatars_controller.rb @@ -2,7 +2,7 @@ class Profiles::AvatarsController < Profiles::ApplicationController def destroy @user = current_user - Users::UpdateService.new(@user).execute { |user| user.remove_avatar! } + Users::UpdateService.new(current_user, user: @user).execute { |user| user.remove_avatar! } redirect_to profile_path, status: 302 end diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index ddb67d1c4d1..97db84b92d4 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -5,7 +5,7 @@ class Profiles::EmailsController < Profiles::ApplicationController end def create - @email = Emails::CreateService.new(current_user, email_params).execute + @email = Emails::CreateService.new(current_user, email_params.merge(user: current_user)).execute if @email.errors.blank? NotificationService.new.new_email(@email) @@ -19,7 +19,7 @@ class Profiles::EmailsController < Profiles::ApplicationController def destroy @email = current_user.emails.find(params[:id]) - Emails::DestroyService.new(current_user, email: @email.email).execute + Emails::DestroyService.new(current_user, user: current_user, email: @email.email).execute respond_to do |format| format.html { redirect_to profile_emails_url, status: 302 } diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index 89d6d7f1b52..069e6a810f2 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -14,7 +14,7 @@ class Profiles::KeysController < Profiles::ApplicationController @key = Keys::CreateService.new(current_user, key_params).execute if @key.persisted? - redirect_to profile_key_path(@key) + redirect_to_profile_key_path else @keys = current_user.keys.select(&:persisted?) render :index @@ -50,6 +50,12 @@ class Profiles::KeysController < Profiles::ApplicationController end end + protected + + def redirect_to_profile_key_path + redirect_to profile_key_path(@key) + end + private def key_params diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 960b7512602..8a38ba65d4c 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -7,7 +7,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController end def update - result = Users::UpdateService.new(current_user, user_params).execute + result = Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute if result[:status] == :success flash[:notice] = "Notification settings saved" diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index 7beb52dd8e8..dcfcb855ab5 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -21,10 +21,10 @@ class Profiles::PasswordsController < Profiles::ApplicationController password_automatically_set: false } - result = Users::UpdateService.new(@user, password_attributes).execute + result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute if result[:status] == :success - Users::UpdateService.new(@user, password_expires_at: nil).execute + Users::UpdateService.new(current_user, user: @user, password_expires_at: nil).execute redirect_to root_path, notice: 'Password successfully changed' else @@ -46,7 +46,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController return end - result = Users::UpdateService.new(@user, password_attributes).execute + result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute if result[:status] == :success flash[:notice] = "Password was successfully updated. Please login with it" diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index cce2a847b53..ed0f98179eb 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -6,7 +6,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController def update begin - result = Users::UpdateService.new(user, preferences_params).execute + result = Users::UpdateService.new(current_user, preferences_params.merge(user: user)).execute if result[:status] == :success flash[:notice] = 'Preferences saved.' diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 1a4f77639e7..aa9789f8a0f 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -10,7 +10,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController current_user.otp_grace_period_started_at = Time.current end - Users::UpdateService.new(current_user).execute! + Users::UpdateService.new(current_user, user: current_user).execute! if two_factor_authentication_required? && !current_user.two_factor_enabled? two_factor_authentication_reason( @@ -41,7 +41,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def create if current_user.validate_and_consume_otp!(params[:pin_code]) - Users::UpdateService.new(current_user, otp_required_for_login: true).execute! do |user| + Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user| @codes = user.generate_otp_backup_codes! end @@ -70,7 +70,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController end def codes - Users::UpdateService.new(current_user).execute! do |user| + Users::UpdateService.new(current_user, user: current_user).execute! do |user| @codes = user.generate_otp_backup_codes! end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index d83824fef06..5d87037f012 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -10,7 +10,7 @@ class ProfilesController < Profiles::ApplicationController def update respond_to do |format| - result = Users::UpdateService.new(@user, user_params).execute + result = Users::UpdateService.new(current_user, user_params.merge(user: @user)).execute if result[:status] == :success message = "Profile was successfully updated" @@ -25,7 +25,7 @@ class ProfilesController < Profiles::ApplicationController end def reset_private_token - Users::UpdateService.new(@user).execute! do |user| + Users::UpdateService.new(current_user, user: @user).execute! do |user| user.reset_authentication_token! end @@ -35,7 +35,7 @@ class ProfilesController < Profiles::ApplicationController end def reset_incoming_email_token - Users::UpdateService.new(@user).execute! do |user| + Users::UpdateService.new(current_user, user: @user).execute! do |user| user.reset_incoming_email_token! end @@ -45,7 +45,7 @@ class ProfilesController < Profiles::ApplicationController end def reset_rss_token - Users::UpdateService.new(@user).execute! do |user| + Users::UpdateService.new(current_user, user: @user).execute! do |user| user.reset_rss_token! end @@ -61,7 +61,7 @@ class ProfilesController < Profiles::ApplicationController end def update_username - result = Users::UpdateService.new(@user, username: user_params[:username]).execute + result = Users::UpdateService.new(current_user, user: @user, username: user_params[:username]).execute options = if result[:status] == :success { notice: "Username successfully changed" } diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 2b8f3977e6e..183a6f88a6a 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -41,6 +41,8 @@ class Projects::BlobController < Projects::ApplicationController end format.json do + page_title @blob.path, @ref, @project.name_with_namespace + show_json end end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index f3784f4e07c..f3719059f88 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -35,6 +35,8 @@ class Projects::TreeController < Projects::ApplicationController end format.json do + page_title @path.presence || _("Files"), @ref, @project.name_with_namespace + # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38261 Gitlab::GitalyClient.allow_n_plus_1_calls do render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 1bc6520370a..5ea3a5d5562 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -42,10 +42,12 @@ class RegistrationsController < Devise::RegistrationsController end def after_sign_up_path_for(user) + Gitlab::AppLogger.info("User Created: username=#{user.username} email=#{user.email} ip=#{request.remote_ip} confirmed:#{user.confirmed?}") user.confirmed? ? dashboard_projects_path : users_almost_there_path end - def after_inactive_sign_up_path_for(_resource) + def after_inactive_sign_up_path_for(resource) + Gitlab::AppLogger.info("User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:false") users_almost_there_path end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index be6491d042c..4223c6171a6 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,6 +13,8 @@ class SessionsController < Devise::SessionsController before_action :auto_sign_in_with_provider, only: [:new] before_action :load_recaptcha + after_action :log_failed_login, only: [:new] + def new set_minimum_password_length @ldap_servers = Gitlab::LDAP::Config.available_servers @@ -29,12 +31,13 @@ class SessionsController < Devise::SessionsController end # hide the signed-in notification flash[:notice] = nil - log_audit_event(current_user, with: authentication_method) + log_audit_event(current_user, resource, with: authentication_method) log_user_activity(current_user) end end def destroy + Gitlab::AppLogger.info("User Logout: username=#{current_user.username} ip=#{request.remote_ip}") super # hide the signed_out notice flash[:notice] = nil @@ -42,6 +45,16 @@ class SessionsController < Devise::SessionsController private + def log_failed_login + return unless failed_login? + + Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}") + end + + def failed_login? + (options = env["warden.options"]) && options[:action] == "unauthenticated" + end + def login_counter @login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count') end @@ -55,7 +68,7 @@ class SessionsController < Devise::SessionsController return unless user && user.require_password_creation? - Users::UpdateService.new(user).execute do |user| + Users::UpdateService.new(current_user, user: user).execute do |user| @token = user.generate_reset_token end @@ -123,7 +136,8 @@ class SessionsController < Devise::SessionsController user.invalidate_otp_backup_code!(user_params[:otp_attempt]) end - def log_audit_event(user, options = {}) + def log_audit_event(user, resource, options = {}) + Gitlab::AppLogger.info("Successful Login: username=#{resource.username} ip=#{request.remote_ip} method=#{options[:with]} admin=#{resource.admin?}") AuditEventService.new(user, user, options) .for_authentication.security_event end diff --git a/app/finders/concerns/custom_attributes_filter.rb b/app/finders/concerns/custom_attributes_filter.rb new file mode 100644 index 00000000000..5bbf9ca242d --- /dev/null +++ b/app/finders/concerns/custom_attributes_filter.rb @@ -0,0 +1,20 @@ +module CustomAttributesFilter + def by_custom_attributes(items) + return items unless params[:custom_attributes].is_a?(Hash) + return items unless Ability.allowed?(current_user, :read_custom_attribute) + + association = items.reflect_on_association(:custom_attributes) + attributes_table = association.klass.arel_table + attributable_table = items.model.arel_table + + custom_attributes = association.klass.select('true').where( + attributes_table[association.foreign_key] + .eq(attributable_table[association.association_primary_key]) + ) + + # perform a subquery for each attribute to be filtered + params[:custom_attributes].inject(items) do |scope, (key, value)| + scope.where('EXISTS (?)', custom_attributes.where(key: key, value: value)) + end + end +end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 0a2e3c709d9..24c07f3dc70 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -25,6 +25,28 @@ class IssuableFinder NONE = '0'.freeze + SCALAR_PARAMS = %i[ + assignee_id + assignee_username + author_id + author_username + authorized_only + due_date + group_id + iids + label_name + milestone_title + non_archived + project_id + scope + search + sort + state + ].freeze + ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze + + VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze + attr_accessor :current_user, :params def initialize(current_user, params = {}) diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 33f7ae90598..1a7e97004fb 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -15,6 +15,7 @@ # class UsersFinder include CreatedAtFilter + include CustomAttributesFilter attr_accessor :current_user, :params @@ -32,6 +33,7 @@ class UsersFinder users = by_external_identity(users) users = by_external(users) users = by_created_at(users) + users = by_custom_attributes(users) users end diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 62ac208f16a..7112c6ee470 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -79,6 +79,6 @@ module BoardsHelper end def boards_link_text - _("Board") + s_("IssueBoards|Board") end end diff --git a/app/helpers/breadcrumbs_helper.rb b/app/helpers/breadcrumbs_helper.rb index ee1b7ed083e..e88fe6bcd7e 100644 --- a/app/helpers/breadcrumbs_helper.rb +++ b/app/helpers/breadcrumbs_helper.rb @@ -10,11 +10,7 @@ module BreadcrumbsHelper def breadcrumb_title_link return @breadcrumb_link if @breadcrumb_link - if controller.available_action?(:index) - url_for(action: "index") - else - request.path - end + request.path end def breadcrumb_title(title) @@ -25,7 +21,7 @@ module BreadcrumbsHelper def breadcrumb_list_item(link) content_tag "li" do - link + icon("angle-right", class: "breadcrumbs-list-angle") + link + sprite_icon("angle-right", size: 8, css_class: "breadcrumbs-list-angle") end end diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 08e6443bd0f..ec779c1c447 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -24,9 +24,9 @@ module IconsHelper end def sprite_icon(icon_name, size: nil, css_class: nil) - css_classes = size ? "s#{size}" : nil + css_classes = size ? "s#{size}" : "" css_classes << " #{css_class}" unless css_class.blank? - content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{image_path('icons.svg')}##{icon_name}" } ), class: css_classes) + content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{image_path('icons.svg')}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes) end def audit_icon(names, options = {}) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index df390dd5aab..7713fb0b9f8 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -248,16 +248,25 @@ module IssuablesHelper Gitlab::IssuablesCountForState.new(finder)[state] end - def close_issuable_url(issuable) - issuable_url(issuable, close_reopen_params(issuable, :close)) + def close_issuable_path(issuable) + issuable_path(issuable, close_reopen_params(issuable, :close)) end - def reopen_issuable_url(issuable) - issuable_url(issuable, close_reopen_params(issuable, :reopen)) + def reopen_issuable_path(issuable) + issuable_path(issuable, close_reopen_params(issuable, :reopen)) end - def close_reopen_issuable_url(issuable, should_inverse = false) - issuable.closed? ^ should_inverse ? reopen_issuable_url(issuable) : close_issuable_url(issuable) + def close_reopen_issuable_path(issuable, should_inverse = false) + issuable.closed? ^ should_inverse ? reopen_issuable_path(issuable) : close_issuable_path(issuable) + end + + def issuable_path(issuable, *options) + case issuable + when Issue + issue_path(issuable, *options) + when MergeRequest + merge_request_path(issuable, *options) + end end def issuable_url(issuable, *options) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 5946c475835..18b9bf214a3 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -9,7 +9,7 @@ module PageLayoutHelper end # Segments are seperated by middot - @page_title.join(" \u00b7 ") + @page_title.join(" · ") end # Define or get a description for the current page diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 21fb17e06d6..4c0cce54527 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -21,11 +21,14 @@ module ProjectsHelper classes = %W[avatar avatar-inline s#{opts[:size]}] classes << opts[:avatar_class] if opts[:avatar_class] - image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: classes, alt: '') + avatar = avatar_icon(author, opts[:size]) + src = opts[:lazy_load] ? nil : avatar + + image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar) end def link_to_member(project, author, opts = {}, &block) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false } + default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false, lazy_load: false } opts = default_opts.merge(opts) return "(deleted)" unless author diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index acaa028eaa2..3d5acc00f8f 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -434,7 +434,7 @@ module Ci def update_duration return unless started_at - self.duration = Gitlab::Ci::PipelineDuration.from_pipeline(self) + self.duration = Gitlab::Ci::Pipeline::Duration.from_pipeline(self) end def execute_hooks diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index a0d07902ba2..c6509f89117 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -174,7 +174,7 @@ module Ci end def assignable_for?(project) - !locked? || projects.exists?(id: project.id) + is_shared? || projects.exists?(id: project.id) end def accepting_tags?(build) diff --git a/app/models/commit.rb b/app/models/commit.rb index 2ae8890c1b3..6dba154a6ea 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -25,8 +25,8 @@ class Commit DIFF_HARD_LIMIT_FILES = 1000 DIFF_HARD_LIMIT_LINES = 50000 - # The SHA can be between 7 and 40 hex characters. - COMMIT_SHA_PATTERN = '\h{7,40}'.freeze + MIN_SHA_LENGTH = 7 + COMMIT_SHA_PATTERN = /\h{#{MIN_SHA_LENGTH},40}/.freeze def banzai_render_context(field) context = { pipeline: :single_line, project: self.project } @@ -53,7 +53,7 @@ class Commit # Truncate sha to 8 characters def truncate_sha(sha) - sha[0..7] + sha[0..MIN_SHA_LENGTH] end def max_diff_options @@ -100,7 +100,7 @@ class Commit def self.reference_pattern @reference_pattern ||= %r{ (?:#{Project.reference_pattern}#{reference_prefix})? - (?<commit>\h{7,40}) + (?<commit>#{COMMIT_SHA_PATTERN}) }x end @@ -216,9 +216,8 @@ class Commit @raw.respond_to?(method, include_private) || super end - # Truncate sha to 8 characters def short_id - @raw.short_id(7) + @raw.short_id(MIN_SHA_LENGTH) end def diff_refs diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb index 44deae4234b..54bd5b68777 100644 --- a/app/models/gpg_key.rb +++ b/app/models/gpg_key.rb @@ -73,7 +73,7 @@ class GpgKey < ActiveRecord::Base end def verified_and_belongs_to_email?(email) - emails_with_verified_status.fetch(email, false) + emails_with_verified_status.fetch(email.downcase, false) end def update_invalid_gpg_signatures diff --git a/app/models/project.rb b/app/models/project.rb index f7221e4f3b2..44d1190cc5b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -72,6 +72,7 @@ class Project < ActiveRecord::Base attr_accessor :old_path_with_namespace attr_accessor :template_name attr_writer :pipeline_status + attr_accessor :skip_disk_validation alias_attribute :title, :name @@ -227,7 +228,7 @@ class Project < ActiveRecord::Base validates :import_url, importable_url: true, if: [:external_import?, :import_url_changed?] validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create - validate :can_create_repository?, on: [:create, :update], if: ->(project) { !project.persisted? || project.renamed? } + validate :check_repository_path_availability, on: [:create, :update], if: ->(project) { !project.persisted? || project.renamed? } validate :avatar_type, if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } @@ -245,6 +246,9 @@ class Project < ActiveRecord::Base scope :pending_delete, -> { where(pending_delete: true) } scope :without_deleted, -> { where(pending_delete: false) } + scope :with_hashed_storage, -> { where('storage_version >= 1') } + scope :with_legacy_storage, -> { where(storage_version: [nil, 0]) } + scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) } scope :sorted_by_stars, -> { reorder('projects.star_count DESC') } @@ -1015,7 +1019,8 @@ class Project < ActiveRecord::Base end # Check if repository already exists on disk - def can_create_repository? + def check_repository_path_availability + return true if skip_disk_validation return false unless repository_storage_path expires_full_path_cache # we need to clear cache to validate renames correctly @@ -1032,7 +1037,7 @@ class Project < ActiveRecord::Base # Forked import is handled asynchronously return if forked? && !force - if gitlab_shell.add_repository(repository_storage_path, disk_path) + if gitlab_shell.add_repository(repository_storage, disk_path) repository.after_create true else @@ -1550,18 +1555,44 @@ class Project < ActiveRecord::Base end def legacy_storage? - self.storage_version.nil? + [nil, 0].include?(self.storage_version) + end + + def hashed_storage? + self.storage_version && self.storage_version >= 1 end def renamed? persisted? && path_changed? end + def migrate_to_hashed_storage! + return if hashed_storage? + + update!(repository_read_only: true) + + if repo_reference_count > 0 || wiki_reference_count > 0 + ProjectMigrateHashedStorageWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id) + else + ProjectMigrateHashedStorageWorker.perform_async(id) + end + end + + def storage_version=(value) + super + + @storage = nil if storage_version_changed? + end + + def gl_repository(is_wiki:) + Gitlab::GlRepository.gl_repository(self, is_wiki) + end + private def storage @storage ||= - if self.storage_version && self.storage_version >= 1 + if hashed_storage? Storage::HashedProject.new(self) else Storage::LegacyProject.new(self) @@ -1574,6 +1605,14 @@ class Project < ActiveRecord::Base end end + def repo_reference_count + Gitlab::ReferenceCounter.new(gl_repository(is_wiki: false)).value + end + + def wiki_reference_count + Gitlab::ReferenceCounter.new(gl_repository(is_wiki: true)).value + end + # set last_activity_at to the same as created_at def set_last_activity_at update_column(:last_activity_at, self.created_at) diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 698fdf7a20c..c4cc1c1cf22 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -174,7 +174,7 @@ class ProjectWiki private def init_repo(disk_path) - gitlab_shell.add_repository(project.repository_storage_path, disk_path) + gitlab_shell.add_repository(project.repository_storage, disk_path) end def commit_details(action, message = nil, title = nil) diff --git a/app/models/repository.rb b/app/models/repository.rb index 90cede9d3d4..a0f57f1e54d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -34,7 +34,10 @@ class Repository CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide changelog license_blob license_key gitignore koding_yml gitlab_ci_yml branch_names tag_names branch_count - tag_count avatar exists? empty? root_ref).freeze + tag_count avatar exists? empty? root_ref has_visible_content?).freeze + + # Methods that use cache_method but only memoize the value + MEMOIZED_CACHED_METHODS = %i(license empty_repo?).freeze # Certain method caches should be refreshed when certain types of files are # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to @@ -91,12 +94,6 @@ class Repository ) end - # we need to have this method here because it is not cached in ::Git and - # the method is called multiple times for every request - def has_visible_content? - branch_count > 0 - end - def inspect "#<#{self.class.name}:#{@disk_path}>" end @@ -275,7 +272,7 @@ class Repository end def expire_branches_cache - expire_method_caches(%i(branch_names branch_count)) + expire_method_caches(%i(branch_names branch_count has_visible_content?)) @local_branches = nil @branch_exists_memo = nil end @@ -346,7 +343,7 @@ class Repository def expire_emptiness_caches return unless empty? - expire_method_caches(%i(empty?)) + expire_method_caches(%i(empty? has_visible_content?)) end def lookup_cache @@ -489,13 +486,7 @@ class Repository def exists? return false unless full_path - Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| - if enabled - raw_repository.exists? - else - refs_directory_exists? - end - end + raw_repository.exists? end cache_method :exists? @@ -529,13 +520,17 @@ class Repository delegate :tag_names, to: :raw_repository cache_method :tag_names, fallback: [] - delegate :branch_count, :tag_count, to: :raw_repository + delegate :branch_count, :tag_count, :has_visible_content?, to: :raw_repository cache_method :branch_count, fallback: 0 cache_method :tag_count, fallback: 0 + cache_method :has_visible_content?, fallback: false def avatar - if tree = file_on_head(:avatar) - tree.path + # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38327 + Gitlab::GitalyClient.allow_n_plus_1_calls do + if tree = file_on_head(:avatar) + tree.path + end end end cache_method :avatar @@ -1060,12 +1055,6 @@ class Repository blob.data end - def refs_directory_exists? - circuit_breaker.perform do - File.exist?(File.join(path_to_repo, 'refs')) - end - end - def cache # TODO: should we use UUIDs here? We could move repositories without clearing this cache @cache ||= RepositoryCache.new(full_path, @project.id) @@ -1117,10 +1106,6 @@ class Repository Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, false)) end - def circuit_breaker - @circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(project.repository_storage) - end - def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) ref ||= root_ref diff --git a/app/models/storage/hashed_project.rb b/app/models/storage/hashed_project.rb index fae1b64961a..f025f40994e 100644 --- a/app/models/storage/hashed_project.rb +++ b/app/models/storage/hashed_project.rb @@ -4,6 +4,7 @@ module Storage delegate :gitlab_shell, :repository_storage_path, to: :project ROOT_PATH_PREFIX = '@hashed'.freeze + STORAGE_VERSION = 1 def initialize(project) @project = project diff --git a/app/models/user.rb b/app/models/user.rb index 09c9b3250eb..4e71a3e11c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -60,7 +60,7 @@ class User < ActiveRecord::Base lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i) return unless lease.try_obtain - Users::UpdateService.new(self).execute(validate: false) + Users::UpdateService.new(self, user: self).execute(validate: false) end attr_accessor :force_random_password @@ -130,6 +130,8 @@ class User < ActiveRecord::Base has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent + has_many :custom_attributes, class_name: 'UserCustomAttribute' + # # Validations # @@ -526,8 +528,8 @@ class User < ActiveRecord::Base def update_emails_with_primary_email primary_email_record = emails.find_by(email: email) if primary_email_record - Emails::DestroyService.new(self, email: email).execute - Emails::CreateService.new(self, email: email_was).execute + Emails::DestroyService.new(self, user: self, email: email).execute + Emails::CreateService.new(self, user: self, email: email_was).execute end end @@ -690,7 +692,11 @@ class User < ActiveRecord::Base end def ldap_user? - identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) + if identities.loaded? + identities.find { |identity| identity.provider.start_with?('ldap') && !identity.extern_uid.nil? } + else + identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) + end end def ldap_identity @@ -1000,7 +1006,7 @@ class User < ActiveRecord::Base if attempts_exceeded? lock_access! unless access_locked? else - Users::UpdateService.new(self).execute(validate: false) + Users::UpdateService.new(self, user: self).execute(validate: false) end end @@ -1061,6 +1067,12 @@ class User < ActiveRecord::Base user_synced_attributes_metadata&.read_only?(attribute) end + # override, from Devise + def lock_access! + Gitlab::AppLogger.info("Account Locked: username=#{username}") + super + end + protected # override, from Devise::Validatable @@ -1186,7 +1198,7 @@ class User < ActiveRecord::Base &creation_block ) - Users::UpdateService.new(user).execute(validate: false) + Users::UpdateService.new(user, user: user).execute(validate: false) user ensure Gitlab::ExclusiveLease.cancel(lease_key, uuid) diff --git a/app/models/user_custom_attribute.rb b/app/models/user_custom_attribute.rb new file mode 100644 index 00000000000..eff25b31f9b --- /dev/null +++ b/app/models/user_custom_attribute.rb @@ -0,0 +1,6 @@ +class UserCustomAttribute < ActiveRecord::Base + belongs_to :user + + validates :user_id, :key, :value, presence: true + validates :key, uniqueness: { scope: [:user_id] } +end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 1be7bbe9953..64e550d19d0 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -11,6 +11,8 @@ class GlobalPolicy < BasePolicy with_options scope: :user, score: 0 condition(:access_locked) { @user.access_locked? } + condition(:can_create_fork, scope: :user) { @user.manageable_namespaces.any? { |namespace| @user.can?(:create_projects, namespace) } } + rule { anonymous }.policy do prevent :log_in prevent :access_api @@ -40,6 +42,10 @@ class GlobalPolicy < BasePolicy enable :create_group end + rule { can_create_fork }.policy do + enable :create_fork + end + rule { access_locked }.policy do prevent :log_in end @@ -47,4 +53,9 @@ class GlobalPolicy < BasePolicy rule { ~(anonymous & restricted_public_level) }.policy do enable :read_users_list end + + rule { admin }.policy do + enable :read_custom_attribute + enable :update_custom_attribute + end end diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb index 85b67f0a237..92213f0155e 100644 --- a/app/policies/namespace_policy.rb +++ b/app/policies/namespace_policy.rb @@ -1,10 +1,14 @@ class NamespacePolicy < BasePolicy rule { anonymous }.prevent_all + condition(:personal_project, scope: :subject) { @subject.kind == 'user' } + condition(:can_create_personal_project, scope: :user) { @user.can_create_project? } condition(:owner) { @subject.owner == @user } rule { owner | admin }.policy do enable :create_projects enable :admin_namespace end + + rule { personal_project & ~can_create_personal_project }.prevent :create_projects end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index d20de9b16a4..31a712ccc1b 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -2,110 +2,55 @@ module Ci class CreatePipelineService < BaseService attr_reader :pipeline - def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil) + SEQUENCE = [Gitlab::Ci::Pipeline::Chain::Validate::Abilities, + Gitlab::Ci::Pipeline::Chain::Validate::Repository, + Gitlab::Ci::Pipeline::Chain::Validate::Config, + Gitlab::Ci::Pipeline::Chain::Skip, + Gitlab::Ci::Pipeline::Chain::Create].freeze + + def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block) @pipeline = Ci::Pipeline.new( source: source, project: project, ref: ref, sha: sha, before_sha: before_sha, - tag: tag?, + tag: tag_exists?, trigger_requests: Array(trigger_request), user: current_user, pipeline_schedule: schedule, protected: project.protected_for?(ref) ) - result = validate_project_and_git_items || - validate_pipeline(ignore_skip_ci: ignore_skip_ci, - save_on_errors: save_on_errors) + command = OpenStruct.new(ignore_skip_ci: ignore_skip_ci, + save_incompleted: save_on_errors, + seeds_block: block, + project: project, + current_user: current_user) - return result if result + sequence = Gitlab::Ci::Pipeline::Chain::Sequence + .new(pipeline, command, SEQUENCE) - begin - Ci::Pipeline.transaction do - pipeline.save! + sequence.build! do |pipeline, sequence| + update_merge_requests_head_pipeline if pipeline.persisted? - yield(pipeline) if block_given? + if sequence.complete? + cancel_pending_pipelines if project.auto_cancel_pending_pipelines? + pipeline_created_counter.increment(source: source) - Ci::CreatePipelineStagesService - .new(project, current_user) - .execute(pipeline) + pipeline.process! 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_project_and_git_items - unless project.builds_enabled? - return error('Pipeline is disabled') - end - - unless allowed_to_trigger_pipeline? - if can?(current_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? - return error('Reference not found') - end - - unless commit - return error('Commit not found') - end - end - - def validate_pipeline(ignore_skip_ci:, save_on_errors:) - unless pipeline.config_processor - unless pipeline.ci_yaml_file - return error("Missing #{pipeline.ci_yaml_file_path} file") - end - return error(pipeline.yaml_errors, save: save_on_errors) - end - - if !ignore_skip_ci && skip_ci? - pipeline.skip if save_on_errors - return pipeline - end - - unless pipeline.has_stage_seeds? - return error('No stages / jobs for this pipeline.') - end - end - - def allowed_to_trigger_pipeline? - if current_user - allowed_to_create? - else # legacy triggers don't have a corresponding user - !project.protected_for?(ref) - end + def commit + @commit ||= project.commit(origin_sha || origin_ref) end - def allowed_to_create? - return unless can?(current_user, :create_pipeline, project) - - access = Gitlab::UserAccess.new(current_user, project: 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 + def sha + commit.try(:id) end def update_merge_requests_head_pipeline @@ -115,11 +60,6 @@ module Ci .update_all(head_pipeline_id: @pipeline.id) end - def skip_ci? - return false unless pipeline.git_commit_message - pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i - end - def cancel_pending_pipelines Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines) do |cancelables| cancelables.find_each do |cancelable| @@ -136,14 +76,6 @@ module Ci .created_or_pending end - def commit - @commit ||= project.commit(origin_sha || origin_ref) - end - - def sha - commit.try(:id) - end - def before_sha params[:checkout_sha] || params[:before] || Gitlab::Git::BLANK_SHA end @@ -156,41 +88,17 @@ module Ci params[:ref] end - def branch? - return @is_branch if defined?(@is_branch) - - @is_branch = - project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref) - end - - def tag? - return @is_tag if defined?(@is_tag) - - @is_tag = - project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref) + def tag_exists? + project.repository.tag_exists?(ref) end def ref @ref ||= Gitlab::Git.ref_name(origin_ref) end - def valid_sha? - origin_sha && origin_sha != Gitlab::Git::BLANK_SHA - end - - def error(message, save: false) - pipeline.tap do - pipeline.errors.add(:base, message) - - if save - pipeline.drop - update_merge_requests_head_pipeline - end - end - end - def pipeline_created_counter - @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created") + @pipeline_created_counter ||= Gitlab::Metrics + .counter(:pipelines_created_total, "Counter of pipelines created") end end end diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb index ace49889097..7f591c89411 100644 --- a/app/services/emails/base_service.rb +++ b/app/services/emails/base_service.rb @@ -1,7 +1,8 @@ module Emails class BaseService - def initialize(user, opts) - @user = user + def initialize(current_user, opts) + @current_user = current_user + @user = opts.delete(:user) @email = opts[:email] end end diff --git a/app/services/emails/destroy_service.rb b/app/services/emails/destroy_service.rb index d586b9dfe0c..44011cc36c8 100644 --- a/app/services/emails/destroy_service.rb +++ b/app/services/emails/destroy_service.rb @@ -1,13 +1,13 @@ module Emails class DestroyService < ::Emails::BaseService def execute - Email.find_by_email!(@email).destroy && update_secondary_emails! + update_secondary_emails! if Email.find_by_email!(@email).destroy end private def update_secondary_emails! - result = ::Users::UpdateService.new(@user).execute do |user| + result = ::Users::UpdateService.new(@current_user, user: @user).execute do |user| user.update_secondary_emails! end diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 07cbd8f92a9..bf26859dd6d 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -14,13 +14,13 @@ module MergeRequests @merge_request = merge_request unless @merge_request.mergeable? - return log_merge_error('Merge request is not mergeable', save_message_on_model: true) + return handle_merge_error(log_message: 'Merge request is not mergeable', save_message_on_model: true) end @source = find_merge_source unless @source - return log_merge_error('No source for merge', save_message_on_model: true) + return handle_merge_error(log_message: 'No source for merge', save_message_on_model: true) end merge_request.in_locked_state do @@ -31,8 +31,7 @@ module MergeRequests end end rescue MergeError => e - clean_merge_jid - log_merge_error(e.message, save_message_on_model: true) + handle_merge_error(log_message: e.message, save_message_on_model: true) end private @@ -74,10 +73,16 @@ module MergeRequests @merge_request.force_remove_source_branch? ? @merge_request.author : current_user end - def log_merge_error(message, save_message_on_model: false) - Rails.logger.error("MergeService ERROR: #{merge_request_info} - #{message}") + # Logs merge error message and cleans `MergeRequest#merge_jid`. + # + def handle_merge_error(log_message:, save_message_on_model: false) + Rails.logger.error("MergeService ERROR: #{merge_request_info} - #{log_message}") - @merge_request.update(merge_error: message) if save_message_on_model + if save_message_on_model + @merge_request.update(merge_error: log_message, merge_jid: nil) + else + clean_merge_jid + end end def merge_request_info diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index 261a8bfa200..b1d6bac4d4a 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -14,6 +14,7 @@ module MergeRequests notification_service.merge_mr(merge_request, current_user) execute_hooks(merge_request, 'merge') invalidate_cache_counts(merge_request, users: merge_request.assignees) + merge_request.update_project_counter_caches end private diff --git a/app/services/projects/hashed_storage_migration_service.rb b/app/services/projects/hashed_storage_migration_service.rb new file mode 100644 index 00000000000..41259de3a16 --- /dev/null +++ b/app/services/projects/hashed_storage_migration_service.rb @@ -0,0 +1,68 @@ +module Projects + class HashedStorageMigrationService < BaseService + include Gitlab::ShellAdapter + + attr_reader :old_disk_path, :new_disk_path + + def initialize(project, logger = nil) + @project = project + @logger ||= Rails.logger + end + + def execute + return if project.hashed_storage? + + @old_disk_path = project.disk_path + has_wiki = project.wiki.repository_exists? + + project.storage_version = Storage::HashedProject::STORAGE_VERSION + project.ensure_storage_path_exists + + @new_disk_path = project.disk_path + + result = move_repository(@old_disk_path, @new_disk_path) + + if has_wiki + result &&= move_repository("#{@old_disk_path}.wiki", "#{@new_disk_path}.wiki") + end + + unless result + rollback_folder_move + return + end + + project.repository_read_only = false + project.save! + + block_given? ? yield : result + end + + private + + def move_repository(from_name, to_name) + from_exists = gitlab_shell.exists?(project.repository_storage_path, "#{from_name}.git") + to_exists = gitlab_shell.exists?(project.repository_storage_path, "#{to_name}.git") + + # If we don't find the repository on either original or target we should log that as it could be an issue if the + # project was not originally empty. + if !from_exists && !to_exists + logger.warn "Can't find a repository on either source or target paths for #{project.full_path} (ID=#{project.id}) ..." + return false + elsif !from_exists + # Repository have been moved already. + return true + end + + gitlab_shell.mv_repository(project.repository_storage_path, from_name, to_name) + end + + def rollback_folder_move + move_repository(@new_disk_path, @old_disk_path) + move_repository("#{@new_disk_path}.wiki", "#{@old_disk_path}.wiki") + end + + def logger + @logger + end + end +end diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb index b3f4a72d6fe..cc76d0df3a1 100644 --- a/app/services/tags/create_service.rb +++ b/app/services/tags/create_service.rb @@ -11,7 +11,7 @@ module Tags begin new_tag = repository.add_tag(current_user, tag_name, target, message) - rescue Rugged::TagError + rescue Gitlab::Git::Repository::TagExistsError return error("Tag #{tag_name} already exists") rescue Gitlab::Git::HooksService::PreReceiveError => ex return error(ex.message) diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb index 6188b8a4349..15ca1a55a5b 100644 --- a/app/services/users/update_service.rb +++ b/app/services/users/update_service.rb @@ -2,22 +2,21 @@ module Users class UpdateService < BaseService include NewUserNotifier - def initialize(user, params = {}) - @user = user + def initialize(current_user, params = {}) + @current_user = current_user + @user = params.delete(:user) @params = params.dup end def execute(validate: true, &block) yield(@user) if block_given? - assign_attributes(&block) - user_exists = @user.persisted? - if @user.save(validate: validate) - notify_new_user(@user, nil) unless user_exists + assign_attributes(&block) - success + if @user.save(validate: validate) + notify_success(user_exists) else error(@user.errors.full_messages.uniq.join('. ')) end @@ -33,6 +32,12 @@ module Users private + def notify_success(user_exists) + notify_new_user(@user, nil) unless user_exists + + success + end + def assign_attributes(&block) if @user.user_synced_attributes_metadata params.except!(*@user.user_synced_attributes_metadata.read_only_attributes) diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index e5842bd1ea0..3ef8f2a3acb 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Background Jobs" -= render 'admin/monitoring/head' %div{ class: container_class } %h3.page-title Background Jobs diff --git a/app/views/admin/cohorts/index.html.haml b/app/views/admin/cohorts/index.html.haml index bff53da1d9a..5e9a8c083af 100644 --- a/app/views/admin/cohorts/index.html.haml +++ b/app/views/admin/cohorts/index.html.haml @@ -1,6 +1,5 @@ - breadcrumb_title "Cohorts" - @no_container = true -= render "admin/dashboard/head" %div{ class: container_class } - if @cohorts diff --git a/app/views/admin/conversational_development_index/show.html.haml b/app/views/admin/conversational_development_index/show.html.haml index 833d4c612f8..30dd87f0463 100644 --- a/app/views/admin/conversational_development_index/show.html.haml +++ b/app/views/admin/conversational_development_index/show.html.haml @@ -1,8 +1,6 @@ - @no_container = true - page_title 'ConvDev Index' -= render 'admin/monitoring/head' - .container - if show_callout?('convdev_intro_callout_dismissed') = render 'callout' diff --git a/app/views/admin/dashboard/_head.html.haml b/app/views/admin/dashboard/_head.html.haml deleted file mode 100644 index c2151710884..00000000000 --- a/app/views/admin/dashboard/_head.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - = nav_link(controller: :dashboard, html_options: {class: 'home'}) do - = link_to admin_root_path, title: 'Overview' do - %span - Overview - = nav_link(controller: [:admin, :projects]) do - = link_to admin_projects_path, title: 'Projects' do - %span - Projects - = nav_link(controller: :users) do - = link_to admin_users_path, title: 'Users' do - %span - Users - = nav_link(controller: :groups) do - = link_to admin_groups_path, title: 'Groups' do - %span - Groups - = nav_link path: 'builds#index' do - = link_to admin_jobs_path, title: 'Jobs' do - %span - Jobs - = nav_link path: ['runners#index', 'runners#show'] do - = link_to admin_runners_path, title: 'Runners' do - %span - Runners - = nav_link path: 'cohorts#index' do - = link_to admin_cohorts_path, title: 'Cohorts' do - %span - Cohorts - = nav_link(controller: :conversational_development_index) do - = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do - %span - ConvDev Index diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index d212c7ca965..2f0143c7eff 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - breadcrumb_title "Dashboard" -= render "admin/dashboard/head" %div{ class: container_class } .admin-dashboard.prepend-top-default diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index e5f380c78e2..535251fef5e 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Groups" -= render "admin/dashboard/head" %div{ class: container_class } .top-area diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml index 517db50b97f..10a3bed0a4f 100644 --- a/app/views/admin/health_check/show.html.haml +++ b/app/views/admin/health_check/show.html.haml @@ -1,7 +1,6 @@ - @no_container = true - page_title _('Health Check') - no_errors = @errors.blank? && @failing_storage_statuses.blank? -= render 'admin/monitoring/head' %div{ class: container_class } %h3.page-title= page_title diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index aa6e9db3900..0310498ae54 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -1,6 +1,5 @@ - breadcrumb_title "Jobs" - @no_container = true -= render "admin/dashboard/head" %div{ class: container_class } diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index ee87f25a225..78757b6384f 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Logs" -= render 'admin/monitoring/head' %div{ class: container_class } %ul.nav-links.log-tabs diff --git a/app/views/admin/monitoring/_head.html.haml b/app/views/admin/monitoring/_head.html.haml deleted file mode 100644 index b3530915068..00000000000 --- a/app/views/admin/monitoring/_head.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - = nav_link(controller: :system_info) do - = link_to admin_system_info_path, title: 'System Info' do - %span - System Info - = nav_link(controller: :background_jobs) do - = link_to admin_background_jobs_path, title: 'Background Jobs' do - %span - Background Jobs - = nav_link(controller: :logs) do - = link_to admin_logs_path, title: 'Logs' do - %span - Logs - = nav_link(controller: :health_check) do - = link_to admin_health_check_path, title: 'Health Check' do - %span - Health Check - = nav_link(controller: :requests_profiles) do - = link_to admin_requests_profiles_path, title: 'Requests Profiles' do - %span - Requests Profiles diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 3301f55b8a8..3f202fbf4fe 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -2,7 +2,6 @@ - page_title "Projects" - params[:visibility_level] ||= [] -= render "admin/dashboard/head" %div{ class: container_class } .top-area diff --git a/app/views/admin/requests_profiles/index.html.haml b/app/views/admin/requests_profiles/index.html.haml index b7db18b2d32..cb02a750490 100644 --- a/app/views/admin/requests_profiles/index.html.haml +++ b/app/views/admin/requests_profiles/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title 'Requests Profiles' -= render 'admin/monitoring/head' %div{ class: container_class } %h3.page-title diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 6793ce557c4..43cea1358cc 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -1,6 +1,5 @@ - breadcrumb_title "Runners" - @no_container = true -= render "admin/dashboard/head" %div{ class: container_class } .bs-callout diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml index fd0281e4961..6bf979a937e 100644 --- a/app/views/admin/system_info/show.html.haml +++ b/app/views/admin/system_info/show.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "System Info" -= render 'admin/monitoring/head' %div{ class: container_class } .prepend-top-default diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 5516134d8a0..38ce1564eff 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Users" -= render "admin/dashboard/head" %div{ class: container_class } .prepend-top-default diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml index a508b7537a2..4d1037807be 100644 --- a/app/views/devise/mailer/confirmation_instructions.html.haml +++ b/app/views/devise/mailer/confirmation_instructions.html.haml @@ -1,9 +1,10 @@ +- confirmation_link = confirmation_url(@resource, confirmation_token: @token) - if @resource.unconfirmed_email.present? #content = email_default_heading(@resource.unconfirmed_email) %p Click the link below to confirm your email address. #cta - = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) + = link_to confirmation_link, confirmation_link - else #content - if Gitlab.com? @@ -12,4 +13,4 @@ = email_default_heading("Welcome, #{@resource.name}!") %p To get started, click the link below to confirm your account. #cta - = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) + = link_to confirmation_link, confirmation_link diff --git a/app/views/groups/_head.html.haml b/app/views/groups/_head.html.haml deleted file mode 100644 index 0f63774fb9b..00000000000 --- a/app/views/groups/_head.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: container_class } - = nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do - = link_to group_path(@group), title: 'Group Home' do - %span - Home - - = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do - %span - Activity - -.hidden-xs - = render "projects/last_push" diff --git a/app/views/groups/_head_issues.html.haml b/app/views/groups/_head_issues.html.haml deleted file mode 100644 index d554bc23743..00000000000 --- a/app/views/groups/_head_issues.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: container_class } - = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do - = link_to issues_group_path(@group), title: 'List' do - %span - List - - = nav_link(path: 'labels#index') do - = link_to group_labels_path(@group), title: 'Labels' do - %span - Labels - - = nav_link(path: 'milestones#index') do - = link_to group_milestones_path(@group), title: 'Milestones' do - %span - Milestones diff --git a/app/views/groups/_settings_head.html.haml b/app/views/groups/_settings_head.html.haml deleted file mode 100644 index 623d233a46a..00000000000 --- a/app/views/groups/_settings_head.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: container_class } - = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), title: 'General' do - %span - General - - = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects' do - %span - Projects - - = nav_link(controller: :ci_cd) do - = link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do - %span - Pipelines diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml index 3969e56f937..cb7dab26332 100644 --- a/app/views/groups/activity.html.haml +++ b/app/views/groups/activity.html.haml @@ -2,7 +2,6 @@ = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") - page_title "Activity" -= render 'groups/head' %section.activities = render 'activities' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 0d3308833b7..15606dd30fd 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,5 +1,4 @@ - breadcrumb_title "General Settings" -= render "groups/settings_head" .panel.panel-default.prepend-top-default .panel-heading Group settings diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 7f411927429..07e64d9aeaf 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,6 +1,5 @@ - page_title "Issues" - group_issues_exists = group_issues(@group).exists? -= render "head_issues" = content_for :meta_tags do = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues") diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 89165096fe2..d10efdad53b 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,8 +1,5 @@ - page_title 'Labels' -= render "groups/head_issues" - - .top-area.adjust .nav-text Labels can be applied to issues and merge requests. Group labels are available for any project within the group. diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index ed582e521c4..cb4fc69d5b8 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,7 +1,5 @@ - page_title "Milestones" -= render "groups/head_issues" - .top-area = render 'shared/milestones_filter', counts: @milestone_states diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 7f3f2f707f7..8d2bc810a7d 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,5 +1,4 @@ - breadcrumb_title "Projects" -= render "groups/settings_head" .panel.panel-default.prepend-top-default .panel-heading diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml index 9f9ae01e7c5..472da2a6a72 100644 --- a/app/views/groups/settings/ci_cd/show.html.haml +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -1,5 +1,4 @@ - breadcrumb_title "CI / CD Settings" - page_title "CI / CD" -= render "groups/settings_head" = render 'ci/variables/index' diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index f4f76887422..3ca63f9c3e0 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -4,7 +4,6 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") -= render 'groups/head' = render 'groups/home_panel' .groups-header{ class: container_class } diff --git a/app/views/groups/subgroups.html.haml b/app/views/groups/subgroups.html.haml index 7abc84412c6..869b3b243c6 100644 --- a/app/views/groups/subgroups.html.haml +++ b/app/views/groups/subgroups.html.haml @@ -1,7 +1,6 @@ - breadcrumb_title "Details" - @no_container = true -= render 'head' = render 'groups/home_panel' .groups-header{ class: container_class } diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index e6a10e500a4..e3a9e99250e 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -32,10 +32,6 @@ = stylesheet_link_tag "test", media: "all" if Rails.env.test? = stylesheet_link_tag 'performance_bar' if performance_bar_enabled? - // TODO: Combine these 2 stylesheets into application.scss - = stylesheet_link_tag "new_nav", media: "all" - = stylesheet_link_tag "new_sidebar", media: "all" - = Gon::Base.render_data - if content_for?(:library_javascripts) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index cd7a47da4a1..29387d6627e 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -21,8 +21,8 @@ %a Loading... = dropdown_loading - %i.search-icon - %i.clear-icon.js-clear-input + = sprite_icon('search', size: 16, css_class: 'search-icon') + = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input') = hidden_field_tag :group_id, @group.try(:id), class: 'js-search-group-options', data: group_data_attrs diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index d8fc371497d..7e9b76da570 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -22,29 +22,29 @@ = render 'layouts/search' unless current_controller?(:search) %li.visible-sm-inline-block.visible-xs-inline-block = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('search') + = sprite_icon('search', size: 16) - if current_user - %li.user-counter + = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = custom_icon('issues') + = sprite_icon('issues', size: 16) - issues_count = assigned_issuables_count(:issues) %span.badge.issues-count{ class: ('hidden' if issues_count.zero?) } = number_with_delimiter(issues_count) - %li.user-counter + = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do = link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = custom_icon('mr_bold') + = sprite_icon('git-merge', size: 16) - merge_requests_count = assigned_issuables_count(:merge_requests) %span.badge.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) } = number_with_delimiter(merge_requests_count) - %li.user-counter + = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = custom_icon('todo_done') + = sprite_icon('todo-done', size: 16) %span.badge.todos-count{ class: ('hidden' if todos_pending_count.zero?) } = todos_count_format(todos_pending_count) %li.header-user.dropdown = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do = image_tag avatar_icon(current_user, 23), width: 23, height: 23, class: "header-user-avatar" - = custom_icon('caret_down') + = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu-nav.dropdown-menu-align-right %ul %li.current-user @@ -73,7 +73,7 @@ %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') + = sprite_icon('more', size: 16, css_class: 'more-icon js-navbar-toggle-right') + = sprite_icon('close', size: 16, css_class: 'close-icon js-navbar-toggle-left') = render 'shared/outdated_browser' diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index 63d1c077ecd..088f2785092 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -1,7 +1,7 @@ %li.header-new.dropdown = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do - = custom_icon('plus_square') - = custom_icon('caret_down') + = sprite_icon('plus-square', size: 16) + = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu-nav.dropdown-menu-align-right %ul - if @group&.persisted? diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index 7bd3f5306a2..002922e13f1 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -16,5 +16,5 @@ = breadcrumb_list_item link_to(extra[:text], extra[:link]) = render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after %li - %h2.breadcrumbs-sub-title= @breadcrumb_title + %h2.breadcrumbs-sub-title= link_to @breadcrumb_title, breadcrumb_title_link = yield :header_content diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index c254ee02dd8..e0d8d9cb402 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -2,7 +2,7 @@ = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects" }) do %a{ href: "#", data: { toggle: "dropdown" } } Projects - = custom_icon('caret_down') + = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.projects-dropdown-menu = render "layouts/nav/projects_dropdown/show" @@ -25,7 +25,7 @@ %li.header-more.dropdown.hidden-lg %a{ href: "#", data: { toggle: "dropdown" } } More - = custom_icon('caret_down') + = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu %ul = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "visible-xs" }) do @@ -54,7 +54,7 @@ - if current_user.admin? = nav_link(controller: 'admin/dashboard') do = link_to admin_root_path, class: 'admin-icon', title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('wrench fw') + = sprite_icon('admin', size: 18) - if Gitlab::Sherlock.enabled? %li = link_to sherlock_transactions_path, class: 'admin-icon', title: 'Sherlock Transactions', diff --git a/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml b/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml index 28022eebb19..610ff9001f7 100644 --- a/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml +++ b/app/views/layouts/nav/breadcrumbs/_collapsed_dropdown.html.haml @@ -4,7 +4,7 @@ %li.dropdown %button.text-expander.has-tooltip.js-breadcrumbs-collapsed-expander{ type: "button", data: { toggle: "dropdown", container: "body" }, "aria-label": button_tooltip, title: button_tooltip } = icon("ellipsis-h") - = icon("angle-right", class: "breadcrumbs-list-angle") + = sprite_icon("angle-right", css_class: "breadcrumbs-list-angle") .dropdown-menu %ul - @breadcrumb_dropdown_links[dropdown_location].each_with_index do |link, index| diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 9eef006b6a8..0ec07605631 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -3,7 +3,7 @@ .context-header = link_to admin_root_path, title: 'Admin Overview' do .avatar-container.s40.settings-avatar - = icon('wrench') + = sprite_icon('admin', size: 24) .sidebar-context-title Admin Area %ul.sidebar-top-level-items = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: {class: 'home'}) do @@ -12,7 +12,6 @@ = sprite_icon('overview') %span.nav-item-name Overview - %ul.sidebar-sub-level-items = nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do = link_to admin_root_path do diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index a015c94c60e..458b5010d36 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -3,7 +3,7 @@ .context-header = link_to profile_path, title: 'Profile Settings' do .avatar-container.s40.settings-avatar - = icon('user') + = sprite_icon('user', size: 24) .sidebar-context-title User Settings %ul.sidebar-top-level-items = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do diff --git a/app/views/projects/_head.html.haml b/app/views/projects/_head.html.haml deleted file mode 100644 index dba84838a52..00000000000 --- a/app/views/projects/_head.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: container_class } - = nav_link(path: 'projects#show') do - = link_to project_path(@project), title: _('Project home'), class: 'shortcuts-project' do - %span= _('Home') - - = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do - %span= _('Activity') - - - if can?(current_user, :read_cycle_analytics, @project) - = nav_link(path: 'cycle_analytics#show') do - = link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do - %span= _('Cycle Analytics') diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index f80dadb8037..d0ab39033cf 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -2,8 +2,6 @@ - page_title _("Activity") -= render "projects/head" - = render 'projects/last_push' = render 'projects/activity' diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index 4cc3218d967..fe02cbcbf95 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -1,10 +1,9 @@ - breadcrumb_title _('Artifacts') - page_title @path.presence, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs' -= render "projects/pipelines/head" = render "projects/jobs/header", show_controls: false -- add_to_breadcrumbs(_('Jobs'), project_jobs_path(@project)) +- add_to_breadcrumbs(s_('CICD|Jobs'), project_jobs_path(@project)) - add_to_breadcrumbs("##{@build.id}", project_jobs_path(@project)) .tree-holder diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml index b85bbcb980e..2942d618a42 100644 --- a/app/views/projects/artifacts/file.html.haml +++ b/app/views/projects/artifacts/file.html.haml @@ -1,5 +1,4 @@ - page_title @path, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs' -= render "projects/pipelines/head" = render "projects/jobs/header", show_controls: false diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 60ac202bde0..e45861ac08d 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -1,7 +1,6 @@ - @no_container = true - project_duration = age_map_duration(@blame_groups, @project) - page_title "Blame", @blob.path, @ref -= render "projects/commits/head" %div{ class: container_class } #blob-content-holder.tree-holder diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 992fe7f717f..626cbc9e41d 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -4,7 +4,6 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') = page_specific_javascript_bundle_tag('blob') -= render "projects/commits/head" %div{ class: container_class } - if @conflict diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 240e62d5ac5..c4712bf3736 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -2,7 +2,6 @@ - @no_container = true - page_title @blob.path, @ref -= render "projects/commits/head" - content_for :page_specific_javascripts do = webpack_bundle_tag 'blob' diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index ea6e7e9db6c..7d9645d79e6 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title _('Branches') -= render "projects/commits/head" %div{ class: container_class } .top-area.adjust diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index f45cc7f0f45..f880556a9f7 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -4,12 +4,11 @@ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do = custom_icon('icon_fork') %span= s_('GoToYourFork|Fork') - - elsif !current_user.can_create_project? - = link_to new_project_fork_path(@project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do - = custom_icon('icon_fork') - %span= s_('CreateNewFork|Fork') - else - = link_to new_project_fork_path(@project), class: 'btn' do + - can_create_fork = current_user.can?(:create_fork) + = link_to new_project_fork_path(@project), + class: "btn btn-default #{'has-tooltip disabled' unless can_create_fork}", + title: (_('You have reached your project limit') unless can_create_fork) do = custom_icon('icon_fork') %span= s_('CreateNewFork|Fork') .count-with-arrow diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 717de85c5d2..abb292f8f27 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -6,7 +6,6 @@ - @content_class = limited_container_width - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description -= render "projects/commits/head" .container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml deleted file mode 100644 index e1549baef89..00000000000 --- a/app/views/projects/commits/_head.html.haml +++ /dev/null @@ -1,36 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do - = link_to project_tree_path(@project) do - #{ _('Files') } - - = nav_link(controller: [:commit, :commits]) do - = link_to project_commits_path(@project, current_ref) do - #{ _('Commits') } - - = nav_link(html_options: {class: branches_tab_class}) do - = link_to project_branches_path(@project) do - #{ _('Branches') } - - = nav_link(controller: [:tags, :releases]) do - = link_to project_tags_path(@project) do - #{ _('Tags') } - - = nav_link(path: 'graphs#show') do - = link_to project_graph_path(@project, current_ref) do - #{ _('Contributors') } - - = nav_link(controller: %w(network)) do - = link_to project_network_path(@project, current_ref) do - #{ s_('ProjectNetworkGraph|Graph') } - - = nav_link(controller: :compare) do - = link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do - #{ _('Compare') } - - = nav_link(path: 'graphs#charts') do - = link_to charts_project_graph_path(@project, current_ref) do - #{ _('Charts') } diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index e873b931683..ef305120525 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -5,9 +5,6 @@ = 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 :sub_nav do - = render "head" - .js-project-commits-show{ 'data-commits-limit' => @limit } %div{ class: container_class } .tree-holder diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 1ce3ad0c0fd..3ad0166e9cd 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,7 +1,6 @@ - @no_container = true - breadcrumb_title "Compare Revisions" - page_title "Compare" -= render "projects/commits/head" %div{ class: container_class } .sub-header-block diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 7cc42455394..f87f1d476f5 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,7 +1,6 @@ - @no_container = true - add_to_breadcrumbs "Compare Revisions", project_compare_index_path(@project) - page_title "#{params[:from]}...#{params[:to]}" -= render "projects/commits/head" %div{ class: container_class } .sub-header-block.no-bottom-space diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 8d008be5aae..c06e9f323af 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -4,8 +4,6 @@ = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('cycle_analytics') -= render "projects/head" - #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data .landing.content-block{ "v-if" => "!isOverviewDialogDismissed" } diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 0a3045604f4..8ae4fd94146 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -3,8 +3,6 @@ - @content_class = "limit-container-width" unless fluid_layout - expanded = Rails.env.test? -= render "projects/settings/head" - .project-edit-container %section.settings.general-settings .settings-header diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index d5b83b53ebb..3f3ce10419f 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -3,7 +3,6 @@ = render partial: 'flash_messages', locals: { project: @project } -= render "projects/head" = render "home_panel" .row-content-block.second-block.center diff --git a/app/views/projects/environments/edit.html.haml b/app/views/projects/environments/edit.html.haml index 3871165763c..d6ff3f729b4 100644 --- a/app/views/projects/environments/edit.html.haml +++ b/app/views/projects/environments/edit.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Edit", @environment.name, "Environments" -= render "projects/pipelines/head" %div{ class: container_class } %h3.page-title diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index f7e3733ba0b..1bcc955ddc8 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Environments" -= render "projects/pipelines/head" - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index acc80b49dd0..2e85f608823 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -1,7 +1,6 @@ - @no_container = true - page_title "Environments" - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) -= render "projects/pipelines/head" - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 4a65b46f029..e0aedcac5e1 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -4,7 +4,6 @@ = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_d3' = webpack_bundle_tag 'monitoring' -= render "projects/pipelines/head" .prometheus-container{ class: container_class } .top-area @@ -21,4 +20,3 @@ "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect'), "additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json), "has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } } - diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml index 88f43a1e7e4..62b08e85e22 100644 --- a/app/views/projects/environments/new.html.haml +++ b/app/views/projects/environments/new.html.haml @@ -1,7 +1,6 @@ - @no_container = true - breadcrumb_title "Environments" - page_title 'New Environment' -= render "projects/pipelines/head" %div{ class: container_class } %h3.page-title diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index c35d1b5aaee..d7859c9fbeb 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -2,7 +2,6 @@ - add_to_breadcrumbs "Environments", project_environments_path(@project) - breadcrumb_title @environment.name - page_title "Environments" -= render "projects/pipelines/head" %div{ class: container_class } .row.top-area.adjust diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index 464135b5ac7..a073a164f11 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Terminal for environment", @environment.name -= render "projects/pipelines/head" - content_for :page_specific_javascripts do = stylesheet_link_tag "xterm/xterm" diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 021575160ea..a3467eb6f05 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -1,5 +1,4 @@ - page_title "Find File", @ref -= render "projects/commits/head" .file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) } .nav-block diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 0f36e1a7353..906774a21e3 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -13,7 +13,7 @@ - if @namespaces.present? %label.label-light %span - Click to fork the project to a user or group + Click to fork the project - @namespaces.in_groups_of(6, false) do |group| .row - group.each do |namespace| @@ -29,8 +29,12 @@ .caption = namespace.human_name - else - .fork-thumbnail - = link_to project_forks_path(@project, namespace_key: namespace.id), method: "POST" do + - can_create_project = current_user.can?(:create_projects, namespace) + .fork-thumbnail{ class: ("disabled" unless can_create_project) } + = link_to project_forks_path(@project, namespace_key: namespace.id), + method: "POST", + class: ("disabled has-tooltip" unless can_create_project), + title: (_('You have reached your project limit') unless can_create_project) do - if /no_((\w*)_)*avatar/.match(avatar) .no-avatar = icon 'question' diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index f0ef647ddb3..ffb9238a65a 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -4,7 +4,6 @@ = webpack_bundle_tag('common_d3') = webpack_bundle_tag('graphs') = webpack_bundle_tag('graphs_charts') -= render "projects/commits/head" .repo-charts{ class: container_class } %h4.sub-header diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 08b38428b50..70156c03e3c 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -5,8 +5,6 @@ = webpack_bundle_tag('graphs') = webpack_bundle_tag('graphs_show') -= render 'projects/commits/head' - .js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .sub-header-block .tree-ref-holder diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml index ab5a7b117d7..1cf4105bd27 100644 --- a/app/views/projects/hook_logs/show.html.haml +++ b/app/views/projects/hook_logs/show.html.haml @@ -1,5 +1,3 @@ -= render 'projects/settings/head' - .row.prepend-top-default.append-bottom-default .col-lg-3 %h4.prepend-top-0 diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml index c8c17d2d828..b1219f019d7 100644 --- a/app/views/projects/hooks/edit.html.haml +++ b/app/views/projects/hooks/edit.html.haml @@ -1,5 +1,4 @@ - page_title 'Integrations' -= render 'projects/settings/head' .row.prepend-top-default .col-lg-3 @@ -19,4 +18,3 @@ %hr = render partial: 'projects/hook_logs/index', locals: { hook: @hook, hook_logs: @hook_logs, project: @project } - diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml deleted file mode 100644 index e9f21594a71..00000000000 --- a/app/views/projects/issues/_head.html.haml +++ /dev/null @@ -1,33 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - - if project_nav_tab?(:issues) && !current_controller?(:merge_requests) - = nav_link(controller: :issues) do - = link_to project_issues_path(@project), title: 'Issues' do - %span - List - - = nav_link(controller: :boards) do - = link_to project_boards_path(@project), title: 'Board' do - %span - Board - - - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests) - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project), title: 'Merge Requests' do - %span - Merge Requests - - - if project_nav_tab? :labels - = nav_link(controller: :labels) do - = link_to project_labels_path(@project), title: 'Labels' do - %span - Labels - - - if project_nav_tab? :milestones - = nav_link(controller: :milestones) do - = link_to project_milestones_path(@project), title: 'Milestones' do - %span - Milestones diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index e72c94695bc..bfaf024428d 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -3,8 +3,6 @@ - page_title "Issues" - new_issue_email = @project.new_issue_address(current_user) -= content_for :sub_nav do - = render "projects/issues/head" - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index 8604c7d3ea4..4a238b99b58 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Jobs" -= render "projects/pipelines/head" %div{ class: container_class } .top-area diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 975c08c06e6..ce0e3872240 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -2,7 +2,6 @@ - add_to_breadcrumbs "Jobs", project_jobs_path(@project) - breadcrumb_title "##{@build.id}" - page_title "#{@build.name} (##{@build.id})", "Jobs" -= render "projects/pipelines/head" %div{ class: container_class } .build-page.js-build-page diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 84b0b65d1c0..b8ee4305142 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Edit", @label.name, "Labels" -= render "shared/mr_head" %div{ class: container_class } %h3.page-title diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 10d07ce8e45..80e4dce1a80 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -3,8 +3,6 @@ - hide_class = '' - can_admin_label = can?(current_user, :admin_label, @project) -= render "shared/mr_head" - - if @labels.exists? || @prioritized_labels.exists? %div{ class: container_class } .top-area.adjust diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 562b6fb8d8c..02f59f30a39 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,7 +1,6 @@ - @no_container = true - breadcrumb_title "Labels" - page_title "New Label" -= render "shared/mr_head" %div{ class: container_class } %h3.page-title diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml deleted file mode 100644 index 1e505222887..00000000000 --- a/app/views/projects/merge_requests/_head.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - = nav_link(controller: :merge_requests) do - = link_to project_merge_requests_path(@project), title: 'Merge Requests' do - %span - List - - - if project_nav_tab? :labels - = nav_link(controller: :labels) do - = link_to project_labels_path(@project), title: 'Labels' do - %span - Labels - - - if project_nav_tab? :milestones - = nav_link(controller: :milestones) do - = link_to project_milestones_path(@project), title: 'Milestones' do - %span - Milestones diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 2c53891a92d..6b8dcb3e60b 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -4,9 +4,6 @@ - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project - page_title "Merge Requests" -- unless @project.issues_enabled? - = content_for :sub_nav do - = render "projects/merge_requests/head" - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index 1e66c6079e3..af3f25c6a30 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Edit", @milestone.title, "Milestones" -= render "shared/mr_head" %div{ class: container_class } diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index f3abecdd302..fcbf7cb802b 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,8 +1,6 @@ - @no_container = true - page_title 'Milestones' -= render "shared/mr_head" - %div{ class: container_class } .top-area = render 'shared/milestones_filter', counts: milestone_counts(@project.milestones) diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 84ffbc0a926..c301f517013 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,7 +1,6 @@ - @no_container = true - breadcrumb_title "Milestones" - page_title "New Milestone" -= render "shared/mr_head" %div{ class: container_class } %h3.page-title diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 1f5f18801ad..a5153df1159 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -3,7 +3,6 @@ - breadcrumb_title @milestone.title - page_title @milestone.title, "Milestones" - page_description @milestone.description -= render "shared/mr_head" %div{ class: container_class } .detail-page-header.milestone-page-header diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index e29cb277389..8a19497c55b 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -2,7 +2,6 @@ - page_title "Graph", @ref - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('network') -= render "projects/commits/head" = render "head" %div{ class: container_class } .project-network diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index de76832331a..4961835f12a 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -1,7 +1,8 @@ +- access = note_max_access_for_user(note) - if note.has_special_role?(Note::SpecialRole::FIRST_TIME_CONTRIBUTOR) - %span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project. Handle with care.") } + %span.note-role.note-role-special.has-tooltip{ title: _("This is the author's first Merge Request to this project.") } = issuable_first_contribution_icon -- if access = note_max_access_for_user(note) +- if access.nonzero? %span.note-role.note-role-access= Gitlab::Access.human_access(access) - if note.resolvable? diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 098b0ef56ef..04e647c0dc6 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,5 +1,4 @@ - page_title 'Pages' -= render "projects/settings/head" %h3.page_title Pages diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index 2b081786b6a..4fbdd1dd5e4 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -7,8 +7,6 @@ - @no_container = true - page_title _("Pipeline Schedules") -= render "projects/pipelines/head" - %div{ class: container_class } #pipeline-schedules-callout{ data: { docs_url: help_page_path('user/project/pipelines/schedules') } } .top-area diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml deleted file mode 100644 index ee2f236cec4..00000000000 --- a/app/views/projects/pipelines/_head.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: (container_class) } - - if project_nav_tab? :pipelines - = nav_link(path: ['pipelines#index', 'pipelines#show']) do - = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do - %span - Pipelines - - - if project_nav_tab? :builds - = nav_link(controller: [:jobs, :artifacts]) do - = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do - %span - Jobs - - - if project_nav_tab? :pipelines - = nav_link(controller: :pipeline_schedules) do - = link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do - %span - Schedules - - - if project_nav_tab? :environments - = nav_link(controller: :environments) do - = link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do - %span - Environments - - - if @project.feature_available?(:builds, current_user) && !@project.empty_repo? - = nav_link(path: 'pipelines#charts') do - = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do - %span - Charts diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index 487ac87186d..ba55bc23add 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -4,7 +4,6 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_d3') = page_specific_javascript_bundle_tag('graphs') -= render 'head' %div{ class: container_class } .sub-header-block diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 4f53efcf791..a10a7c23924 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -1,6 +1,5 @@ - @no_container = true - page_title "Pipelines" -= render "projects/pipelines/head" %div{ 'class' => container_class } - if show_auto_devops_callout?(@project) diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 7cc9fe79afd..2174154b207 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -2,7 +2,6 @@ - add_to_breadcrumbs "Pipelines", project_pipelines_path(@project) - breadcrumb_title "##{@pipeline.id}" - page_title "Pipeline" -= render "projects/pipelines/head" .js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } } - if @commit diff --git a/app/views/projects/pipelines_settings/_show.html.haml b/app/views/projects/pipelines_settings/_show.html.haml index 21d01242c0e..77211099830 100644 --- a/app/views/projects/pipelines_settings/_show.html.haml +++ b/app/views/projects/pipelines_settings/_show.html.haml @@ -26,7 +26,8 @@ %strong Disable Auto DevOps %br %span.descr - An explicit <code>.gitlab-ci.yml</code> needs to be specified before you can begin using Continious Integration and Delivery. + An explicit <code>.gitlab-ci.yml</code> needs to be specified before you can begin using Continuous Integration and Delivery. + .radio = form.label :enabled_nil do = form.radio_button :enabled, '' diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index c786298e341..4d962f9433f 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -2,7 +2,6 @@ - add_to_breadcrumbs "Tags", project_tags_path(@project) - breadcrumb_title @tag.name - page_title "Edit", @tag.name, "Tags" -= render "projects/commits/head" %div{ class: container_class } .sub-header-block.no-bottom-space diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml index 3e2a24a4c32..25770df1c90 100644 --- a/app/views/projects/services/edit.html.haml +++ b/app/views/projects/services/edit.html.haml @@ -2,5 +2,4 @@ - page_title @service.title, "Services" - add_to_breadcrumbs("Settings", edit_project_path(@project)) -= render "projects/settings/head" = render 'form' diff --git a/app/views/projects/settings/_head.html.haml b/app/views/projects/settings/_head.html.haml deleted file mode 100644 index 7d24c6a9122..00000000000 --- a/app/views/projects/settings/_head.html.haml +++ /dev/null @@ -1,30 +0,0 @@ -= content_for :sub_nav do - .scrolling-tabs-container.sub-nav-scroll - = render 'shared/nav_scroll' - .nav-links.sub-nav.scrolling-tabs - %ul{ class: container_class } - - can_edit = can?(current_user, :admin_project, @project) - - if can_edit - = nav_link(controller: :projects) do - = link_to edit_project_path(@project), title: 'General' do - %span - General - - if can_edit - = nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do - = link_to project_settings_integrations_path(@project), title: 'Integrations' do - %span - Integrations - = nav_link(controller: :repository) do - = link_to project_settings_repository_path(@project), title: 'Repository' do - %span - Repository - - if @project.feature_available?(:builds, current_user) - = nav_link(controller: :ci_cd) do - = link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do - %span - Pipelines - - if @project.pages_available? - = nav_link(controller: :pages) do - = link_to project_pages_path(@project), title: 'Pages' do - %span - Pages diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 47c056d097a..62455d0d40d 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -2,8 +2,6 @@ - page_title "CI / CD Settings" - page_title "CI / CD" -= render "projects/settings/head" - - expanded = Rails.env.test? %section.settings#js-general-pipeline-settings diff --git a/app/views/projects/settings/integrations/show.html.haml b/app/views/projects/settings/integrations/show.html.haml index 933daa7f549..2f1a548e119 100644 --- a/app/views/projects/settings/integrations/show.html.haml +++ b/app/views/projects/settings/integrations/show.html.haml @@ -1,6 +1,5 @@ - @content_class = "limit-container-width" unless fluid_layout - breadcrumb_title "Integrations Settings" - page_title 'Integrations' -= render "projects/settings/head" = render 'projects/hooks/index' = render 'projects/services/index' diff --git a/app/views/projects/settings/members/show.html.haml b/app/views/projects/settings/members/show.html.haml index 1e7695ac397..ea2cd36b212 100644 --- a/app/views/projects/settings/members/show.html.haml +++ b/app/views/projects/settings/members/show.html.haml @@ -1,6 +1,5 @@ - @content_class = "limit-container-width" unless fluid_layout - page_title "Members" -= render "projects/settings/head" = render "projects/project_members/index" diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 6d4af72b8ea..517d51993d2 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -2,8 +2,6 @@ - page_title "Repository" - @content_class = "limit-container-width" unless fluid_layout -= render "projects/settings/head" - - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('deploy_keys') diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index d8f5114f4b5..705a4607ad2 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -7,7 +7,6 @@ = render partial: 'flash_messages', locals: { project: @project } -= render "projects/head" = render "projects/last_push" = render "home_panel" diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index a6fe02fcae0..27d58d4c0e8 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -2,7 +2,6 @@ - @sort ||= sort_value_recently_updated - page_title "Tags" - add_to_breadcrumbs("Repository", project_tree_path(@project)) -= render "projects/commits/head" .flex-list{ class: container_class } .top-area.adjust diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 5d6eb4f4026..43aa2b27af6 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -2,7 +2,6 @@ - add_to_breadcrumbs "Tags", project_tags_path(@project) - breadcrumb_title @tag.name - page_title @tag.name, "Tags" -= render "projects/commits/head" %div{ class: container_class } .top-area.multi-line diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 853e2a6e7ec..6cddc38d11a 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -1,10 +1,6 @@ .tree-ref-container .tree-ref-holder = render 'shared/ref_switcher', destination: 'tree', path: @path - - if show_new_repo? - .tree-ref-target-holder.js-tree-ref-target-holder - = icon('long-arrow-right', title: 'to target branch') - = render 'shared/target_switcher', destination: 'tree', path: @path - unless show_new_repo? = render 'projects/tree/old_tree_header' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index d84a1fd7ee1..0cc6674842a 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -11,10 +11,8 @@ = webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'repo' -= render "projects/commits/head" - %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } - - if show_auto_devops_callout?(@project) + - if show_auto_devops_callout?(@project) && !show_new_repo? = render 'shared/auto_devops_callout' = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index e5a1fccf9ba..4e265bf733a 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,4 +1,5 @@ -- commit_message = @page.persisted? ? "Update #{@page.title}" : "Create #{@page.title}" +- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}") +- commit_message = commit_message % { page_title: @page.title } = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form common-note-form prepend-top-default js-quick-submit' } do |f| = form_errors(@page) @@ -12,13 +13,13 @@ .form-group .col-sm-12= f.label :format, class: 'control-label-full-width' .col-sm-12 - = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control" + = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: 'form-control' .form-group .col-sm-12= f.label :content, class: 'control-label-full-width' .col-sm-12 = render layout: 'projects/md_preview', locals: { url: project_wiki_preview_markdown_path(@project, @page.slug) } do - = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: 'Write your content or drag files here...' + = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: s_("WikiPage|Write your content or drag files here...") = render 'shared/notes/hints' .clearfix @@ -26,12 +27,11 @@ .help-block = succeed '.' do - To link to a (new) page, simply type - %code [Link Title](page-slug) + = (s_("WikiMarkdownTip|To link to a (new) page, simply type %{link_example}") % { link_example: '<code>[Link Title](page-slug)</code>' }).html_safe = succeed '.' do - More examples are in the - = link_to 'documentation', help_page_path("user/markdown", anchor: "wiki-specific-markdown") + - markdown_link = link_to s_("WikiMarkdownDocs|documentation"), help_page_path('user/markdown', anchor: 'wiki-specific-markdown') + = (s_("WikiMarkdownDocs|More examples are in the %{docs_link}") % { docs_link: markdown_link }).html_safe .form-group .col-sm-12= f.label :commit_message, class: 'control-label-full-width' @@ -39,10 +39,10 @@ .form-actions - if @page && @page.persisted? - = f.submit 'Save changes', class: "btn-save btn" + = f.submit _("Save changes"), class: 'btn-save btn' .pull-right - = link_to "Cancel", project_wiki_path(@project, @page), class: "btn btn-cancel btn-grouped" + = link_to _("Cancel"), project_wiki_path(@project, @page), class: 'btn btn-cancel btn-grouped' - else - = f.submit 'Create page', class: "btn-create btn" + = f.submit s_("Wiki|Create page"), class: 'btn-create btn' .pull-right - = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel" + = link_to _("Cancel"), project_wiki_path(@project, :home), class: 'btn btn-cancel' diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 3bbd8042c3a..cadda0a33c2 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -1,9 +1,9 @@ - if (@page && @page.persisted?) - if can?(current_user, :create_wiki, @project) = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do - New page + = s_("Wiki|New page") = link_to project_wiki_history_path(@project, @page), class: "btn" do - Page history + = s_("Wiki|Page history") - if can?(current_user, :create_wiki, @project) && @page.latest? = link_to project_wiki_edit_path(@project, @page), class: "btn js-wiki-edit" do - Edit + = _("Edit") diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index 13dd8461433..06a3cac12d5 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -3,16 +3,15 @@ .modal-content .modal-header %a.close{ href: "#", "data-dismiss" => "modal" } × - %h3.page-title New Wiki Page + %h3.page-title= s_("WikiNewPageTitle|New Wiki Page") .modal-body %form.new-wiki-page .form-group = label_tag :new_wiki_path do - %span Page slug - = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project), autofocus: true + %span= s_("WikiPage|Page slug") + = text_field_tag :new_wiki_path, nil, placeholder: s_("WikiNewPagePlaceholder|how-to-setup"), class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project), autofocus: true %span.new-wiki-page-slug-tip = icon('lightbulb-o') - Tip: You can specify the full path for the new file. - We will automatically create any missing directories. + = s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.") .form-actions - = button_tag 'Create page', class: 'build-new-wiki btn btn-create' + = button_tag s_("Wiki|Create page"), class: "build-new-wiki btn btn-create" diff --git a/app/views/projects/wikis/_pages_wiki_page.html.haml b/app/views/projects/wikis/_pages_wiki_page.html.haml index 7c2f562d422..0a1ccbc5f1c 100644 --- a/app/views/projects/wikis/_pages_wiki_page.html.haml +++ b/app/views/projects/wikis/_pages_wiki_page.html.haml @@ -2,4 +2,4 @@ = link_to wiki_page.title, project_wiki_path(@project, wiki_page) %small (#{wiki_page.format}) .pull-right - %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} + %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.commit.authored_date) }).html_safe diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index f7283ae4739..5b781294d68 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -8,7 +8,7 @@ = link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do = succeed ' ' do = icon('cloud-download') - Clone repository + = _("Clone repository") .blocks-container .block.block-first @@ -17,6 +17,6 @@ .block = link_to project_wikis_pages_path(@project), class: 'btn btn-block' do - More Pages + = s_("Wiki|More Pages") = render 'projects/wikis/new' diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 8fd60216536..0d77e5bd16d 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,11 +1,10 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout -- page_title "Edit", @page.title.capitalize, "Wiki" +- 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. + - page_link = link_to s_("WikiPageConflictMessage|the page"), project_wiki_path(@project, @page), target: "_blank" + = (s_("WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs.") % { page_link: page_link }).html_safe .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } @@ -20,20 +19,20 @@ %span.light · - if @page.persisted? - Edit Page + = s_("Wiki|Edit Page") - else - Create Page + = s_("Wiki|Create Page") .nav-controls - if can?(current_user, :create_wiki, @project) = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do - New page + = s_("Wiki|New page") - if @page.persisted? = link_to project_wiki_history_path(@project, @page), class: "btn" do - Page history + = s_("Wiki|Page history") - if can?(current_user, :admin_wiki, @project) - = link_to project_wiki_path(@project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger" do - Delete + = link_to project_wiki_path(@project, @page), data: { confirm: s_("WikiPageConfirmDelete|Are you sure you want to delete this page?")}, method: :delete, class: "btn btn-danger" do + = _("Delete") = render 'form' diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml index 7dfa405d063..911e1339541 100644 --- a/app/views/projects/wikis/empty.html.haml +++ b/app/views/projects/wikis/empty.html.haml @@ -1,6 +1,6 @@ -- page_title "Wiki" +- page_title _("Wiki") -%h3.page-title Empty page +%h3.page-title= _("Wiki|Empty page") %hr .error_message - You are not allowed to create wiki pages + = s_("WikiEmptyPageError|You are not allowed to create wiki pages") diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index e740fb93ea4..10dbbc0e42c 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -1,36 +1,34 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout -- page_title "Git Access", "Wiki" +- page_title s_("WikiClone|Git Access"), _("Wiki") .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.visible-xs.visible-sm.pull-right.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } = icon('angle-double-left') .git-access-header - Clone repository + = _("Clone repository") %strong= @project_wiki.full_path = render "shared/clone_panel", project: @project_wiki .wiki-git-access - %h3 Install Gollum + %h3= s_("WikiClone|Install Gollum") %pre.dark :preserve gem install gollum %p - It is recommended to install - %code github-markdown - so that GFM features render locally: + = (s_("WikiClone|It is recommended to install %{markdown} so that GFM features render locally:") % { markdown: "<code>github-markdown</code>" }).html_safe %pre.dark :preserve gem install github-markdown - %h3 Clone your wiki + %h3= s_("WikiClone|Clone your wiki") %pre.dark :preserve git clone #{ content_tag(:span, h(default_url_to_repo(@project_wiki)), class: 'clone')} cd #{h @project_wiki.path} - %h3 Start Gollum and edit locally + %h3= s_("WikiClone|Start Gollum and edit locally") %pre.dark :preserve gollum diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index 306feeff259..bc1ab5065e4 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -1,4 +1,4 @@ -- page_title "History", @page.title.capitalize, "Wiki" +- page_title _("History"), @page.title.capitalize, _("Wiki") .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } @@ -9,17 +9,17 @@ = link_to @page.title.capitalize, project_wiki_path(@project, @page) %span.light · - History + = _("History") .table-holder %table.table %thead %tr - %th Page version - %th Author - %th Commit Message - %th Last updated - %th Format + %th= s_("Wiki|Page version") + %th= _("Author") + %th= _("Commit Message") + %th= _("Last updated") + %th= _("Format") %tbody - @page.versions.each_with_index do |version, index| - commit = version diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index d533c611a38..aeef64fd7eb 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,19 +1,19 @@ - @no_container = true - add_to_breadcrumbs "Wiki", get_project_wiki_path(@project) -- breadcrumb_title "Pages" -- page_title "Pages", "Wiki" +- breadcrumb_title s_("Wiki|Pages") +- page_title s_("Wiki|Pages"), _("Wiki") %div{ class: container_class } .wiki-page-header .nav-text %h2.wiki-page-title - Wiki Pages + = s_("Wiki|Wiki Pages") .nav-controls = link_to project_wikis_git_access_path(@project), class: 'btn' do = icon('cloud-download') - Clone repository + = _("Clone repository") %ul.wiki-pages-list.content-list = render @wiki_entries, context: 'pages' diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index b066a812ec8..62c18cc4582 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,8 +1,8 @@ - @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout - breadcrumb_title @page.title.capitalize - wiki_breadcrumb_dropdown_links(@page.slug) -- page_title @page.title.capitalize, "Wiki" -- add_to_breadcrumbs "Wiki", get_project_wiki_path(@project) +- page_title @page.title.capitalize, _("Wiki") +- add_to_breadcrumbs _("Wiki"), get_project_wiki_path(@project) .wiki-page-header.has-sidebar-toggle %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } @@ -11,9 +11,7 @@ .nav-text %h2.wiki-page-title= @page.title.capitalize %span.wiki-last-edit-by - Last edited by - %strong - #{@page.commit.author.name} + = (_("Last edited by %{name}") % { name: "<strong>#{@page.commit.author.name}</strong>" }).html_safe #{time_ago_with_tooltip(@page.commit.authored_date)} .nav-controls @@ -21,8 +19,10 @@ - if @page.historical? .warning_message - This is an old version of this page. - You can view the #{link_to "most recent version", project_wiki_path(@project, @page)} or browse the #{link_to "history", project_wiki_history_path(@project, @page)}. + = s_("WikiHistoricalPage|This is an old version of this page.") + - most_recent_link = link_to s_("WikiHistoricalPage|most recent version"), project_wiki_path(@project, @page) + - history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page) + = (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe .wiki-holder.prepend-top-default.append-bottom-default .wiki diff --git a/app/views/shared/_auto_devops_callout.html.haml b/app/views/shared/_auto_devops_callout.html.haml index 2f09c2fec87..7c633175a06 100644 --- a/app/views/shared/_auto_devops_callout.html.haml +++ b/app/views/shared/_auto_devops_callout.html.haml @@ -6,10 +6,10 @@ .svg-container = custom_icon('icon_autodevops') .user-callout-copy - %h4= _('Auto DevOps (Beta)') - %p= _('Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.') + %h4= s_('AutoDevOps|Auto DevOps (Beta)') + %p= s_('AutoDevOps|Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.') %p - #{s_('AutoDevOps|Learn more in the')} - = link_to _('Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer' + - link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer') + = s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link } - = link_to _('Enable in settings'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'), class: 'btn btn-primary js-close-callout' + = link_to s_('AutoDevOps|Enable in settings'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'), class: 'btn btn-primary js-close-callout' diff --git a/app/views/shared/_mr_head.html.haml b/app/views/shared/_mr_head.html.haml deleted file mode 100644 index e7355ae2eea..00000000000 --- a/app/views/shared/_mr_head.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -- if @project.issues_enabled? - = render "projects/issues/head" -- else - = render "projects/merge_requests/head" diff --git a/app/views/shared/_nav_scroll.html.haml b/app/views/shared/_nav_scroll.html.haml deleted file mode 100644 index 61646f150c1..00000000000 --- a/app/views/shared/_nav_scroll.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.fade-left - = icon('angle-left') -.fade-right - = icon('angle-right') diff --git a/app/views/shared/_sidebar_toggle_button.html.haml b/app/views/shared/_sidebar_toggle_button.html.haml index eb5ddb0dde4..2530db986e0 100644 --- a/app/views/shared/_sidebar_toggle_button.html.haml +++ b/app/views/shared/_sidebar_toggle_button.html.haml @@ -1,8 +1,8 @@ %a.toggle-sidebar-button.js-toggle-sidebar{ role: "button", type: "button", title: "Toggle sidebar" } - = icon('angle-double-left') - = icon('angle-double-right') + = sprite_icon('angle-double-left', css_class: 'icon-angle-double-left') + = sprite_icon('angle-double-right', css_class: 'icon-angle-double-right') %span.collapse-text Collapse sidebar = button_tag class: 'close-nav-button', type: 'button' do - = icon ('times') + = sprite_icon('close', size: 16) %span.collapse-text Close sidebar diff --git a/app/views/shared/_target_switcher.html.haml b/app/views/shared/_target_switcher.html.haml deleted file mode 100644 index bbe9692a7da..00000000000 --- a/app/views/shared/_target_switcher.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -- dropdown_toggle_text = @ref || @project.default_branch -= form_tag nil, method: :get, class: "project-refs-form project-refs-target-form" do - = hidden_field_tag :destination, destination - - if defined?(path) - = hidden_field_tag :path, path - - @options && @options.each do |key, value| - = hidden_field_tag key, value, id: nil - .dropdown - = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project, find: ['branches']), field_name: 'ref', input_field_name: 'new-branch', submit_form_on_click: true, visit: false }, { toggle_class: "js-project-refs-dropdown" } - .dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } - = dropdown_title _("Create a new branch") - = dropdown_input _("Create a new branch") - = dropdown_title _("Select existing branch"), options: {close: false} - = dropdown_filter _("Search branches and tags") - = dropdown_content - = dropdown_loading diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index 722890a01e5..ee8ad8e3999 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -12,13 +12,11 @@ %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" %script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal -= render "projects/issues/head" +#board-app.boards-app{ "v-cloak" => true, data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" } + .hidden-xs.hidden-sm + = render 'shared/issuable/search_bar', type: :boards -.hidden-xs.hidden-sm - = render 'shared/issuable/search_bar', type: :boards - -#board-app.boards-app{ "v-cloak" => true, data: board_data } - .boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" } + .boards-list .boards-app-loading.text-center{ "v-if" => "loading" } = icon("spinner spin") %board{ "v-cloak" => true, diff --git a/app/views/shared/icons/_icon_status_canceled.svg b/app/views/shared/icons/_icon_status_canceled.svg index bd5d04e1cd7..bd5d04e1cd7 100755..100644 --- a/app/views/shared/icons/_icon_status_canceled.svg +++ b/app/views/shared/icons/_icon_status_canceled.svg diff --git a/app/views/shared/icons/_icon_status_created.svg b/app/views/shared/icons/_icon_status_created.svg index 326ad04e017..326ad04e017 100755..100644 --- a/app/views/shared/icons/_icon_status_created.svg +++ b/app/views/shared/icons/_icon_status_created.svg diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg index 64da5aa31fc..64da5aa31fc 100755..100644 --- a/app/views/shared/icons/_icon_status_failed.svg +++ b/app/views/shared/icons/_icon_status_failed.svg diff --git a/app/views/shared/icons/_icon_status_manual.svg b/app/views/shared/icons/_icon_status_manual.svg index c98839f51a9..c98839f51a9 100755..100644 --- a/app/views/shared/icons/_icon_status_manual.svg +++ b/app/views/shared/icons/_icon_status_manual.svg diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg index 02d5da407e3..02d5da407e3 100755..100644 --- a/app/views/shared/icons/_icon_status_pending.svg +++ b/app/views/shared/icons/_icon_status_pending.svg diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg index 532f4fee33c..532f4fee33c 100755..100644 --- a/app/views/shared/icons/_icon_status_running.svg +++ b/app/views/shared/icons/_icon_status_running.svg diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg index a9ba29c922c..a9ba29c922c 100755..100644 --- a/app/views/shared/icons/_icon_status_skipped.svg +++ b/app/views/shared/icons/_icon_status_skipped.svg diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg index eed5006bebe..eed5006bebe 100755..100644 --- a/app/views/shared/icons/_icon_status_success.svg +++ b/app/views/shared/icons/_icon_status_success.svg diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg index cb785635b7e..cb785635b7e 100755..100644 --- a/app/views/shared/icons/_icon_status_warning.svg +++ b/app/views/shared/icons/_icon_status_warning.svg diff --git a/app/views/shared/issuable/_close_reopen_button.html.haml b/app/views/shared/issuable/_close_reopen_button.html.haml index f16bc8dd430..9ef015047c9 100644 --- a/app/views/shared/issuable/_close_reopen_button.html.haml +++ b/app/views/shared/issuable/_close_reopen_button.html.haml @@ -3,9 +3,9 @@ - button_method = issuable_close_reopen_button_method(issuable) - if can_update && is_current_user - = link_to "Close #{display_issuable_type}", close_issuable_url(issuable), method: button_method, + = link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method, class: "hidden-xs hidden-sm btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)}", title: "Close #{display_issuable_type}" - = link_to "Reopen #{display_issuable_type}", reopen_issuable_url(issuable), method: button_method, + = link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}" - elsif can_update && !is_current_user = render 'shared/issuable/close_reopen_report_toggle', issuable: issuable diff --git a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml index a38cd319e3c..39a5171c1d6 100644 --- a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml +++ b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml @@ -7,7 +7,7 @@ - button_method = issuable_close_reopen_button_method(issuable) .pull-left.btn-group.prepend-left-10.issuable-close-dropdown.droplab-dropdown.js-issuable-close-dropdown - = link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_url(issuable), + = link_to "#{display_button_action} #{display_issuable_type}", close_reopen_issuable_path(issuable), method: button_method, class: "#{button_class} btn-#{button_action}", title: "#{display_button_action} #{display_issuable_type}" = button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color", @@ -16,7 +16,7 @@ %ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ class: button_responsive_class, data: { dropdown: true } } %li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}", - data: { text: "Close #{display_issuable_type}", url: close_issuable_url(issuable), + data: { text: "Close #{display_issuable_type}", url: close_issuable_path(issuable), button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color", method: button_method } } %button.btn.btn-transparent = icon('check', class: 'icon') @@ -26,7 +26,7 @@ = display_issuable_type %li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}", - data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_url(issuable), + data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_path(issuable), button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color", method: button_method } } %button.btn.btn-transparent = icon('check', class: 'icon') diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index 8a71819aa8e..d2b62557e03 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -11,7 +11,7 @@ .hide-collapsed.participants-list - participants.each do |participant| .participants-author.js-participants-author - = link_to_member(@project, participant, name: false, size: 24) + = link_to_member(@project, participant, name: false, size: 24, lazy_load: true) - if participants_extra > 0 .hide-collapsed.participants-more %a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } } diff --git a/app/workers/project_migrate_hashed_storage_worker.rb b/app/workers/project_migrate_hashed_storage_worker.rb new file mode 100644 index 00000000000..ca276d7801c --- /dev/null +++ b/app/workers/project_migrate_hashed_storage_worker.rb @@ -0,0 +1,11 @@ +class ProjectMigrateHashedStorageWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + def perform(project_id) + project = Project.find_by(id: project_id) + return if project.nil? || project.pending_delete? + + ::Projects::HashedStorageMigrationService.new(project, logger).execute + end +end diff --git a/app/workers/storage_migrator_worker.rb b/app/workers/storage_migrator_worker.rb new file mode 100644 index 00000000000..b48ead799b9 --- /dev/null +++ b/app/workers/storage_migrator_worker.rb @@ -0,0 +1,30 @@ +class StorageMigratorWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue + + BATCH_SIZE = 100 + + def perform(start, finish) + projects = build_relation(start, finish) + + projects.with_route.find_each(batch_size: BATCH_SIZE) do |project| + Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..." + + begin + project.migrate_to_hashed_storage! + rescue => err + Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}") + end + end + end + + def build_relation(start, finish) + relation = Project + table = Project.arel_table + + relation = relation.where(table[:id].gteq(start)) if start + relation = relation.where(table[:id].lteq(finish)) if finish + + relation + end +end diff --git a/changelogs/unreleased/12673-fix_v3_project_hooks_build_events b/changelogs/unreleased/12673-fix_v3_project_hooks_build_events deleted file mode 100644 index 59bc646406f..00000000000 --- a/changelogs/unreleased/12673-fix_v3_project_hooks_build_events +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Fix v3 api project_hooks POST and PUT operations for build_events" -merge_request: 12673 -author: Richard Clamp diff --git a/changelogs/unreleased/12892-reset-css-text-align-to-initial-for-rtl.md b/changelogs/unreleased/12892-reset-css-text-align-to-initial-for-rtl.md deleted file mode 100644 index 87e95240bba..00000000000 --- a/changelogs/unreleased/12892-reset-css-text-align-to-initial-for-rtl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "reset text-align to initial to let elements with dir="auto" align texts to right in RTL languages ( default is left )" -merge_request: 12892 -author: goshhob diff --git a/changelogs/unreleased/13637-show-account-confirmation-link-in-e-mail-text.yml b/changelogs/unreleased/13637-show-account-confirmation-link-in-e-mail-text.yml new file mode 100644 index 00000000000..5f98d0cc766 --- /dev/null +++ b/changelogs/unreleased/13637-show-account-confirmation-link-in-e-mail-text.yml @@ -0,0 +1,5 @@ +--- +title: Confirmation email shows link as text instead of human readable text +merge_request: 14243 +author: bitsapien +type: changed diff --git a/changelogs/unreleased/13711-allow-same-period-housekeeping.yml b/changelogs/unreleased/13711-allow-same-period-housekeeping.yml index 6749e22cf6a..607a8683aff 100644 --- a/changelogs/unreleased/13711-allow-same-period-housekeeping.yml +++ b/changelogs/unreleased/13711-allow-same-period-housekeeping.yml @@ -2,5 +2,5 @@ title: Allow to use same periods for different housekeeping tasks (effectively skipping the lesser task) merge_request: 13711 -author: @cernvcs +author: cernvcs type: added diff --git a/changelogs/unreleased/22619-add-an-email-address-to-unsubscribe-list-header-in-email b/changelogs/unreleased/22619-add-an-email-address-to-unsubscribe-list-header-in-email deleted file mode 100644 index f4011b756a5..00000000000 --- a/changelogs/unreleased/22619-add-an-email-address-to-unsubscribe-list-header-in-email +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Handle unsubscribe from email notifications via replying to reply+%{key}+unsubscribe@ address -merge_request: 6597 -author: diff --git a/changelogs/unreleased/26908-make-timelogs-use-foreign-keys b/changelogs/unreleased/26908-make-timelogs-use-foreign-keys deleted file mode 100644 index 0e8f7093b34..00000000000 --- a/changelogs/unreleased/26908-make-timelogs-use-foreign-keys +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Refactor Timelogs structure to use foreign keys. -merge_request: 8769 -author: diff --git a/changelogs/unreleased/32340-correct-jobs-api-documentation b/changelogs/unreleased/32340-correct-jobs-api-documentation deleted file mode 100644 index 4ada62356eb..00000000000 --- a/changelogs/unreleased/32340-correct-jobs-api-documentation +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Correction to documention for manual steps on the Jobs API" -merge_request: 11411 -author: Zac Sturgess
\ No newline at end of file diff --git a/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml b/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml new file mode 100644 index 00000000000..727f3cecd52 --- /dev/null +++ b/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml @@ -0,0 +1,5 @@ +--- +title: Link SAML users to LDAP by email. +merge_request: 14216 +author: +type: changed diff --git a/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml b/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml new file mode 100644 index 00000000000..d34e685b5f5 --- /dev/null +++ b/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml @@ -0,0 +1,5 @@ +--- +title: Load sidebar participants avatars only when visible +merge_request: 14270 +author: +type: other diff --git a/changelogs/unreleased/3523-i18n-autodevops.yml b/changelogs/unreleased/3523-i18n-autodevops.yml new file mode 100644 index 00000000000..10cb22b42a0 --- /dev/null +++ b/changelogs/unreleased/3523-i18n-autodevops.yml @@ -0,0 +1,5 @@ +--- +title: Improves i18n for Auto Devops callout +merge_request: +author: +type: other diff --git a/changelogs/unreleased/35942-api-binary-encoding.yaml b/changelogs/unreleased/35942-api-binary-encoding.yaml deleted file mode 100644 index 4f7960d860e..00000000000 --- a/changelogs/unreleased/35942-api-binary-encoding.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -title: "Fix API to serve binary diffs that are treated as text." -merge_request: 14038 diff --git a/changelogs/unreleased/36631-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml b/changelogs/unreleased/36631-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml new file mode 100644 index 00000000000..a2e1d07158b --- /dev/null +++ b/changelogs/unreleased/36631-activerecord-statementinvalid-pg-querycanceled-error-canceling-statement-due-to-statement-timeout.yml @@ -0,0 +1,6 @@ +--- +title: Reschedule merge request diff background migrations to catch failures from + 9.5 run +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/36953-add-gitLab-pages-version-to-admin-dashboard.yml b/changelogs/unreleased/36953-add-gitLab-pages-version-to-admin-dashboard.yml index 680ef0cef92..9ac4a0ae7f3 100644 --- a/changelogs/unreleased/36953-add-gitLab-pages-version-to-admin-dashboard.yml +++ b/changelogs/unreleased/36953-add-gitLab-pages-version-to-admin-dashboard.yml @@ -1,5 +1,5 @@ --- title: Add GitLab-Pages version to Admin Dashboard merge_request: 14040 -author: @travismiller +author: travismiller type: added diff --git a/changelogs/unreleased/37335-counter-active-state.yml b/changelogs/unreleased/37335-counter-active-state.yml new file mode 100644 index 00000000000..a9632201a89 --- /dev/null +++ b/changelogs/unreleased/37335-counter-active-state.yml @@ -0,0 +1,5 @@ +--- +title: Add active states to nav bar counters +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/37467-helper-method-from-users-endpoint-overrides-api-helper-method.yml b/changelogs/unreleased/37467-helper-method-from-users-endpoint-overrides-api-helper-method.yml new file mode 100644 index 00000000000..1984ec6e81c --- /dev/null +++ b/changelogs/unreleased/37467-helper-method-from-users-endpoint-overrides-api-helper-method.yml @@ -0,0 +1,5 @@ +--- +title: find_user Users helper method no longer overrides find_user API helper method. +merge_request: 14418 +author: +type: fixed diff --git a/changelogs/unreleased/38052-use-simple-api-for-projects.yml b/changelogs/unreleased/38052-use-simple-api-for-projects.yml new file mode 100644 index 00000000000..49c7485861e --- /dev/null +++ b/changelogs/unreleased/38052-use-simple-api-for-projects.yml @@ -0,0 +1,5 @@ +--- +title: Use `simple=true` for projects API in Projects dropdown for better search performance +merge_request: +author: +type: other diff --git a/changelogs/unreleased/38280-undefined-run_command-when-running-rake-gitlab-check.yml b/changelogs/unreleased/38280-undefined-run_command-when-running-rake-gitlab-check.yml deleted file mode 100644 index 7d3fb7d43cc..00000000000 --- a/changelogs/unreleased/38280-undefined-run_command-when-running-rake-gitlab-check.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Some checks in `rake gitlab:check` were failling with 'undefined method `run_command`' -merge_request: 14469 -author: -type: fixed diff --git a/changelogs/unreleased/38432-fix-notes-type-for-import.yml b/changelogs/unreleased/38432-fix-notes-type-for-import.yml new file mode 100644 index 00000000000..db8371f4420 --- /dev/null +++ b/changelogs/unreleased/38432-fix-notes-type-for-import.yml @@ -0,0 +1,6 @@ +--- +title: Fix notes type created from import. This should fix some missing notes issues + from imported projects +merge_request: 14524 +author: +type: fixed diff --git a/changelogs/unreleased/38476-improve-merge-jid-cleanup-on-merge-process.yml b/changelogs/unreleased/38476-improve-merge-jid-cleanup-on-merge-process.yml new file mode 100644 index 00000000000..43dec51029b --- /dev/null +++ b/changelogs/unreleased/38476-improve-merge-jid-cleanup-on-merge-process.yml @@ -0,0 +1,5 @@ +--- +title: Adjust MRs being stuck on "process of being merged" for more than 2 hours +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/38528-build-url.yml b/changelogs/unreleased/38528-build-url.yml new file mode 100644 index 00000000000..357b9aacea8 --- /dev/null +++ b/changelogs/unreleased/38528-build-url.yml @@ -0,0 +1,5 @@ +--- +title: Fixes data parameter not being sent in ajax request for jobs log +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/38571-fix-exception-in-raven-report.yml b/changelogs/unreleased/38571-fix-exception-in-raven-report.yml new file mode 100644 index 00000000000..62e3b8d304c --- /dev/null +++ b/changelogs/unreleased/38571-fix-exception-in-raven-report.yml @@ -0,0 +1,6 @@ +--- +title: Ensure no exception is raised when Raven tries to get the current user in API + context +merge_request: 14580 +author: +type: fixed diff --git a/changelogs/unreleased/38582-popover-badge.yml b/changelogs/unreleased/38582-popover-badge.yml new file mode 100644 index 00000000000..ccec679a13f --- /dev/null +++ b/changelogs/unreleased/38582-popover-badge.yml @@ -0,0 +1,5 @@ +--- +title: Improves UX of autodevops popover to match gpg one +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml b/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml new file mode 100644 index 00000000000..a203bff8410 --- /dev/null +++ b/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml @@ -0,0 +1,5 @@ +--- +title: Fix comment deletion confirmation dialog typo +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml b/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml new file mode 100644 index 00000000000..49d0671233a --- /dev/null +++ b/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml @@ -0,0 +1,5 @@ +--- +title: Whitelist authorized_keys.lock in the gitlab:check rake task +merge_request: 14624 +author: +type: fixed diff --git a/changelogs/unreleased/add-composite-index-on-merge-requests-merge-commit-sha.yml b/changelogs/unreleased/add-composite-index-on-merge-requests-merge-commit-sha.yml new file mode 100644 index 00000000000..9a600282716 --- /dev/null +++ b/changelogs/unreleased/add-composite-index-on-merge-requests-merge-commit-sha.yml @@ -0,0 +1,5 @@ +--- +title: Add index for merge_requests.merge_commit_sha +merge_request: +author: +type: other diff --git a/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml b/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml deleted file mode 100644 index 4c81d21a94b..00000000000 --- a/changelogs/unreleased/add-mock-deployment-and-monitoring-service-for-development.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added mock deployment and monitoring service with environments fixtures -merge_request: -author: diff --git a/changelogs/unreleased/breadcrumb-item-links.yml b/changelogs/unreleased/breadcrumb-item-links.yml new file mode 100644 index 00000000000..9b66456efca --- /dev/null +++ b/changelogs/unreleased/breadcrumb-item-links.yml @@ -0,0 +1,5 @@ +--- +title: Fixed issue/merge request breadcrumb titles not having links +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/breadcrumbs-line-height-padding.yml b/changelogs/unreleased/breadcrumbs-line-height-padding.yml new file mode 100644 index 00000000000..3ac56c8b593 --- /dev/null +++ b/changelogs/unreleased/breadcrumbs-line-height-padding.yml @@ -0,0 +1,5 @@ +--- +title: breadcrumbs receives padding when double lined +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/bvl-fix-close-issuable-link.yml b/changelogs/unreleased/bvl-fix-close-issuable-link.yml new file mode 100644 index 00000000000..140a9d35cc1 --- /dev/null +++ b/changelogs/unreleased/bvl-fix-close-issuable-link.yml @@ -0,0 +1,5 @@ +--- +title: Fix CSRF validation issue when closing/opening merge requests from the UI +merge_request: 14555 +author: +type: fixed diff --git a/changelogs/unreleased/commit-side-by-side-comment.yml b/changelogs/unreleased/commit-side-by-side-comment.yml new file mode 100644 index 00000000000..f9bea285a77 --- /dev/null +++ b/changelogs/unreleased/commit-side-by-side-comment.yml @@ -0,0 +1,5 @@ +--- +title: Fixed commenting on side-by-side commit diff +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dm-api-unauthorized.yml b/changelogs/unreleased/dm-api-unauthorized.yml new file mode 100644 index 00000000000..26b45bd4c40 --- /dev/null +++ b/changelogs/unreleased/dm-api-unauthorized.yml @@ -0,0 +1,5 @@ +--- +title: Make sure API responds with 401 when invalid authentication info is provided +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dm-bitbucket-import-truncated-shas.yml b/changelogs/unreleased/dm-bitbucket-import-truncated-shas.yml new file mode 100644 index 00000000000..057407b78d9 --- /dev/null +++ b/changelogs/unreleased/dm-bitbucket-import-truncated-shas.yml @@ -0,0 +1,6 @@ +--- +title: Fix bug that caused merge requests with diff notes imported from Bitbucket + to raise errors +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dm-simple-project-avatar-url.yml b/changelogs/unreleased/dm-simple-project-avatar-url.yml new file mode 100644 index 00000000000..e517345f5d2 --- /dev/null +++ b/changelogs/unreleased/dm-simple-project-avatar-url.yml @@ -0,0 +1,5 @@ +--- +title: Expose avatar_url when requesting list of projects from API with simple=true +merge_request: +author: +type: added diff --git a/changelogs/unreleased/docs-28814-clarify-artifacts-ref.yml b/changelogs/unreleased/docs-28814-clarify-artifacts-ref.yml new file mode 100644 index 00000000000..3cdcff8caaf --- /dev/null +++ b/changelogs/unreleased/docs-28814-clarify-artifacts-ref.yml @@ -0,0 +1,5 @@ +--- +title: Clarify artifact download via the API only accepts branch or tag name for ref +merge_request: +author: +type: other diff --git a/changelogs/unreleased/docs-38152-bump-recommended-mysql-version.yml b/changelogs/unreleased/docs-38152-bump-recommended-mysql-version.yml new file mode 100644 index 00000000000..eea679d0814 --- /dev/null +++ b/changelogs/unreleased/docs-38152-bump-recommended-mysql-version.yml @@ -0,0 +1,5 @@ +--- +title: Change recommended MySQL version to 5.6 +merge_request: +author: +type: other diff --git a/changelogs/unreleased/expose-last-pipeline-details-in-api-for-single-commit.yml b/changelogs/unreleased/expose-last-pipeline-details-in-api-for-single-commit.yml new file mode 100644 index 00000000000..d16e052cd92 --- /dev/null +++ b/changelogs/unreleased/expose-last-pipeline-details-in-api-for-single-commit.yml @@ -0,0 +1,5 @@ +--- +title: Expose last pipeline details in API response when getting a single commit +merge_request: 13521 +author: Mehdi Lahmam (@mehlah) +type: added diff --git a/changelogs/unreleased/feature-custom-attributes.yml b/changelogs/unreleased/feature-custom-attributes.yml new file mode 100644 index 00000000000..98736bc8d72 --- /dev/null +++ b/changelogs/unreleased/feature-custom-attributes.yml @@ -0,0 +1,4 @@ +--- +title: Support custom attributes on users +merge_request: 13038 +author: Markus Koller diff --git a/changelogs/unreleased/fix-gpg-case-insensitive.yml b/changelogs/unreleased/fix-gpg-case-insensitive.yml new file mode 100644 index 00000000000..744ec00a4a8 --- /dev/null +++ b/changelogs/unreleased/fix-gpg-case-insensitive.yml @@ -0,0 +1,5 @@ +--- +title: Compare email addresses case insensitively when verifying GPG signatures +merge_request: 14376 +author: Tim Bishop +type: fixed diff --git a/changelogs/unreleased/fix-mr-sidebar-counter-after-merge.yml b/changelogs/unreleased/fix-mr-sidebar-counter-after-merge.yml new file mode 100644 index 00000000000..22a3efb8b1e --- /dev/null +++ b/changelogs/unreleased/fix-mr-sidebar-counter-after-merge.yml @@ -0,0 +1,5 @@ +--- +title: Fix merge request counter updates after merge +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fork-btn-enabled-user-groups.yml b/changelogs/unreleased/fork-btn-enabled-user-groups.yml new file mode 100644 index 00000000000..3bd7581a961 --- /dev/null +++ b/changelogs/unreleased/fork-btn-enabled-user-groups.yml @@ -0,0 +1,5 @@ +--- +title: Fixed fork button being disabled for users who can fork to a group +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/hash-mr-scroll-load.yml b/changelogs/unreleased/hash-mr-scroll-load.yml new file mode 100644 index 00000000000..7e3965add03 --- /dev/null +++ b/changelogs/unreleased/hash-mr-scroll-load.yml @@ -0,0 +1,5 @@ +--- +title: Fixed notes not being scrolled to in merge requests +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/hashed-storage-migration-path.yml b/changelogs/unreleased/hashed-storage-migration-path.yml new file mode 100644 index 00000000000..5890eb09c38 --- /dev/null +++ b/changelogs/unreleased/hashed-storage-migration-path.yml @@ -0,0 +1,5 @@ +--- +title: Script to migrate project's repositories to new Hashed Storage +merge_request: 14067 +author: +type: added diff --git a/changelogs/unreleased/lint-changelog-yaml.yml b/changelogs/unreleased/lint-changelog-yaml.yml new file mode 100644 index 00000000000..dcc8bf54827 --- /dev/null +++ b/changelogs/unreleased/lint-changelog-yaml.yml @@ -0,0 +1,5 @@ +--- +title: Detect when changelog entries are invalid +merge_request: +author: +type: other diff --git a/changelogs/unreleased/mr-side-by-side-breadcrumbs-container.yml b/changelogs/unreleased/mr-side-by-side-breadcrumbs-container.yml new file mode 100644 index 00000000000..39b636bdfda --- /dev/null +++ b/changelogs/unreleased/mr-side-by-side-breadcrumbs-container.yml @@ -0,0 +1,5 @@ +--- +title: Fixed breadcrumbs container expanding in side-by-side diff view +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml b/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml new file mode 100644 index 00000000000..69695e403a9 --- /dev/null +++ b/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml @@ -0,0 +1,5 @@ +--- +title: Fix case sensitive email confirmation on signup +merge_request: 14606 +author: robdel12 +type: fixed diff --git a/changelogs/unreleased/remote_user.yml b/changelogs/unreleased/remote_user.yml new file mode 100644 index 00000000000..75a941fa95f --- /dev/null +++ b/changelogs/unreleased/remote_user.yml @@ -0,0 +1,4 @@ +--- +title: Add username as GL_USERNAME in hooks +merge_request: +author: diff --git a/changelogs/unreleased/remove-temporary-ci-index.yml b/changelogs/unreleased/remove-temporary-ci-index.yml new file mode 100644 index 00000000000..a319f7fff7f --- /dev/null +++ b/changelogs/unreleased/remove-temporary-ci-index.yml @@ -0,0 +1,5 @@ +--- +title: Remove an index on ci_builds meant to be only temporary +merge_request: +author: +type: other diff --git a/changelogs/unreleased/repository-name-emojis b/changelogs/unreleased/repository-name-emojis deleted file mode 100644 index fe52df8eedc..00000000000 --- a/changelogs/unreleased/repository-name-emojis +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Added ability to put emojis into repository name -merge_request: 7420 -author: Vincent Composieux diff --git a/changelogs/unreleased/rotated_profile_image.yml b/changelogs/unreleased/rotated_profile_image.yml new file mode 100644 index 00000000000..1e221e47379 --- /dev/null +++ b/changelogs/unreleased/rotated_profile_image.yml @@ -0,0 +1,5 @@ +--- +title: Fix profile image orientation based on EXIF data gvieira37 +merge_request: 14461 +author: gvieira37 +type: fixed diff --git a/changelogs/unreleased/rs-allow-name-on-anchors.yml b/changelogs/unreleased/rs-allow-name-on-anchors.yml deleted file mode 100644 index 59e95ed8a0e..00000000000 --- a/changelogs/unreleased/rs-allow-name-on-anchors.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Re-allow `name` attribute on user-provided anchor HTML -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-import-repos.yml b/changelogs/unreleased/sh-fix-import-repos.yml new file mode 100644 index 00000000000..5764b3bdc01 --- /dev/null +++ b/changelogs/unreleased/sh-fix-import-repos.yml @@ -0,0 +1,5 @@ +--- +title: Fix gitlab-rake gitlab:import:repos task failing +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-issue-38646.yml b/changelogs/unreleased/sh-fix-issue-38646.yml new file mode 100644 index 00000000000..5c205775662 --- /dev/null +++ b/changelogs/unreleased/sh-fix-issue-38646.yml @@ -0,0 +1,5 @@ +--- +title: Fix pushes to an empty repository not invalidating has_visible_content? cache +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-restore-all-refs-backup.yml b/changelogs/unreleased/sh-restore-all-refs-backup.yml new file mode 100644 index 00000000000..eaac0c71dd0 --- /dev/null +++ b/changelogs/unreleased/sh-restore-all-refs-backup.yml @@ -0,0 +1,5 @@ +--- +title: Ensure all refs are restored on a restore from backup +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-thread-safe-markdown.yml b/changelogs/unreleased/sh-thread-safe-markdown.yml new file mode 100644 index 00000000000..af7d9d58a9f --- /dev/null +++ b/changelogs/unreleased/sh-thread-safe-markdown.yml @@ -0,0 +1,5 @@ +--- +title: Make Redcarpet Markdown renderer thread-safe +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/zj-repo-gitaly.yml b/changelogs/unreleased/zj-repo-gitaly.yml new file mode 100644 index 00000000000..634f6ba1b8b --- /dev/null +++ b/changelogs/unreleased/zj-repo-gitaly.yml @@ -0,0 +1,5 @@ +--- +title: Gitaly RepositoryExists remains opt-in for all method calls +merge_request: +author: +type: fixed diff --git a/config/application.rb b/config/application.rb index da9bb25c8b9..30117b6a98e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -105,8 +105,6 @@ module Gitlab config.assets.precompile << "lib/ace.js" config.assets.precompile << "vendor/assets/fonts/*" config.assets.precompile << "test.css" - config.assets.precompile << "new_nav.css" - config.assets.precompile << "new_sidebar.css" # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 3aed2136f1b..0ba0d791054 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -36,7 +36,7 @@ Devise.setup do |config| # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] + config.case_insensitive_keys = [:email, :email_confirmation] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index d169c38a693..8235e3853dc 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -62,3 +62,5 @@ - [update_user_activity, 1] - [propagate_service_template, 1] - [background_migration, 1] + - [project_migrate_hashed_storage, 1] + - [storage_migrator, 1] diff --git a/db/migrate/20160713200638_add_repository_read_only_to_projects.rb b/db/migrate/20160713200638_add_repository_read_only_to_projects.rb new file mode 100644 index 00000000000..8ee8b55f210 --- /dev/null +++ b/db/migrate/20160713200638_add_repository_read_only_to_projects.rb @@ -0,0 +1,9 @@ +class AddRepositoryReadOnlyToProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :projects, :repository_read_only, :boolean + end +end diff --git a/db/migrate/20170720122741_create_user_custom_attributes.rb b/db/migrate/20170720122741_create_user_custom_attributes.rb new file mode 100644 index 00000000000..b1c0bebc633 --- /dev/null +++ b/db/migrate/20170720122741_create_user_custom_attributes.rb @@ -0,0 +1,17 @@ +class CreateUserCustomAttributes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :user_custom_attributes do |t| + t.timestamps_with_timezone null: false + t.references :user, null: false, foreign_key: { on_delete: :cascade } + t.string :key, null: false + t.string :value, null: false + + t.index [:user_id, :key], unique: true + t.index [:key, :value] + end + end +end diff --git a/db/migrate/20170919211300_remove_temporary_ci_builds_index.rb b/db/migrate/20170919211300_remove_temporary_ci_builds_index.rb new file mode 100644 index 00000000000..b2009b282e9 --- /dev/null +++ b/db/migrate/20170919211300_remove_temporary_ci_builds_index.rb @@ -0,0 +1,27 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveTemporaryCiBuildsIndex < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # To use create/remove index concurrently + disable_ddl_transaction! + + def up + return unless index_exists?(:ci_builds, :id, name: 'index_for_ci_builds_retried_migration') + remove_concurrent_index(:ci_builds, :id, name: "index_for_ci_builds_retried_migration") + end + + def down + # this was a temporary index for a migration that was never + # present previously so this probably shouldn't be here but it's + # easier to test the drop if we have a way to create it. + add_concurrent_index("ci_builds", ["id"], + name: "index_for_ci_builds_retried_migration", + where: "(retried IS NULL)", + using: :btree) + end +end diff --git a/db/migrate/20170928100231_add_composite_index_on_merge_requests_merge_commit_sha.rb b/db/migrate/20170928100231_add_composite_index_on_merge_requests_merge_commit_sha.rb new file mode 100644 index 00000000000..9f02daf04c1 --- /dev/null +++ b/db/migrate/20170928100231_add_composite_index_on_merge_requests_merge_commit_sha.rb @@ -0,0 +1,33 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddCompositeIndexOnMergeRequestsMergeCommitSha < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # The default index name is too long for PostgreSQL and would thus be + # truncated. + INDEX_NAME = 'index_merge_requests_on_tp_id_and_merge_commit_sha_and_id' + + COLUMNS = [:target_project_id, :merge_commit_sha, :id] + + disable_ddl_transaction! + + def up + return if index_is_present? + + add_concurrent_index(:merge_requests, COLUMNS, name: INDEX_NAME) + end + + def down + return unless index_is_present? + + remove_concurrent_index(:merge_requests, COLUMNS, name: INDEX_NAME) + end + + def index_is_present? + index_exists?(:merge_requests, COLUMNS, name: INDEX_NAME) + end +end diff --git a/db/post_migrate/20170926150348_schedule_merge_request_diff_migrations_take_two.rb b/db/post_migrate/20170926150348_schedule_merge_request_diff_migrations_take_two.rb new file mode 100644 index 00000000000..5732cb85ea5 --- /dev/null +++ b/db/post_migrate/20170926150348_schedule_merge_request_diff_migrations_take_two.rb @@ -0,0 +1,32 @@ +class ScheduleMergeRequestDiffMigrationsTakeTwo < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + BATCH_SIZE = 500 + MIGRATION = 'DeserializeMergeRequestDiffsAndCommits' + DELAY_INTERVAL = 10.minutes + + disable_ddl_transaction! + + class MergeRequestDiff < ActiveRecord::Base + self.table_name = 'merge_request_diffs' + + include ::EachBatch + + default_scope { where('st_commits IS NOT NULL OR st_diffs IS NOT NULL') } + end + + # By this point, we assume ScheduleMergeRequestDiffMigrations - the first + # version of this - has already run. On GitLab.com, we have ~220k un-migrated + # rows, but these rows will, in general, take a long time. + # + # With a gap of 10 minutes per batch, and 500 rows per batch, these migrations + # are scheduled over 220_000 / 500 / 6 ~= 74 hours, which is a little over + # three days. + def up + queue_background_migration_jobs_by_range_at_intervals(MergeRequestDiff, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE) + end + + def down + end +end diff --git a/db/post_migrate/20170927112318_update_legacy_diff_notes_type_for_import.rb b/db/post_migrate/20170927112318_update_legacy_diff_notes_type_for_import.rb new file mode 100644 index 00000000000..a238216253b --- /dev/null +++ b/db/post_migrate/20170927112318_update_legacy_diff_notes_type_for_import.rb @@ -0,0 +1,16 @@ +class UpdateLegacyDiffNotesTypeForImport < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + update_column_in_batches(:notes, :type, 'LegacyDiffNote') do |table, query| + query.where(table[:type].eq('Github::Import::LegacyDiffNote')) + end + end + + def down + end +end diff --git a/db/post_migrate/20170927112319_update_notes_type_for_import.rb b/db/post_migrate/20170927112319_update_notes_type_for_import.rb new file mode 100644 index 00000000000..1e70acd9868 --- /dev/null +++ b/db/post_migrate/20170927112319_update_notes_type_for_import.rb @@ -0,0 +1,16 @@ +class UpdateNotesTypeForImport < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + update_column_in_batches(:notes, :type, 'Note') do |table, query| + query.where(table[:type].eq('Github::Import::Note')) + end + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index a706eb2a04e..8443f86b21c 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: 20170921115009) do +ActiveRecord::Schema.define(version: 20170928100231) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -256,7 +256,6 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree - add_index "ci_builds", ["id"], name: "index_for_ci_builds_retried_migration", where: "(retried IS NULL)", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree @@ -898,6 +897,7 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree + add_index "merge_requests", ["target_project_id", "merge_commit_sha", "id"], name: "index_merge_requests_on_tp_id_and_merge_commit_sha_and_id", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} @@ -1219,6 +1219,7 @@ ActiveRecord::Schema.define(version: 20170921115009) do t.integer "storage_version", limit: 2 t.boolean "resolve_outdated_diff_discussions" t.boolean "merge_requests_ff_only_enabled", default: false, null: false + t.boolean "repository_read_only" end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree @@ -1538,6 +1539,17 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_index "user_agent_details", ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree + create_table "user_custom_attributes", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "user_id", null: false + t.string "key", null: false + t.string "value", null: false + end + + add_index "user_custom_attributes", ["key", "value"], name: "index_user_custom_attributes_on_key_and_value", using: :btree + add_index "user_custom_attributes", ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true, using: :btree + create_table "user_synced_attributes_metadata", force: :cascade do |t| t.boolean "name_synced", default: false t.boolean "email_synced", default: false @@ -1764,6 +1776,7 @@ ActiveRecord::Schema.define(version: 20170921115009) do add_foreign_key "todos", "projects", name: "fk_45054f9c45", on_delete: :cascade add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "u2f_registrations", "users" + add_foreign_key "user_custom_attributes", "users", on_delete: :cascade add_foreign_key "user_synced_attributes_metadata", "users", on_delete: :cascade add_foreign_key "users_star_projects", "projects", name: "fk_22cd27ddfc", on_delete: :cascade add_foreign_key "web_hook_logs", "web_hooks", on_delete: :cascade diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md new file mode 100644 index 00000000000..bac8fa4bd9d --- /dev/null +++ b/doc/administration/raketasks/storage.md @@ -0,0 +1,107 @@ +# Repository Storage Rake Tasks + +This is a collection of rake tasks you can use to help you list and migrate +existing projects from Legacy storage to the new Hashed storage type. + +You can read more about the storage types [here][storage-types]. + +## List projects on Legacy storage + +To have a simple summary of projects using **Legacy** storage: + +**Omnibus Installation** + +```bash +gitlab-rake gitlab:storage:legacy_projects +``` + +**Source Installation** + +```bash +rake gitlab:storage:legacy_projects + +``` + +------ + +To list projects using **Legacy** storage: + +**Omnibus Installation** + +```bash +gitlab-rake gitlab:storage:list_legacy_projects +``` + +**Source Installation** + +```bash +rake gitlab:storage:list_legacy_projects + +``` + +## List projects on Hashed storage + +To have a simple summary of projects using **Hashed** storage: + +**Omnibus Installation** + +```bash +gitlab-rake gitlab:storage:hashed_projects +``` + +**Source Installation** + +```bash +rake gitlab:storage:hashed_projects + +``` + +------ + +To list projects using **Hashed** storage: + +**Omnibus Installation** + +```bash +gitlab-rake gitlab:storage:list_hashed_projects +``` + +**Source Installation** + +```bash +rake gitlab:storage:list_hashed_projects + +``` + +## Migrate existing projects to Hashed storage + +Before migrating your existing projects, you should +[enable hashed storage][storage-migration] for the new projects as well. + +This task will schedule all your existing projects to be migrated to the +**Hashed** storage type: + +**Omnibus Installation** + +```bash +gitlab-rake gitlab:storage:migrate_to_hashed +``` + +**Source Installation** + +```bash +rake gitlab:storage:migrate_to_hashed + +``` + +You can monitor the progress in the _Admin > Monitoring > Background jobs_ screen. +There is a specific Queue you can watch to see how long it will take to finish: **project_migrate_hashed_storage** + +After it reaches zero, you can confirm every project has been migrated by running the commands above. +If you find it necessary, you can run this migration script again to schedule missing projects. + +Any error or warning will be logged in the sidekiq log file. + + +[storage-types]: ../repository_storage_types.md +[storage-migration]: ../repository_storage_types.md#how-to-migrate-to-hashed-storage diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md new file mode 100644 index 00000000000..fa882bbe28a --- /dev/null +++ b/doc/administration/repository_storage_types.md @@ -0,0 +1,69 @@ +# Repository Storage Types + +> [Introduced][ce-28283] in GitLab 10.0. + +## Legacy Storage + +Legacy Storage is the storage behavior prior to version 10.0. For historical reasons, GitLab replicated the same +mapping structure from the projects URLs: + + * Project's repository: `#{namespace}/#{project_name}.git` + * Project's wiki: `#{namespace}/#{project_name}.wiki.git` + +This structure made simple to migrate from existing solutions to GitLab and easy for Administrators to find where the +repository is stored. + +On the other hand this has some drawbacks: + +Storage location will concentrate huge amount of top-level namespaces. The impact can be reduced by the introduction of [multiple storage paths][storage-paths]. + +Because Backups are a snapshot of the same URL mapping, if you try to recover a very old backup, you need to verify +if any project has taken the place of an old removed project sharing the same URL. This means that `mygroup/myproject` +from your backup may not be the same original project that is today in the same URL. + +Any change in the URL will need to be reflected on disk (when groups / users or projects are renamed). This can add a lot +of load in big installations, and can be even worst if they are using any type of network based filesystem. + +Last, for GitLab Geo, this storage type means we have to synchronize the disk state, replicate renames in the correct +order or we may end-up with wrong repository or missing data temporarily. + +## Hashed Storage + +Hashed Storage is the new storage behavior we are rolling out with 10.0. It's not enabled by default yet, but we +encourage everyone to try-it and take the time to fix any script you may have that depends on the old behavior. + +Instead of coupling project URL and the folder structure where the repository will be stored on disk, we are coupling +a hash, based on the project's ID. + +This makes the folder structure immutable, and therefore eliminates any requirement to synchronize state from URLs to +disk structure. This means that renaming a group, user or project will cost only the database transaction, and will take +effect immediately. + +The hash also helps to spread the repositories more evenly on the disk, so the top-level directory will contain less +folders than the total amount of top-level namespaces. + +Hash format is based on hexadecimal representation of SHA256: `SHA256(project.id)`. +Top-level folder uses first 2 characters, followed by another folder with the next 2 characters. They are both stored in +a special folder `@hashed`, to co-exist with existing Legacy projects: + +```ruby +# Project's repository: +"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git" + +# Wiki's repository: +"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git" +``` + +This new format also makes possible to restore backups with confidence, as when restoring a repository from the backup, +you will never mistakenly restore a repository in the wrong project (considering the backup is made after the migration). + +### How to migrate to Hashed Storage + +In GitLab, go to **Admin > Settings**, find the **Repository Storage** section and select +"_Create new projects using hashed storage paths_". + +To migrate your existing projects to the new storage type, check the specific [rake tasks]. + +[ce-28283]: https://gitlab.com/gitlab-org/gitlab-ce/issues/28283 +[rake tasks]: raketasks/storage.md#migrate-existing-projects-to-hashed-storage +[storage-paths]: repository_storage_types.md diff --git a/doc/api/README.md b/doc/api/README.md index 6cbea29bda6..3fd4c97e536 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -14,6 +14,7 @@ following locations: - [Project-level Variables](project_level_variables.md) - [Group-level Variables](group_level_variables.md) - [Commits](commits.md) +- [Custom Attributes](custom_attributes.md) - [Deployments](deployments.md) - [Deploy Keys](deploy_keys.md) - [Environments](environments.md) diff --git a/doc/api/commits.md b/doc/api/commits.md index 2a78553782f..5a4a8d888b3 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -181,6 +181,12 @@ Example response: "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], + "last_pipeline" : { + "id": 8, + "ref": "master", + "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0" + "status": "created" + } "stats": { "additions": 15, "deletions": 10, diff --git a/doc/api/custom_attributes.md b/doc/api/custom_attributes.md new file mode 100644 index 00000000000..8b26f7093ab --- /dev/null +++ b/doc/api/custom_attributes.md @@ -0,0 +1,105 @@ +# Custom Attributes API + +Every API call to custom attributes must be authenticated as administrator. + +## List custom attributes + +Get all custom attributes on a user. + +``` +GET /users/:id/custom_attributes +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a user | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/custom_attributes +``` + +Example response: + +```json +[ + { + "key": "location", + "value": "Antarctica" + }, + { + "key": "role", + "value": "Developer" + } +] +``` + +## Single custom attribute + +Get a single custom attribute on a user. + +``` +GET /users/:id/custom_attributes/:key +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a user | +| `key` | string | yes | The key of the custom attribute | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/custom_attributes/location +``` + +Example response: + +```json +{ + "key": "location", + "value": "Antarctica" +} +``` + +## Set custom attribute + +Set a custom attribute on a user. The attribute will be updated if it already exists, +or newly created otherwise. + +``` +PUT /users/:id/custom_attributes/:key +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a user | +| `key` | string | yes | The key of the custom attribute | +| `value` | string | yes | The value of the custom attribute | + +```bash +curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "value=Greenland" https://gitlab.example.com/api/v4/users/42/custom_attributes/location +``` + +Example response: + +```json +{ + "key": "location", + "value": "Greenland" +} +``` + +## Delete custom attribute + +Delete a custom attribute on a user. + +``` +DELETE /users/:id/custom_attributes/:key +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a user | +| `key` | string | yes | The key of the custom attribute | + +```bash +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/custom_attributes/location +``` diff --git a/doc/api/jobs.md b/doc/api/jobs.md index d60c7c12881..e7060e154f4 100644 --- a/doc/api/jobs.md +++ b/doc/api/jobs.md @@ -336,7 +336,7 @@ Parameters | Attribute | Type | Required | Description | |-------------|---------|----------|-------------------------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | -| `ref_name` | string | yes | The ref from a repository | +| `ref_name` | string | yes | The ref from a repository (can only be branch or tag name, not HEAD or SHA) | | `job` | string | yes | The name of the job | Example request: diff --git a/doc/api/users.md b/doc/api/users.md index 6d5db16b36a..1643c584244 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -154,6 +154,12 @@ You can search users by creation date time range with: GET /users?created_before=2001-01-02T00:00:00.060Z&created_after=1999-01-02T00:00:00.060 ``` +You can filter by [custom attributes](custom_attributes.md) with: + +``` +GET /users?custom_attributes[key]=value&custom_attributes[other_key]=other_value +``` + ## Single user Get a single user. diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index f28c9791bee..4586caa457d 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -250,6 +250,8 @@ By default, when using `docker:dind`, Docker uses the `vfs` storage driver which copies the filesystem on every run. This is a very disk-intensive operation which can be avoided if a different driver is used, for example `overlay2`. +### Requirements + 1. Make sure a recent kernel is used, preferably `>= 4.2`. 1. Check whether the `overlay` module is loaded: @@ -271,14 +273,27 @@ which can be avoided if a different driver is used, for example `overlay2`. overlay ``` -1. Use the driver by defining a variable at the top of your `.gitlab-ci.yml`: +### Use driver per project - ``` - variables: - DOCKER_DRIVER: overlay2 - ``` - -> **Note:** +You can enable the driver for each project individually by editing the project's `.gitlab-ci.yml`: + +``` +variables: + DOCKER_DRIVER: overlay2 +``` + +### Use driver for every project + +To enable the driver for every project, you can set the environment variable for every build by adding `environment` in the `[[runners]]` section of `config.toml`: + +```toml +environment = ["DOCKER_DRIVER=overlay2"] +``` + +If you're running multiple Runners you will have to modify all configuration files. + +> **Notes:** +- More information about the Runner configuration is available in the [Runner documentation](https://docs.gitlab.com/runner/configuration/). - For more information about using OverlayFS with Docker, you can read [Use the OverlayFS storage driver](https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/). diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index aad81843299..38bd0450a09 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1570,6 +1570,11 @@ Read more on [GitLab Pages user documentation](../../user/project/pages/index.md Each instance of GitLab CI has an embedded debug tool called Lint. You can find the link under `/ci/lint` of your gitlab instance. +## Using reserved keywords + +If you get validation error when using specific values (e.g., `true` or `false`), +try to quote them, or change them to a different form (e.g., `/bin/true`). + ## Skipping jobs If your commit message contains `[ci skip]` or `[skip ci]`, using any diff --git a/doc/development/README.md b/doc/development/README.md index 3096d9f25f0..1448a4c0414 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -44,6 +44,7 @@ - [Building a package for testing purposes](build_test_package.md) - [Manage feature flags](feature_flags.md) - [View sent emails or preview mailers](emails.md) +- [Working with Gitaly](gitaly.md) ## Databases diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md index f83a60e49e8..5452b0e7a2f 100644 --- a/doc/development/background_migrations.md +++ b/doc/development/background_migrations.md @@ -215,14 +215,29 @@ same time will ensure that both existing and new data is migrated. In the next release we can remove the `after_commit` hooks and related code. We will also need to add a post-deployment migration that consumes any remaining -jobs. Such a migration would look like this: +jobs and manually run on any un-migrated rows. Such a migration would look like +this: ```ruby class ConsumeRemainingExtractServicesUrlJobs < ActiveRecord::Migration disable_ddl_transaction! + class Service < ActiveRecord::Base + include ::EachBatch + + self.table_name = 'services' + end + def up + # This must be included Gitlab::BackgroundMigration.steal('ExtractServicesUrl') + + # This should be included, but can be skipped - see below + Service.where(url: nil).each_batch(of: 50) do |batch| + range = batch.pluck('MIN(id)', 'MAX(id)').first + + Gitlab::BackgroundMigration::ExtractServicesUrl.new.perform(*range) + end end def down @@ -230,6 +245,15 @@ class ConsumeRemainingExtractServicesUrlJobs < ActiveRecord::Migration end ``` +The final step runs for any un-migrated rows after all of the jobs have been +processed. This is in case a Sidekiq process running the background migrations +received SIGKILL, leading to the jobs being lost. (See +[more reliable Sidekiq queue][reliable-sidekiq] for more information.) + +If the application does not depend on the data being 100% migrated (for +instance, the data is advisory, and not mission-critical), then this final step +can be skipped. + 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. @@ -254,6 +278,9 @@ for more details. 1. Make sure that background migration jobs are idempotent. 1. Make sure that tests you write are not false positives. +1. Make sure that if the data being migrated is critical and cannot be lost, the + clean-up migration also checks the final state of the data before completing. [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 +[reliable-sidekiq]: https://gitlab.com/gitlab-org/gitlab-ce/issues/36791 diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index d84801f91d4..031b12a8e91 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -29,34 +29,6 @@ For our currently-supported browsers, see our [requirements][requirements]. ## Development Process -When you are assigned an issue please follow the next steps: - -### Divide a big feature into small Merge Requests -1. Big Merge Request are painful to review. In order to make this process easier we -must break a big feature into smaller ones and create a Merge Request for each step. -1. First step is to create a branch from `master`, let's call it `new-feature`. This branch -will be the recipient of all the smaller Merge Requests. Only this one will be merged to master. -1. Don't do any work on this one, let's keep it synced with master. -1. Create a new branch from `new-feature`, let's call it `new-feature-step-1`. We advise you -to clearly identify which step the branch represents. -1. Do the first part of the modifications in this branch. The target branch of this Merge Request -should be `new-feature`. -1. Once `new-feature-step-1` gets merged into `new-feature` we can continue our work. Create a new -branch from `new-feature`, let's call it `new-feature-step-2` and repeat the process done before. - -```shell -master -└─ new-feature - ├─ new-feature-step-1 - ├─ new-feature-step-2 - └─ new-feature-step-3 -``` - -**Tips** -- Make sure `new-feature` branch is always synced with `master`: merge master frequently. -- Do the same for the feature branch you have opened. This can be accomplished by merging `master` into `new-feature` and `new-feature` into `new-feature-step-*` -- Avoid rewriting history. - ### Share your work early 1. Before writing code guarantee your vision of the architecture is aligned with GitLab's architecture. diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md new file mode 100644 index 00000000000..e41d258bec6 --- /dev/null +++ b/doc/development/gitaly.md @@ -0,0 +1,84 @@ +# GitLab Developers Guide to Working with Gitaly + +[Gitaly](https://gitlab.com/gitlab-org/gitaly) is a high-level Git RPC service used by GitLab CE/EE, +Workhorse and GitLab-Shell. All Rugged operations in GitLab CE/EE are currently being phased out to +be replaced by Gitaly API calls. + +Visit the [Gitaly Migration Board](https://gitlab.com/gitlab-org/gitaly/boards/331341) for current +status of the migration. + +## Feature Flags + +Gitaly makes heavy use of [feature flags](feature_flags.md). + +Each Rugged-to-Gitaly migration goes through a [series of phases](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/MIGRATION_PROCESS.md): + +* **Opt-In**: by default the Rugged implementation is used. + * Production instances can choose to enable the Gitaly endpoint by enabling the feature flag. + * For testing purposes, you may wish to enable all feature flags by default. This can be done by exporting the following + environment variable: `GITALY_FEATURE_DEFAULT_ON=1`. + * On developer instances (ie, when `Rails.env.development?` is true), the Gitaly endpoint + is enabled by default, but can be _disabled_ using feature flags. +* **Opt-Out**: by default, the Gitaly endpoint is used, but the feature can be explicitly disabled using the feature flag. +* **Mandatory**: The migration is complete and cannot be disabled. The old codepath is removed. + +### Enabling and Disabling Feature + +In the Rails console, type: + +```ruby +Feature.enable(:gitaly_feature_name) +Feature.disable(:gitaly_feature_name) +``` + +Where `gitaly_feature_name` is the name of the Gitaly feature. This can be determined by finding the appropriate +`gitaly_migrate` code block, for example: + +```ruby +gitaly_migrate(:tag_names) do +... +end +``` + +Since Gitaly features are always prefixed with `gitaly_`, the name of the feature flag in this case would be `gitaly_tag_names`. + +## Gitaly-Related Test Failures + +If your test-suite is failing with Gitaly issues, as a first step, try running: + +```shell +rm -rf tmp/tests/gitaly +``` + +## `TooManyInvocationsError` errors + +During development and testing, you may experience `Gitlab::GitalyClient::TooManyInvocationsError` failures. +The `GitalyClient` will attempt to block against potential n+1 issues by raising this error +when Gitaly is called more than 30 times in a single Rails request or Sidekiq execution. + +As a temporary measure, export `GITALY_DISABLE_REQUEST_LIMITS=1` to suppress the error. This will disable the n+1 detection +in your development environment. + +Please raise an issue in the GitLab CE or EE repositories to report the issue. Include the labels ~Gitaly +~performance ~"technical debt". Please ensure that the issue contains the full stack trace and error message of the +`TooManyInvocationsError`. Also include any known failing tests if possible. + +Isolate the source of the n+1 problem. This will normally be a loop that results in Gitaly being called for each +element in an array. If you are unable to isolate the problem, please contact a member +of the [Gitaly Team](https://gitlab.com/groups/gl-gitaly/group_members) for assistance. + +Once the source has been found, wrap it in an `allow_n_plus_1_calls` block, as follows: + +```ruby +# n+1: link to n+1 issue +Gitlab::GitalyClient.allow_n_plus_1_calls do + # original code + commits.each { |commit| ... } +end +``` + +Once the code is wrapped in this block, this code-path will be excluded from n+1 detection. + +--- + +[Return to Development documentation](README.md) diff --git a/doc/development/licensing.md b/doc/development/licensing.md index 9a5811d8474..a75cdf22f40 100644 --- a/doc/development/licensing.md +++ b/doc/development/licensing.md @@ -65,6 +65,7 @@ Libraries with the following licenses are unacceptable for use: - [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects. - [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU]. - [Facebook BSD + PATENTS][Facebook]: is a 3-clause BSD license with a patent grant that has been deemed [Category X][x-list] by the Apache foundation. +- [WTFPL][WTFPL]: is a public domain dedication [rejected by the OSI (3.2)][WTFPL-OSI]. Also has a strong language which is not in accordance with our diversity policy. ## Requesting Approval for Licenses @@ -108,3 +109,5 @@ Gems which are included only in the "development" or "test" groups by Bundler ar [x-list]: https://www.apache.org/legal/resolved.html#category-x [Acceptable-Licenses]: #acceptable-licenses [Unacceptable-Licenses]: #unacceptable-licenses +[WTFPL]: https://wtfpl.net +[WTFPL-OSI]: https://opensource.org/minutes20090304 diff --git a/doc/development/testing.md b/doc/development/testing.md index 83269303005..d856b003353 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -150,6 +150,16 @@ always in-sync with the codebase. [GitLab QA]: https://gitlab.com/gitlab-org/gitlab-qa [part of GitLab Rails]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa +## Test for what should not be there + +This is particularly important for permission calls and might be called a +negative assertion: make sure only the bare minimum is returned and nothing else. + +See an issue about [leaking tokens] as an example of a vulnerability that is +captured by such a test. + +[leaking tokens]: https://gitlab.com/gitlab-org/gitlab-ce/issues/37948 + ## How to test at the correct level? As many things in life, deciding what to test at each level of testing is a @@ -493,24 +503,24 @@ Here are some things to keep in mind regarding test performance: Our current CI parallelization setup is as follows: -1. The `knapsack` job in the prepare stage that is supposed to ensure we have a - `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file: +1. The `retrieve-tests-metadata` job in the `prepare` stage ensures that we have + a `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file: - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched from S3, if it's not here we initialize the file with `{}`. -1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly - distributed share of tests: +1. Each `rspec-pg x y`/`rspec-mysql x y` job is run with `knapsack rspec` and + should have an evenly distributed share of tests: - It works because the jobs have access to the `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts from all previous stages are passed by default". [^1] - - the jobs set their own report path to + - The jobs set their own report path to `KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`. - - if knapsack is doing its job, test files that are run should be listed under + - If knapsack is doing its job, test files that are run should be listed under `Report specs`, not under `Leftover specs`. -1. The `update-knapsack` job takes all the +1. The `update-tests-metadata` job takes all the `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json` - files from the `rspec x y` jobs and merge them all together into a single - `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then - uploaded to S3. + files from the `rspec-pg x y`/`rspec-mysql x y`jobs and merge them all together + into a single `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that + is then uploaded to S3. After that, the next pipeline will use the up-to-date `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. The same strategy diff --git a/doc/development/ux_guide/illustrations.md b/doc/development/ux_guide/illustrations.md new file mode 100644 index 00000000000..7e16c300921 --- /dev/null +++ b/doc/development/ux_guide/illustrations.md @@ -0,0 +1,86 @@ +# Illustrations + +The illustrations should always align with topics and goals in specific context. + +## Principles + +#### Be simple. +- For clarity, we use simple and specific elements to create our illustrations. + +#### Be optimistic. +- We are an open-minded, optimistic, and friendly team. We should reflect those values in our illustrations to connect with our brand experience. + +#### Be gentle. +- Our illustrations assist users in understanding context and guide users in the right direction. Illustrations are supportive, so they should be obvious but not aggressive. + + +## Style + +#### Shapes +- All illustrations are geometric rather than organic. +- The illustrations are made by circles, rectangles, squares, and triangles. + +<img src="img/illustrations-geometric.png" width=224px alt="Example for geometric" /> + +#### Stroke +- Standard border thickness: **4px** +- Depending on the situation, border thickness can be changed to **3px**. For example, when the illustration size is small, an illustration with 4px border thickness would look tight. In this case, the border thickness can be changed to 3px. +- We use **rounded caps** and **rounded corner**. + +| Do | Don't | +| -------- | -------- | +| <img src="img/illustrations-caps-do.png" width= 133px alt="Do: caps and corner" /> | <img src="img/illustrations-caps-don't.png" width= 133px alt="Don't: caps and corner"/> | + +#### Radius +- Standard corner radius: **10px** +- Depending on the situation, corner radius can be changed to **5px**. For example, when the illustration size is small, an illustration with 10px corner radius would be over-rounded. In this case, the corner radius can be changed to 5px. + +<img src="img/illustrations-border-radius.png" width= 464px alt="Example for border radius"/> + +#### Size +Depends on the situation, the illustration size can be the 3 types below: + +**Large** +* Use case: Empty states, error pages(e.g. 404, 403) +* For vertical layout, the illustration should not larger than **430*300 px**. +* For horizontal layout, the illustration should not larger than **430*380 px**. + +| Vertical layout | Horizontal layout | +| --------------- | ----------------- | +| <img src="img/illustration-size-large-vertical.png" /> | <img src="img/illustration-size-large-horizontal.png" /> + +**Medium** +* Use case: Banner +* The illustration should not larger than **240*160 px** +* The illustration should keep simple and clear. We recommend not including too many elements in the medium size illustration. + +<img src="img/illustration-size-medium.png" width=983px /> + +**Small** +* Use case: Graphics for explanatory text, graphics for status. +* The illustration should not larger than **160*90 px**. +* The illustration should keep simple and clear. We recommend not including too many elements in the small size illustration. + +<img src="img/illustration-size-small.png" width=983px /> + +**Illustration on mobile** +- Keep the proportions in original ratio. + +#### Colors palette + +For consistency, we recommend choosing colors from our color palette. + +| Orange | Purple | Grey | +| -------- | -------- | -------- | +| <img src="img/illustrations-color-orange.png" width= 160px alt="Orange" /> | <img src="img/illustrations-color-purple.png" width= 160px alt="Purple" /> | <img src="img/illustrations-color-grey.png" width= 160px alt="Grey" /> | +| #FC6D26 | #6B4FBB | #EEEEEE | + +#### Don't +- Don't include the typography in the illustration. +- Don't include tanuki in the illustration. If necessary, we recommend having tanuki monochromatic. + +--- + +| Orange | Purple | +| -------- | -------- | +| <img src="img/illustrations-palette-oragne.png" width= 160px alt="Palette - Orange" /> | <img src="img/illustrations-palette-purple.png" width= 160px alt="Palette - Purple" /> | diff --git a/doc/development/ux_guide/img/illustration-size-large-horizontal.png b/doc/development/ux_guide/img/illustration-size-large-horizontal.png Binary files differnew file mode 100755 index 00000000000..8aa835adccc --- /dev/null +++ b/doc/development/ux_guide/img/illustration-size-large-horizontal.png diff --git a/doc/development/ux_guide/img/illustration-size-large-vertical.png b/doc/development/ux_guide/img/illustration-size-large-vertical.png Binary files differnew file mode 100644 index 00000000000..813b6a065e5 --- /dev/null +++ b/doc/development/ux_guide/img/illustration-size-large-vertical.png diff --git a/doc/development/ux_guide/img/illustration-size-medium.png b/doc/development/ux_guide/img/illustration-size-medium.png Binary files differnew file mode 100755 index 00000000000..55cfe1dcb91 --- /dev/null +++ b/doc/development/ux_guide/img/illustration-size-medium.png diff --git a/doc/development/ux_guide/img/illustration-size-small.png b/doc/development/ux_guide/img/illustration-size-small.png Binary files differnew file mode 100644 index 00000000000..0124f58f48e --- /dev/null +++ b/doc/development/ux_guide/img/illustration-size-small.png diff --git a/doc/development/ux_guide/img/illustrations-border-radius.png b/doc/development/ux_guide/img/illustrations-border-radius.png Binary files differnew file mode 100755 index 00000000000..4e2fef5c7f5 --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-border-radius.png diff --git a/doc/development/ux_guide/img/illustrations-caps-do.png b/doc/development/ux_guide/img/illustrations-caps-do.png Binary files differnew file mode 100755 index 00000000000..7a2c74382f6 --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-caps-do.png diff --git a/doc/development/ux_guide/img/illustrations-caps-don't.png b/doc/development/ux_guide/img/illustrations-caps-don't.png Binary files differnew file mode 100755 index 00000000000..848f72dbe30 --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-caps-don't.png diff --git a/doc/development/ux_guide/img/illustrations-color-grey.png b/doc/development/ux_guide/img/illustrations-color-grey.png Binary files differnew file mode 100755 index 00000000000..63855026c2b --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-color-grey.png diff --git a/doc/development/ux_guide/img/illustrations-color-orange.png b/doc/development/ux_guide/img/illustrations-color-orange.png Binary files differnew file mode 100755 index 00000000000..96765c8c28c --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-color-orange.png diff --git a/doc/development/ux_guide/img/illustrations-color-purple.png b/doc/development/ux_guide/img/illustrations-color-purple.png Binary files differnew file mode 100755 index 00000000000..745d2c853ba --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-color-purple.png diff --git a/doc/development/ux_guide/img/illustrations-geometric.png b/doc/development/ux_guide/img/illustrations-geometric.png Binary files differnew file mode 100755 index 00000000000..33f05547bac --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-geometric.png diff --git a/doc/development/ux_guide/img/illustrations-palette-oragne.png b/doc/development/ux_guide/img/illustrations-palette-oragne.png Binary files differnew file mode 100755 index 00000000000..15f35912646 --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-palette-oragne.png diff --git a/doc/development/ux_guide/img/illustrations-palette-purple.png b/doc/development/ux_guide/img/illustrations-palette-purple.png Binary files differnew file mode 100755 index 00000000000..e0f5839705e --- /dev/null +++ b/doc/development/ux_guide/img/illustrations-palette-purple.png diff --git a/doc/development/ux_guide/index.md b/doc/development/ux_guide/index.md index 8a849f239dc..42bcf234e12 100644 --- a/doc/development/ux_guide/index.md +++ b/doc/development/ux_guide/index.md @@ -21,6 +21,11 @@ Guidance on the timing, curving and motion for GitLab. --- +### [Illustrations](illustrations.md) +Guidelines for principals and styles related to illustrations for GitLab. + +--- + ### [Copy](copy.md) Conventions on text and messaging within labels, buttons, and other components. diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md index b1eb020a592..68ba3dd2da3 100644 --- a/doc/development/writing_documentation.md +++ b/doc/development/writing_documentation.md @@ -1,6 +1,6 @@ # Writing documentation - - **General Documentation**: written by the developers responsible by creating features. Should be submitted in the same merge request containing code. Feature proposals (by GitLab contributors) should also be accompanied by its respective documentation. They can be later improved by PMs and Technical Writers. + - **General Documentation**: written by the [developers responsible by creating features](#contributing-to-docs). Should be submitted in the same merge request containing code. Feature proposals (by GitLab contributors) should also be accompanied by its respective documentation. They can be later improved by PMs and Technical Writers. - **Technical Articles**: written by any [GitLab Team](https://about.gitlab.com/team/) member, GitLab contributors, or [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/). - **Indexes per topic**: initially prepared by the Technical Writing Team, and kept up-to-date by developers and PMs in the same merge request containing code. They gather all resources for that topic in a single page (user and admin documentation, articles, and third-party docs). @@ -69,6 +69,51 @@ Use the [writing method](https://about.gitlab.com/handbook/product/technical-wri All the docs follow the same [styleguide](doc_styleguide.md). +### Contributing to docs + +Whenever a feature is changed, updated, introduced, or deprecated, the merge +request introducing these changes must be accompanied by the documentation +(either updating existing ones or creating new ones). This is also valid when +changes are introduced to the UI. + +The one resposible for writing the first piece of documentation is the developer who +wrote the code. It's the job of the Product Manager to ensure all features are +shipped with its docs, whether is a small or big change. At the pace GitLab evolves, +this is the only way to keep the docs up-to-date. If you have any questions about it, +please ask a Technical Writer. Otherwise, when your content is ready, assign one of +them to review it for you. + +We use the [monthly release blog post](https://about.gitlab.com/handbook/marketing/blog/release-posts/#monthly-releases) as a changelog checklist to ensure everything +is documented. + +### Feature overview and use cases + +Every major feature (regardless if present in GitLab Community or Enterprise editions) +should present, at the beginning of the document, two main sections: **overview** and +**use cases**. Every GitLab EE-only feature should also contain these sections. + +**Overview**: at the name suggests, the goal here is to provide an overview of the feature. +Describe what is it, what it does, why it is important/cool/nice-to-have, +what problem it solves, and what you can do with this feature that you couldn't +do before. + +**Use cases**: provide at least two, ideally three, use cases for every major feature. +You should answer this question: what can you do with this feature/change? Use cases +are examples of how this feauture or change can be used in real life. + +Examples: +- CE and EE: [Issues](../user/project/issues/index.md#use-cases) +- CE and EE: [Merge Requests](../user/project/merge_requests/index.md#overview) +- EE-only: [Geo](https://docs.gitlab.com/ee/gitlab-geo/README.html#overview) +- EE-only: [Jenkins integration](https://docs.gitlab.com/ee/integration/jenkins.md#overview) + +Note that if you don't have anything to add between the doc title (`<h1>`) and +the header `## Overview`, you can omit the header, but keep the content of the +overview there. + +> **Overview** and **use cases** are required to **every** Enterprise Edition feature, +and for every **major** feature present in Community Edition. + ### Markdown Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future. diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 5c128f54a76..f9ba1508705 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -1,11 +1,12 @@ # Database MySQL >**Note:** -We do not recommend using MySQL due to various issues. For example, case +- We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). +- We recommend using MySQL version 5.6 or later. Please see the following [issue][ce-38152]. ## Initial database setup @@ -13,7 +14,7 @@ and [problems](https://bugs.mysql.com/bug.php?id=65830) that # Install the database packages sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev -# Ensure you have MySQL version 5.5.14 or later +# Ensure you have MySQL version 5.6 or later mysql --version # Pick a MySQL root password (can be anything), type it and press enter @@ -293,3 +294,4 @@ Details can be found in the [PostgreSQL][postgres-text-type] and [postgres-text-type]: http://www.postgresql.org/docs/9.2/static/datatype-character.html [mysql-text-types]: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html +[ce-38152]: https://gitlab.com/gitlab-org/gitlab-ce/issues/38152 diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md index 177124c8291..ddfd47df099 100644 --- a/doc/install/kubernetes/gitlab_chart.md +++ b/doc/install/kubernetes/gitlab_chart.md @@ -1,8 +1,13 @@ # GitLab Helm Chart > **Note**: -* This chart will be replaced by the [gitlab-omnibus](gitlab_omnibus.md) chart, once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). +* This chart will be replaced by the [gitlab-omnibus](gitlab_omnibus.md) chart, once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). For more information on available charts, please see our [overview](index.md#chart-overview). * These charts have been tested on Google Container Engine and Azure Container Service. Other Kubernetes installations may work as well, if not please [open an issue](https://gitlab.com/charts/charts.gitlab.io/issues). + +For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview). + +## Introduction + The `gitlab` Helm chart deploys just GitLab into your Kubernetes cluster, and offers extensive configuration options. This chart requires advanced knowledge of Kubernetes to successfully use. For most deployments we **strongly recommended** the [gitlab-omnibus](gitlab_omnibus.md) chart, which will replace this chart once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). Due to the difficulty in supporting upgrades to the `omnibus-gitlab` chart, migrating will require exporting data out of this instance and importing it into the new deployment. This chart includes the following: @@ -15,8 +20,6 @@ This chart includes the following: - Optional PostgreSQL deployment using the [PostgreSQL Chart](https://github.com/kubernetes/charts/tree/master/stable/postgresql) (defaults to enabled) - Optional Ingress (defaults to disabled) -For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview). - ## Prerequisites - _At least_ 3 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required. diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md index 150eb3a8bce..8c110a37380 100644 --- a/doc/install/kubernetes/gitlab_omnibus.md +++ b/doc/install/kubernetes/gitlab_omnibus.md @@ -148,8 +148,15 @@ helm install --name gitlab --set baseDomain=gitlab.io,baseIP=1.1.1.1,gitlab=ee,g ## Updating GitLab using the Helm Chart +>**Note**: If you are upgrading from a previous version to 0.1.35 or above, you will need to change the access mode values for GitLab's storage. To do this, set the following in `values.yaml` or on the CLI: +``` +gitlabDataAccessMode=ReadWriteMany +gitlabRegistryAccessMode=ReadWriteMany +gitlabConfigAccessMode=ReadWriteMany +``` + Once your GitLab Chart is installed, configuration changes and chart updates -should we done using `helm upgrade`: +should be done using `helm upgrade`: ```bash helm upgrade -f values.yaml gitlab gitlab/gitlab-omnibus @@ -179,5 +186,13 @@ To uninstall the GitLab Chart, run the following: helm delete gitlab ``` +## Troubleshooting + +### Storage errors when updating `gitlab-omnibus` versions prior to 0.1.35 + +Users upgrading `gitlab-omnibus` from a version prior to 0.1.35, may see an error like: `Error: UPGRADE FAILED: PersistentVolumeClaim "gitlab-gitlab-config-storage" is invalid: spec: Forbidden: field is immutable after creation`. + +This is due to a change in the access mode for GitLab storage in version 0.1.35. To successfully upgrade, the access mode flags must be set to `ReadWriteMany` as detailed in the [update section](#updating-gitlab-using-the-helm-chart). + [kube-srv]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types [storageclass]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#storageclasses diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md index 467d5b92e0c..aed00ae9e2c 100644 --- a/doc/install/kubernetes/index.md +++ b/doc/install/kubernetes/index.md @@ -9,12 +9,12 @@ should be deployed, upgraded, and configured. ## Chart Overview -* **[GitLab-Omnibus](#gitlab-omnibus-chart-recommended)**: The best way to run GitLab on Kubernetes today. It is suited for small to medium deployments, and is in beta while support for backups and other features are added. -* **[Upcoming Cloud Native Charts](#upcoming-cloud-native-helm-charts)**: The next generation of charts, currently in development. Will support large deployments, with horizontal scaling of individual GitLab components. +* **[GitLab-Omnibus](gitlab_omnibus.md)**: The best way to run GitLab on Kubernetes today. It is suited for small to medium deployments, and is in beta while support for backups and other features are added. +* **[Cloud Native GitLab Chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md)**: The next generation GitLab chart, currently in development. Will support large deployments with horizontal scaling of individual GitLab components. * Other Charts - * [GitLab Runner Chart](#gitlab-runner-chart): For deploying just the GitLab Runner. - * [Advanced GitLab Installation](#advanced-gitlab-installation): Provides additional deployment options, but provides less functionality out-of-the-box. It's beta, no longer actively developed, and will be deprecated by [gitlab-omnibus](#gitlab-omnibus-chart-recommended) once it supports these options. - * [Community Contributed Charts](#community-contributed-charts): Community contributed charts, deprecated by the official GitLab charts. + * [GitLab Runner Chart](gitlab_runner_chart.md): For deploying just the GitLab Runner. + * [Advanced GitLab Installation](gitlab_chart.md): Provides additional deployment options, but provides less functionality out-of-the-box. It's beta, no longer actively developed, and will be deprecated by [gitlab-omnibus](#gitlab-omnibus-chart-recommended) once it supports these options. + * [Community Contributed Charts](#community-contributed-charts): Community contributed charts, deprecated by the official GitLab chart. ## GitLab-Omnibus Chart (Recommended) > **Note**: This chart is in beta while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being added. @@ -25,9 +25,9 @@ Once the [cloud native charts](#upcoming-cloud-native-helm-charts) are ready for Learn more about the [gitlab-omnibus chart.](gitlab_omnibus.md) -## Upcoming Cloud Native Charts +## Cloud Native GitLab Chart -GitLab is working towards building a [cloud native deployment method](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into its [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended). +GitLab is working towards building a [cloud native GitLab chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into its [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended). By offering individual containers and charts, we will be able to provide a number of benefits: * Easier horizontal scaling of each service, @@ -37,6 +37,8 @@ By offering individual containers and charts, we will be able to provide a numbe This is a large project and will be worked on over the span of multiple releases. For the most up-to-date status and release information, please see our [tracking issue](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420). We do not expect these to be production ready before the second half of 2018. +Learn more about the [cloud native GitLab chart.](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) + ## Other Charts ### GitLab Runner Chart @@ -55,7 +57,7 @@ Learn more about the [gitlab chart.](gitlab_chart.md) ### Community Contributed Charts -The community has also [contributed GitLab charts](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](#official-gitlab-helm-charts-recommended). +The community has also contributed GitLab [CE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) and [EE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ee) charts to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](gitlab_omnibus.md). [chart]: https://github.com/kubernetes/charts [helm]: https://github.com/kubernetes/helm/blob/master/README.md diff --git a/doc/integration/azure.md b/doc/integration/azure.md index 5e3e9f5ab77..f3c9c498634 100644 --- a/doc/integration/azure.md +++ b/doc/integration/azure.md @@ -74,6 +74,9 @@ To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your ap tenant_id: "TENANT ID" } } ``` + The `base_azure_url` is optional and can be added for different locales; + e.g. `base_azure_url: "https://login.microsoftonline.de"`. + 1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above. 1. Save the configuration file. diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 0b7c11ea700..5561784ed0b 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -511,7 +511,7 @@ sudo gitlab-rails console Then run: ```ruby -Feature.get(:auto_devops_banner_disabled).disable +Feature.get(:auto_devops_banner_disabled).enable ``` Or through the HTTP API with an admin access token: diff --git a/doc/university/training/gitlab_flow.md b/doc/university/training/gitlab_flow.md index a7db1f2e069..a7db1f2e069 100755..100644 --- a/doc/university/training/gitlab_flow.md +++ b/doc/university/training/gitlab_flow.md diff --git a/doc/university/training/index.md b/doc/university/training/index.md index 03179ff5a77..03179ff5a77 100755..100644 --- a/doc/university/training/index.md +++ b/doc/university/training/index.md diff --git a/doc/university/training/topics/additional_resources.md b/doc/university/training/topics/additional_resources.md index 3ed601625cf..3ed601625cf 100755..100644 --- a/doc/university/training/topics/additional_resources.md +++ b/doc/university/training/topics/additional_resources.md diff --git a/doc/university/training/topics/agile_git.md b/doc/university/training/topics/agile_git.md index e6e4fea9b51..e6e4fea9b51 100755..100644 --- a/doc/university/training/topics/agile_git.md +++ b/doc/university/training/topics/agile_git.md diff --git a/doc/university/training/topics/bisect.md b/doc/university/training/topics/bisect.md index a60c4365e0c..a60c4365e0c 100755..100644 --- a/doc/university/training/topics/bisect.md +++ b/doc/university/training/topics/bisect.md diff --git a/doc/university/training/topics/cherry_picking.md b/doc/university/training/topics/cherry_picking.md index af7a70a2818..af7a70a2818 100755..100644 --- a/doc/university/training/topics/cherry_picking.md +++ b/doc/university/training/topics/cherry_picking.md diff --git a/doc/university/training/topics/env_setup.md b/doc/university/training/topics/env_setup.md index 8149379b36f..8149379b36f 100755..100644 --- a/doc/university/training/topics/env_setup.md +++ b/doc/university/training/topics/env_setup.md diff --git a/doc/university/training/topics/explore_gitlab.md b/doc/university/training/topics/explore_gitlab.md index b65457728c0..b65457728c0 100755..100644 --- a/doc/university/training/topics/explore_gitlab.md +++ b/doc/university/training/topics/explore_gitlab.md diff --git a/doc/university/training/topics/feature_branching.md b/doc/university/training/topics/feature_branching.md index 4b34406ea75..4b34406ea75 100755..100644 --- a/doc/university/training/topics/feature_branching.md +++ b/doc/university/training/topics/feature_branching.md diff --git a/doc/university/training/topics/getting_started.md b/doc/university/training/topics/getting_started.md index ec7bb2631aa..ec7bb2631aa 100755..100644 --- a/doc/university/training/topics/getting_started.md +++ b/doc/university/training/topics/getting_started.md diff --git a/doc/university/training/topics/git_add.md b/doc/university/training/topics/git_add.md index 9ffb4b9c859..9ffb4b9c859 100755..100644 --- a/doc/university/training/topics/git_add.md +++ b/doc/university/training/topics/git_add.md diff --git a/doc/university/training/topics/git_intro.md b/doc/university/training/topics/git_intro.md index ca1ff29d93b..ca1ff29d93b 100755..100644 --- a/doc/university/training/topics/git_intro.md +++ b/doc/university/training/topics/git_intro.md diff --git a/doc/university/training/topics/git_log.md b/doc/university/training/topics/git_log.md index 32ebceff491..32ebceff491 100755..100644 --- a/doc/university/training/topics/git_log.md +++ b/doc/university/training/topics/git_log.md diff --git a/doc/university/training/topics/gitlab_flow.md b/doc/university/training/topics/gitlab_flow.md index 8e5d3baf959..8e5d3baf959 100755..100644 --- a/doc/university/training/topics/gitlab_flow.md +++ b/doc/university/training/topics/gitlab_flow.md diff --git a/doc/university/training/topics/merge_conflicts.md b/doc/university/training/topics/merge_conflicts.md index 77807b3e7ef..77807b3e7ef 100755..100644 --- a/doc/university/training/topics/merge_conflicts.md +++ b/doc/university/training/topics/merge_conflicts.md diff --git a/doc/university/training/topics/merge_requests.md b/doc/university/training/topics/merge_requests.md index 5b446f02f63..5b446f02f63 100755..100644 --- a/doc/university/training/topics/merge_requests.md +++ b/doc/university/training/topics/merge_requests.md diff --git a/doc/university/training/topics/rollback_commits.md b/doc/university/training/topics/rollback_commits.md index cf647284604..cf647284604 100755..100644 --- a/doc/university/training/topics/rollback_commits.md +++ b/doc/university/training/topics/rollback_commits.md diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md index c1bdda32645..c1bdda32645 100755..100644 --- a/doc/university/training/topics/stash.md +++ b/doc/university/training/topics/stash.md diff --git a/doc/university/training/topics/subtree.md b/doc/university/training/topics/subtree.md index 5d869af64c1..5d869af64c1 100755..100644 --- a/doc/university/training/topics/subtree.md +++ b/doc/university/training/topics/subtree.md diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md index e9607b5a875..e9607b5a875 100755..100644 --- a/doc/university/training/topics/tags.md +++ b/doc/university/training/topics/tags.md diff --git a/doc/university/training/topics/unstage.md b/doc/university/training/topics/unstage.md index 17dbb64b9e6..17dbb64b9e6 100755..100644 --- a/doc/university/training/topics/unstage.md +++ b/doc/university/training/topics/unstage.md diff --git a/doc/university/training/user_training.md b/doc/university/training/user_training.md index 9e38df26b6a..9e38df26b6a 100755..100644 --- a/doc/university/training/user_training.md +++ b/doc/university/training/user_training.md diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md index 70934f9960a..843fb4ce26b 100644 --- a/doc/user/admin_area/monitoring/health_check.md +++ b/doc/user/admin_area/monitoring/health_check.md @@ -18,7 +18,7 @@ traffic until the system is ready or restart the container as needed. To access monitoring resources, the client IP needs to be included in a whitelist. -[Read how to add IPs to a whitelist for the monitoring endpoints.][admin]. +[Read how to add IPs to a whitelist for the monitoring endpoints][admin]. ## Using the endpoint diff --git a/doc/user/project/integrations/img/kubernetes_configuration.png b/doc/user/project/integrations/img/kubernetes_configuration.png Binary files differindex 349a2dc8456..e535e2b8d46 100644 --- a/doc/user/project/integrations/img/kubernetes_configuration.png +++ b/doc/user/project/integrations/img/kubernetes_configuration.png diff --git a/doc/user/project/integrations/img/webhook_logs.png b/doc/user/project/integrations/img/webhook_logs.png Binary files differindex 917068d9398..917068d9398 100755..100644 --- a/doc/user/project/integrations/img/webhook_logs.png +++ b/doc/user/project/integrations/img/webhook_logs.png diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md index f4000523938..e9738b683f9 100644 --- a/doc/user/project/integrations/kubernetes.md +++ b/doc/user/project/integrations/kubernetes.md @@ -1,3 +1,7 @@ +--- +last_updated: 2017-09-25 +--- + # GitLab Kubernetes / OpenShift integration GitLab can be configured to interact with Kubernetes, or other systems using the @@ -6,62 +10,114 @@ Kubernetes API (such as OpenShift). Each project can be configured to connect to a different Kubernetes cluster, see the [configuration](#configuration) section. -If you have a single cluster that you want to use for all your projects, -you can pre-fill the settings page with a default template. To configure the -template, see the [Services Templates](services_templates.md) document. - ## Configuration Navigate to the [Integrations page](project_services.md#accessing-the-project-services) -of your project and select the **Kubernetes** service to configure it. +of your project and select the **Kubernetes** service to configure it. Fill in +all the needed parameters, check the "Active" checkbox and hit **Save changes** +for the changes to take effect. ![Kubernetes configuration settings](img/kubernetes_configuration.png) -The Kubernetes service takes the following arguments: - -1. API URL -1. Custom CA bundle -1. Kubernetes namespace -1. Service token - -The API URL is the URL that GitLab uses to access the Kubernetes API. Kubernetes -exposes several APIs - we want the "base" URL that is common to all of them, -e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`. - -GitLab authenticates against Kubernetes using service tokens, which are -scoped to a particular `namespace`. If you don't have a service token yet, -you can follow the -[Kubernetes documentation](http://kubernetes.io/docs/user-guide/service-accounts/) -to create one. You can also view or create service tokens in the -[Kubernetes dashboard](http://kubernetes.io/docs/user-guide/ui/) - visit -`Config -> Secrets`. - -Fill in the service token and namespace according to the values you just got. -If the API is using a self-signed TLS certificate, you'll also need to include -the `ca.crt` contents as the `Custom CA bundle`. +The Kubernetes service takes the following parameters: + +- **API URL** - + It's the URL that GitLab uses to access the Kubernetes API. Kubernetes + exposes several APIs, we want the "base" URL that is common to all of them, + e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`. +- **CA certificate** (optional) - + If the API is using a self-signed TLS certificate, you'll also need to include + the `ca.crt` contents here. +- **Project namespace** (optional) - The following apply: + - By default you don't have to fill it in; by leaving it blank, GitLab will + create one for you. + - Each project should have a unique namespace. + - The project namespace is not necessarily the namespace of the secret, if + you're using a secret with broader permissions, like the secret from `default`. + - You should **not** use `default` as the project namespace. + - If you or someone created a secret specifically for the project, usually + with limited permissions, the secret's namespace and project namespace may + be the same. +- **Token** - + GitLab authenticates against Kubernetes using service tokens, which are + scoped to a particular `namespace`. If you don't have a service token yet, + you can follow the + [Kubernetes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) + to create one. You can also view or create service tokens in the + [Kubernetes dashboard](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#config) + (under **Config > Secrets**). + +TIP: **Tip:** +If you have a single cluster that you want to use for all your projects, +you can pre-fill the settings page with a default template. To configure the +template, see [Services Templates](services_templates.md). ## Deployment variables -The Kubernetes service exposes following +The Kubernetes service exposes the following [deployment variables](../../../ci/variables/README.md#deployment-variables) in the -GitLab CI build environment: +GitLab CI/CD build environment: -- `KUBE_URL` - equal to the API URL -- `KUBE_TOKEN` +- `KUBE_URL` - Equal to the API URL. +- `KUBE_TOKEN` - The Kubernetes token. - `KUBE_NAMESPACE` - The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. -- `KUBE_CA_PEM_FILE` - only present if a custom CA bundle was specified. Path +- `KUBE_CA_PEM_FILE` - Only present if a custom CA bundle was specified. Path to a file containing PEM data. -- `KUBE_CA_PEM` (deprecated)- only if a custom CA bundle was specified. Raw PEM data. -- `KUBECONFIG` - Path to a file containing kubeconfig for this deployment. CA bundle would be embedded if specified. +- `KUBE_CA_PEM` (deprecated) - Only if a custom CA bundle was specified. Raw PEM data. +- `KUBECONFIG` - Path to a file containing `kubeconfig` for this deployment. + CA bundle would be embedded if specified. + +## What you can get with the Kubernetes integration + +Here's what you can do with GitLab if you enable the Kubernetes integration. + +### Deploy Boards (EEP) + +> Available in [GitLab Enterprise Edition Premium][ee]. -## Web terminals +GitLab's Deploy Boards offer a consolidated view of the current health and +status of each CI [environment](../../../ci/environments.md) running on Kubernetes, +displaying the status of the pods in the deployment. Developers and other +teammates can view the progress and status of a rollout, pod by pod, in the +workflow they already use without any need to access Kubernetes. ->**NOTE:** -Added in GitLab 8.15. You must be the project owner or have `master` permissions -to use terminals. Support is currently limited to the first container in the +[> Read more about Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html) + +### Canary Deployments (EEP) + +> Available in [GitLab Enterprise Edition Premium][ee]. + +Leverage [Kubernetes' Canary deployments](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments) +and visualize your canary deployments right inside the Deploy Board, without +the need to leave GitLab. + +[> Read more about Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html) + +### Kubernetes monitoring + +Automatically detect and monitor Kubernetes metrics. Automatic monitoring of +[NGINX ingress](./prometheus_library/nginx.md) is also supported. + +[> Read more about Kubernetes monitoring](prometheus_library/kubernetes.md) + +### Auto DevOps + +Auto DevOps automatically detects, builds, tests, deploys, and monitors your +applications. + +To make full use of Auto DevOps(Auto Deploy, Auto Review Apps, and Auto Monitoring) +you will need the Kubernetes project integration enabled. + +[> Read more about Auto DevOps](../../../topics/autodevops/index.md) + +### Web terminals + +NOTE: **Note:** +Introduced in GitLab 8.15. You must be the project owner or have `master` permissions +to use terminals. Support is limited to the first container in the first pod of your environment. When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals) @@ -70,3 +126,5 @@ Docker and Kubernetes, so you get a new shell session within your existing containers. To use this integration, you should deploy to Kubernetes using the deployment variables above, ensuring any pods you create are labelled with `app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest! + +[ee]: https://about.gitlab.com/gitlab-ee/ diff --git a/doc/user/project/integrations/prometheus_library/cloudwatch.md b/doc/user/project/integrations/prometheus_library/cloudwatch.md index cc5cee36d28..34a0b97a171 100644 --- a/doc/user/project/integrations/prometheus_library/cloudwatch.md +++ b/doc/user/project/integrations/prometheus_library/cloudwatch.md @@ -1,8 +1,13 @@ # Monitoring AWS Resources + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4 GitLab has support for automatically detecting and monitoring AWS resources, starting with the [Elastic Load Balancer](https://aws.amazon.com/elasticloadbalancing/). This is provided by leveraging the official [Cloudwatch exporter](https://github.com/prometheus/cloudwatch_exporter), which translates [Cloudwatch metrics](https://aws.amazon.com/cloudwatch/) into a Prometheus readable form. +## Requirements + +The [Prometheus service](../prometheus/index.md) must be enabled. + ## Metrics supported | Name | Query | diff --git a/doc/user/project/integrations/prometheus_library/haproxy.md b/doc/user/project/integrations/prometheus_library/haproxy.md index d4b5911a91c..518018e5839 100644 --- a/doc/user/project/integrations/prometheus_library/haproxy.md +++ b/doc/user/project/integrations/prometheus_library/haproxy.md @@ -3,6 +3,10 @@ GitLab has support for automatically detecting and monitoring HAProxy. This is provided by leveraging the [HAProxy Exporter](https://github.com/prometheus/haproxy_exporter), which translates HAProxy statistics into a Prometheus readable form. +## Requirements + +The [Prometheus service](../prometheus/index.md) must be enabled. + ## Metrics supported | Name | Query | diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md index 4d39ae0c4fa..518683965e8 100644 --- a/doc/user/project/integrations/prometheus_library/kubernetes.md +++ b/doc/user/project/integrations/prometheus_library/kubernetes.md @@ -1,7 +1,13 @@ # Monitoring Kubernetes + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8935) in GitLab 9.0 -GitLab has support for automatically detecting and monitoring Kubernetes metrics. Kubernetes exposes Node level metrics out of the box via the built-in [Prometheus metrics support in cAdvisor](https://github.com/google/cadvisor). No additional services or exporters are needed. +GitLab has support for automatically detecting and monitoring Kubernetes metrics. + +## Requirements + +The [Prometheus](../prometheus.md) and [Kubernetes](../kubernetes.md) +integration services must be enabled. ## Metrics supported @@ -23,4 +29,4 @@ Prometheus server up and running. You have two options here: 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). -If you are using [GitLab Auto-Deploy][../../../ci/autodeploy/index.md] and one of the two [provided Kubernetes monitoring solutions](../prometheus.md#getting-started-with-prometheus-monitoring), the `environment` label will be automatically added. +If you are using [GitLab Auto-Deploy](../../../../ci/autodeploy/index.md) and one of the two [provided Kubernetes monitoring solutions](../prometheus.md#getting-started-with-prometheus-monitoring), the `environment` label will be automatically added. diff --git a/doc/user/project/integrations/prometheus_library/nginx.md b/doc/user/project/integrations/prometheus_library/nginx.md index bab22f9a384..7fb8369d3c1 100644 --- a/doc/user/project/integrations/prometheus_library/nginx.md +++ b/doc/user/project/integrations/prometheus_library/nginx.md @@ -1,8 +1,13 @@ # Monitoring NGINX + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12621) in GitLab 9.4 GitLab has support for automatically detecting and monitoring NGINX. This is provided by leveraging the [NGINX VTS exporter](https://github.com/hnlq715/nginx-vts-exporter), which translates [VTS statistics](https://github.com/vozlt/nginx-module-vts) into a Prometheus readable form. +## Requirements + +The [Prometheus service](../prometheus/index.md) must be enabled. + ## Metrics supported | Name | Query | diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md index 17a47cfa646..e6f13d0630b 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -1,8 +1,13 @@ # Monitoring NGINX Ingress Controller + > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13438) in GitLab 9.5 GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) of the ingress. +## Requirements + +The [Prometheus service](../prometheus/index.md) must be enabled. + ## Metrics supported | Name | Query | diff --git a/doc/user/project/issues/img/button_close_issue.png b/doc/user/project/issues/img/button_close_issue.png Binary files differindex 8fb2e23f58a..8fb2e23f58a 100755..100644 --- a/doc/user/project/issues/img/button_close_issue.png +++ b/doc/user/project/issues/img/button_close_issue.png diff --git a/doc/user/project/issues/img/closing_and_related_issues.png b/doc/user/project/issues/img/closing_and_related_issues.png Binary files differindex c6543e85fdb..c6543e85fdb 100755..100644 --- a/doc/user/project/issues/img/closing_and_related_issues.png +++ b/doc/user/project/issues/img/closing_and_related_issues.png diff --git a/doc/user/project/issues/img/confidential_issues_create.png b/doc/user/project/issues/img/confidential_issues_create.png Binary files differindex 0a141eb39f8..0a141eb39f8 100755..100644 --- a/doc/user/project/issues/img/confidential_issues_create.png +++ b/doc/user/project/issues/img/confidential_issues_create.png diff --git a/doc/user/project/issues/img/confidential_issues_search_guest.png b/doc/user/project/issues/img/confidential_issues_search_guest.png Binary files differindex dc1b4ba8ad7..dc1b4ba8ad7 100755..100644 --- a/doc/user/project/issues/img/confidential_issues_search_guest.png +++ b/doc/user/project/issues/img/confidential_issues_search_guest.png diff --git a/doc/user/project/issues/img/confidential_issues_search_master.png b/doc/user/project/issues/img/confidential_issues_search_master.png Binary files differindex fc01f4da9db..fc01f4da9db 100755..100644 --- a/doc/user/project/issues/img/confidential_issues_search_master.png +++ b/doc/user/project/issues/img/confidential_issues_search_master.png diff --git a/doc/user/project/issues/img/due_dates_create.png b/doc/user/project/issues/img/due_dates_create.png Binary files differindex ece35d44213..ece35d44213 100755..100644 --- a/doc/user/project/issues/img/due_dates_create.png +++ b/doc/user/project/issues/img/due_dates_create.png diff --git a/doc/user/project/issues/img/due_dates_edit_sidebar.png b/doc/user/project/issues/img/due_dates_edit_sidebar.png Binary files differindex d1c7d1eb7e9..d1c7d1eb7e9 100755..100644 --- a/doc/user/project/issues/img/due_dates_edit_sidebar.png +++ b/doc/user/project/issues/img/due_dates_edit_sidebar.png diff --git a/doc/user/project/issues/img/due_dates_issues_index_page.png b/doc/user/project/issues/img/due_dates_issues_index_page.png Binary files differindex 94679436b32..94679436b32 100755..100644 --- a/doc/user/project/issues/img/due_dates_issues_index_page.png +++ b/doc/user/project/issues/img/due_dates_issues_index_page.png diff --git a/doc/user/project/issues/img/due_dates_todos.png b/doc/user/project/issues/img/due_dates_todos.png Binary files differindex 4c124c97f67..4c124c97f67 100755..100644 --- a/doc/user/project/issues/img/due_dates_todos.png +++ b/doc/user/project/issues/img/due_dates_todos.png diff --git a/doc/user/project/issues/img/issue_board.png b/doc/user/project/issues/img/issue_board.png Binary files differindex 1759b28a9ef..1759b28a9ef 100755..100644 --- a/doc/user/project/issues/img/issue_board.png +++ b/doc/user/project/issues/img/issue_board.png diff --git a/doc/user/project/issues/img/issue_template.png b/doc/user/project/issues/img/issue_template.png Binary files differindex c63229a4af2..c63229a4af2 100755..100644 --- a/doc/user/project/issues/img/issue_template.png +++ b/doc/user/project/issues/img/issue_template.png diff --git a/doc/user/project/issues/img/mention_in_issue.png b/doc/user/project/issues/img/mention_in_issue.png Binary files differindex c762a812138..c762a812138 100755..100644 --- a/doc/user/project/issues/img/mention_in_issue.png +++ b/doc/user/project/issues/img/mention_in_issue.png diff --git a/doc/user/project/issues/img/mention_in_merge_request.png b/doc/user/project/issues/img/mention_in_merge_request.png Binary files differindex 681e086d6e0..681e086d6e0 100755..100644 --- a/doc/user/project/issues/img/mention_in_merge_request.png +++ b/doc/user/project/issues/img/mention_in_merge_request.png diff --git a/doc/user/project/issues/img/merge_request_closes_issue.png b/doc/user/project/issues/img/merge_request_closes_issue.png Binary files differindex 6fd27738843..6fd27738843 100755..100644 --- a/doc/user/project/issues/img/merge_request_closes_issue.png +++ b/doc/user/project/issues/img/merge_request_closes_issue.png diff --git a/doc/user/project/issues/img/new_issue.png b/doc/user/project/issues/img/new_issue.png Binary files differindex e72ac49d6b9..e72ac49d6b9 100755..100644 --- a/doc/user/project/issues/img/new_issue.png +++ b/doc/user/project/issues/img/new_issue.png diff --git a/doc/user/project/issues/img/new_issue_from_issue_board.png b/doc/user/project/issues/img/new_issue_from_issue_board.png Binary files differindex 9c2b3ff50fa..9c2b3ff50fa 100755..100644 --- a/doc/user/project/issues/img/new_issue_from_issue_board.png +++ b/doc/user/project/issues/img/new_issue_from_issue_board.png diff --git a/doc/user/project/issues/img/new_issue_from_open_issue.png b/doc/user/project/issues/img/new_issue_from_open_issue.png Binary files differindex 2aed5372830..2aed5372830 100755..100644 --- a/doc/user/project/issues/img/new_issue_from_open_issue.png +++ b/doc/user/project/issues/img/new_issue_from_open_issue.png diff --git a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png Binary files differindex cddf36b7457..cddf36b7457 100755..100644 --- a/doc/user/project/issues/img/new_issue_from_projects_dashboard.png +++ b/doc/user/project/issues/img/new_issue_from_projects_dashboard.png diff --git a/doc/user/project/issues/img/new_issue_from_tracker_list.png b/doc/user/project/issues/img/new_issue_from_tracker_list.png Binary files differindex 7e5413f0b7d..7e5413f0b7d 100755..100644 --- a/doc/user/project/issues/img/new_issue_from_tracker_list.png +++ b/doc/user/project/issues/img/new_issue_from_tracker_list.png diff --git a/doc/user/project/issues/img/sidebar_confidential_issue.png b/doc/user/project/issues/img/sidebar_confidential_issue.png Binary files differindex d99a1ca756e..d99a1ca756e 100755..100644 --- a/doc/user/project/issues/img/sidebar_confidential_issue.png +++ b/doc/user/project/issues/img/sidebar_confidential_issue.png diff --git a/doc/user/project/issues/img/sidebar_not_confidential_issue.png b/doc/user/project/issues/img/sidebar_not_confidential_issue.png Binary files differindex 2e6cbbc5b3a..2e6cbbc5b3a 100755..100644 --- a/doc/user/project/issues/img/sidebar_not_confidential_issue.png +++ b/doc/user/project/issues/img/sidebar_not_confidential_issue.png diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md index 46fa4378fe7..453e10184f0 100644 --- a/doc/user/project/pages/getting_started_part_one.md +++ b/doc/user/project/pages/getting_started_part_one.md @@ -62,7 +62,7 @@ which is highly recommendable and much faster than hardcoding. If you set up a GitLab Pages project on GitLab.com, it will automatically be accessible under a -[subdomain of `namespace.pages.io`](https://docs.gitlab.com/ce/user/project/pages/). +[subdomain of `namespace.pages.io`](introduction.md#gitlab-pages-on-gitlab-com). The `namespace` is defined by your username on GitLab.com, or the group name you created this project under. @@ -73,6 +73,8 @@ Pages wildcard domain. This guide is valid for any GitLab instance, you just need to replace Pages wildcard domain on GitLab.com (`*.gitlab.io`) with your own. +Learn more about [namespaces](../../group/index.md#namespaces). + ### Practical examples #### Project Websites diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md index 53fd1786cfa..0096f8507d2 100644 --- a/doc/user/project/pages/getting_started_part_three.md +++ b/doc/user/project/pages/getting_started_part_three.md @@ -1,9 +1,14 @@ +--- +last_updated: 2017-09-28 +--- + # GitLab Pages from A to Z: Part 3 -> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide || +> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles)**: user guide || > **Level**: beginner || > **Author**: [Marcia Ramos](https://gitlab.com/marcia) || -> **Publication date:** 2017/02/22 +> **Publication date:** 2017-02-22 || +> **Last updated**: 2017-09-28 - [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md) - [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md) @@ -16,6 +21,21 @@ As described in the previous part of this series, setting up GitLab Pages with c These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`. +### Adding your custom domain to GitLab Pages + +To use one or more custom domain with your Pages site, there are two things +you should consider first, which we'll cover in this guide: + +1. Either if you're adding a **root domain** or a **subdomain**, for which +you'll need to set up [DNS records](#dns-records) +1. Whether you want to add an [SSL/TLS certificate](#ssl-tls-certificates) or not + +To finish the association, you need to [add your domain to your project's Pages settings](#add-your-custom-domain-to-gitlab-pages-settings). + +Let's start from the beginning with [DNS records](#dns-records). +If you already know how they work and want to skip the introduction to DNS, +you may be interested in skipping it until the [TL;DR](#tl-dr) section below. + ### DNS Records A Domain Name System (DNS) web service routes visitors to websites @@ -99,6 +119,29 @@ domain. E.g., **do not** point your `subdomain.domain.com` to `namespace.gitlab.io.` or `namespace.gitlab.io/`. > - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`. +### Add your custom domain to GitLab Pages settings + +Once you've set the DNS record, you'll need navigate to your project's +**Setting > Pages** and click **+ New domain** to add your custom domain to +GitLab Pages. You can choose whether to add an [SSL/TLS certificate](#ssl-tls-certificates) +to make your website accessible under HTTPS or leave it blank. If don't add a certificate, +your site will be accessible only via HTTP: + +![Add new domain](img/add_certificate_to_pages.png) + +You can add more than one alias (custom domains and subdomains) to the same project. +An alias can be understood as having many doors leading to the same room. + +All the aliases you've set to your site will be listed on **Setting > Pages**. +From that page, you can view, add, and remove them. + +Note that [DNS propagation may take some time (up to 24h)](http://www.inmotionhosting.com/support/domain-names/dns-nameserver-changes/domain-names-dns-changes), +although it's usually a matter of minutes to complete. Until it does, visit attempts +to your domain will respond with a 404. + +Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding +custom domains to GitLab Pages sites. + ### SSL/TLS Certificates Every GitLab Pages project on GitLab.com will be available under diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md index 9ecf7a3a8e7..4fcdfa7b281 100644 --- a/doc/user/project/pages/introduction.md +++ b/doc/user/project/pages/introduction.md @@ -28,7 +28,8 @@ In general there are two types of pages one might create: - Pages per project (`username.example.io/projectname` or `groupname.example.io/projectname`) In GitLab, usernames and groupnames are unique and we often refer to them -as namespaces. There can be only one namespace in a GitLab instance. Below you +as [namespaces](../../group/index.md#namespaces). There can be only one namespace +in a GitLab instance. Below you can see the connection between the type of GitLab Pages, what the project name that is created on GitLab looks like and the website URL it will be ultimately be served on. @@ -98,6 +99,9 @@ The steps to create a project page for a user or a group are identical: A user's project will be served under `http(s)://username.example.io/projectname` whereas a group's project under `http(s)://groupname.example.io/projectname`. +For practical examples for group and project Pages, read through the guide +[GitLab Pages from A to Z: Part 1 - Static sites and GitLab Pages domains](getting_started_part_one.md#practical-examples). + ## Quick Start Read through [GitLab Pages Quick Start Guide][pages-quick] or watch the video tutorial on @@ -111,6 +115,9 @@ The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that gives you absolute control over the build process. You can actually watch your website being built live by following the CI job traces. +For a simplified user guide on setting up GitLab CI/CD for Pages, read through +the article [GitLab Pages from A to Z: Part 4 - Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md#creating-and-tweaking-gitlab-ci-yml-for-gitlab-pages) + > **Note:** > Before reading this section, make sure you familiarize yourself with GitLab CI > and the specific syntax of[`.gitlab-ci.yml`][yaml] by @@ -311,6 +318,9 @@ Visit the GitLab Pages group for a full list of example projects: ### Add a custom domain to your Pages website +For a complete guide on Pages domains, read through the article +[GitLab Pages from A to Z: Part 3 - Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md#setting-up-custom-domains-dns-records-and-ssl-tls-certificates) + If this setting is enabled by your GitLab administrator, you should be able to see the **New Domain** button when visiting your project's settings through the gear icon in the top right and then navigating to **Pages**. @@ -349,6 +359,9 @@ private key when adding a new domain. ![Pages upload cert](img/pages_upload_cert.png) +For a complete guide on Pages domains, read through the article +[GitLab Pages from A to Z: Part 3 - Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md#setting-up-custom-domains-dns-records-and-ssl-tls-certificates) + ### Custom error codes pages You can provide your own 403 and 404 error pages by creating the `403.html` and @@ -387,6 +400,8 @@ If you are using GitLab.com to host your website, then: The rest of the guide still applies. +See also: [GitLab Pages from A to Z: Part 1 - Static sites and GitLab Pages domains](getting_started_part_one.md#gitlab-pages-domain). + ## Limitations When using Pages under the general domain of a GitLab instance (`*.example.io`), diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md index dfe43c6b691..29e04a0ccf0 100644 --- a/doc/user/project/repository/gpg_signed_commits/index.md +++ b/doc/user/project/repository/gpg_signed_commits/index.md @@ -113,25 +113,25 @@ started: 1. Use the following command to list the private GPG key you just created: ``` - gpg --list-secret-keys --keyid-format 0xLONG mr@robot.sh + gpg --list-secret-keys --keyid-format LONG mr@robot.sh ``` Replace `mr@robot.sh` with the email address you entered above. 1. Copy the GPG key ID that starts with `sec`. In the following example, that's - `0x30F2B65B9246B6CA`: + `30F2B65B9246B6CA`: ``` - sec rsa4096/0x30F2B65B9246B6CA 2017-08-18 [SC] + sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC] D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA uid [ultimate] Mr. Robot <mr@robot.sh> - ssb rsa4096/0xB7ABC0813E4028C0 2017-08-18 [E] + ssb rsa4096/B7ABC0813E4028C0 2017-08-18 [E] ``` 1. Export the public key of that ID (replace your key ID from the previous step): ``` - gpg --armor --export 0x30F2B65B9246B6CA + gpg --armor --export 30F2B65B9246B6CA ``` 1. Finally, copy the public key and [add it in your profile settings](#adding-a-gpg-key-to-your-account) @@ -167,28 +167,28 @@ key to use. 1. Use the following command to list the private GPG key you just created: ``` - gpg --list-secret-keys --keyid-format 0xLONG mr@robot.sh + gpg --list-secret-keys --keyid-format LONG mr@robot.sh ``` Replace `mr@robot.sh` with the email address you entered above. 1. Copy the GPG key ID that starts with `sec`. In the following example, that's - `0x30F2B65B9246B6CA`: + `30F2B65B9246B6CA`: ``` - sec rsa4096/0x30F2B65B9246B6CA 2017-08-18 [SC] + sec rsa4096/30F2B65B9246B6CA 2017-08-18 [SC] D5E4F29F3275DC0CDA8FFC8730F2B65B9246B6CA uid [ultimate] Mr. Robot <mr@robot.sh> - ssb rsa4096/0xB7ABC0813E4028C0 2017-08-18 [E] + ssb rsa4096/B7ABC0813E4028C0 2017-08-18 [E] ``` 1. Tell Git to use that key to sign the commits: ``` - git config --global user.signingkey 0x30F2B65B9246B6CA + git config --global user.signingkey 30F2B65B9246B6CA ``` - Replace `0x30F2B65B9246B6CA` with your GPG key ID. + Replace `30F2B65B9246B6CA` with your GPG key ID. ## Signing commits diff --git a/doc/user/project/repository/img/contributors_graph.png b/doc/user/project/repository/img/contributors_graph.png Binary files differindex c31da7aa1ff..c31da7aa1ff 100755..100644 --- a/doc/user/project/repository/img/contributors_graph.png +++ 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 differindex 28da8ad9589..28da8ad9589 100755..100644 --- a/doc/user/project/repository/img/repo_graph.png +++ b/doc/user/project/repository/img/repo_graph.png diff --git a/doc/user/project/settings/img/general_settings.png b/doc/user/project/settings/img/general_settings.png Binary files differindex 96f5b84871f..96f5b84871f 100755..100644 --- a/doc/user/project/settings/img/general_settings.png +++ b/doc/user/project/settings/img/general_settings.png diff --git a/doc/user/project/settings/img/merge_requests_settings.png b/doc/user/project/settings/img/merge_requests_settings.png Binary files differindex b1f2dfa7376..b1f2dfa7376 100755..100644 --- a/doc/user/project/settings/img/merge_requests_settings.png +++ b/doc/user/project/settings/img/merge_requests_settings.png diff --git a/doc/user/search/img/issues_any_assignee.png b/doc/user/search/img/issues_any_assignee.png Binary files differindex 2f902bcc66c..2f902bcc66c 100755..100644 --- a/doc/user/search/img/issues_any_assignee.png +++ b/doc/user/search/img/issues_any_assignee.png diff --git a/doc/user/search/img/issues_assigned_to_you.png b/doc/user/search/img/issues_assigned_to_you.png Binary files differindex 36c670eedd5..36c670eedd5 100755..100644 --- a/doc/user/search/img/issues_assigned_to_you.png +++ b/doc/user/search/img/issues_assigned_to_you.png diff --git a/doc/user/search/img/issues_author.png b/doc/user/search/img/issues_author.png Binary files differindex 792f9746db6..792f9746db6 100755..100644 --- a/doc/user/search/img/issues_author.png +++ b/doc/user/search/img/issues_author.png diff --git a/doc/user/search/img/issues_mrs_shortcut.png b/doc/user/search/img/issues_mrs_shortcut.png Binary files differindex 6380b337b54..6380b337b54 100755..100644 --- a/doc/user/search/img/issues_mrs_shortcut.png +++ b/doc/user/search/img/issues_mrs_shortcut.png diff --git a/doc/user/search/img/left_menu_bar.png b/doc/user/search/img/left_menu_bar.png Binary files differindex d68a71cba8e..d68a71cba8e 100755..100644 --- a/doc/user/search/img/left_menu_bar.png +++ b/doc/user/search/img/left_menu_bar.png diff --git a/doc/user/search/img/project_search.png b/doc/user/search/img/project_search.png Binary files differindex 3150b40de29..3150b40de29 100755..100644 --- a/doc/user/search/img/project_search.png +++ b/doc/user/search/img/project_search.png diff --git a/doc/user/search/img/search_issues_board.png b/doc/user/search/img/search_issues_board.png Binary files differindex 84048ae6a02..84048ae6a02 100755..100644 --- a/doc/user/search/img/search_issues_board.png +++ b/doc/user/search/img/search_issues_board.png diff --git a/doc/user/search/img/sort_projects.png b/doc/user/search/img/sort_projects.png Binary files differindex 9bf2770b299..9bf2770b299 100755..100644 --- a/doc/user/search/img/sort_projects.png +++ b/doc/user/search/img/sort_projects.png diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index c4c0fdda665..e79f988f549 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -75,7 +75,7 @@ module API raise RevokedError when AccessTokenValidationService::VALID - @current_user = User.find(access_token.resource_owner_id) + User.find(access_token.resource_owner_id) end end @@ -84,11 +84,13 @@ module API return nil unless token_string.present? - find_user_by_authentication_token(token_string) || find_user_by_personal_access_token(token_string, scopes) - end + user = + find_user_by_authentication_token(token_string) || + find_user_by_personal_access_token(token_string, scopes) + + raise UnauthorizedError unless user - def current_user - @current_user + user end private @@ -107,7 +109,16 @@ module API end def find_access_token - @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) + return @access_token if defined?(@access_token) + + token = Doorkeeper::OAuth::Token.from_request(doorkeeper_request, *Doorkeeper.configuration.access_token_methods) + return @access_token = nil unless token + + @access_token = Doorkeeper::AccessToken.by_token(token) + raise UnauthorizedError unless @access_token + + @access_token.revoke_previous_refresh_token! + @access_token end def doorkeeper_request @@ -169,6 +180,7 @@ module API TokenNotFoundError = Class.new(StandardError) ExpiredError = Class.new(StandardError) RevokedError = Class.new(StandardError) + UnauthorizedError = Class.new(StandardError) class InsufficientScopeError < StandardError attr_reader :scopes diff --git a/lib/api/custom_attributes_endpoints.rb b/lib/api/custom_attributes_endpoints.rb new file mode 100644 index 00000000000..5000aa0d9ac --- /dev/null +++ b/lib/api/custom_attributes_endpoints.rb @@ -0,0 +1,77 @@ +module API + module CustomAttributesEndpoints + extend ActiveSupport::Concern + + included do + attributable_class = name.demodulize.singularize + attributable_key = attributable_class.underscore + attributable_name = attributable_class.humanize(capitalize: false) + attributable_finder = "find_#{attributable_key}" + + helpers do + params :custom_attributes_key do + requires :key, type: String, desc: 'The key of the custom attribute' + end + end + + desc "Get all custom attributes on a #{attributable_name}" do + success Entities::CustomAttribute + end + get ':id/custom_attributes' do + resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend + authorize! :read_custom_attribute + + present resource.custom_attributes, with: Entities::CustomAttribute + end + + desc "Get a custom attribute on a #{attributable_name}" do + success Entities::CustomAttribute + end + params do + use :custom_attributes_key + end + get ':id/custom_attributes/:key' do + resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend + authorize! :read_custom_attribute + + custom_attribute = resource.custom_attributes.find_by!(key: params[:key]) + + present custom_attribute, with: Entities::CustomAttribute + end + + desc "Set a custom attribute on a #{attributable_name}" + params do + use :custom_attributes_key + requires :value, type: String, desc: 'The value of the custom attribute' + end + put ':id/custom_attributes/:key' do + resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend + authorize! :update_custom_attribute + + custom_attribute = resource.custom_attributes + .find_or_initialize_by(key: params[:key]) + + custom_attribute.update(value: params[:value]) + + if custom_attribute.valid? + present custom_attribute, with: Entities::CustomAttribute + else + render_validation_error!(custom_attribute) + end + end + + desc "Delete a custom attribute on a #{attributable_name}" + params do + use :custom_attributes_key + end + delete ':id/custom_attributes/:key' do + resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend + authorize! :update_custom_attribute + + resource.custom_attributes.find_by!(key: params[:key]).destroy + + status 204 + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 71d358907d1..1c0e6873c37 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -89,6 +89,9 @@ module API expose :ssh_url_to_repo, :http_url_to_repo, :web_url expose :name, :name_with_namespace expose :path, :path_with_namespace + expose :avatar_url do |project, options| + project.avatar_url(only_path: false) + end expose :star_count, :forks_count expose :created_at, :last_activity_at end @@ -146,9 +149,7 @@ module API expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? } expose :import_status expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } - expose :avatar_url do |user, options| - user.avatar_url(only_path: false) - end + 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 @@ -193,8 +194,8 @@ module API class Group < Grape::Entity expose :id, :name, :path, :description, :visibility expose :lfs_enabled?, as: :lfs_enabled - expose :avatar_url do |user, options| - user.avatar_url(only_path: false) + expose :avatar_url do |group, options| + group.avatar_url(only_path: false) end expose :web_url expose :request_access_enabled @@ -234,6 +235,7 @@ module API class RepoCommitDetail < RepoCommit expose :stats, using: Entities::RepoCommitStats expose :status + expose :last_pipeline, using: 'API::Entities::PipelineBasic' end class RepoBranch < Grape::Entity @@ -1036,5 +1038,10 @@ module API expose :failing_on_hosts expose :total_failures end + + class CustomAttribute < Grape::Entity + expose :key + expose :value + end end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 00dbc2aee7a..4964a76bef6 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -3,6 +3,8 @@ module API include Gitlab::Utils include Helpers::Pagination + UnauthorizedError = Class.new(StandardError) + SUDO_HEADER = "HTTP_SUDO".freeze SUDO_PARAM = :sudo @@ -139,7 +141,7 @@ module API end def authenticate! - unauthorized! unless current_user && can?(initial_current_user, :access_api) + unauthorized! unless current_user end def authenticate_non_get! @@ -397,19 +399,27 @@ module API def initial_current_user return @initial_current_user if defined?(@initial_current_user) - Gitlab::Auth::UniqueIpsLimiter.limit_user! do - @initial_current_user ||= find_user_by_private_token(scopes: scopes_registered_for_endpoint) - @initial_current_user ||= doorkeeper_guard(scopes: scopes_registered_for_endpoint) - @initial_current_user ||= find_user_from_warden - - unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed? - @initial_current_user = nil - end - @initial_current_user + begin + @initial_current_user = Gitlab::Auth::UniqueIpsLimiter.limit_user! { find_current_user } + rescue APIGuard::UnauthorizedError, UnauthorizedError + unauthorized! end end + def find_current_user + user = + find_user_by_private_token(scopes: scopes_registered_for_endpoint) || + doorkeeper_guard(scopes: scopes_registered_for_endpoint) || + find_user_from_warden + + return nil unless user + + raise UnauthorizedError unless Gitlab::UserAccess.new(user).allowed? && user.can?(:access_api) + + user + end + def sudo! return unless sudo_identifier return unless initial_current_user @@ -454,10 +464,12 @@ module API header(*Gitlab::Workhorse.send_artifacts_entry(build, entry)) end - # The Grape Error Middleware only has access to env but no params. We workaround this by - # defining a method that returns the right value. + # The Grape Error Middleware only has access to `env` but not `params` nor + # `request`. We workaround this by defining methods that returns the right + # values. def define_params_for_grape_middleware - self.define_singleton_method(:params) { Rack::Request.new(env).params.symbolize_keys } + self.define_singleton_method(:request) { Rack::Request.new(env) } + self.define_singleton_method(:params) { request.params.symbolize_keys } end # We could get a Grape or a standard Ruby exception. We should only report anything that diff --git a/lib/api/internal.rb b/lib/api/internal.rb index c0fef56378f..6e78ac2c903 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -31,6 +31,12 @@ module API protocol = params[:protocol] actor.update_last_used_at if actor.is_a?(Key) + user = + if actor.is_a?(Key) + actor.user + else + actor + end access_checker_klass = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess access_checker = access_checker_klass @@ -47,6 +53,7 @@ module API { status: true, gl_repository: gl_repository, + gl_username: user&.username, repository_path: repository_path, gitaly: gitaly_payload(params[:action]) } @@ -136,7 +143,7 @@ module API codes = nil - ::Users::UpdateService.new(user).execute! do |user| + ::Users::UpdateService.new(current_user, user: user).execute! do |user| codes = user.generate_otp_backup_codes! end diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb index bcc0833aa5c..0266bf2f717 100644 --- a/lib/api/notification_settings.rb +++ b/lib/api/notification_settings.rb @@ -35,7 +35,7 @@ module API new_notification_email = params.delete(:notification_email) if new_notification_email - ::Users::UpdateService.new(current_user, notification_email: new_notification_email).execute + ::Users::UpdateService.new(current_user, user: current_user, notification_email: new_notification_email).execute end notification_setting.update(declared_params(include_missing: false)) diff --git a/lib/api/users.rb b/lib/api/users.rb index bdebda58d3f..d07dc302717 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -6,12 +6,14 @@ module API allow_access_with_scope :read_user, if: -> (request) { request.get? } resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do + include CustomAttributesEndpoints + before do authenticate_non_get! end helpers do - def find_user(params) + def find_user_by_id(params) id = params[:user_id] || params[:id] User.find_by(id: id) || not_found!('User') end @@ -166,7 +168,7 @@ module API user_params[:password_expires_at] = Time.now if user_params[:password].present? - result = ::Users::UpdateService.new(user, user_params.except(:extern_uid, :provider)).execute + result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute if result[:status] == :success present user, with: Entities::UserPublic @@ -326,7 +328,7 @@ module API user = User.find_by(id: params.delete(:id)) not_found!('User') unless user - email = Emails::CreateService.new(user, declared_params(include_missing: false)).execute + email = Emails::CreateService.new(current_user, declared_params(include_missing: false).merge(user: user)).execute if email.errors.blank? NotificationService.new.new_email(email) @@ -367,7 +369,7 @@ module API not_found!('Email') unless email destroy_conditionally!(email) do |email| - Emails::DestroyService.new(current_user, email: email.email).execute + Emails::DestroyService.new(current_user, user: user, email: email.email).execute end user.update_secondary_emails! @@ -430,7 +432,7 @@ module API resource :impersonation_tokens do helpers do def finder(options = {}) - user = find_user(params) + user = find_user_by_id(params) PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options)) end @@ -672,7 +674,7 @@ module API requires :email, type: String, desc: 'The new email' end post "emails" do - email = Emails::CreateService.new(current_user, declared_params).execute + email = Emails::CreateService.new(current_user, declared_params.merge(user: current_user)).execute if email.errors.blank? NotificationService.new.new_email(email) @@ -691,7 +693,7 @@ module API not_found!('Email') unless email destroy_conditionally!(email) do |email| - Emails::DestroyService.new(current_user, email: email.email).execute + Emails::DestroyService.new(current_user, user: current_user, email: email.email).execute end current_user.update_secondary_emails! diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 4e92be85110..3ad09a1b421 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -78,7 +78,7 @@ module Backup project.ensure_storage_path_exists cmd = if File.exist?(path_to_project_bundle) - %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) + %W(#{Gitlab.config.git.bin_path} clone --bare --mirror #{path_to_project_bundle} #{path_to_project_repo}) else %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo}) end diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb index ee73fa91589..9cac303e645 100644 --- a/lib/banzai/filter/markdown_filter.rb +++ b/lib/banzai/filter/markdown_filter.rb @@ -1,6 +1,18 @@ module Banzai module Filter class MarkdownFilter < HTML::Pipeline::TextFilter + # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + REDCARPET_OPTIONS = { + fenced_code_blocks: true, + footnotes: true, + lax_spacing: true, + no_intra_emphasis: true, + space_after_headers: true, + strikethrough: true, + superscript: true, + tables: true + }.freeze + def initialize(text, context = nil, result = nil) super text, context, result @text = @text.delete "\r" @@ -13,27 +25,11 @@ module Banzai end def self.renderer - @renderer ||= begin + Thread.current[:banzai_markdown_renderer] ||= begin renderer = Banzai::Renderer::HTML.new - Redcarpet::Markdown.new(renderer, redcarpet_options) + Redcarpet::Markdown.new(renderer, REDCARPET_OPTIONS) end end - - def self.redcarpet_options - # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use - @redcarpet_options ||= { - fenced_code_blocks: true, - footnotes: true, - lax_spacing: true, - no_intra_emphasis: true, - space_after_headers: true, - strikethrough: true, - superscript: true, - tables: true - }.freeze - end - - private_class_method :redcarpet_options end end end diff --git a/lib/github/client.rb b/lib/github/client.rb index 9c476df7d46..29bd9c1f39e 100644 --- a/lib/github/client.rb +++ b/lib/github/client.rb @@ -1,6 +1,7 @@ module Github class Client TIMEOUT = 60 + DEFAULT_PER_PAGE = 100 attr_reader :connection, :rate_limit @@ -20,7 +21,7 @@ module Github exceed, reset_in = rate_limit.get sleep reset_in if exceed - Github::Response.new(connection.get(url, query)) + Github::Response.new(connection.get(url, { per_page: DEFAULT_PER_PAGE }.merge(query))) end private diff --git a/lib/github/import.rb b/lib/github/import.rb index 9354e142d3d..c0cd8382875 100644 --- a/lib/github/import.rb +++ b/lib/github/import.rb @@ -1,46 +1,13 @@ require_relative 'error' +require_relative 'import/issue' +require_relative 'import/legacy_diff_note' +require_relative 'import/merge_request' +require_relative 'import/note' module Github class Import include Gitlab::ShellAdapter - class MergeRequest < ::MergeRequest - self.table_name = 'merge_requests' - - self.reset_callbacks :create - self.reset_callbacks :save - self.reset_callbacks :commit - self.reset_callbacks :update - self.reset_callbacks :validate - end - - class Issue < ::Issue - self.table_name = 'issues' - - self.reset_callbacks :save - self.reset_callbacks :create - self.reset_callbacks :commit - self.reset_callbacks :update - self.reset_callbacks :validate - end - - class Note < ::Note - self.table_name = 'notes' - - self.reset_callbacks :save - self.reset_callbacks :commit - self.reset_callbacks :update - self.reset_callbacks :validate - end - - class LegacyDiffNote < ::LegacyDiffNote - self.table_name = 'notes' - - self.reset_callbacks :commit - self.reset_callbacks :update - self.reset_callbacks :validate - end - attr_reader :project, :repository, :repo, :repo_url, :wiki_url, :options, :errors, :cached, :verbose @@ -202,13 +169,8 @@ module Github merge_request.save!(validate: false) merge_request.merge_request_diffs.create - # Fetch review comments review_comments_url = "/repos/#{repo}/pulls/#{pull_request.iid}/comments" fetch_comments(merge_request, :review_comment, review_comments_url, LegacyDiffNote) - - # Fetch comments - comments_url = "/repos/#{repo}/issues/#{pull_request.iid}/comments" - fetch_comments(merge_request, :comment, comments_url) rescue => e error(:pull_request, pull_request.url, e.message) ensure @@ -241,12 +203,17 @@ module Github # for both features, like manipulating assignees, labels # and milestones, are provided within the Issues API. if representation.pull_request? - return unless representation.has_labels? + return unless representation.has_labels? || representation.has_comments? merge_request = MergeRequest.find_by!(target_project_id: project.id, iid: representation.iid) - merge_request.update_attribute(:label_ids, label_ids(representation.labels)) + + if representation.has_labels? + merge_request.update_attribute(:label_ids, label_ids(representation.labels)) + end + + fetch_comments_conditionally(merge_request, representation) else - return if Issue.where(iid: representation.iid, project_id: project.id).exists? + return if Issue.exists?(iid: representation.iid, project_id: project.id) author_id = user_id(representation.author, project.creator_id) issue = Issue.new @@ -263,17 +230,20 @@ module Github issue.updated_at = representation.updated_at issue.save!(validate: false) - # Fetch comments - if representation.has_comments? - comments_url = "/repos/#{repo}/issues/#{issue.iid}/comments" - fetch_comments(issue, :comment, comments_url) - end + fetch_comments_conditionally(issue, representation) end rescue => e error(:issue, representation.url, e.message) end end + def fetch_comments_conditionally(issuable, representation) + if representation.has_comments? + comments_url = "/repos/#{repo}/issues/#{issuable.iid}/comments" + fetch_comments(issuable, :comment, comments_url) + end + end + def fetch_comments(noteable, type, url, klass = Note) while url comments = Github::Client.new(options).get(url) diff --git a/lib/github/import/issue.rb b/lib/github/import/issue.rb new file mode 100644 index 00000000000..171f0872666 --- /dev/null +++ b/lib/github/import/issue.rb @@ -0,0 +1,13 @@ +module Github + class Import + class Issue < ::Issue + self.table_name = 'issues' + + self.reset_callbacks :save + self.reset_callbacks :create + self.reset_callbacks :commit + self.reset_callbacks :update + self.reset_callbacks :validate + end + end +end diff --git a/lib/github/import/legacy_diff_note.rb b/lib/github/import/legacy_diff_note.rb new file mode 100644 index 00000000000..18adff560b6 --- /dev/null +++ b/lib/github/import/legacy_diff_note.rb @@ -0,0 +1,12 @@ +module Github + class Import + class LegacyDiffNote < ::LegacyDiffNote + self.table_name = 'notes' + self.store_full_sti_class = false + + self.reset_callbacks :commit + self.reset_callbacks :update + self.reset_callbacks :validate + end + end +end diff --git a/lib/github/import/merge_request.rb b/lib/github/import/merge_request.rb new file mode 100644 index 00000000000..c258e5d5e0e --- /dev/null +++ b/lib/github/import/merge_request.rb @@ -0,0 +1,13 @@ +module Github + class Import + class MergeRequest < ::MergeRequest + self.table_name = 'merge_requests' + + self.reset_callbacks :create + self.reset_callbacks :save + self.reset_callbacks :commit + self.reset_callbacks :update + self.reset_callbacks :validate + end + end +end diff --git a/lib/github/import/note.rb b/lib/github/import/note.rb new file mode 100644 index 00000000000..8cf4f30e6b7 --- /dev/null +++ b/lib/github/import/note.rb @@ -0,0 +1,13 @@ +module Github + class Import + class Note < ::Note + self.table_name = 'notes' + self.store_full_sti_class = false + + self.reset_callbacks :save + self.reset_callbacks :commit + self.reset_callbacks :update + self.reset_callbacks :validate + end + end +end diff --git a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb index 3fde1b09efb..8e5c95f2287 100644 --- a/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb +++ b/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits.rb @@ -3,11 +3,18 @@ module Gitlab class DeserializeMergeRequestDiffsAndCommits attr_reader :diff_ids, :commit_rows, :file_rows + class Error < StandardError + def backtrace + cause.backtrace + end + end + class MergeRequestDiff < ActiveRecord::Base self.table_name = 'merge_request_diffs' end BUFFER_ROWS = 1000 + DIFF_FILE_BUFFER_ROWS = 100 def perform(start_id, stop_id) merge_request_diffs = MergeRequestDiff @@ -26,13 +33,17 @@ module Gitlab if diff_ids.length > BUFFER_ROWS || commit_rows.length > BUFFER_ROWS || - file_rows.length > BUFFER_ROWS + file_rows.length > DIFF_FILE_BUFFER_ROWS flush_buffers! end end flush_buffers! + rescue => e + Rails.logger.info("#{self.class.name}: failed for IDs #{merge_request_diffs.map(&:id)} with #{e.class.name}") + + raise Error.new(e.inspect) end private @@ -45,17 +56,28 @@ module Gitlab def flush_buffers! if diff_ids.any? - MergeRequestDiff.transaction do - Gitlab::Database.bulk_insert('merge_request_diff_commits', commit_rows) - Gitlab::Database.bulk_insert('merge_request_diff_files', file_rows) + commit_rows.each_slice(BUFFER_ROWS).each do |commit_rows_slice| + bulk_insert('merge_request_diff_commits', commit_rows_slice) + end - MergeRequestDiff.where(id: diff_ids).update_all(st_commits: nil, st_diffs: nil) + file_rows.each_slice(DIFF_FILE_BUFFER_ROWS).each do |file_rows_slice| + bulk_insert('merge_request_diff_files', file_rows_slice) end + + MergeRequestDiff.where(id: diff_ids).update_all(st_commits: nil, st_diffs: nil) end reset_buffers! end + def bulk_insert(table, rows) + Gitlab::Database.bulk_insert(table, rows) + rescue ActiveRecord::RecordNotUnique + ids = rows.map { |row| row[:merge_request_diff_id] }.uniq.sort + + Rails.logger.info("#{self.class.name}: rows inserted twice for IDs #{ids}") + end + def single_diff_rows(merge_request_diff) sha_attribute = Gitlab::Database::ShaAttribute.new commits = YAML.load(merge_request_diff.st_commits) rescue [] diff --git a/lib/gitlab/bare_repository_importer.rb b/lib/gitlab/bare_repository_importer.rb index 9323bfc7fb2..1d98d187805 100644 --- a/lib/gitlab/bare_repository_importer.rb +++ b/lib/gitlab/bare_repository_importer.rb @@ -56,7 +56,8 @@ module Gitlab name: project_path, path: project_path, repository_storage: storage_name, - namespace_id: group&.id + namespace_id: group&.id, + skip_disk_validation: true } project = Projects::CreateService.new(user, project_params).execute diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 28bbf3b384e..d1979bb7ed3 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -149,16 +149,21 @@ module Gitlab description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) description += pull_request.description + source_branch_sha = pull_request.source_branch_sha + target_branch_sha = pull_request.target_branch_sha + source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha + target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + merge_request = project.merge_requests.create!( iid: pull_request.iid, title: pull_request.title, description: description, source_project: project, source_branch: pull_request.source_branch_name, - source_branch_sha: pull_request.source_branch_sha, + source_branch_sha: source_branch_sha, target_project: project, target_branch: pull_request.target_branch_name, - target_branch_sha: pull_request.target_branch_sha, + target_branch_sha: target_branch_sha, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb new file mode 100644 index 00000000000..8d82e1b288d --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/base.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + module Pipeline + module Chain + class Base + attr_reader :pipeline, :project, :current_user + + def initialize(pipeline, command) + @pipeline = pipeline + @command = command + + @project = command.project + @current_user = command.current_user + end + + def perform! + raise NotImplementedError + end + + def break? + raise NotImplementedError + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/create.rb b/lib/gitlab/ci/pipeline/chain/create.rb new file mode 100644 index 00000000000..d5e17a123df --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/create.rb @@ -0,0 +1,29 @@ +module Gitlab + module Ci + module Pipeline + module Chain + class Create < Chain::Base + include Chain::Helpers + + def perform! + ::Ci::Pipeline.transaction do + pipeline.save! + + @command.seeds_block&.call(pipeline) + + ::Ci::CreatePipelineStagesService + .new(project, current_user) + .execute(pipeline) + end + rescue ActiveRecord::RecordInvalid => e + error("Failed to persist the pipeline: #{e}") + end + + def break? + !pipeline.persisted? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/helpers.rb b/lib/gitlab/ci/pipeline/chain/helpers.rb new file mode 100644 index 00000000000..02d81286f21 --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/helpers.rb @@ -0,0 +1,25 @@ +module Gitlab + module Ci + module Pipeline + module Chain + module Helpers + def branch_exists? + return @is_branch if defined?(@is_branch) + + @is_branch = project.repository.branch_exists?(pipeline.ref) + end + + def tag_exists? + return @is_tag if defined?(@is_tag) + + @is_tag = project.repository.tag_exists?(pipeline.ref) + end + + def error(message) + pipeline.errors.add(:base, message) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/sequence.rb b/lib/gitlab/ci/pipeline/chain/sequence.rb new file mode 100644 index 00000000000..015f2988327 --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/sequence.rb @@ -0,0 +1,36 @@ +module Gitlab + module Ci + module Pipeline + module Chain + class Sequence + def initialize(pipeline, command, sequence) + @pipeline = pipeline + @completed = [] + + @sequence = sequence.map do |chain| + chain.new(pipeline, command) + end + end + + def build! + @sequence.each do |step| + step.perform! + + break if step.break? + + @completed << step + end + + @pipeline.tap do + yield @pipeline, self if block_given? + end + end + + def complete? + @completed.size == @sequence.size + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/skip.rb b/lib/gitlab/ci/pipeline/chain/skip.rb new file mode 100644 index 00000000000..9a72de87bab --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/skip.rb @@ -0,0 +1,33 @@ +module Gitlab + module Ci + module Pipeline + module Chain + class Skip < Chain::Base + SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i + + def perform! + if skipped? + @pipeline.skip if @command.save_incompleted + end + end + + def skipped? + !@command.ignore_skip_ci && commit_message_skips_ci? + end + + def break? + skipped? + end + + private + + def commit_message_skips_ci? + return false unless @pipeline.git_commit_message + + @skipped ||= !!(@pipeline.git_commit_message =~ SKIP_PATTERN) + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb new file mode 100644 index 00000000000..4913a604079 --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb @@ -0,0 +1,54 @@ +module Gitlab + module Ci + module Pipeline + module Chain + module Validate + class Abilities < Chain::Base + include Gitlab::Allowable + include Chain::Helpers + + def perform! + unless project.builds_enabled? + return error('Pipelines are disabled!') + end + + unless allowed_to_trigger_pipeline? + if can?(current_user, :create_pipeline, project) + return error("Insufficient permissions for protected ref '#{pipeline.ref}'") + else + return error('Insufficient permissions to create a new pipeline') + end + end + end + + def break? + @pipeline.errors.any? + end + + def allowed_to_trigger_pipeline? + if current_user + allowed_to_create? + else # legacy triggers don't have a corresponding user + !project.protected_for?(@pipeline.ref) + end + end + + def allowed_to_create? + return unless can?(current_user, :create_pipeline, project) + + access = Gitlab::UserAccess.new(current_user, project: project) + + if branch_exists? + access.can_update_branch?(@pipeline.ref) + elsif tag_exists? + access.can_create_tag?(@pipeline.ref) + else + true # Allow it for now and we'll reject when we check ref existence + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/validate/config.rb b/lib/gitlab/ci/pipeline/chain/validate/config.rb new file mode 100644 index 00000000000..489bcd79655 --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/validate/config.rb @@ -0,0 +1,35 @@ +module Gitlab + module Ci + module Pipeline + module Chain + module Validate + class Config < Chain::Base + include Chain::Helpers + + def perform! + unless @pipeline.config_processor + unless @pipeline.ci_yaml_file + return error("Missing #{@pipeline.ci_yaml_file_path} file") + end + + if @command.save_incompleted && @pipeline.has_yaml_errors? + @pipeline.drop + end + + return error(@pipeline.yaml_errors) + end + + unless @pipeline.has_stage_seeds? + return error('No stages / jobs for this pipeline.') + end + end + + def break? + @pipeline.errors.any? || @pipeline.persisted? + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/chain/validate/repository.rb b/lib/gitlab/ci/pipeline/chain/validate/repository.rb new file mode 100644 index 00000000000..70a4cfdbdea --- /dev/null +++ b/lib/gitlab/ci/pipeline/chain/validate/repository.rb @@ -0,0 +1,30 @@ +module Gitlab + module Ci + module Pipeline + module Chain + module Validate + class Repository < Chain::Base + include Chain::Helpers + + def perform! + unless branch_exists? || tag_exists? + return error('Reference not found') + end + + ## TODO, we check commit in the service, that is why + # there is no repository access here. + # + unless pipeline.sha + return error('Commit not found') + end + end + + def break? + @pipeline.errors.any? + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/duration.rb b/lib/gitlab/ci/pipeline/duration.rb new file mode 100644 index 00000000000..469fc094cc8 --- /dev/null +++ b/lib/gitlab/ci/pipeline/duration.rb @@ -0,0 +1,143 @@ +module Gitlab + module Ci + module Pipeline + # # Introduction - total running time + # + # The problem this module is trying to solve is finding the total running + # time amongst all the jobs, excluding retries and pending (queue) time. + # We could reduce this problem down to finding the union of periods. + # + # So each job would be represented as a `Period`, which consists of + # `Period#first` as when the job started and `Period#last` as when the + # job was finished. A simple example here would be: + # + # * A (1, 3) + # * B (2, 4) + # * C (6, 7) + # + # Here A begins from 1, and ends to 3. B begins from 2, and ends to 4. + # C begins from 6, and ends to 7. Visually it could be viewed as: + # + # 0 1 2 3 4 5 6 7 + # AAAAAAA + # BBBBBBB + # CCCC + # + # The union of A, B, and C would be (1, 4) and (6, 7), therefore the + # total running time should be: + # + # (4 - 1) + (7 - 6) => 4 + # + # # The Algorithm + # + # The algorithm used here for union would be described as follow. + # First we make sure that all periods are sorted by `Period#first`. + # Then we try to merge periods by iterating through the first period + # to the last period. The goal would be merging all overlapped periods + # so that in the end all the periods are discrete. When all periods + # are discrete, we're free to just sum all the periods to get real + # running time. + # + # Here we begin from A, and compare it to B. We could find that + # before A ends, B already started. That is `B.first <= A.last` + # that is `2 <= 3` which means A and B are overlapping! + # + # When we found that two periods are overlapping, we would need to merge + # them into a new period and disregard the old periods. To make a new + # period, we take `A.first` as the new first because remember? we sorted + # them, so `A.first` must be smaller or equal to `B.first`. And we take + # `[A.last, B.last].max` as the new last because we want whoever ended + # later. This could be broken into two cases: + # + # 0 1 2 3 4 + # AAAAAAA + # BBBBBBB + # + # Or: + # + # 0 1 2 3 4 + # AAAAAAAAAA + # BBBB + # + # So that we need to take whoever ends later. Back to our example, + # after merging and discard A and B it could be visually viewed as: + # + # 0 1 2 3 4 5 6 7 + # DDDDDDDDDD + # CCCC + # + # Now we could go on and compare the newly created D and the old C. + # We could figure out that D and C are not overlapping by checking + # `C.first <= D.last` is `false`. Therefore we need to keep both C + # and D. The example would end here because there are no more jobs. + # + # After having the union of all periods, we just need to sum the length + # of all periods to get total time. + # + # (4 - 1) + (7 - 6) => 4 + # + # That is 4 is the answer in the example. + module Duration + extend self + + Period = Struct.new(:first, :last) do + def duration + last - first + end + end + + def from_pipeline(pipeline) + status = %w[success failed running canceled] + builds = pipeline.builds.latest + .where(status: status).where.not(started_at: nil).order(:started_at) + + from_builds(builds) + end + + def from_builds(builds) + now = Time.now + + periods = builds.map do |b| + Period.new(b.started_at, b.finished_at || now) + end + + from_periods(periods) + end + + # periods should be sorted by `first` + def from_periods(periods) + process_duration(process_periods(periods)) + end + + private + + def process_periods(periods) + return periods if periods.empty? + + periods.drop(1).inject([periods.first]) do |result, current| + previous = result.last + + if overlap?(previous, current) + result[-1] = merge(previous, current) + result + else + result << current + end + end + end + + def overlap?(previous, current) + current.first <= previous.last + end + + def merge(previous, current) + Period.new(previous.first, [previous.last, current.last].max) + end + + def process_duration(periods) + periods.sum(&:duration) + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline_duration.rb b/lib/gitlab/ci/pipeline_duration.rb deleted file mode 100644 index 3208cc2bef6..00000000000 --- a/lib/gitlab/ci/pipeline_duration.rb +++ /dev/null @@ -1,141 +0,0 @@ -module Gitlab - module Ci - # # Introduction - total running time - # - # The problem this module is trying to solve is finding the total running - # time amongst all the jobs, excluding retries and pending (queue) time. - # We could reduce this problem down to finding the union of periods. - # - # So each job would be represented as a `Period`, which consists of - # `Period#first` as when the job started and `Period#last` as when the - # job was finished. A simple example here would be: - # - # * A (1, 3) - # * B (2, 4) - # * C (6, 7) - # - # Here A begins from 1, and ends to 3. B begins from 2, and ends to 4. - # C begins from 6, and ends to 7. Visually it could be viewed as: - # - # 0 1 2 3 4 5 6 7 - # AAAAAAA - # BBBBBBB - # CCCC - # - # The union of A, B, and C would be (1, 4) and (6, 7), therefore the - # total running time should be: - # - # (4 - 1) + (7 - 6) => 4 - # - # # The Algorithm - # - # The algorithm used here for union would be described as follow. - # First we make sure that all periods are sorted by `Period#first`. - # Then we try to merge periods by iterating through the first period - # to the last period. The goal would be merging all overlapped periods - # so that in the end all the periods are discrete. When all periods - # are discrete, we're free to just sum all the periods to get real - # running time. - # - # Here we begin from A, and compare it to B. We could find that - # before A ends, B already started. That is `B.first <= A.last` - # that is `2 <= 3` which means A and B are overlapping! - # - # When we found that two periods are overlapping, we would need to merge - # them into a new period and disregard the old periods. To make a new - # period, we take `A.first` as the new first because remember? we sorted - # them, so `A.first` must be smaller or equal to `B.first`. And we take - # `[A.last, B.last].max` as the new last because we want whoever ended - # later. This could be broken into two cases: - # - # 0 1 2 3 4 - # AAAAAAA - # BBBBBBB - # - # Or: - # - # 0 1 2 3 4 - # AAAAAAAAAA - # BBBB - # - # So that we need to take whoever ends later. Back to our example, - # after merging and discard A and B it could be visually viewed as: - # - # 0 1 2 3 4 5 6 7 - # DDDDDDDDDD - # CCCC - # - # Now we could go on and compare the newly created D and the old C. - # We could figure out that D and C are not overlapping by checking - # `C.first <= D.last` is `false`. Therefore we need to keep both C - # and D. The example would end here because there are no more jobs. - # - # After having the union of all periods, we just need to sum the length - # of all periods to get total time. - # - # (4 - 1) + (7 - 6) => 4 - # - # That is 4 is the answer in the example. - module PipelineDuration - extend self - - Period = Struct.new(:first, :last) do - def duration - last - first - end - end - - def from_pipeline(pipeline) - status = %w[success failed running canceled] - builds = pipeline.builds.latest - .where(status: status).where.not(started_at: nil).order(:started_at) - - from_builds(builds) - end - - def from_builds(builds) - now = Time.now - - periods = builds.map do |b| - Period.new(b.started_at, b.finished_at || now) - end - - from_periods(periods) - end - - # periods should be sorted by `first` - def from_periods(periods) - process_duration(process_periods(periods)) - end - - private - - def process_periods(periods) - return periods if periods.empty? - - periods.drop(1).inject([periods.first]) do |result, current| - previous = result.last - - if overlap?(previous, current) - result[-1] = merge(previous, current) - result - else - result << current - end - end - end - - def overlap?(previous, current) - current.first <= previous.last - end - - def merge(previous, current) - Period.new(previous.first, [previous.last, current.last].max) - end - - def process_duration(periods) - periods.sum(&:duration) - end - end - end -end diff --git a/lib/gitlab/diff/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb index 371cbe04b9b..c98eefbce25 100644 --- a/lib/gitlab/diff/diff_refs.rb +++ b/lib/gitlab/diff/diff_refs.rb @@ -13,9 +13,9 @@ module Gitlab def ==(other) other.is_a?(self.class) && - base_sha == other.base_sha && - start_sha == other.start_sha && - head_sha == other.head_sha + shas_equal?(base_sha, other.base_sha) && + shas_equal?(start_sha, other.start_sha) && + shas_equal?(head_sha, other.head_sha) end alias_method :eql?, :== @@ -47,6 +47,22 @@ module Gitlab CompareService.new(project, head_sha).execute(project, start_sha, straight: straight) end end + + private + + def shas_equal?(sha1, sha2) + return true if sha1 == sha2 + return false if sha1.nil? || sha2.nil? + return false unless sha1.class == sha2.class + + length = [sha1.length, sha2.length].min + + # If either of the shas is below the minimum length, we cannot be sure + # that they actually refer to the same commit because of hash collision. + return false if length < Commit::MIN_SHA_LENGTH + + sha1[0, length] == sha2[0, length] + end end end end diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb index f80afb20f0c..b8db3adef0a 100644 --- a/lib/gitlab/diff/position.rb +++ b/lib/gitlab/diff/position.rb @@ -49,12 +49,13 @@ module Gitlab coder['attributes'] = self.to_h end - def key - @key ||= [base_sha, start_sha, head_sha, Digest::SHA1.hexdigest(old_path || ""), Digest::SHA1.hexdigest(new_path || ""), old_line, new_line] - end - def ==(other) - other.is_a?(self.class) && key == other.key + other.is_a?(self.class) && + other.diff_refs == diff_refs && + other.old_path == old_path && + other.new_path == new_path && + other.old_line == old_line && + other.new_line == new_line end def to_h diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index 096301d300f..ca94b4baa59 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -24,41 +24,13 @@ module Gitlab SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze - class << self - # The maximum size of a diff to display. - def size_limit - if RequestStore.active? - RequestStore['gitlab_git_diff_size_limit'] ||= find_size_limit - else - find_size_limit - end - end - - # The maximum size before a diff is collapsed. - def collapse_limit - if RequestStore.active? - RequestStore['gitlab_git_diff_collapse_limit'] ||= find_collapse_limit - else - find_collapse_limit - end - end + # The maximum size of a diff to display. + SIZE_LIMIT = 100.kilobytes - def find_size_limit - if Feature.enabled?('gitlab_git_diff_size_limit_increase') - 200.kilobytes - else - 100.kilobytes - end - end - - def find_collapse_limit - if Feature.enabled?('gitlab_git_diff_size_limit_increase') - 100.kilobytes - else - 10.kilobytes - end - end + # The maximum size before a diff is collapsed. + COLLAPSE_LIMIT = 10.kilobytes + class << self def between(repo, head, base, options = {}, *paths) straight = options.delete(:straight) || false @@ -172,7 +144,7 @@ module Gitlab def too_large? if @too_large.nil? - @too_large = @diff.bytesize >= self.class.size_limit + @too_large = @diff.bytesize >= SIZE_LIMIT else @too_large end @@ -190,7 +162,7 @@ module Gitlab def collapsed? return @collapsed if defined?(@collapsed) - @collapsed = !expanded && @diff.bytesize >= self.class.collapse_limit + @collapsed = !expanded && @diff.bytesize >= COLLAPSE_LIMIT end def collapse! @@ -275,14 +247,14 @@ module Gitlab hunk.each_line do |line| size += line.content.bytesize - if size >= self.class.size_limit + if size >= SIZE_LIMIT too_large! return true end end end - if !expanded && size >= self.class.collapse_limit + if !expanded && size >= COLLAPSE_LIMIT collapse! return true end diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb index 208e4bbaf60..e29a1f7afa1 100644 --- a/lib/gitlab/git/hook.rb +++ b/lib/gitlab/git/hook.rb @@ -22,22 +22,22 @@ module Gitlab File.exist?(path) end - def trigger(gl_id, oldrev, newrev, ref) + def trigger(gl_id, gl_username, oldrev, newrev, ref) return [true, nil] unless exists? Bundler.with_clean_env do case name when "pre-receive", "post-receive" - call_receive_hook(gl_id, oldrev, newrev, ref) + call_receive_hook(gl_id, gl_username, oldrev, newrev, ref) when "update" - call_update_hook(gl_id, oldrev, newrev, ref) + call_update_hook(gl_id, gl_username, oldrev, newrev, ref) end end end private - def call_receive_hook(gl_id, oldrev, newrev, ref) + def call_receive_hook(gl_id, gl_username, oldrev, newrev, ref) changes = [oldrev, newrev, ref].join(" ") exit_status = false @@ -45,6 +45,7 @@ module Gitlab vars = { 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username, 'PWD' => repo_path, 'GL_PROTOCOL' => GL_PROTOCOL, 'GL_REPOSITORY' => repository.gl_repository @@ -80,9 +81,13 @@ module Gitlab [exit_status, exit_message] end - def call_update_hook(gl_id, oldrev, newrev, ref) + def call_update_hook(gl_id, gl_username, oldrev, newrev, ref) Dir.chdir(repo_path) do - stdout, stderr, status = Open3.capture3({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev) + env = { + 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username + } + stdout, stderr, status = Open3.capture3(env, path, ref, oldrev, newrev) [status.success?, (stderr.presence || stdout).gsub(/\R/, "<br>").html_safe] end end diff --git a/lib/gitlab/git/hooks_service.rb b/lib/gitlab/git/hooks_service.rb index ea8a87a1290..c327e9b1616 100644 --- a/lib/gitlab/git/hooks_service.rb +++ b/lib/gitlab/git/hooks_service.rb @@ -5,12 +5,13 @@ module Gitlab attr_accessor :oldrev, :newrev, :ref - def execute(committer, repository, oldrev, newrev, ref) - @repository = repository - @gl_id = committer.gl_id - @oldrev = oldrev - @newrev = newrev - @ref = ref + def execute(pusher, repository, oldrev, newrev, ref) + @repository = repository + @gl_id = pusher.gl_id + @gl_username = pusher.name + @oldrev = oldrev + @newrev = newrev + @ref = ref %w(pre-receive update).each do |hook_name| status, message = run_hook(hook_name) @@ -29,7 +30,7 @@ module Gitlab def run_hook(name) hook = Gitlab::Git::Hook.new(name, @repository) - hook.trigger(@gl_id, oldrev, newrev, ref) + hook.trigger(@gl_id, @gl_username, oldrev, newrev, ref) end end end diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb index 786e2e7e8dc..d835dcca8ba 100644 --- a/lib/gitlab/git/operation_service.rb +++ b/lib/gitlab/git/operation_service.rb @@ -152,13 +152,15 @@ module Gitlab # (and have!) accidentally reset the ref to an earlier state, clobbering # commits. See also https://github.com/libgit2/libgit2/issues/1534. command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z] - _, status = popen( + + output, status = popen( command, repository.path) do |stdin| stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00") end unless status.zero? + Gitlab::GitLogger.error("'git update-ref' in #{repository.path}: #{output}") raise Gitlab::Git::CommitError.new( "Could not update branch #{Gitlab::Git.branch_name(ref)}." \ " Please refresh and try again.") diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 616b075c087..22b735c6f7b 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -20,13 +20,11 @@ module Gitlab GitError = Class.new(StandardError) DeleteBranchError = Class.new(StandardError) CreateTreeError = Class.new(StandardError) + TagExistsError = Class.new(StandardError) class << self - # Unlike `new`, `create` takes the storage path, not the storage name - def create(storage_path, name, bare: true, symlink_hooks_to: nil) - repo_path = File.join(storage_path, name) - repo_path += '.git' unless repo_path.end_with?('.git') - + # Unlike `new`, `create` takes the repository path + def create(repo_path, bare: true, symlink_hooks_to: nil) FileUtils.mkdir_p(repo_path, mode: 0770) # Equivalent to `git --git-path=#{repo_path} init [--bare]` @@ -73,8 +71,6 @@ module Gitlab delegate :empty?, to: :rugged - delegate :exists?, to: :gitaly_repository_client - def ==(other) path == other.path end @@ -102,6 +98,18 @@ module Gitlab @circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(storage) end + def exists? + Gitlab::GitalyClient.migrate(:repository_exists) do |enabled| + if enabled + gitaly_repository_client.exists? + else + circuit_breaker.perform do + File.exist?(File.join(@path, 'refs')) + end + end + end + end + # Returns an Array of branch names # sorted by name ASC def branch_names @@ -181,6 +189,28 @@ module Gitlab end end + def has_local_branches? + gitaly_migrate(:has_local_branches) do |is_enabled| + if is_enabled + gitaly_ref_client.has_local_branches? + else + has_local_branches_rugged? + end + end + end + + def has_local_branches_rugged? + rugged.branches.each(:local).any? do |ref| + begin + ref.name && ref.target # ensures the branch is valid + + true + rescue Rugged::ReferenceError + false + end + end + end + # Returns the number of valid tags def tag_count gitaly_migrate(:tag_names) do |is_enabled| @@ -626,34 +656,23 @@ module Gitlab end def add_branch(branch_name, user:, target:) - target_object = Ref.dereference_object(lookup(target)) - raise InvalidRef.new("target not found: #{target}") unless target_object - - OperationService.new(user, self).add_branch(branch_name, target_object.oid) - find_branch(branch_name) - rescue Rugged::ReferenceError => ex - raise InvalidRef, ex + gitaly_migrate(:operation_user_create_branch) do |is_enabled| + if is_enabled + gitaly_add_branch(branch_name, user, target) + else + rugged_add_branch(branch_name, user, target) + end + end end def add_tag(tag_name, user:, target:, message: nil) - target_object = Ref.dereference_object(lookup(target)) - raise InvalidRef.new("target not found: #{target}") unless target_object - - user = Gitlab::Git::User.from_gitlab(user) unless user.respond_to?(:gl_id) - - options = nil # Use nil, not the empty hash. Rugged cares about this. - if message - options = { - message: message, - tagger: Gitlab::Git.committer_hash(email: user.email, name: user.name) - } + gitaly_migrate(:operation_user_add_tag) do |is_enabled| + if is_enabled + gitaly_add_tag(tag_name, user: user, target: target, message: message) + else + rugged_add_tag(tag_name, user: user, target: target, message: message) + end end - - OperationService.new(user, self).add_tag(tag_name, target_object.oid, options) - - find_tag(tag_name) - rescue Rugged::ReferenceError => ex - raise InvalidRef, ex end def rm_branch(branch_name, user:) @@ -661,7 +680,13 @@ module Gitlab end def rm_tag(tag_name, user:) - OperationService.new(user, self).rm_tag(find_tag(tag_name)) + gitaly_migrate(:operation_user_delete_tag) do |is_enabled| + if is_enabled + gitaly_operations_client.rm_tag(tag_name, user) + else + Gitlab::Git::OperationService.new(user, self).rm_tag(find_tag(tag_name)) + end + end end def find_tag(name) @@ -937,7 +962,11 @@ module Gitlab if start_repository == self yield commit(start_branch_name) else - sha = start_repository.commit(start_branch_name).sha + start_commit = start_repository.commit(start_branch_name) + + return yield nil unless start_commit + + sha = start_commit.sha if branch_commit = commit(sha) yield branch_commit @@ -966,8 +995,9 @@ module Gitlab with_repo_branch_commit(source_repository, source_branch) do |commit| if commit write_ref(local_ref, commit.sha) + true else - raise Rugged::ReferenceError, 'source repository is empty' + false end end end @@ -1026,11 +1056,17 @@ module Gitlab # This method return true if repository contains some content visible in project page. # def has_visible_content? - branch_count > 0 + return @has_visible_content if defined?(@has_visible_content) + + @has_visible_content = has_local_branches? end def gitaly_repository - Gitlab::GitalyClient::Util.repository(@storage, @relative_path) + Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) + end + + def gitaly_operations_client + @gitaly_operations_client ||= Gitlab::GitalyClient::OperationService.new(self) end def gitaly_ref_client @@ -1045,6 +1081,10 @@ module Gitlab @gitaly_repository_client ||= Gitlab::GitalyClient::RepositoryService.new(self) end + def gitaly_operation_client + @gitaly_operation_client ||= Gitlab::GitalyClient::OperationService.new(self) + end + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) Gitlab::GitalyClient.migrate(method, status: status, &block) rescue GRPC::NotFound => e @@ -1367,6 +1407,33 @@ module Gitlab false end + def gitaly_add_tag(tag_name, user:, target:, message: nil) + gitaly_operations_client.add_tag(tag_name, user, target, message) + end + + def rugged_add_tag(tag_name, user:, target:, message: nil) + target_object = Ref.dereference_object(lookup(target)) + raise InvalidRef.new("target not found: #{target}") unless target_object + + user = Gitlab::Git::User.from_gitlab(user) unless user.respond_to?(:gl_id) + + options = nil # Use nil, not the empty hash. Rugged cares about this. + if message + options = { + message: message, + tagger: Gitlab::Git.committer_hash(email: user.email, name: user.name) + } + end + + Gitlab::Git::OperationService.new(user, self).add_tag(tag_name, target_object.oid, options) + + find_tag(tag_name) + rescue Rugged::ReferenceError => ex + raise InvalidRef, ex + rescue Rugged::TagError + raise TagExistsError + end + def rugged_create_branch(ref, start_point) rugged_ref = rugged.branches.create(ref, start_point) target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target) @@ -1409,6 +1476,22 @@ module Gitlab file.write(gitattributes_content) end end + + def gitaly_add_branch(branch_name, user, target) + gitaly_operation_client.user_create_branch(branch_name, user, target) + rescue GRPC::FailedPrecondition => ex + raise InvalidRef, ex + end + + def rugged_add_branch(branch_name, user, target) + target_object = Ref.dereference_object(lookup(target)) + raise InvalidRef.new("target not found: #{target}") unless target_object + + OperationService.new(user, self).add_branch(branch_name, target_object.oid) + find_branch(branch_name) + rescue Rugged::ReferenceError + raise InvalidRef, ex + end end end end diff --git a/lib/gitlab/git/user.rb b/lib/gitlab/git/user.rb index ea634d39668..cb1af5f3b7c 100644 --- a/lib/gitlab/git/user.rb +++ b/lib/gitlab/git/user.rb @@ -1,24 +1,21 @@ module Gitlab module Git class User - attr_reader :name, :email, :gl_id + attr_reader :username, :name, :email, :gl_id def self.from_gitlab(gitlab_user) - new(gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user)) + new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user)) end - def self.from_gitaly(gitaly_user) - new(gitaly_user.name, gitaly_user.email, gitaly_user.gl_id) - end - - def initialize(name, email, gl_id) + def initialize(username, name, email, gl_id) + @username = username @name = name @email = email @gl_id = gl_id end def ==(other) - [name, email, gl_id] == [other.name, other.email, other.gl_id] + [username, name, email, gl_id] == [other.username, other.name, other.email, other.gl_id] end end end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 62d1ecae676..db67ede9d9e 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -16,7 +16,8 @@ module Gitlab account_blocked: 'Your account has been blocked.', command_not_allowed: "The command you're trying to execute is not allowed.", upload_pack_disabled_over_http: 'Pulling over HTTP is not allowed.', - receive_pack_disabled_over_http: 'Pushing over HTTP is not allowed.' + receive_pack_disabled_over_http: 'Pushing over HTTP is not allowed.', + readonly: 'The repository is temporarily read-only. Please try again later.' }.freeze DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }.freeze @@ -159,6 +160,10 @@ module Gitlab end def check_push_access!(changes) + if project.repository_read_only? + raise UnauthorizedError, ERROR_MESSAGES[:readonly] + end + if deploy_key check_deploy_key_push_access! elsif user diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 955d2307f88..e75e0500ed8 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -151,7 +151,7 @@ module Gitlab actual_call_count = increment_call_count("gitaly_#{call_site}_actual") # Do no enforce limits in production - return if Rails.env.production? + return if Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"] # Check if this call is nested within a allow_n_plus_1_calls # block and skip check if it is diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb new file mode 100644 index 00000000000..46bd5c18603 --- /dev/null +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -0,0 +1,65 @@ +module Gitlab + module GitalyClient + class OperationService + def initialize(repository) + @gitaly_repo = repository.gitaly_repository + @repository = repository + end + + def rm_tag(tag_name, user) + request = Gitaly::UserDeleteTagRequest.new( + repository: @gitaly_repo, + tag_name: GitalyClient.encode(tag_name), + user: Util.gitaly_user(user) + ) + + response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request) + + if pre_receive_error = response.pre_receive_error.presence + raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error + end + end + + def add_tag(tag_name, user, target, message) + request = Gitaly::UserCreateTagRequest.new( + repository: @gitaly_repo, + user: Util.gitaly_user(user), + tag_name: GitalyClient.encode(tag_name), + target_revision: GitalyClient.encode(target), + message: GitalyClient.encode(message.to_s) + ) + + response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request) + if pre_receive_error = response.pre_receive_error.presence + raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error + elsif response.exists + raise Gitlab::Git::Repository::TagExistsError + end + + Util.gitlab_tag_from_gitaly_tag(@repository, response.tag) + rescue GRPC::FailedPrecondition => e + raise Gitlab::Git::Repository::InvalidRef, e + end + + def user_create_branch(branch_name, user, start_point) + request = Gitaly::UserCreateBranchRequest.new( + repository: @gitaly_repo, + branch_name: GitalyClient.encode(branch_name), + user: Util.gitaly_user(user), + start_point: GitalyClient.encode(start_point) + ) + response = GitalyClient.call(@repository.storage, :operation_service, + :user_create_branch, request) + if response.pre_receive_error.present? + raise Gitlab::Git::HooksService::PreReceiveError.new(response.pre_receive_error) + end + + branch = response.branch + return nil unless branch + + target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) + Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit) + end + end + end +end diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index 8ef873d5848..8214b7d63fa 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -57,6 +57,14 @@ module Gitlab branch_names.count end + # TODO implement a more efficient RPC for this https://gitlab.com/gitlab-org/gitaly/issues/616 + def has_local_branches? + request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo) + response = GitalyClient.call(@storage, :ref_service, :find_all_branch_names, request).first + + response&.names.present? + end + def local_branches(sort_by: nil) request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo) request.sort_by = sort_by_param(sort_by) if sort_by @@ -155,19 +163,7 @@ module Gitlab def consume_tags_response(response) response.flat_map do |message| - message.tags.map do |gitaly_tag| - if gitaly_tag.target_commit.present? - gitaly_commit = Gitlab::Git::Commit.decorate(@repository, gitaly_tag.target_commit) - end - - Gitlab::Git::Tag.new( - @repository, - encode!(gitaly_tag.name.dup), - gitaly_tag.id, - gitaly_commit, - encode!(gitaly_tag.message.chomp) - ) - end + message.tags.map { |gitaly_tag| Util.gitlab_tag_from_gitaly_tag(@repository, gitaly_tag) } end end diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 177a1284f38..fdf912214e0 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -53,6 +53,11 @@ module Gitlab GitalyClient.call(@storage, :repository_service, :fetch_remote, request) end + + def create_repository + request = Gitaly::CreateRepositoryRequest.new(repository: @gitaly_repo) + GitalyClient.call(@storage, :repository_service, :create_repository, request) + end end end end diff --git a/lib/gitlab/gitaly_client/util.rb b/lib/gitlab/gitaly_client/util.rb index 8fc937496af..da43c616b94 100644 --- a/lib/gitlab/gitaly_client/util.rb +++ b/lib/gitlab/gitaly_client/util.rb @@ -2,14 +2,39 @@ module Gitlab module GitalyClient module Util class << self - def repository(repository_storage, relative_path) + def repository(repository_storage, relative_path, gl_repository) Gitaly::Repository.new( storage_name: repository_storage, relative_path: relative_path, + gl_repository: gl_repository, git_object_directory: Gitlab::Git::Env['GIT_OBJECT_DIRECTORY'].to_s, git_alternate_object_directories: Array.wrap(Gitlab::Git::Env['GIT_ALTERNATE_OBJECT_DIRECTORIES']) ) end + + def gitaly_user(gitlab_user) + return unless gitlab_user + + Gitaly::User.new( + gl_id: Gitlab::GlId.gl_id(gitlab_user), + name: GitalyClient.encode(gitlab_user.name), + email: GitalyClient.encode(gitlab_user.email) + ) + end + + def gitlab_tag_from_gitaly_tag(repository, gitaly_tag) + if gitaly_tag.target_commit.present? + commit = Gitlab::Git::Commit.decorate(repository, gitaly_tag.target_commit) + end + + Gitlab::Git::Tag.new( + repository, + Gitlab::EncodingHelper.encode!(gitaly_tag.name.dup), + gitaly_tag.id, + commit, + Gitlab::EncodingHelper.encode!(gitaly_tag.message.chomp) + ) + end end end end diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index fb68627dedf..e60ceba27c8 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -16,7 +16,7 @@ module Gitlab def self.allowed?(user) self.open(user) do |access| if access.allowed? - Users::UpdateService.new(user, last_credential_check_at: Time.now).execute + Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute true else diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index cd7e4ca7b7e..0afaa2306b5 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -22,8 +22,8 @@ module Gitlab Gitlab::LDAP::Config.new(provider) end - def users(field, value, limit = nil) - options = user_options(field, value, limit) + def users(fields, value, limit = nil) + options = user_options(Array(fields), value, limit) entries = ldap_search(options).select do |entry| entry.respond_to? config.uid @@ -72,20 +72,24 @@ module Gitlab private - def user_options(field, value, limit) - options = { attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq } + def user_options(fields, value, limit) + options = { + attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq, + base: config.base + } + options[:size] = limit if limit - if field.to_sym == :dn + if fields.include?('dn') + raise ArgumentError, 'It is not currently possible to search the DN and other fields at the same time.' if fields.size > 1 + options[:base] = value options[:scope] = Net::LDAP::SearchScope_BaseObject - options[:filter] = user_filter else - options[:base] = config.base - options[:filter] = user_filter(Net::LDAP::Filter.eq(field, value)) + filter = fields.map { |field| Net::LDAP::Filter.eq(field, value) }.inject(:|) end - options + options.merge(filter: user_filter(filter)) end def user_filter(filter = nil) diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 4d6f8ac79de..9a6f7827b16 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -17,6 +17,12 @@ module Gitlab adapter.user('dn', dn) end + def self.find_by_email(email, adapter) + email_fields = adapter.config.attributes['email'] + + adapter.user(email_fields, email) + end + def self.disabled_via_active_directory?(dn, adapter) adapter.dn_matches_filter?(dn, AD_USER_DISABLED) end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 3bf27b37ae6..1793097363e 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -17,41 +17,19 @@ module Gitlab end end - def initialize(auth_hash) - super - update_user_attributes - end - def save super('LDAP') end # instance methods - def gl_user - @gl_user ||= find_by_uid_and_provider || find_by_email || build_new_user + def find_user + find_by_uid_and_provider || find_by_email || build_new_user end def find_by_uid_and_provider self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider) end - def find_by_email - ::User.find_by(email: auth_hash.email.downcase) if auth_hash.has_attribute?(:email) - end - - def update_user_attributes - if persisted? - # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. - identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider } - identity ||= gl_user.identities.build(provider: auth_hash.provider) - - # For a new identity set extern_uid to the LDAP DN - # For an existing identity with matching email but changed DN, update the DN. - # For an existing identity with no change in DN, this line changes nothing. - identity.extern_uid = auth_hash.uid - end - end - def changed? gl_user.changed? || gl_user.identities.any?(&:changed?) end diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb deleted file mode 100644 index 306923902e0..00000000000 --- a/lib/gitlab/markdown/pipeline.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Gitlab - module Markdown - class Pipeline - def self.[](name) - name ||= :full - const_get("#{name.to_s.camelize}Pipeline") - end - - def self.filters - [] - end - - def self.transform_context(context) - context - end - - def self.html_pipeline - @html_pipeline ||= HTML::Pipeline.new(filters) - end - - class << self - %i(call to_document to_html).each do |meth| - define_method(meth) do |text, context| - context = transform_context(context) - - html_pipeline.__send__(meth, text, context) # rubocop:disable GitlabSecurity/PublicSend - end - end - end - end - end -end diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 7704bf715e4..68815be4d13 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -13,6 +13,7 @@ module Gitlab def initialize(auth_hash) self.auth_hash = auth_hash update_profile if sync_profile_from_provider? + add_or_update_user_identities end def persisted? @@ -32,7 +33,7 @@ module Gitlab block_after_save = needs_blocking? - Users::UpdateService.new(gl_user).execute! + Users::UpdateService.new(gl_user, user: gl_user).execute! gl_user.block if block_after_save @@ -44,47 +45,54 @@ module Gitlab end def gl_user - @user ||= find_by_uid_and_provider + return @gl_user if defined?(@gl_user) - if auto_link_ldap_user? - @user ||= find_or_create_ldap_user - end + @gl_user = find_user + end - if signup_enabled? - @user ||= build_new_user - end + def find_user + user = find_by_uid_and_provider - if external_provider? && @user - @user.external = true - end + user ||= find_or_build_ldap_user if auto_link_ldap_user? + user ||= build_new_user if signup_enabled? + + user.external = true if external_provider? && user - @user + user end protected - def find_or_create_ldap_user + def add_or_update_user_identities + # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. + identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider } + + identity ||= gl_user.identities.build(provider: auth_hash.provider) + identity.extern_uid = auth_hash.uid + + if auto_link_ldap_user? && !gl_user.ldap_user? && ldap_person + log.info "Correct LDAP account has been found. identity to user: #{gl_user.username}." + gl_user.identities.build(provider: ldap_person.provider, extern_uid: ldap_person.dn) + end + end + + def find_or_build_ldap_user return unless ldap_person - # If a corresponding person exists with same uid in a LDAP server, - # check if the user already has a GitLab account. user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider) if user - # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account. log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity." - user.identities.find_or_initialize_by(extern_uid: auth_hash.uid, provider: auth_hash.provider) - else - log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account." - user = find_by_uid_and_provider - if user.nil? - log.info "No user found using #{auth_hash.provider} provider. Creating a new one." - user = build_new_user - end - log.info "Correct account has been found. Adding LDAP identity to user: #{user.username}." - user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn) + return user end - user + log.info "No user found using #{auth_hash.provider} provider. Creating a new one." + build_new_user + end + + def find_by_email + return unless auth_hash.has_attribute?(:email) + + ::User.find_by(email: auth_hash.email.downcase) end def auto_link_ldap_user? @@ -108,9 +116,9 @@ module Gitlab end def find_ldap_person(auth_hash, adapter) - by_uid = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) - # The `uid` might actually be a DN. Try it next. - by_uid || Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) + Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) || + Gitlab::LDAP::Person.find_by_email(auth_hash.uid, adapter) || + Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) end def ldap_config @@ -152,7 +160,7 @@ module Gitlab end def build_new_user - user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true) + user_params = user_attributes.merge(skip_confirmation: true) Users::BuildService.new(nil, user_params).execute(skip_authorization: true) end diff --git a/lib/gitlab/saml/user.rb b/lib/gitlab/saml/user.rb index 0f323a9e8b2..e0a9d1dee77 100644 --- a/lib/gitlab/saml/user.rb +++ b/lib/gitlab/saml/user.rb @@ -10,41 +10,20 @@ module Gitlab super('SAML') end - def gl_user - if auto_link_ldap_user? - @user ||= find_or_create_ldap_user - end - - @user ||= find_by_uid_and_provider - - if auto_link_saml_user? - @user ||= find_by_email - end + def find_user + user = find_by_uid_and_provider - if signup_enabled? - @user ||= build_new_user - end + user ||= find_by_email if auto_link_saml_user? + user ||= find_or_build_ldap_user if auto_link_ldap_user? + user ||= build_new_user if signup_enabled? - if external_users_enabled? && @user + if external_users_enabled? && user # Check if there is overlap between the user's groups and the external groups # setting then set user as external or internal. - @user.external = - if (auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? - false - else - true - end + user.external = !(auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? end - @user - end - - def find_by_email - if auth_hash.has_attribute?(:email) - user = ::User.find_by(email: auth_hash.email.downcase) - user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider) if user - user - end + user end def changed? diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 81ecdf43ef9..a99f8e2b5f8 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -65,7 +65,7 @@ module Gitlab # Init new repository # - # storage - project's storage path + # storage - project's storage name # name - project path with namespace # # Ex. @@ -73,7 +73,19 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387 def add_repository(storage, name) - Gitlab::Git::Repository.create(storage, name, bare: true, symlink_hooks_to: gitlab_shell_hooks_path) + relative_path = name.dup + relative_path << '.git' unless relative_path.end_with?('.git') + + gitaly_migrate(:create_repository) do |is_enabled| + if is_enabled + repository = Gitlab::Git::Repository.new(storage, relative_path, '') + repository.gitaly_repository_client.create_repository + true + else + repo_path = File.join(Gitlab.config.repositories.storages[storage]['path'], relative_path) + Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path) + end + end rescue => err Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}") false diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 17550cf9074..45f246242f1 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -22,9 +22,9 @@ module Gitlab params = { GL_ID: Gitlab::GlId.gl_id(user), GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki), + GL_USERNAME: user&.username, RepoPath: repo_path } - server = { address: Gitlab::GitalyClient.address(project.repository_storage), token: Gitlab::GitalyClient.token(project.repository_storage) diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb index 7b486d78cf0..dfa8b8b3f5b 100644 --- a/lib/system_check/app/git_user_default_ssh_config_check.rb +++ b/lib/system_check/app/git_user_default_ssh_config_check.rb @@ -5,6 +5,7 @@ module SystemCheck # whitelisted as it may change the SSH client's behaviour dramatically. WHITELIST = %w[ authorized_keys + authorized_keys.lock authorized_keys2 known_hosts ].freeze diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 42825f29e32..0e6aed32c52 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -79,7 +79,7 @@ namespace :gitlab do if File.exist?(path_to_repo) print '-' else - if Gitlab::Shell.new.add_repository(project.repository_storage_path, + if Gitlab::Shell.new.add_repository(project.repository_storage, project.disk_path) print '.' else diff --git a/lib/tasks/gitlab/storage.rake b/lib/tasks/gitlab/storage.rake new file mode 100644 index 00000000000..e05be4a3405 --- /dev/null +++ b/lib/tasks/gitlab/storage.rake @@ -0,0 +1,85 @@ +namespace :gitlab do + namespace :storage do + desc 'GitLab | Storage | Migrate existing projects to Hashed Storage' + task migrate_to_hashed: :environment do + legacy_projects_count = Project.with_legacy_storage.count + + if legacy_projects_count == 0 + puts 'There are no projects using legacy storage. Nothing to do!' + + next + end + + print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{batch_size}" + + project_id_batches do |start, finish| + StorageMigratorWorker.perform_async(start, finish) + + print '.' + end + + puts ' Done!' + end + + desc 'Gitlab | Storage | Summary of existing projects using Legacy Storage' + task legacy_projects: :environment do + projects_summary(Project.with_legacy_storage) + end + + desc 'Gitlab | Storage | List existing projects using Legacy Storage' + task list_legacy_projects: :environment do + projects_list(Project.with_legacy_storage) + end + + desc 'Gitlab | Storage | Summary of existing projects using Hashed Storage' + task hashed_projects: :environment do + projects_summary(Project.with_hashed_storage) + end + + desc 'Gitlab | Storage | List existing projects using Hashed Storage' + task list_hashed_projects: :environment do + projects_list(Project.with_hashed_storage) + end + + def batch_size + ENV.fetch('BATCH', 200).to_i + end + + def project_id_batches(&block) + Project.with_legacy_storage.in_batches(of: batch_size, start: ENV['ID_FROM'], finish: ENV['ID_TO']) do |relation| # rubocop: disable Cop/InBatches + ids = relation.pluck(:id) + + yield ids.min, ids.max + end + end + + def projects_summary(relation) + projects_count = relation.count + puts "* Found #{projects_count} projects".color(:green) + + projects_count + end + + def projects_list(relation) + projects_count = projects_summary(relation) + + projects = relation.with_route + limit = ENV.fetch('LIMIT', 500).to_i + + return unless projects_count > 0 + + puts " ! Displaying first #{limit} projects..." if projects_count > limit + + counter = 0 + projects.find_in_batches(batch_size: batch_size) do |batch| + batch.each do |project| + counter += 1 + + puts " - #{project.full_path} (id: #{project.id})".color(:red) + + return if counter >= limit # rubocop:disable Lint/NonLocalExitFromIterator + end + end + end + end +end diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po index 9d90f4ed5b1..38d63315fdc 100644 --- a/locale/bg/gitlab.po +++ b/locale/bg/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Bulgarian\n" "Language: bg_BG\n" @@ -29,6 +29,9 @@ msgstr[1] "%s Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð±Ñха пропуÑнати, за да не Ñ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} подаде %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "1 Ñхема" msgstr[1] "%d Ñхеми" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Ðабор от графики отноÑно непрекъÑнатата интеграциÑ" @@ -93,7 +99,7 @@ msgstr "ДобавÑне на нова папка" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Прикачете файл чрез влачене и пуÑкане или %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Клон" @@ -194,6 +227,90 @@ msgstr "Превключване на клона" msgid "Branches" msgstr "Клонове" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Преглед на папката" @@ -337,9 +454,6 @@ msgstr "Подадено от" msgid "Compare" msgstr "Сравнение" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "РъководÑтво за ÑътрудничеÑтво" @@ -489,6 +603,9 @@ msgstr "Редактиране на плана %{id} за Ñхема" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ð’Ñеки меÑец (на 1-во чиÑло, в 4 ч. Ñутринта msgid "Every week (Sundays at 4:00am)" msgstr "Ð’ÑÑка Ñедмица (в неделÑ, в 4 ч. Ñутринта)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "СобÑтвеникът не може да бъде променен" @@ -572,7 +692,28 @@ msgstr "Към Вашето разклонение" msgid "GoToYourFork|Fork" msgstr "Разклонение" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Ðачало" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "ОÑвежаването започна уÑпешно" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Изключено" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "СобÑтвеник" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "Ñ ÐµÑ‚Ð°Ð¿Ð¸" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "Връзката към изнеÑените данни на Ð¿Ñ€Ð¾ÐµÐºÑ msgid "Project export started. A download link will be sent by email." msgstr "ИзнаÑÑнето на проекта започна. Ще получите връзка към данните по е-поща." -msgid "Project home" -msgstr "Ðачална Ñтраница на проекта" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Етап" msgid "ProjectNetworkGraph|Graph" msgstr "Графика" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Клонове" msgid "RefSwitcher|Tags" msgstr "Етикети" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Свързани подаваниÑ" @@ -1073,6 +1223,9 @@ msgstr "Запазване на плана за Ñхема" msgid "Schedule a new pipeline" msgstr "Създаване на нов план за Ñхема" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Планиране на Ñхемите" @@ -1112,6 +1265,12 @@ msgstr "зададете парола" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Показване на %d Ñъбитие" @@ -1120,6 +1279,102 @@ msgstr[1] "Показване на %d ÑъбитиÑ" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Изходен код" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Звезда" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Създайте %{new_merge_request} Ñ Ñ‚ÐµÐ·Ð¸ промени" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Преминаване към клон/етикет" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Етикет" @@ -1206,6 +1467,9 @@ msgstr "СтойноÑтта, коÑто Ñе намира в Ñредата нРmsgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Това означава, че нÑма да можете да изпращате код, докато не Ñъздадете празно хранилище или не внеÑете ÑъщеÑтвуващо такова." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Използване на глобалната Ви наÑтройка за извеÑтиÑта" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Преглед на отворената заÑвка за Ñливане" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Вътрешен" @@ -1456,6 +1726,12 @@ msgstr "ÐÑма да можете да изтеглÑте или изпраща msgid "Your name" msgstr "Вашето име" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "ден" diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po index 19961043ede..fc3c60166b7 100644 --- a/locale/de/gitlab.po +++ b/locale/de/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: German\n" "Language: de_DE\n" @@ -18,8 +18,8 @@ msgstr "" msgid "%d commit" msgid_plural "%d commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%d Commit" +msgstr[1] "%d Commits" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." @@ -27,6 +27,9 @@ msgstr[0] "%s zusätzlicher Commit wurde ausgelassen um Leistungsprobleme zu ver msgstr[1] "%s zusätzliche Commits wurden ausgelassen um Leistungsprobleme zu verhindern." msgid "%{commit_author_link} committed %{commit_timeago}" +msgstr "%{commit_author_link} hat %{commit_timeago} committet" + +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." @@ -40,8 +43,8 @@ msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%{storage_name}: fehlgeschlagener Speicherzugriff auf Host:" +msgstr[1] "%{storage_name}: %{failed_attempts} fehlgeschlagene Speicherzugriffe:" msgid "(checkout the %{link} for information on how to install it)." msgstr "(beachte die Informationen zur Installation auf %{link})." @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Eine Sammlung von Graphen bezüglich kontinuierlicher Integration" @@ -67,13 +73,13 @@ msgid "Access to failing storages has been temporarily disabled to allow the mou msgstr "Zugriff auf fehlerhafte Speicher wurde vorübergehend deaktiviert, um die Wiederherstellung zu ermöglichen. Für den zukünftigen Zugriff, behebe bitte das Problem und setze danach die Speicherinformationen zurück." msgid "Account" -msgstr "" +msgstr "Konto" msgid "Active" msgstr "Aktiv" msgid "Activity" -msgstr "" +msgstr "Aktivität" msgid "Add Changelog" msgstr "Änderungsliste hinzufügen " @@ -93,8 +99,8 @@ msgstr "Erstelle eine neues Verzeichnis" msgid "All" msgstr "Alle" -msgid "Appearances" -msgstr "Erscheinungsbild" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "Anwendungen" @@ -117,10 +123,34 @@ msgstr "Bist Du sicher, dass Du den Systemüberwachungstoken zurücksetzen wills msgid "Are you sure?" msgstr "Bist Du sicher?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Datei mittels Drag & Drop oder %{upload_link} hinzufügen" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,10 +207,13 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Zweig" +msgstr[1] "Zweige" msgid "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}" msgstr "Branch <strong>%{branch_name}</strong> wurde erstellt. Um die automatische Bereitstellung einzurichten, wähle eine GitLab CI Yaml Vorlage und committe Deine Änderungen. %{link_to_autodeploy_doc}" @@ -194,6 +227,90 @@ msgstr "Branch wechseln" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Verzeichnisse durchsuchen" @@ -210,7 +327,7 @@ msgid "ByAuthor|by" msgstr "von" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "CI-Konfiguration" @@ -240,7 +357,7 @@ msgid "Charts" msgstr "Diagramme" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" msgstr "Diesen Commit herauspicken " @@ -307,8 +424,8 @@ msgstr "Kommentare" msgid "Commit" msgid_plural "Commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Commit" +msgstr[1] "Commits" msgid "Commit duration in minutes for last 30 commits" msgstr "Dauer der Commits in Minuten für die letzten 30 Commits" @@ -323,7 +440,7 @@ msgid "CommitMessage|Add %{file_name}" msgstr "%{file_name} hinzufügen" msgid "Commits" -msgstr "" +msgstr "Commits" msgid "Commits feed" msgstr "Liste der Commits" @@ -337,9 +454,6 @@ msgstr "Committed von" msgid "Compare" msgstr "Vergleichen" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Mitarbeitsanleitung" @@ -442,7 +556,7 @@ msgid "Description" msgstr "Beschreibung" msgid "Details" -msgstr "" +msgstr "Details" msgid "Directory name" msgstr "Verzeichnisname" @@ -489,6 +603,9 @@ msgstr "Pipeline Zeitplan bearbeiten %{id}" msgid "Emails" msgstr "E-Mails" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "Filtere alle" @@ -516,6 +633,9 @@ msgstr "Monatlich (am Ersten um 4:00 Uhr)" msgid "Every week (Sundays at 4:00am)" msgstr "Wöchentlich (Sonntags um 4:00 Uhr)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Wechsel des Besitzers fehlgeschlagen" @@ -572,8 +692,29 @@ msgstr "Gehe zu Deinem Ableger" msgid "GoToYourFork|Fork" msgstr "Ableger" -msgid "Group overview" -msgstr "Gruppen-Ãœbersicht" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "Systemzustand" @@ -593,12 +734,6 @@ msgstr "Keine Probleme erkannt" msgid "HealthCheck|Unhealthy" msgstr "Problematisch" -msgid "Home" -msgstr "Startseite" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Aufräumen erfolgreich gestartet" @@ -620,6 +755,9 @@ msgstr "Ticketereignisse" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Deaktiviert" @@ -662,7 +800,7 @@ msgid "Leave project" msgstr "Verlasse das Projekt" msgid "License" -msgstr "Lizenz" +msgstr "" msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" @@ -670,7 +808,7 @@ msgstr[0] "Limitiere die Anzeige auf höchstens %d Ereignis" msgstr[1] "Limitiere die Anzeige auf höchstens %d Ereignisse" msgid "Locked Files" -msgstr "Gesperrte Dateien" +msgstr "" msgid "Median" msgstr "Median" @@ -684,14 +822,17 @@ msgstr "" msgid "Merge events" msgstr "Ereignisse zusammenführen" -msgid "Messages" +msgid "Merge request" msgstr "" +msgid "Messages" +msgstr "Nachrichten" + msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "einen SSH Schlüssel hinzufügst" msgid "Monitoring" -msgstr "" +msgstr "Ãœberwachung" msgid "More information is available|here" msgstr "hier" @@ -812,6 +953,18 @@ msgstr "Ãœbersicht" msgid "Owner" msgstr "Besitzer" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "Passwort" @@ -900,7 +1053,7 @@ msgid "Pipelines for last week" msgstr "" msgid "Pipelines for last year" -msgstr "" +msgstr "Pipelines des letzten Jahres" msgid "Pipeline|all" msgstr "Alle" @@ -917,12 +1070,9 @@ msgstr "mit Stages" msgid "Preferences" msgstr "" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "Projekt" - msgid "Project '%{project_name}' queued for deletion." msgstr "Das Projekt '%{project_name}' wurde zur Löschung eingeplant." @@ -953,12 +1103,6 @@ msgstr "Der Link für den Export des Projektes ist abgelaufen. Bitte generiere e msgid "Project export started. A download link will be sent by email." msgstr "Export des Projektes gestartet. Ein Link zum herunterladen wir Dir per E-Mail zugesandt." -msgid "Project home" -msgstr "Startseite des Projektes" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "Abonnieren" @@ -983,27 +1127,30 @@ msgstr "Stage" msgid "ProjectNetworkGraph|Graph" msgstr "Diagramm" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "ProjectsDropdown | Projekte, die Sie häufig besuchen, werden hier angezeigt" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "Ãœbertragungsereignisse" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Zugehörige Commits" @@ -1065,7 +1215,7 @@ msgid "Revert this merge request" msgstr "Merge Request zurücksetzen" msgid "SSH Keys" -msgstr "" +msgstr "SSH-Schlüssel" msgid "Save pipeline schedule" msgstr "Zeitplan der Pipeline speichern" @@ -1073,6 +1223,9 @@ msgstr "Zeitplan der Pipeline speichern" msgid "Schedule a new pipeline" msgstr "Plane eine neue Pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Pipelines planen" @@ -1110,6 +1263,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "ein Passwort festlegst" msgid "Settings" +msgstr "Einstellungen" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1120,11 +1279,107 @@ msgstr[1] "Zeige %d Ereignisse" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Quellcode" msgid "Spam Logs" -msgstr "" +msgstr "Spam-Protokolle" msgid "Specify the following URL during the Runner setup:" msgstr "Lege die folgende URL während des Runner Setups fest:" @@ -1132,6 +1387,9 @@ msgstr "Lege die folgende URL während des Runner Setups fest:" msgid "StarProject|Star" msgstr "Favorisieren" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Beginne einen %{new_merge_request} mit diesen Änderungen" @@ -1141,6 +1399,9 @@ msgstr "Starte den Runner!" msgid "Switch branch/tag" msgstr "Zu Branch/Tag wechseln" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Me msgid "There are problems accessing Git storage: " msgstr "Es gibt ein Problem beim Zugriff auf den Gitspeicher:" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Dies bedeutet, dass Du keinen Code übertragen kannst, bevor Du kein leeres Repositorium erstellt oder ein Existierendes importiert hast." @@ -1222,7 +1486,7 @@ msgid "Time until first merge request" msgstr "Zeit bis zum ersten Merge Request" msgid "Timeago|%s days ago" -msgstr "seit %s Tagen" +msgstr "" msgid "Timeago|%s days remaining" msgstr "%s Tage verbleibend" @@ -1231,13 +1495,13 @@ msgid "Timeago|%s hours remaining" msgstr "%s Stunden verbleibend" msgid "Timeago|%s minutes ago" -msgstr "seit %s Minuten " +msgstr "" msgid "Timeago|%s minutes remaining" msgstr "%s Minuten verbleibend" msgid "Timeago|%s months ago" -msgstr "seit %s Monaten" +msgstr "" msgid "Timeago|%s months remaining" msgstr "%s Monate verbleibend" @@ -1246,13 +1510,13 @@ msgid "Timeago|%s seconds remaining" msgstr "%s Sekunden verbleibend" msgid "Timeago|%s weeks ago" -msgstr "seit %s Wochen" +msgstr "" msgid "Timeago|%s weeks remaining" msgstr "%s Wochen verbleibend" msgid "Timeago|%s years ago" -msgstr "seit %s Jahren" +msgstr "" msgid "Timeago|%s years remaining" msgstr "%s Jahre verbleibend" @@ -1381,9 +1645,15 @@ msgstr "Benutze den folgenden Registrierungstoken während des Setups:" msgid "Use your global notification setting" msgstr "Benutze Deine globalen Benachrichtigungseinstellungen" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Zeige offene Merge Requests." +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Intern" @@ -1456,6 +1726,12 @@ msgstr "Du kannst erst mittels SSH übertragen (push) oder abrufen (pull), nachd msgid "Your name" msgstr "Dein Name" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "Tag" diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po index 0ac591d4927..b50685514e1 100644 --- a/locale/en/gitlab.po +++ b/locale/en/gitlab.po @@ -1057,7 +1057,7 @@ msgstr "" msgid "Timeago|a week ago" msgstr "" -msgid "Timeago|a while" +msgid "Timeago|in a while" msgstr "" msgid "Timeago|a year ago" diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po index f9f61a109f6..e8c2195e4e3 100644 --- a/locale/eo/gitlab.po +++ b/locale/eo/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Esperanto\n" "Language: eo_UY\n" @@ -29,6 +29,9 @@ msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troÅarÄi la sistemon." msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} enmetis %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "1 ĉenstablo" msgstr[1] "%d ĉenstabloj" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Aro da diagramoj pri la seninterrompa integrado" @@ -93,7 +99,7 @@ msgstr "Aldoni novan dosierujon" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Alkroĉu dosieron per Åovmetado aÅ %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Branĉo" @@ -194,6 +227,90 @@ msgstr "Iri al branĉo" msgid "Branches" msgstr "Branĉoj" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Foliumi dosierujon" @@ -337,9 +454,6 @@ msgstr "Enmetita de" msgid "Compare" msgstr "Kompari" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Gvidlinioj por kontribuado" @@ -489,6 +603,9 @@ msgstr "Redakti ĉenstablan planon %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ĉiumonate (en la 1a de la monato, je 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Ĉiusemajne (en dimanĉo, je 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ne eblas ÅanÄi la posedanton" @@ -572,7 +692,28 @@ msgstr "Al via disbranĉigo" msgid "GoToYourFork|Fork" msgstr "Disbranĉigo" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Hejmo" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "La refreÅigo komenciÄis sukcese" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "MalÅaltita" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Posedanto" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "kun etapoj" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "La ligilo por la projekta elporto eksvalidiÄis. Bonvolu krei novan elpo msgid "Project export started. A download link will be sent by email." msgstr "La elporto de la projekto komenciÄis. Vi ricevos ligilon per retpoÅto por elÅuti la datenoj." -msgid "Project home" -msgstr "Hejmo de la projekto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapo" msgid "ProjectNetworkGraph|Graph" msgstr "Grafeo" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branĉoj" msgid "RefSwitcher|Tags" msgstr "Etikedoj" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Rilataj enmetadoj" @@ -1073,6 +1223,9 @@ msgstr "Konservi ĉenstablan planon" msgid "Schedule a new pipeline" msgstr "Plani novan ĉenstablon" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Planado de la ĉenstabloj" @@ -1112,6 +1265,12 @@ msgstr "kreos pasvorton" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Estas montrata %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Estas montrataj %d eventoj" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Kodo" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Steligi" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Kreu %{new_merge_request} kun ĉi tiuj ÅanÄoj" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Iri al branĉo/etikedo" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Etikedo" @@ -1206,6 +1467,9 @@ msgstr "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemp msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Ĉi tiu signifas, ke vi ne povos alpuÅi kodon, antaÅ ol vi kreos malplenan deponejon aÅ enportos jam ekzistantan." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Uzi vian Äeneralan agordon pri la sciigoj" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Vidi la malfermitan peton pri kunfando" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interna" @@ -1456,6 +1726,12 @@ msgstr "Vi ne povos eltiri aÅ alpuÅi kodon per SSH antaÅ ol vi %{add_ssh_key_ msgid "Your name" msgstr "Via nomo" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "tago" diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po index ccf4b0abf9f..29a010f9428 100644 --- a/locale/es/gitlab.po +++ b/locale/es/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Spanish\n" "Language: es_ES\n" @@ -29,6 +29,9 @@ msgstr[1] "%s cambios adicionales han sido omitidos para evitar problemas de ren msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} cambió %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Una colección de gráficos sobre Integración Continua" @@ -93,7 +99,7 @@ msgstr "Agregar nuevo directorio" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Adjunte un archivo arrastrando & soltando o %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Rama" @@ -194,6 +227,90 @@ msgstr "Cambiar rama" msgid "Branches" msgstr "Ramas" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Examinar directorio" @@ -337,9 +454,6 @@ msgstr "Enviado por" msgid "Compare" msgstr "Comparar" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "GuÃa de contribución" @@ -489,6 +603,9 @@ msgstr "Editar Programación del Pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Todos los meses (el dÃa 1 a las 4:00 am)" msgid "Every week (Sundays at 4:00am)" msgstr "Todas las semanas (domingos a las 4:00 am)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Error al cambiar el propietario" @@ -572,7 +692,28 @@ msgstr "Ir a tu bifurcación" msgid "GoToYourFork|Fork" msgstr "Bifurcación" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Inicio" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Servicio de limpieza iniciado con éxito" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Deshabilitado" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Propietario" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "con etapas" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "El enlace de exportación del proyecto ha caducado. Por favor, genera un msgid "Project export started. A download link will be sent by email." msgstr "Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico." -msgid "Project home" -msgstr "Inicio del proyecto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapa" msgid "ProjectNetworkGraph|Graph" msgstr "Historial gráfico" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Ramas" msgid "RefSwitcher|Tags" msgstr "Etiquetas" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Cambios Relacionados" @@ -1073,6 +1223,9 @@ msgstr "Guardar programación del pipeline" msgid "Schedule a new pipeline" msgstr "Programar un nuevo pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Programación de Pipelines" @@ -1112,6 +1265,12 @@ msgstr "establecer una contraseña" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Mostrando %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Mostrando %d eventos" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Código fuente" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Destacar" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Iniciar una %{new_merge_request} con estos cambios" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Cambiar rama/etiqueta" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Etiqueta" @@ -1206,6 +1467,9 @@ msgstr "El valor en el punto medio de una serie de valores observados. Por ejemp msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Esto significa que no puede enviar código hasta que cree un repositorio vacÃo o importe uno existente." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Utiliza tu configuración de notificación global" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Ver solicitud de fusión abierta" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "No podrás actualizar o enviar código al proyecto a través de SSH hast msgid "Your name" msgstr "Tu nombre" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "dÃa" diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po index c98156e026e..28d9c6a3e56 100644 --- a/locale/fr/gitlab.po +++ b/locale/fr/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: French\n" "Language: fr_FR\n" @@ -29,27 +29,33 @@ msgstr[1] "%s validations supplémentaires ont été masquées afin d'éviter de msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} a validé %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} sur %{maximum_failures} tentative(s). GitLab va vous permettre d'accéder à la prochaine tentative." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab va bloquer l’accès pendant %{number_of_seconds} secondes." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab ne va plus réessayer automatiquement. Réinitialisez les informations de stockage lorsque le problème est résolu." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%{storage_name} : la tentative d’accès au stockage a échouée sur l’hôte :" +msgstr[1] "%{storage_name} : %{failed_attempts} tentatives d’accès au stockage ont échouées :" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(Lisez %{link} pour savoir comment l'installer)." msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "1 pipeline" +msgstr[1] "%d pipelines" + +msgid "1st contribution!" +msgstr "" msgid "A collection of graphs regarding Continuous Integration" msgstr "Un ensemble de graphiques concernant l’Intégration Continue (CI)" @@ -58,16 +64,16 @@ msgid "About auto deploy" msgstr "A propos de l'auto-déploiement" msgid "Abuse Reports" -msgstr "" +msgstr "Rapports d’abus" msgid "Access Tokens" -msgstr "" +msgstr "Jetons d'Accès" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "L'accès aux stockages défaillants a été temporairement désactivé pour permettre au montage de récupérer. Réinitialiser les informations de stockage dès que le problème est résolu pour permettre l’accès à nouveau." msgid "Account" -msgstr "" +msgstr "Compte" msgid "Active" msgstr "Actif" @@ -91,13 +97,13 @@ msgid "Add new directory" msgstr "Ajouter un nouveau dossier" msgid "All" -msgstr "" +msgstr "Tous" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Applications" msgid "Archived project! Repository is read-only" msgstr "Projet archivé ! Le dépôt est en lecture seule" @@ -106,21 +112,45 @@ msgid "Are you sure you want to delete this pipeline schedule?" msgstr "Êtes-vous sûr de vouloir supprimer ce pipeline programmé" msgid "Are you sure you want to discard your changes?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir annuler vos modifications ?" msgid "Are you sure you want to reset registration token?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton d’inscription ?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton de bilan de santé ?" msgid "Are you sure?" +msgstr "Êtes-vous certain ?" + +msgid "Artifacts" msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Attachez un fichier par glisser & déposer ou %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,10 +207,13 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Branche" +msgstr[1] "Branches" msgid "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}" msgstr "La branche <strong>%{branch_name}</strong> a été crée. Pour mettre en place le déploiement automatisé, sélectionnez un modèle de fichier Yaml pour l'intégration continue (CI) de GitLab, et validez les modifications. %{link_to_autodeploy_doc}" @@ -192,6 +225,90 @@ msgid "BranchSwitcherTitle|Switch branch" msgstr "Changer de branche" msgid "Branches" +msgstr "Branches" + +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" msgstr "" msgid "Browse Directory" @@ -210,7 +327,7 @@ msgid "ByAuthor|by" msgstr "par" msgid "CI / CD" -msgstr "" +msgstr "Intégration continu / Déploiement continu" msgid "CI configuration" msgstr "Configuration de l'intégration continue (CI)" @@ -219,19 +336,19 @@ msgid "Cancel" msgstr "Annuler" msgid "Cancel edit" -msgstr "" +msgstr "Annuler modification" msgid "ChangeTypeActionLabel|Pick into branch" msgstr "Sélectionner dans la branche" msgid "ChangeTypeActionLabel|Revert in branch" -msgstr "Annuler dans la branche" +msgstr "Défaire dans la branche" msgid "ChangeTypeAction|Cherry-pick" msgstr "Sélectionner" msgid "ChangeTypeAction|Revert" -msgstr "Annuler" +msgstr "Défaire" msgid "Changelog" msgstr "Journal des modifications" @@ -240,7 +357,7 @@ msgid "Charts" msgstr "Graphiques" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" msgstr "Sélectionner cette validation" @@ -261,10 +378,10 @@ msgid "CiStatusLabel|manual action" msgstr "action manuelle" msgid "CiStatusLabel|passed" -msgstr "passé" +msgstr "réussi" msgid "CiStatusLabel|passed with warnings" -msgstr "passé avec des avertissements" +msgstr "réussi avec des avertissements" msgid "CiStatusLabel|pending" msgstr "en attente" @@ -279,7 +396,7 @@ msgid "CiStatusText|blocked" msgstr "bloqué" msgid "CiStatusText|canceled" -msgstr "annulé " +msgstr "annulé" msgid "CiStatusText|created" msgstr "créé" @@ -291,7 +408,7 @@ msgid "CiStatusText|manual" msgstr "manuel" msgid "CiStatusText|passed" -msgstr "passé" +msgstr "réussi" msgid "CiStatusText|pending" msgstr "en attente" @@ -303,7 +420,7 @@ msgid "CiStatus|running" msgstr "en cours" msgid "Comments" -msgstr "" +msgstr "Commentaires" msgid "Commit" msgid_plural "Commits" @@ -337,9 +454,6 @@ msgstr "Validé par" msgid "Compare" msgstr "Comparer" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guilde de contribution" @@ -359,7 +473,7 @@ msgid "Create New Directory" msgstr "Créer un nouveau dossier" msgid "Create a new branch" -msgstr "" +msgstr "Créer une nouvelle branche" msgid "Create a personal access token on your account to pull or push via %{protocol}." msgstr "Créer un jeton d’accès personnel pour votre compte afin de récupérer ou pousser par %{protocol}." @@ -436,19 +550,19 @@ msgstr[0] "Déploiement" msgstr[1] "Déploiements" msgid "Deploy Keys" -msgstr "" +msgstr "Clés de déploiement" msgid "Description" -msgstr "" +msgstr "Description" msgid "Details" -msgstr "" +msgstr "Détails" msgid "Directory name" msgstr "Nom du dossier" msgid "Discard changes" -msgstr "" +msgstr "Supprimer les modifications" msgid "Don't show again" msgstr "Ne plus montrer" @@ -487,25 +601,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Éditer le pipeline programmé %{id}" msgid "Emails" +msgstr "Courriels" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Aucun filtre" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Filtrer par commentaires" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Filtrer par événements d'incident" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Filtrer par événements de fusion" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "Filtrer par événements de poussée" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "Filtrer par équipe" msgid "Every day (at 4:00am)" msgstr "Chaque jour (à 4:00 du matin)" @@ -516,6 +633,9 @@ msgstr "Chaque mois (le 1er à 4:00 du matin)" msgid "Every week (Sundays at 4:00am)" msgstr "Chaque semaine (dimanche à 4:00 du matin)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Échec du changement de propriétaire" @@ -555,16 +675,16 @@ msgid "From merge request merge until deploy to production" msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en production" msgid "GPG Keys" -msgstr "" +msgstr "Clés GPG" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Les informations de santé du stockage Git ont été réinitialisées" msgid "GitLab Runner section" -msgstr "" +msgstr "Section de Runner GitLab" msgid "Go to your fork" msgstr "Aller à votre fourche" @@ -572,33 +692,48 @@ msgstr "Aller à votre fourche" msgid "GoToYourFork|Fork" msgstr "Fourche" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "Accueil" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Bilan de santé" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Des informations de santé peuvent être récupérées depuis les adresses suivantes. Plus d’informations" + +msgid "HealthCheck|Access token is" +msgstr "Le jeton d’accès est" + +msgid "HealthCheck|Healthy" +msgstr "En bonne santé" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Aucun problème détecté" + +msgid "HealthCheck|Unhealthy" +msgstr "En mauvaise santé" + msgid "Housekeeping successfully started" msgstr "Maintenance démarrée avec succès" @@ -606,7 +741,7 @@ msgid "Import repository" msgstr "Importer un dépôt" msgid "Install a Runner compatible with GitLab CI" -msgstr "" +msgstr "Installez un Runner compatible avec l'intégration continue de GitLab" msgid "Interval Pattern" msgstr "Schéma d’intervalle" @@ -615,9 +750,12 @@ msgid "Introducing Cycle Analytics" msgstr "Introduction à l'analyseur de cycle" msgid "Issue events" -msgstr "" +msgstr "Événements de l'incident" msgid "Issues" +msgstr "Incidents" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -644,10 +782,10 @@ msgid "Last commit" msgstr "Dernière validation" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Vous avez poussé sur" msgid "LastPushEvent|at" -msgstr "" +msgstr "à " msgid "Learn more in the" msgstr "En apprendre plus dans le" @@ -676,25 +814,28 @@ msgid "Median" msgstr "Médian" msgid "Members" -msgstr "" +msgstr "Membres" msgid "Merge Requests" -msgstr "" +msgstr "Demandes de fusion" msgid "Merge events" +msgstr "Événements de fusion" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "Messages" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "ajouter une clef SSH" msgid "Monitoring" -msgstr "" +msgstr "Surveillance" msgid "More information is available|here" -msgstr "" +msgstr "ici" msgid "New Issue" msgid_plural "New Issues" @@ -795,7 +936,7 @@ msgid "NotificationLevel|Watch" msgstr "Surveillé" msgid "Notifications" -msgstr "" +msgstr "Notifications" msgid "OfSearchInADropdown|Filter" msgstr "Filtre" @@ -804,20 +945,32 @@ msgid "OpenedNDaysAgo|Opened" msgstr "Ouvert" msgid "Options" -msgstr "" +msgstr "Paramètres" msgid "Overview" -msgstr "" +msgstr "Vue d'ensemble" msgid "Owner" msgstr "Propriétaire" -msgid "Password" +msgid "Pagination|Last »" msgstr "" -msgid "Pipeline" +msgid "Pagination|Next" msgstr "" +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Mot de Passe" + +msgid "Pipeline" +msgstr "Pipeline" + msgid "Pipeline Health" msgstr "Santé du Pipeline" @@ -888,19 +1041,19 @@ msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Personnalisé" msgid "Pipelines" -msgstr "" +msgstr "Pipelines" msgid "Pipelines charts" msgstr "Graphique des pipelines" msgid "Pipelines for last month" -msgstr "" +msgstr "Pipelines pour le dernier mois" msgid "Pipelines for last week" -msgstr "" +msgstr "Pipelines pour la dernière semaine" msgid "Pipelines for last year" -msgstr "" +msgstr "Pipelines pour la dernière année" msgid "Pipeline|all" msgstr "Tous" @@ -915,12 +1068,9 @@ msgid "Pipeline|with stages" msgstr "avec les étapes" msgid "Preferences" -msgstr "" - -msgid "Profile Settings" -msgstr "" +msgstr "Préférences" -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -939,7 +1089,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "L’accès au projet doit être explicitement accordé à chaque utilisateur." msgid "Project details" -msgstr "" +msgstr "Détails du projet" msgid "Project export could not be deleted." msgstr "L'export du projet n'a pas pu être supprimé." @@ -953,14 +1103,8 @@ msgstr "Le lien de l’export du projet a expiré. Merci de générer un nouvel msgid "Project export started. A download link will be sent by email." msgstr "L'export du projet a débuté. Un lien de téléchargement sera envoyé par courriel." -msgid "Project home" -msgstr "Accueil du projet" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "S’abonner" msgid "ProjectFeature|Disabled" msgstr "Désactivé" @@ -983,29 +1127,32 @@ msgstr "Étape" msgid "ProjectNetworkGraph|Graph" msgstr "Graphique " -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Chargement des projets" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Les projets que vous visitez souvent apparaîtront ici" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "Chercher dans vos projets" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "Désolé, aucun projet ne correspond à votre recherche" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Cette fonctionnalité requiert le support du localStorage par votre navigateur" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Évènements de poussée" msgid "Read more" msgstr "Lire plus" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Étiquettes" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Validations liés" @@ -1044,19 +1194,19 @@ msgid "Remove project" msgstr "Supprimer le projet" msgid "Repository" -msgstr "" +msgstr "Dépôt" msgid "Request Access" msgstr "Demander l'accès" msgid "Reset git storage health information" -msgstr "" +msgstr "Réinitialiser les informations de santé du stockage Git" msgid "Reset health check access token" -msgstr "" +msgstr "Réinitialiser le jeton d’accès au bilan de santé" msgid "Reset runners registration token" -msgstr "" +msgstr "Réinitialiser le jeton d’inscription des Runners" msgid "Revert this commit" msgstr "Annuler cette validation" @@ -1065,7 +1215,7 @@ msgid "Revert this merge request" msgstr "Annuler cette demande de fusion" msgid "SSH Keys" -msgstr "" +msgstr "Clés SSH" msgid "Save pipeline schedule" msgstr "Sauvegarder le pipeline programmé" @@ -1073,6 +1223,9 @@ msgstr "Sauvegarder le pipeline programmé" msgid "Schedule a new pipeline" msgstr "Programmer un nouveau pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Programmer des pipelines" @@ -1086,13 +1239,13 @@ msgid "Select a timezone" msgstr "Sélectionnez un fuseau horaire" msgid "Select existing branch" -msgstr "" +msgstr "Sélectionnez une branche existante" msgid "Select target branch" msgstr "Sélectionnez une branche cible" msgid "Service Templates" -msgstr "" +msgstr "Modèles de service" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser par %{protocol}." @@ -1110,6 +1263,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "définir un mot de passe" msgid "Settings" +msgstr "Paramètres" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1118,29 +1277,131 @@ msgstr[0] "Affichage de %d évènement" msgstr[1] "Affichage de %d évènements" msgid "Snippets" +msgstr "Extraits de code" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "Code source" msgid "Spam Logs" -msgstr "" +msgstr "Journaux des messages indésirables" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Spécifiez l’URL suivante lors de la configuration du Runner :" msgid "StarProject|Star" msgstr "S'abonner" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Créer une %{new_merge_request} avec ces changements" msgid "Start the Runner!" -msgstr "" +msgstr "Démarrer le Runner !" msgid "Switch branch/tag" msgstr "Changer de branche / d'étiquette" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Étiquette" @@ -1153,7 +1414,7 @@ msgid "Target Branch" msgstr "Branche cible" msgid "Team" -msgstr "" +msgstr "Équipe" msgid "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." msgstr "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." @@ -1204,6 +1465,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "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." msgid "There are problems accessing Git storage: " +msgstr "Il y a des difficultés à accéder aux données Git : " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1376,14 +1640,20 @@ msgid "UploadLink|click to upload" msgstr "Cliquez pour envoyer" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :" msgid "Use your global notification setting" msgstr "Utiliser vos paramètres de notification globaux" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Afficher la demande de fusion" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interne" @@ -1403,7 +1673,7 @@ msgid "We don't have enough data to show this stage." msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "Retirer la demande d'accès" @@ -1456,6 +1726,12 @@ msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous msgid "Your name" msgstr "Votre nom" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "jour" @@ -1469,6 +1745,6 @@ msgstr "courriels de notification" msgid "parent" msgid_plural "parents" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "parent" +msgstr[1] "parents" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bc476a706cb..53e37c53377 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-21 14:20+0530\n" -"PO-Revision-Date: 2017-09-21 14:20+0530\n" +"POT-Creation-Date: 2017-09-28 13:28-0400\n" +"PO-Revision-Date: 2017-09-28 13:28-0400\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" @@ -31,6 +31,9 @@ msgstr[1] "" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -131,28 +134,31 @@ msgstr "" msgid "Authentication Log" msgstr "" -msgid "Auto DevOps (Beta)" +msgid "Author" msgstr "" -msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." msgstr "" -msgid "Auto DevOps documentation" +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." msgstr "" -msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." msgstr "" -msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgid "AutoDevOps|Auto DevOps (Beta)" msgstr "" -msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgid "AutoDevOps|Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "AutoDevOps|Auto DevOps documentation" msgstr "" -msgid "AutoDevOps|Learn more in the" +msgid "AutoDevOps|Enable in settings" msgstr "" -msgid "Board" +msgid "AutoDevOps|Learn more in the %{link_to_documentation}" msgstr "" msgid "Branch" @@ -172,12 +178,81 @@ msgstr "" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + msgid "Branches|This branch hasn’t been merged into %{default_branch}." msgstr "" msgid "Branches|To avoid data loss, consider merging this branch before deleting it." msgstr "" +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "" @@ -199,6 +274,9 @@ msgstr "" msgid "CI configuration" msgstr "" +msgid "CICD|Jobs" +msgstr "" + msgid "Cancel" msgstr "" @@ -286,6 +364,9 @@ msgstr "" msgid "CiStatus|running" msgstr "" +msgid "Clone repository" +msgstr "" + msgid "Comments" msgstr "" @@ -294,6 +375,9 @@ msgid_plural "Commits" msgstr[0] "" msgstr[1] "" +msgid "Commit Message" +msgstr "" + msgid "Commit duration in minutes for last 30 commits" msgstr "" @@ -402,6 +486,12 @@ msgstr "" msgid "CycleAnalyticsStage|Test" msgstr "" +msgid "DashboardProjects|All" +msgstr "" + +msgid "DashboardProjects|Personal" +msgstr "" + msgid "Define a custom pattern with cron syntax" msgstr "" @@ -467,9 +557,6 @@ msgstr "" msgid "Emails" msgstr "" -msgid "Enable in settings" -msgstr "" - msgid "EventFilterBy|Filter by all" msgstr "" @@ -532,6 +619,9 @@ msgstr[1] "" msgid "ForkedFromProjectPath|Forked from" msgstr "" +msgid "Format" +msgstr "" + msgid "From issue creation until deploy to production" msgstr "" @@ -568,7 +658,7 @@ msgstr "" msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner." +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" @@ -595,7 +685,7 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" +msgid "History" msgstr "" msgid "Housekeeping successfully started" @@ -616,10 +706,10 @@ msgstr "" msgid "Issue events" msgstr "" -msgid "Issues" +msgid "IssueBoards|Board" msgstr "" -msgid "Jobs" +msgid "Issues" msgstr "" msgid "LFSStatus|Disabled" @@ -645,6 +735,15 @@ msgstr "" msgid "Last commit" msgstr "" +msgid "Last edited %{date}" +msgstr "" + +msgid "Last edited by %{name}" +msgstr "" + +msgid "Last updated" +msgstr "" + msgid "LastPushEvent|You pushed to" msgstr "" @@ -680,6 +779,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -955,9 +1057,6 @@ msgstr "" msgid "Project export started. A download link will be sent by email." msgstr "" -msgid "Project home" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -1066,6 +1165,9 @@ msgstr "" msgid "SSH Keys" msgstr "" +msgid "Save changes" +msgstr "" + msgid "Save pipeline schedule" msgstr "" @@ -1128,6 +1230,93 @@ msgstr[1] "" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + msgid "Source code" msgstr "" @@ -1220,7 +1409,7 @@ msgstr "" msgid "There are problems accessing Git storage: " msgstr "" -msgid "This is the author's first Merge Request to this project. Handle with care." +msgid "This is the author's first Merge Request to this project." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1398,9 +1587,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "" @@ -1422,6 +1617,105 @@ msgstr "" msgid "Wiki" msgstr "" +msgid "WikiClone|Clone your wiki" +msgstr "" + +msgid "WikiClone|Git Access" +msgstr "" + +msgid "WikiClone|Install Gollum" +msgstr "" + +msgid "WikiClone|It is recommended to install %{markdown} so that GFM features render locally:" +msgstr "" + +msgid "WikiClone|Start Gollum and edit locally" +msgstr "" + +msgid "WikiEmptyPageError|You are not allowed to create wiki pages" +msgstr "" + +msgid "WikiHistoricalPage|This is an old version of this page." +msgstr "" + +msgid "WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}." +msgstr "" + +msgid "WikiHistoricalPage|history" +msgstr "" + +msgid "WikiHistoricalPage|most recent version" +msgstr "" + +msgid "WikiMarkdownDocs|More examples are in the %{docs_link}" +msgstr "" + +msgid "WikiMarkdownDocs|documentation" +msgstr "" + +msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}" +msgstr "" + +msgid "WikiNewPagePlaceholder|how-to-setup" +msgstr "" + +msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories." +msgstr "" + +msgid "WikiNewPageTitle|New Wiki Page" +msgstr "" + +msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?" +msgstr "" + +msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs." +msgstr "" + +msgid "WikiPageConflictMessage|the page" +msgstr "" + +msgid "WikiPageCreate|Create %{page_title}" +msgstr "" + +msgid "WikiPageEdit|Update %{page_title}" +msgstr "" + +msgid "WikiPage|Page slug" +msgstr "" + +msgid "WikiPage|Write your content or drag files here..." +msgstr "" + +msgid "Wiki|Create Page" +msgstr "" + +msgid "Wiki|Create page" +msgstr "" + +msgid "Wiki|Edit Page" +msgstr "" + +msgid "Wiki|Empty page" +msgstr "" + +msgid "Wiki|More Pages" +msgstr "" + +msgid "Wiki|New page" +msgstr "" + +msgid "Wiki|Page history" +msgstr "" + +msgid "Wiki|Page version" +msgstr "" + +msgid "Wiki|Pages" +msgstr "" + +msgid "Wiki|Wiki Pages" +msgstr "" + msgid "Withdraw Access Request" msgstr "" diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po index 0249c4fe9eb..804817e96e9 100644 --- a/locale/it/gitlab.po +++ b/locale/it/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Italian\n" "Language: it_IT\n" @@ -29,6 +29,9 @@ msgstr[1] "%s commit aggiuntivi sono stati omessi per evitare degradi di prestaz msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} ha committato %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Un insieme di grafici riguardo la Continuous Integration" @@ -93,7 +99,7 @@ msgstr "Aggiungi una directory (cartella)" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Aggiungi un file tramite trascina & rilascia ( drag & drop) o %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -194,6 +227,90 @@ msgstr "Cambia branch" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Naviga direttori" @@ -337,9 +454,6 @@ msgstr "Committato da " msgid "Compare" msgstr "Confronta" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guida per contribuire" @@ -489,6 +603,9 @@ msgstr "Cambia programmazione della pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ogni primo giorno del mese (alle 4 del mattino)" msgid "Every week (Sundays at 4:00am)" msgstr "Ogni settimana (Di domenica alle 4 del mattino)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Impossibile cambiare owner" @@ -572,7 +692,28 @@ msgstr "Vai il tuo fork" msgid "GoToYourFork|Fork" msgstr "Fork" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Housekeeping iniziato con successo" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Disabilitato" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "con più stadi" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "Il link d'esportazione del progetto è scaduto. Genera una nuova esporta msgid "Project export started. A download link will be sent by email." msgstr "Esportazione del progetto iniziata. Un link di download sarà inviato via email." -msgid "Project home" -msgstr "Home di progetto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Stadio" msgid "ProjectNetworkGraph|Graph" msgstr "Grafico" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Commit correlati" @@ -1073,6 +1223,9 @@ msgstr "Salva pianificazione pipeline" msgid "Schedule a new pipeline" msgstr "Pianifica una nuova Pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Pianificazione pipelines" @@ -1112,6 +1265,12 @@ msgstr "imposta una password" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Visualizza %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Visualizza %d eventi" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Codice Sorgente" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Star" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "inizia una %{new_merge_request} con queste modifiche" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Cambia branch/tag" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5, msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Questo significa che non è possibile effettuare push di codice fino a che non crei una repository vuota o ne importi una esistente" @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Usa le tue impostazioni globali " +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Mostra la richieste di merge aperte" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "Non sarai in grado di effettuare push o pull tramite SSH fino a che %{ad msgid "Your name" msgstr "Il tuo nome" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "giorno" diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po index c66dd3c1b6b..2a08abda7ce 100644 --- a/locale/ja/gitlab.po +++ b/locale/ja/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Japanese\n" "Language: ja_JP\n" @@ -27,6 +27,9 @@ msgstr[0] "パフォーマンス低下をé¿ã‘ã‚‹ãŸã‚ %s 個ã®ã‚³ãƒŸãƒƒãƒˆã‚ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_timeago}ã«%{commit_author_link}ãŒã‚³ãƒŸãƒƒãƒˆã—ã¾ã—ãŸã€‚" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d 個ã®ãƒ‘イプライン" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•" @@ -89,7 +95,7 @@ msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’è¿½åŠ " msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "ドラッグ&ドãƒãƒƒãƒ—ã¾ãŸã¯ %{upload_link} ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’添付" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "ブランãƒ" @@ -189,6 +222,90 @@ msgstr "ブランãƒã‚’切替" msgid "Branches" msgstr "ブランãƒ" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ディレクトリを表示" @@ -331,9 +448,6 @@ msgstr "コミット担当者: " msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "貢献者å‘ã‘ガイド" @@ -482,6 +596,9 @@ msgstr "パイプラインスケジュール %{id} を編集" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -509,6 +626,9 @@ msgstr "毎月 (1æ—¥ã®åˆå‰4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "毎週 (日曜日ã®åˆå‰4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "オーナーを変更ã§ãã¾ã›ã‚“ã§ã—ãŸ" @@ -564,7 +684,28 @@ msgstr "自分ã®ãƒ•ã‚©ãƒ¼ã‚¯ã¸ç§»å‹•" msgid "GoToYourFork|Fork" msgstr "フォーク" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "ホーム" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "ãƒã‚¦ã‚¹ã‚ーピングã¯æ£å¸¸ã«èµ·å‹•ã—ã¾ã—ãŸã€‚" @@ -612,6 +747,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "無効" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "オーナー" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,10 +1059,7 @@ msgstr "ステージã‚ã‚Š" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -942,12 +1092,6 @@ msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒªãƒ³ã‚¯ã¯æœŸé™åˆ‡ã‚Œã«ãªã‚Š msgid "Project export started. A download link will be sent by email." msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウンãƒãƒ¼ãƒ‰ã®ãƒªãƒ³ã‚¯ã¯ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã—ã¾ã™" -msgid "Project home" -msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ›ãƒ¼ãƒ " - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -972,27 +1116,30 @@ msgstr "ステージ" msgid "ProjectNetworkGraph|Graph" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1008,6 +1155,9 @@ msgstr "ブランãƒ" msgid "RefSwitcher|Tags" msgstr "ã‚¿ã‚°" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "関連ã™ã‚‹ã‚³ãƒŸãƒƒãƒˆ" @@ -1062,6 +1212,9 @@ msgstr "パイプラインスケジュールをä¿å˜" msgid "Schedule a new pipeline" msgstr "æ–°ã—ã„パイプラインã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’作æˆ" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "パイプラインスケジューリング" @@ -1101,6 +1254,12 @@ msgstr "パスワードをè¨å®š" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示ä¸" @@ -1108,6 +1267,102 @@ msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示ä¸" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "ソースコード" @@ -1120,6 +1375,9 @@ msgstr "" msgid "StarProject|Star" msgstr "スターを付ã‘ã‚‹" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ã“ã®å¤‰æ›´ã§ %{new_merge_request} を作æˆã™ã‚‹" @@ -1129,6 +1387,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "ã‚¿ã‚°" @@ -1193,6 +1454,9 @@ msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸å¤® msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "空レãƒã‚¸ãƒˆãƒªãƒ¼ã‚’作æˆã¾ãŸã¯æ—¢å˜ãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã‚’インãƒãƒ¼ãƒˆã‚’ã—ãªã‘ã‚Œã°ã€ã‚³ãƒ¼ãƒ‰ã®ãƒ—ッシュã¯ã§ãã¾ã›ã‚“。" @@ -1366,9 +1630,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "全体通知è¨å®šã‚’利用" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "オープンãªãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "内部" @@ -1441,6 +1711,12 @@ msgstr "%{add_ssh_key_link} をプãƒãƒ•ã‚¡ã‚¤ãƒ«ã«è¿½åŠ ã—ã¦ã„ãªã„ã®ã§ã msgid "Your name" msgstr "åå‰" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "æ—¥" diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po index bbf4aa15cd7..de4a13d3765 100644 --- a/locale/ko/gitlab.po +++ b/locale/ko/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Korean\n" "Language: ko_KR\n" @@ -27,6 +27,9 @@ msgstr[0] "%s 추가 ì»¤ë°‹ì€ ì„±ëŠ¥ ì´ìŠˆë¥¼ 방지하기 위해 ìƒëžµë˜ì—ˆ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_timeago} ì— %{commit_author_link} ë‹˜ì´ ì»¤ë°‹í•˜ì˜€ìŠµë‹ˆë‹¤. " +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ë‹¤ìŒ ì‹œë„ì—ì„œ 성공하면 ì ‘ê·¼ì„ í—ˆìš©í• ê²ƒìž…ë‹ˆë‹¤." @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d 파ì´í”„ë¼ì¸" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "지ì†ì ì¸ í†µí•©ì— ê´€í•œ 그래프 모ìŒ" @@ -89,7 +95,7 @@ msgstr "새 ë””ë ‰í† ë¦¬ 추가" msgid "All" msgstr "ì „ì²´" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "헬스 ì²´í¬ í† í°ì„ 초기화 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" msgid "Are you sure?" msgstr "확실합니까?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "드래그 & ë“œë¡ ë˜ëŠ” %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "브랜치" @@ -189,6 +222,90 @@ msgstr "브랜치 변경" msgid "Branches" msgstr "브랜치" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ë””ë ‰í† ë¦¬ 찾아보기" @@ -331,9 +448,6 @@ msgstr "커밋한 사용ìž" msgid "Compare" msgstr "비êµ" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "ê¸°ì—¬ì— ëŒ€í•œ 안내" @@ -482,6 +596,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케줄 편집 %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "ëª¨ë“ ê°’ì„ ê¸°ì¤€ìœ¼ë¡œ í•„í„°" @@ -509,6 +626,9 @@ msgstr "매월 (1ì¼ ì˜¤ì „ 4ì‹œ)" msgid "Every week (Sundays at 4:00am)" msgstr "매주 (ì¼ìš”ì¼ ì˜¤ì „ 4ì‹œì—)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "ì†Œìœ ìžë¥¼ 변경하지 못했습니다" @@ -564,7 +684,28 @@ msgstr "ë‹¹ì‹ ì˜ í¬í¬ë¡œ ì´ë™í•˜ì„¸ìš”" msgid "GoToYourFork|Fork" msgstr "í¬í¬" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr " 헬스 ë¬¸ì œê°€ 발견ë˜ì§€ 않았습니다." msgid "HealthCheck|Unhealthy" msgstr "ë¹„ì •ìƒ" -msgid "Home" -msgstr "홈" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Housekeepingì´ ì„±ê³µì 으로 시작ë˜ì—ˆìŠµë‹ˆë‹¤" @@ -612,6 +747,9 @@ msgstr "ì´ìŠˆ ì´ë²¤íŠ¸" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Disabled" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "머지 ì´ë²¤íŠ¸" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "ì†Œìœ ìž" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,10 +1059,7 @@ msgstr "스테ì´ì§•" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -942,12 +1092,6 @@ msgstr "프로ì 트 내보내기 ë§í¬ê°€ 만료ë˜ì—ˆìŠµë‹ˆë‹¤. 프로ì 트 msgid "Project export started. A download link will be sent by email." msgstr "프로ì 트 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ëŠ” ì´ë©”ì¼ë¡œ ì „ì†¡ë©ë‹ˆë‹¤." -msgid "Project home" -msgstr "프로ì 트 홈" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "구ë…" @@ -972,27 +1116,30 @@ msgstr "스테ì´ì§•" msgid "ProjectNetworkGraph|Graph" msgstr "그래프" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "푸쉬 ì´ë²¤íŠ¸" @@ -1008,6 +1155,9 @@ msgstr "브랜치" msgid "RefSwitcher|Tags" msgstr "태그" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "ê´€ë ¨ 커밋" @@ -1062,6 +1212,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케줄 ì €ìž¥" msgid "Schedule a new pipeline" msgstr "새로운 파ì´í”„ë¼ì¸ 스케줄 잡기" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "파ì´í”„ë¼ì¸ 스케줄ë§" @@ -1101,6 +1254,12 @@ msgstr "패스워드 ì„¤ì •" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중" @@ -1108,6 +1267,102 @@ msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "소스 코드" @@ -1120,6 +1375,9 @@ msgstr "Runner ì„¤ì • 중 ë‹¤ìŒ URLì„ ì§€ì •í•˜ì„¸ìš”." msgid "StarProject|Star" msgstr "별표" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ì´ ë³€ê²½ 사í•ìœ¼ë¡œ %{new_merge_request} ì„ ì‹œìž‘í•˜ì‹ì‹œì˜¤." @@ -1129,6 +1387,9 @@ msgstr "Runner 시작!" msgid "Switch branch/tag" msgstr "스위치 브랜치/태그" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "태그" @@ -1193,6 +1454,9 @@ msgstr "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— ìžˆìŠµë‹ˆë‹¤. 예를 들어, 3, 5, msgid "There are problems accessing Git storage: " msgstr "git storageì— ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. " +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "즉, 빈 ì €ìž¥ì†Œë¥¼ 만들거나 기존 ì €ìž¥ì†Œë¥¼ ê°€ì ¸ì˜¬ 때까지 코드를 Push í• ìˆ˜ 없습니다." @@ -1366,9 +1630,15 @@ msgstr "ì„¤ì • ì¤‘ì— ë‹¤ìŒ ë“±ë¡ í† í° ì´ìš© : " msgid "Use your global notification setting" msgstr "ì „ì²´ 알림 ì„¤ì • 사용" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "열린 머지 리퀘스트보기" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "내부" @@ -1441,6 +1711,12 @@ msgstr "ë‹¹ì‹ ì˜ í”„ë¡œí•„ì— %{add_ssh_key_link} 를 하기 ì „ì—는 SSH를 í msgid "Your name" msgstr "ê·€í•˜ì˜ ì´ë¦„" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "ì¼" diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po index 250d3bd413c..45a444fac43 100644 --- a/locale/nl_NL/gitlab.po +++ b/locale/nl_NL/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Dutch\n" "Language: nl_NL\n" @@ -18,17 +18,20 @@ msgstr "" msgid "%d commit" msgid_plural "%d commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%d commit" +msgstr[1] "%d commits" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%s andere commit is weggelaten om prestatieproblemen te voorkomen." +msgstr[1] "%s andere commits zijn weggelaten om prestatieproblemen te voorkomen." msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -44,60 +47,63 @@ msgstr[0] "" msgstr[1] "" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(bekijk de %{link} voor meer info over hoe je het kan installeren)." msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "" msgid "About auto deploy" -msgstr "" +msgstr "Over auto deploy" msgid "Abuse Reports" -msgstr "" +msgstr "Misbruik rapporten" msgid "Access Tokens" -msgstr "" +msgstr "Toegangstokens" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." msgstr "" msgid "Account" -msgstr "" +msgstr "Account" msgid "Active" -msgstr "" +msgstr "Actief" msgid "Activity" -msgstr "" +msgstr "Activiteit" msgid "Add Changelog" -msgstr "" +msgstr "Changelog toevoegen" msgid "Add Contribution guide" msgstr "" msgid "Add License" -msgstr "" +msgstr "Licentie toevoegen" msgid "Add an SSH key to your profile to pull or push via SSH." msgstr "" msgid "Add new directory" -msgstr "" +msgstr "Nieuwe map toevoegen" msgid "All" -msgstr "" +msgstr "Alles" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Applicaties" msgid "Archived project! Repository is read-only" msgstr "" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -192,35 +225,119 @@ msgid "BranchSwitcherTitle|Switch branch" msgstr "" msgid "Branches" +msgstr "Branches" + +msgid "Branches|Cant find HEAD commit for this branch" msgstr "" -msgid "Browse Directory" +msgid "Branches|Compare" msgstr "" -msgid "Browse File" +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" msgstr "" -msgid "Browse Files" +msgid "Branches|Delete branch" msgstr "" -msgid "Browse files" +msgid "Branches|Delete merged branches" msgstr "" -msgid "ByAuthor|by" +msgid "Branches|Delete protected branch" msgstr "" -msgid "CI / CD" +msgid "Branches|Delete protected branch '%{branch_name}'?" msgstr "" -msgid "CI configuration" +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" msgstr "" -msgid "Cancel" +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" msgstr "" -msgid "Cancel edit" +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" msgstr "" +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + +msgid "Browse Directory" +msgstr "Bladeren in map" + +msgid "Browse File" +msgstr "Bekijk bestand" + +msgid "Browse Files" +msgstr "Door bestanden bladeren" + +msgid "Browse files" +msgstr "Door bestanden bladeren" + +msgid "ByAuthor|by" +msgstr "door" + +msgid "CI / CD" +msgstr "CI / CD" + +msgid "CI configuration" +msgstr "CI Configuratie" + +msgid "Cancel" +msgstr "Annuleren" + +msgid "Cancel edit" +msgstr "Bewerken annuleren" + msgid "ChangeTypeActionLabel|Pick into branch" msgstr "" @@ -237,25 +354,25 @@ msgid "Changelog" msgstr "" msgid "Charts" -msgstr "" +msgstr "Grafieken" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" -msgstr "" +msgstr "Cherry-pick deze commit" msgid "Cherry-pick this merge request" msgstr "" msgid "CiStatusLabel|canceled" -msgstr "" +msgstr "geannuleerd" msgid "CiStatusLabel|created" -msgstr "" +msgstr "gemaakt" msgid "CiStatusLabel|failed" -msgstr "" +msgstr "mislukt" msgid "CiStatusLabel|manual action" msgstr "" @@ -270,40 +387,40 @@ msgid "CiStatusLabel|pending" msgstr "" msgid "CiStatusLabel|skipped" -msgstr "" +msgstr "overgeslagen" msgid "CiStatusLabel|waiting for manual action" msgstr "" msgid "CiStatusText|blocked" -msgstr "" +msgstr "geblokkeerd" msgid "CiStatusText|canceled" msgstr "" msgid "CiStatusText|created" -msgstr "" +msgstr "gemaakt" msgid "CiStatusText|failed" msgstr "" msgid "CiStatusText|manual" -msgstr "" +msgstr "handmatig" msgid "CiStatusText|passed" -msgstr "" +msgstr "geslaagd" msgid "CiStatusText|pending" msgstr "" msgid "CiStatusText|skipped" -msgstr "" +msgstr "overgeslagen" msgid "CiStatus|running" msgstr "" msgid "Comments" -msgstr "" +msgstr "Opmerkingen" msgid "Commit" msgid_plural "Commits" @@ -317,28 +434,25 @@ msgid "Commit message" msgstr "" msgid "CommitBoxTitle|Commit" -msgstr "" +msgstr "Commit" msgid "CommitMessage|Add %{file_name}" -msgstr "" +msgstr "%{file_name} toevoegen" msgid "Commits" -msgstr "" +msgstr "Commits" msgid "Commits feed" msgstr "" msgid "Commits|History" -msgstr "" +msgstr "Geschiedenis" msgid "Committed by" -msgstr "" +msgstr "Gecommit door" msgid "Compare" -msgstr "" - -msgid "Container Registry" -msgstr "" +msgstr "Vergelijk" msgid "Contribution guide" msgstr "" @@ -365,7 +479,7 @@ msgid "Create a personal access token on your account to pull or push via %{prot msgstr "" msgid "Create directory" -msgstr "" +msgstr "Maak map aan" msgid "Create empty bare repository" msgstr "" @@ -489,6 +603,9 @@ msgstr "" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "" msgid "Every week (Sundays at 4:00am)" msgstr "" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "" @@ -572,7 +692,28 @@ msgstr "" msgid "GoToYourFork|Fork" msgstr "" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -698,8 +839,8 @@ msgstr "" msgid "New Issue" msgid_plural "New Issues" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Nieuwe issue" +msgstr[1] "Nieuwe issues" msgid "New Pipeline Schedule" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "" msgid "Project export started. A download link will be sent by email." msgstr "" -msgid "Project home" -msgstr "" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "" msgid "ProjectNetworkGraph|Graph" msgstr "" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "" msgid "RefSwitcher|Tags" msgstr "" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "" @@ -1073,6 +1223,9 @@ msgstr "" msgid "Schedule a new pipeline" msgstr "" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "" @@ -1112,6 +1265,12 @@ msgstr "" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "" @@ -1120,6 +1279,102 @@ msgstr[1] "" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "" msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "" @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "" @@ -1456,6 +1726,12 @@ msgstr "" msgid "Your name" msgstr "" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "" diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index 5469f77d950..318c719c2ed 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:18-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:42-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" @@ -29,6 +29,9 @@ msgstr[1] "%s commits adicionais foram omitidos para prevenir problemas de perfo msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} fez commit %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Uma coleção de gráficos sobre Integração ContÃnua" @@ -93,7 +99,7 @@ msgstr "Adicionar novo diretório" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Para anexar arquivo, arraste e solte ou %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -194,6 +227,90 @@ msgstr "Mudar de branch" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Navegar no Diretório" @@ -337,9 +454,6 @@ msgstr "Commit feito por" msgid "Compare" msgstr "Comparar" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guia de contribuição" @@ -489,6 +603,9 @@ msgstr "Alterar Agendamento do Pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Todos os meses (no dia primeiro à s 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Toda semana (domingos à s 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Erro ao alterar o proprietário" @@ -572,7 +692,28 @@ msgstr "Ir para seu fork" msgid "GoToYourFork|Fork" msgstr "Fork" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "InÃcio" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Manutenção iniciada com sucesso" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Desabilitado" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Proprietário" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "com etapas" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "O link para a exportação do projeto expirou. Favor gerar uma nova expo msgid "Project export started. A download link will be sent by email." msgstr "Exportação do projeto iniciada. Um link para baixá-la será enviado por email." -msgid "Project home" -msgstr "Página inicial do projeto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapa" msgid "ProjectNetworkGraph|Graph" msgstr "Ãrvore" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Commits Relacionados" @@ -1073,6 +1223,9 @@ msgstr "Salvar agendamento da pipeline" msgid "Schedule a new pipeline" msgstr "Agendar nova pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Agendando pipelines" @@ -1112,6 +1265,12 @@ msgstr "defina uma senha" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Mostrando %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Mostrando %d eventos" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Código-fonte" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Marcar" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Iniciar um %{new_merge_request} a partir dessas alterações" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Trocar branch/tag" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "O valor situado no ponto médio de uma série de valores observados. Ex. msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Isto significa que você não pode entregar código até que crie um repositório vazio ou importe um existente." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Utilizar configuração de notificação global" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Ver merge request aberto" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "Você não conseguirá fazer pull ou push no projeto via SSH até que a msgid "Your name" msgstr "Seu nome" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "dia" diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po index 808bc9dedce..507dc187cdb 100644 --- a/locale/ru/gitlab.po +++ b/locale/ru/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Russian\n" "Language: ru_RU\n" @@ -31,23 +31,26 @@ msgstr[2] "%s добавленные коммиты были иÑключены msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} коммичено %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} из %{maximum_failures} возможных попыток. Ð’Ñ‹ можете попытатьÑÑ ÐµÑ‰Ðµ раз." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab заблокирует доÑтуп на %{number_of_seconds} Ñекунд." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab не будет автоматичеÑки повторÑÑ‚ÑŒ попытку. СброÑьте информацию хранилища поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%{storage_name}: Ð½ÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ° доÑтупа к хранилищу на хоÑте:" +msgstr[1] "%{storage_name}: %{failed_attempts} - неудачные попытки доÑтупа к хранилищу:" +msgstr[2] "%{storage_name}: %{failed_attempts} - неудачные попытки доÑтупа к хранилищу:" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(перейдите по ÑÑылке %{link} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об уÑтановке)." msgid "1 pipeline" msgid_plural "%d pipelines" @@ -55,23 +58,26 @@ msgstr[0] "1 конвейер" msgstr[1] "%d конвейеры" msgstr[2] "%d конвейеры" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Графики отноÑительно непрерывной интеграции" msgid "About auto deploy" -msgstr "ÐвтоматичеÑкое развертывание" +msgstr "Об автоматичеÑком развёртывании" msgid "Abuse Reports" -msgstr "" +msgstr "Отчёты о Жалобах" msgid "Access Tokens" -msgstr "" +msgstr "Токены ДоÑтупа" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "ДоÑтуп к вышедшим из ÑÑ‚Ñ€Ð¾Ñ Ñ…Ñ€Ð°Ð½Ð¸Ð»Ð¸Ñ‰Ð°Ð¼ временно отключен Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñти Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² целÑÑ… воÑÑтановлениÑ. СброÑьте информацию о хранилищах поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹, чтобы разрешить доÑтуп." msgid "Account" -msgstr "" +msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ" msgid "Active" msgstr "Ðктивный" @@ -95,13 +101,13 @@ msgid "Add new directory" msgstr "Добавить каталог" msgid "All" -msgstr "" +msgstr "Ð’Ñе" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "ПриложениÑ" msgid "Archived project! Repository is read-only" msgstr "Ðрхивный проект! Репозиторий доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ" @@ -113,18 +119,42 @@ msgid "Are you sure you want to discard your changes?" msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите отменить Ваши изменениÑ?" msgid "Are you sure you want to reset registration token?" -msgstr "" +msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите ÑброÑить Ñтот ключ региÑтрации?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите ÑброÑить Ñтот ключ проверки работоÑпоÑобноÑти?" msgid "Are you sure?" msgstr "Ð’Ñ‹ уверены?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Приложить файл через drag & drop или %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -181,6 +211,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Ветка" @@ -199,6 +232,90 @@ msgstr "Переключить ветку" msgid "Branches" msgstr "Ветки" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Обзор" @@ -215,7 +332,7 @@ msgid "ByAuthor|by" msgstr "по автору" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "ÐаÑтройка CI" @@ -245,7 +362,7 @@ msgid "Charts" msgstr "Диаграммы" msgid "Chat" -msgstr "" +msgstr "Чат" msgid "Cherry-pick this commit" msgstr "Подобрать в Ñтом коммите" @@ -343,9 +460,6 @@ msgstr "ФикÑировано" msgid "Compare" msgstr "Сравнить" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "РуководÑтво учаÑтника" @@ -443,13 +557,13 @@ msgstr[1] "Размещение" msgstr[2] "Размещение" msgid "Deploy Keys" -msgstr "" +msgstr "Ключи РазвертываниÑ" msgid "Description" msgstr "ОпиÑание" msgid "Details" -msgstr "" +msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" msgid "Directory name" msgstr "Каталог" @@ -494,25 +608,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Изменить раÑпиÑание конвейера %{id}" msgid "Emails" +msgstr "Email-адреÑа" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Фильтр по вÑему" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Фильтр по комментарию" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм обÑуждений" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм ÑлиÑний" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм отправки" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "Фильтр по команде" msgid "Every day (at 4:00am)" msgstr "Ежедневно (в 4:00)" @@ -523,6 +640,9 @@ msgstr "ЕжемеÑÑчно (каждое 1-е чиÑло в 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Еженедельно (по воÑкреÑениÑми в 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ðе удалоÑÑŒ изменить владельца" @@ -563,13 +683,13 @@ msgid "From merge request merge until deploy to production" msgstr "От запроÑа на ÑлиÑние до Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² рабочей Ñреде" msgid "GPG Keys" -msgstr "" +msgstr "GPG Ключи" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ ÑтабильноÑти Git хранилища была Ñброшена" msgid "GitLab Runner section" msgstr "Ð¡ÐµÐºÑ†Ð¸Ñ Gitlab Runner" @@ -580,33 +700,48 @@ msgstr "Перейти к вашему форку" msgid "GoToYourFork|Fork" msgstr "Форк" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "ГлавнаÑ" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Проверка работоÑпоÑобноÑти" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ работоÑпоÑобноÑти может быть получена из Ñледующих точек подключениÑ. ДоÑтупна более Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" + +msgid "HealthCheck|Access token is" +msgstr "Ключ доÑтупа - " + +msgid "HealthCheck|Healthy" +msgstr "Стабильно" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Проблем работоÑпоÑобноÑти не обнаружено" + +msgid "HealthCheck|Unhealthy" +msgstr "ÐеÑтабильный" + msgid "Housekeeping successfully started" msgstr "ОчиÑтка уÑпешно запущена" @@ -623,9 +758,12 @@ msgid "Introducing Cycle Analytics" msgstr "Внедрение Цикла Ðналитик" msgid "Issue events" -msgstr "" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸" msgid "Issues" +msgstr "Задачи" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -635,7 +773,7 @@ msgid "LFSStatus|Enabled" msgstr "Включено" msgid "Labels" -msgstr "" +msgstr "Метки" msgid "Last %d day" msgid_plural "Last %d days" @@ -653,10 +791,10 @@ msgid "Last commit" msgstr "ПоÑледний коммит" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Ð’Ñ‹ отправили в" msgid "LastPushEvent|at" -msgstr "" +msgstr "в" msgid "Learn more in the" msgstr "Узнайте больше в" @@ -686,25 +824,28 @@ msgid "Median" msgstr "Среднее" msgid "Members" -msgstr "" +msgstr "УчаÑтники" msgid "Merge Requests" -msgstr "" +msgstr "ЗапроÑÑ‹ на СлиÑние" msgid "Merge events" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ ÑлиÑний" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "СообщениÑ" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "добавить ключ SSH" msgid "Monitoring" -msgstr "" +msgstr "Мониторинг" msgid "More information is available|here" -msgstr "" +msgstr "Больше информации доÑтупно|тут" msgid "New Issue" msgid_plural "New Issues" @@ -806,7 +947,7 @@ msgid "NotificationLevel|Watch" msgstr "ОтÑлеживать" msgid "Notifications" -msgstr "" +msgstr "УведомлениÑ" msgid "OfSearchInADropdown|Filter" msgstr "Фильтр" @@ -818,14 +959,26 @@ msgid "Options" msgstr "ÐаÑтройки" msgid "Overview" -msgstr "" +msgstr "Обзор" msgid "Owner" msgstr "Владелец" -msgid "Password" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" msgstr "" +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Пароль" + msgid "Pipeline" msgstr "Конвейер" @@ -905,13 +1058,13 @@ msgid "Pipelines charts" msgstr "Диаграмма конвейера" msgid "Pipelines for last month" -msgstr "" +msgstr "Конвеер за поÑледний меÑÑц" msgid "Pipelines for last week" -msgstr "" +msgstr "Конвеер за поÑледнюю неделю" msgid "Pipelines for last year" -msgstr "" +msgstr "Конвееры за поÑледний год" msgid "Pipeline|all" msgstr "вÑе" @@ -926,12 +1079,9 @@ msgid "Pipeline|with stages" msgstr "Ñо ÑтадиÑми" msgid "Preferences" -msgstr "" +msgstr "ПредпочтениÑ" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -950,7 +1100,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю." msgid "Project details" -msgstr "" +msgstr "Детали проекта" msgid "Project export could not be deleted." msgstr "Ðевозможно удалить ÑкÑпорт проекта." @@ -964,14 +1114,8 @@ msgstr "ИÑтек Ñрок дейÑÑ‚Ð²Ð¸Ñ ÑÑылки на проект. СРmsgid "Project export started. A download link will be sent by email." msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ ÑÐºÐ°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ отправлена по Ñлектронной почте." -msgid "Project home" -msgstr "ДомашнÑÑ Ñтраница" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "ПодпиÑатьÑÑ" msgid "ProjectFeature|Disabled" msgstr "Отключено" @@ -994,35 +1138,38 @@ msgstr "Ðтап" msgid "ProjectNetworkGraph|Graph" msgstr "Граф" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Загрузка проектов" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Проекты, которые вы чаÑто поÑещаете, будут отображатьÑÑ Ð·Ð´ÐµÑÑŒ" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "ПоиÑк по вашим проектам" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "К Ñожалению, по вашему запроÑу проекты не найдены" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Ðта функциональноÑÑ‚ÑŒ требует поддержки localStorage в вашем браузере" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸" msgid "Read more" msgstr "Подробнее" msgid "Readme" -msgstr "" +msgstr "ИнÑтрукциÑ" msgid "RefSwitcher|Branches" msgstr "Ветки" @@ -1030,6 +1177,9 @@ msgstr "Ветки" msgid "RefSwitcher|Tags" msgstr "Теги" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "СвÑзанные коммиты" @@ -1055,19 +1205,19 @@ msgid "Remove project" msgstr "Удалить проект" msgid "Repository" -msgstr "" +msgstr "Репозиторий" msgid "Request Access" msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа" msgid "Reset git storage health information" -msgstr "" +msgstr "СброÑить информацию о работоÑпоÑобноÑти Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ git" msgid "Reset health check access token" -msgstr "" +msgstr "СброÑить ключ доÑтупа проверки работоÑпоÑобноÑти" msgid "Reset runners registration token" -msgstr "" +msgstr "СброÑить ключ региÑтрации Gitlab Runners" msgid "Revert this commit" msgstr "Отменить Ñто изменение" @@ -1076,7 +1226,7 @@ msgid "Revert this merge request" msgstr "Отменить Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние" msgid "SSH Keys" -msgstr "" +msgstr "SSH Ключи" msgid "Save pipeline schedule" msgstr "Сохранить раÑпиÑание конвейра" @@ -1084,6 +1234,9 @@ msgstr "Сохранить раÑпиÑание конвейра" msgid "Schedule a new pipeline" msgstr "РаÑпиÑание нового конвейера" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Планирование конвейеров" @@ -1097,13 +1250,13 @@ msgid "Select a timezone" msgstr "Выбор временной зоны" msgid "Select existing branch" -msgstr "" +msgstr "Выбрать ÑущеÑтвующую ветвь" msgid "Select target branch" msgstr "Выбор целевой ветки" msgid "Service Templates" -msgstr "" +msgstr "Шаблоны Служб" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "УÑтановите пароль в Ñвоем аккаунте, чтобы отправлÑÑ‚ÑŒ или получать код через %{protocol}." @@ -1121,6 +1274,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "уÑтановить пароль" msgid "Settings" +msgstr "ÐаÑтройки" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1130,29 +1289,131 @@ msgstr[1] "Показано %d Ñобытий" msgstr[2] "Показано %d Ñобытий" msgid "Snippets" +msgstr "Сниппеты" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "ИÑходный код" msgid "Spam Logs" -msgstr "" +msgstr "Спам Логи" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Укажите Ñледующий URL во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð°Ñтройки Gitlab Runner:" msgid "StarProject|Star" msgstr "Отметить" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Ðачать %{new_merge_request} Ñ Ñтих изменений" msgid "Start the Runner!" -msgstr "" +msgstr "ЗапуÑтить GitLab Runner!" msgid "Switch branch/tag" msgstr "Переключить ветка/тег" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Тег" @@ -1166,7 +1427,7 @@ msgid "Target Branch" msgstr "Ветка" msgid "Team" -msgstr "" +msgstr "Команда" msgid "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." msgstr "Ðа Ñтапе напиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° показывает Ð²Ñ€ÐµÐ¼Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ коммита до ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние. Данные автоматичеÑки добавÑÑ‚ÑÑ Ð¿Ð¾Ñле того, как вы Ñоздать Ñвой первый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние." @@ -1217,6 +1478,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "Среднее значение в Ñ€Ñду. Пример: между 3, 5, 9, Ñреднее 5, между 3, 5, 7, 8, Ñреднее (5+7)/2 = 6." msgid "There are problems accessing Git storage: " +msgstr "Проблемы Ñ Ð´Ð¾Ñтупом к Git хранилищу: " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1391,14 +1655,20 @@ msgid "UploadLink|click to upload" msgstr "кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "ИÑпользуйте Ñледующий токен региÑтрации в процеÑÑе уÑтановки:" msgid "Use your global notification setting" msgstr "ИÑпользуютÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ‹Ð¹ наÑтройки уведомлений" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "ПроÑмотреть открытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Ограниченный" @@ -1418,7 +1688,7 @@ msgid "We don't have enough data to show this stage." msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Ñтапу отÑутÑтвует." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа" @@ -1471,6 +1741,12 @@ msgstr "Ð’Ñ‹ не Ñможете получать и отправлÑÑ‚ÑŒ код msgid "Your name" msgstr "Ваше имÑ" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "день" diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po index 1dc42901daf..ffbbe88cc51 100644 --- a/locale/uk/gitlab.po +++ b/locale/uk/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Ukrainian\n" "Language: uk_UA\n" @@ -31,23 +31,26 @@ msgstr[2] "%s доданих коммітів були виключені Ð´Ð»Ñ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} комміт %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab надаÑÑ‚ÑŒ доÑтуп на наÑтупну Ñпробу." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} із %{maximum_failures} невдач. GitLab заблокує доÑтуп на %{number_of_seconds} Ñекунд." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab автоматично не повторюватиме Ñпробу. Скиньте інформацію Ñховища при уÑуненні проблеми." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%{storage_name}: Ñпроба невдалого доÑтупу до Ñховища на хоÑÑ‚Ñ–:" +msgstr[1] "%{storage_name}: %{failed_attempts} невдалі Ñпроби доÑтупу до Ñховища:" +msgstr[2] "%{storage_name}: %{failed_attempts} невдалих Ñпроб доÑтупу до Ñховища:" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(перейдіть за поÑиланнÑм %{link} Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— ÑтоÑовно вÑтановленнÑ)." msgid "1 pipeline" msgid_plural "%d pipelines" @@ -55,6 +58,9 @@ msgstr[0] "1 конвеєр" msgstr[1] "%d конвеєра" msgstr[2] "%d конвеєрів" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Це набір графічних елементів Ð´Ð»Ñ Ð±ÐµÐ·Ð¿ÐµÑ€ÐµÑ€Ð²Ð½Ð¾Ñ— інтеграції" @@ -62,16 +68,16 @@ msgid "About auto deploy" msgstr "Про авто розгортаннÑ" msgid "Abuse Reports" -msgstr "" +msgstr "Звіти про зловживаннÑ" msgid "Access Tokens" -msgstr "" +msgstr "Токени доÑтупу" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "ДоÑтуп до помилкових Ñховищ тимчаÑово відключений Ð´Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ÑÑ‚Ñ– Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° відновленнÑ. Скиньте інформацію про Ñховища піÑÐ»Ñ ÑƒÑÑƒÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸, щоб дозволити доÑтуп." msgid "Account" -msgstr "" +msgstr "Обліковий запиÑ" msgid "Active" msgstr "Ðктивний" @@ -97,11 +103,11 @@ msgstr "Додати новий каталог" msgid "All" msgstr "Ð’ÑÑ–" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Додатки" msgid "Archived project! Repository is read-only" msgstr "Заархівований проект! Репозиторій доÑтупний лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ" @@ -116,15 +122,39 @@ msgid "Are you sure you want to reset registration token?" msgstr "Ви впевнені, що бажаєте Ñкинути реєÑтраційний токен?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Ви впевнені, що Ви хочете Ñкинути цей ключ перевірки працездатноÑÑ‚Ñ–?" msgid "Are you sure?" +msgstr "Ви впевнені?" + +msgid "Artifacts" msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -181,6 +211,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Гілка" @@ -199,6 +232,90 @@ msgstr "Переключити гілку" msgid "Branches" msgstr "Гілки" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ПереглÑнути каталог" @@ -215,7 +332,7 @@ msgid "ByAuthor|by" msgstr "від" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI" @@ -224,7 +341,7 @@ msgid "Cancel" msgstr "СкаÑувати" msgid "Cancel edit" -msgstr "" +msgstr "Відмінити правку" msgid "ChangeTypeActionLabel|Pick into branch" msgstr "Вибрати в гілці" @@ -245,7 +362,7 @@ msgid "Charts" msgstr "Графіки" msgid "Chat" -msgstr "" +msgstr "Чат" msgid "Cherry-pick this commit" msgstr "Cherry-pick в цьому комміті" @@ -308,7 +425,7 @@ msgid "CiStatus|running" msgstr "виконуєтьÑÑ" msgid "Comments" -msgstr "" +msgstr "Коментарі" msgid "Commit" msgid_plural "Commits" @@ -343,9 +460,6 @@ msgstr "Комміт від" msgid "Compare" msgstr "ПорівнÑти" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Керівництво контриб’юторів" @@ -365,7 +479,7 @@ msgid "Create New Directory" msgstr "Створити новий каталог" msgid "Create a new branch" -msgstr "" +msgstr "Створити нову гілку" msgid "Create a personal access token on your account to pull or push via %{protocol}." msgstr "Створити токен доÑтупу Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ аккауета, щоб відправлÑти або отримувати через %{protocol}." @@ -443,19 +557,19 @@ msgstr[1] "РозгортаннÑ" msgstr[2] "Розгортань" msgid "Deploy Keys" -msgstr "" +msgstr "Ключи Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ" msgid "Description" msgstr "ОпиÑ" msgid "Details" -msgstr "" +msgstr "Деталі" msgid "Directory name" msgstr "Ім'Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñƒ" msgid "Discard changes" -msgstr "" +msgstr "СкаÑувати зміни" msgid "Don't show again" msgstr "Ðе показувати знову" @@ -494,25 +608,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Редагувати Розклад Конвеєра %{id}" msgid "Emails" +msgstr "ÐдреÑи електронної пошти" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Ð’ÑÑ–" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Коментарю" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Проблеми" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Запити на злиттÑ" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "По відправленні комміту" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "За командою" msgid "Every day (at 4:00am)" msgstr "Кожен день (в 4:00 ранку)" @@ -523,6 +640,9 @@ msgstr "Кожен міÑÑць (1-го чиÑла о 4:00 ранку)" msgid "Every week (Sundays at 4:00am)" msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ влаÑника" @@ -563,16 +683,16 @@ msgid "From merge request merge until deploy to production" msgstr "З об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° ПРОД" msgid "GPG Keys" -msgstr "" +msgstr "GPG ключі" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Git була Ñкинута" msgid "GitLab Runner section" -msgstr "" +msgstr "Розділ GitLab Runner" msgid "Go to your fork" msgstr "Перейти до вашого форку" @@ -580,33 +700,48 @@ msgstr "Перейти до вашого форку" msgid "GoToYourFork|Fork" msgstr "Форк" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "Головна" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Перевірки працездатноÑÑ‚Ñ–" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Інформацію про працездатніÑÑ‚ÑŒ можна отримати з наÑтупних ендпойнтів. Більше інформації доÑтупно" + +msgid "HealthCheck|Access token is" +msgstr "Токен доÑтупу Ñ”" + +msgid "HealthCheck|Healthy" +msgstr "Здоровий" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Жодних проблем із здоров'Ñм не виÑвлено" + +msgid "HealthCheck|Unhealthy" +msgstr "Ðездорові" + msgid "Housekeeping successfully started" msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато" @@ -614,7 +749,7 @@ msgid "Import repository" msgstr "Імпорт репозеторіÑ" msgid "Install a Runner compatible with GitLab CI" -msgstr "" +msgstr "Ð’Ñтановіть Runner, ÑуміÑний з GitLab CI" msgid "Interval Pattern" msgstr "Шаблон інтервалу" @@ -623,9 +758,12 @@ msgid "Introducing Cycle Analytics" msgstr "ПредÑтавлÑємо аналітику циклу" msgid "Issue events" -msgstr "" +msgstr "Події проблем" msgid "Issues" +msgstr "Проблеми" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -635,7 +773,7 @@ msgid "LFSStatus|Enabled" msgstr "Увімкнено" msgid "Labels" -msgstr "" +msgstr "Мітки" msgid "Last %d day" msgid_plural "Last %d days" @@ -653,10 +791,10 @@ msgid "Last commit" msgstr "ОÑтанній комміт" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Ви надіÑлали зміни до" msgid "LastPushEvent|at" -msgstr "" +msgstr "в" msgid "Learn more in the" msgstr "ДізнайтеÑÑŒ більше" @@ -686,25 +824,28 @@ msgid "Median" msgstr "Медіана" msgid "Members" -msgstr "" +msgstr "КориÑтувачі" msgid "Merge Requests" -msgstr "" +msgstr "Запит на злиттÑ" msgid "Merge events" +msgstr "Події запит на злиттÑ" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "ПовідомленнÑ" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "не додаÑте SSH ключ" msgid "Monitoring" -msgstr "" +msgstr "Моніторинг" msgid "More information is available|here" -msgstr "" +msgstr "тут" msgid "New Issue" msgid_plural "New Issues" @@ -806,7 +947,7 @@ msgid "NotificationLevel|Watch" msgstr "ВідÑтежувати" msgid "Notifications" -msgstr "" +msgstr "СповіщеннÑ" msgid "OfSearchInADropdown|Filter" msgstr "Фільтр" @@ -818,14 +959,26 @@ msgid "Options" msgstr "Параметри" msgid "Overview" -msgstr "" +msgstr "ОглÑд" msgid "Owner" msgstr "ВлаÑник" -msgid "Password" +msgid "Pagination|Last »" msgstr "" +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Пароль" + msgid "Pipeline" msgstr "Конвеєр" @@ -905,13 +1058,13 @@ msgid "Pipelines charts" msgstr "Чарти Конвеєрів" msgid "Pipelines for last month" -msgstr "" +msgstr "Конвеєри за оÑтанній міÑÑць" msgid "Pipelines for last week" -msgstr "" +msgstr "Конвеєри за оÑтанній тиждень" msgid "Pipelines for last year" -msgstr "" +msgstr "Конвеєри за оÑтанній рік" msgid "Pipeline|all" msgstr "вÑÑ–" @@ -926,12 +1079,9 @@ msgid "Pipeline|with stages" msgstr "зі ÑтадіÑми" msgid "Preferences" -msgstr "" - -msgid "Profile Settings" -msgstr "" +msgstr "ÐалаштуваннÑ" -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -950,7 +1100,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "ДоÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві." msgid "Project details" -msgstr "" +msgstr "Деталі проекту" msgid "Project export could not be deleted." msgstr "Ðеможливо видалити екÑпорт проекту." @@ -964,14 +1114,8 @@ msgstr "ЗакінчивÑÑ Ñ‚ÐµÑ€Ð¼Ñ–Ð½ дії поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° проРmsgid "Project export started. A download link will be sent by email." msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑÐºÐ°Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ надіÑлана електронною поштою." -msgid "Project home" -msgstr "Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка проекту" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "ПідпиÑатиÑÑ" msgid "ProjectFeature|Disabled" msgstr "Вимкнено" @@ -994,29 +1138,32 @@ msgstr "Етап" msgid "ProjectNetworkGraph|Graph" msgstr "ІÑторіÑ" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð²" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Проекти, Ñкі ви чаÑто відвідуєте, будуть відображені тут" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "Пошук по ваших проектах" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "Ðа жаль, по вашоу запиту проектів не знайдено" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” підтримки localStorage вашим браузером" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Push події" msgid "Read more" msgstr "Докладніше" @@ -1030,6 +1177,9 @@ msgstr "Гілки" msgid "RefSwitcher|Tags" msgstr "Теги" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Пов'Ñзані Комміти" @@ -1055,19 +1205,19 @@ msgid "Remove project" msgstr "Видалити проект" msgid "Repository" -msgstr "" +msgstr "Репозиторій" msgid "Request Access" msgstr "Запит доÑтупу" msgid "Reset git storage health information" -msgstr "" +msgstr "Скиньте інформацію про працездатніÑÑ‚ÑŒ Ñховища git" msgid "Reset health check access token" -msgstr "" +msgstr "Скиньте токен доÑтупу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ перевірки працездатноÑÑ‚Ñ–" msgid "Reset runners registration token" -msgstr "" +msgstr "Скинути реєÑтраційний токен runner-ів" msgid "Revert this commit" msgstr "СкаÑувати цей комміт" @@ -1076,7 +1226,7 @@ msgid "Revert this merge request" msgstr "СкаÑувати цей запит на злиттÑ" msgid "SSH Keys" -msgstr "" +msgstr "Ключі SSH" msgid "Save pipeline schedule" msgstr "Зберегти Розклад Конвеєра" @@ -1084,6 +1234,9 @@ msgstr "Зберегти Розклад Конвеєра" msgid "Schedule a new pipeline" msgstr "Розклад нового конвеєра" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²" @@ -1097,13 +1250,13 @@ msgid "Select a timezone" msgstr "Вибрати чаÑовий поÑÑ" msgid "Select existing branch" -msgstr "" +msgstr "Виберіть гілку" msgid "Select target branch" msgstr "Вибір цільової гілки" msgid "Service Templates" -msgstr "" +msgstr "Ð¡ÐµÑ€Ð²Ñ–Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "Ð’Ñтановіть пароль Ñвого облікового запиÑу, щоб відправлÑти або отримувати код через %{protocol}." @@ -1121,6 +1274,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "вÑтановити пароль" msgid "Settings" +msgstr "ÐалаштуваннÑ" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1130,29 +1289,131 @@ msgstr[1] "Показано %d події" msgstr[2] "Показано %d подій" msgid "Snippets" +msgstr "Фрагменти" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "Код" msgid "Spam Logs" -msgstr "" +msgstr "Спам-журнал" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:" msgid "StarProject|Star" msgstr "ПідпиÑатиÑÑ" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Почати %{new_merge_request} з цих змін" msgid "Start the Runner!" -msgstr "" +msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!" msgid "Switch branch/tag" msgstr "тег" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Тег" @@ -1166,7 +1427,7 @@ msgid "Target Branch" msgstr "Цільова гілка" msgid "Team" -msgstr "" +msgstr "Команда" msgid "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." msgstr "Ðа Ñтадії напиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ, показує Ñ‡Ð°Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ комміту до ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на об'єднаннÑ. Дані будуть автоматично додані піÑÐ»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ першого запиту на об'єднаннÑ." @@ -1217,6 +1478,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6." msgid "There are problems accessing Git storage: " +msgstr "Є проблеми з доÑтупом до Ñховища: " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1391,14 +1655,20 @@ msgid "UploadLink|click to upload" msgstr "ÐатиÑніть, щоб завантажити" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "ВикориÑтовувати токен під Ñ‡Ð°Ñ ÑƒÑтановки:" msgid "Use your global notification setting" msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "ПереглÑд відкритих запитів на злиттÑ" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Внутрішній" @@ -1418,7 +1688,7 @@ msgid "We don't have enough data to show this stage." msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ цього етапу." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "СкаÑувати запит доÑтупу" @@ -1471,6 +1741,12 @@ msgstr "Ви не зможете отримувати Ñ– відправлÑти msgid "Your name" msgstr "Ваше ім'Ñ" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "день" diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po index d6f756e813f..4a05b159008 100644 --- a/locale/zh_CN/gitlab.po +++ b/locale/zh_CN/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" @@ -27,6 +27,9 @@ msgstr[0] "为æ高页é¢åŠ 载速度åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失败 %{number_of_failures} 次/最多å…许失败失败 %{maximum_failures} 次,GitLab 将继ç»é‡è¯•ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¡æµæ°´çº¿" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒç»é›†æˆæ•°æ®å›¾" @@ -89,8 +95,8 @@ msgstr "æ·»åŠ ç›®å½•" msgid "All" msgstr "全部" -msgid "Appearances" -msgstr "å¤–è§‚æ ·å¼" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "应用程åº" @@ -113,65 +119,92 @@ msgstr "确定è¦é‡ç½®å¥åº·æ£€æŸ¥ä»¤ç‰Œå—?" msgid "Are you sure?" msgstr "确定å—?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放文件到æ¤å¤„或者 %{upload_link}" -msgid "Authentication log" -msgstr "认è¯æ—¥å¿—" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" +msgstr "" msgid "Billing" -msgstr "è´¦å•" +msgstr "" msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan." -msgstr "%{group_name} ç›®å‰æ£åœ¨ä½¿ç”¨ %{plan_link} 方案。" +msgstr "" msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available." -msgstr "当æŸäº›æ–¹æ¡ˆå½“å‰ä¸å¯ç”¨æ—¶è‡ªåŠ¨é™çº§å’Œå‡çº§ã€‚" +msgstr "" msgid "BillingPlans|Current plan" -msgstr "当å‰æ–¹æ¡ˆ" +msgstr "" msgid "BillingPlans|Customer Support" -msgstr "客户支æŒ" +msgstr "" msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}." -msgstr "通过阅读%{faq_link} 了解关于æ¯ä¸ªæ–¹æ¡ˆçš„更多信æ¯ã€‚" +msgstr "" msgid "BillingPlans|Manage plan" -msgstr "管ç†æ–¹æ¡ˆ" +msgstr "" msgid "BillingPlans|Please contact %{customer_support_link} in that case." -msgstr "在这ç§æƒ…况下,请è”ç³» %{customer_support_link}。" +msgstr "" msgid "BillingPlans|See all %{plan_name} features" -msgstr "查看 %{plan_name} 的所有功能" +msgstr "" msgid "BillingPlans|This group uses the plan associated with its parent group." -msgstr "该群组使用与它的父团队相关è”的计划。" +msgstr "" msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}." -msgstr "请访问 %{parent_billing_page_link} 的计费方案部分æ¥ç®¡ç†è¯¥å›¢é˜Ÿçš„计费方案,。" +msgstr "" msgid "BillingPlans|Upgrade" -msgstr "å‡çº§" +msgstr "" msgid "BillingPlans|You are currently on the %{plan_link} plan." -msgstr "ä½ ç›®å‰æ£åœ¨ä½¿ç”¨ %{plan_link} 方案。" +msgstr "" msgid "BillingPlans|frequently asked questions" -msgstr "常è§é—®é¢˜" +msgstr "" msgid "BillingPlans|monthly" -msgstr "æ¯æœˆ" +msgstr "" msgid "BillingPlans|paid annually at %{price_per_year}" -msgstr "æ¯å¹´æ”¯ä»˜ %{price_per_year}" +msgstr "" msgid "BillingPlans|per user" -msgstr "æ¯ä¸ªç”¨æˆ·" +msgstr "" msgid "Billinglans|Downgrade" -msgstr "é™çº§" +msgstr "" + +msgid "Board" +msgstr "" msgid "Branch" msgid_plural "Branches" @@ -189,6 +222,90 @@ msgstr "切æ¢åˆ†æ”¯" msgid "Branches" msgstr "分支" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "æµè§ˆç›®å½•" @@ -331,9 +448,6 @@ msgstr "æ交者:" msgid "Compare" msgstr "比较" -msgid "Container Registry" -msgstr "容器注册表" - msgid "Contribution guide" msgstr "贡献指å—" @@ -341,7 +455,7 @@ msgid "Contributors" msgstr "贡献者" msgid "Copy SSH public key to clipboard" -msgstr "å°† SSH 公钥å¤åˆ¶åˆ°å‰ªè´´æ¿" +msgstr "" msgid "Copy URL to clipboard" msgstr "å¤åˆ¶ URL 到剪贴æ¿" @@ -482,6 +596,9 @@ msgstr "编辑 %{id} æµæ°´çº¿è®¡åˆ’" msgid "Emails" msgstr "电å邮件" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆæ‰§è¡Œï¼ˆæ¯æœˆ 1 日凌晨 4 点)" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "æ— æ³•å˜æ›´æ‰€æœ‰è€…" @@ -550,7 +670,7 @@ msgid "GPG Keys" msgstr "GPG 密钥" msgid "Geo Nodes" -msgstr "Geo 节点" +msgstr "" msgid "Git storage health information has been reset" msgstr "Git å˜å‚¨å¥åº·ä¿¡æ¯å·²é‡ç½®" @@ -564,8 +684,29 @@ msgstr "跳转到派生项目" msgid "GoToYourFork|Fork" msgstr "跳转到派生项目" -msgid "Group overview" -msgstr "群组概览" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "å¥åº·æ£€æŸ¥" @@ -585,12 +726,6 @@ msgstr "没有检测到å¥åº·é—®é¢˜" msgid "HealthCheck|Unhealthy" msgstr "éžå¥åº·" -msgid "Home" -msgstr "首页" - -msgid "Hooks" -msgstr "é’©å" - msgid "Housekeeping successfully started" msgstr "已开始维护" @@ -612,6 +747,9 @@ msgstr "议题事件" msgid "Issues" msgstr "议题" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -653,14 +791,14 @@ msgid "Leave project" msgstr "退出项目" msgid "License" -msgstr "许å¯" +msgstr "" msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" msgstr[0] "最多显示 %d 个事件" msgid "Locked Files" -msgstr "é”定的文件" +msgstr "" msgid "Median" msgstr "ä¸ä½æ•°" @@ -674,6 +812,9 @@ msgstr "åˆå¹¶è¯·æ±‚" msgid "Merge events" msgstr "åˆå¹¶äº‹ä»¶" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "消æ¯" @@ -801,6 +942,18 @@ msgstr "概览" msgid "Owner" msgstr "所有者" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "密ç " @@ -817,7 +970,7 @@ msgid "Pipeline Schedules" msgstr "æµæ°´çº¿è®¡åˆ’" msgid "Pipeline quota" -msgstr "æµæ°´çº¿é…é¢" +msgstr "" msgid "PipelineCharts|Failed:" msgstr "失败:" @@ -906,11 +1059,8 @@ msgstr "于阶段" msgid "Preferences" msgstr "å好设置" -msgid "Profile Settings" -msgstr "账户设置" - -msgid "Project" -msgstr "项目" +msgid "Profile" +msgstr "" msgid "Project '%{project_name}' queued for deletion." msgstr "项目 '%{project_name}' å·²è¿›å…¥åˆ é™¤é˜Ÿåˆ—ã€‚" @@ -942,12 +1092,6 @@ msgstr "项目导出链接已过期。请从项目设置ä¸é‡æ–°ç”Ÿæˆé¡¹ç›®å¯¼ msgid "Project export started. A download link will be sent by email." msgstr "项目导出已开始。下载链接将通过电å邮件å‘é€ã€‚" -msgid "Project home" -msgstr "项目首页" - -msgid "Project overview" -msgstr "项目概览" - msgid "ProjectActivityRSS|Subscribe" msgstr "订阅" @@ -972,25 +1116,28 @@ msgstr "阶段" msgid "ProjectNetworkGraph|Graph" msgstr "分支图" -msgid "Push Rules" -msgstr "推é€è§„则" - -msgid "ProjectsDropdown|Loading projects" +msgid "ProjectsDropdown|Frequently visited" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgid "ProjectsDropdown|Loading projects" +msgstr "åŠ è½½é¡¹ç›®ä¸" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "您ç»å¸¸è®¿é—®çš„项目将出现在这里" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "æœç´¢æ‚¨çš„项目" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„项目" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "æ¤åŠŸèƒ½éœ€è¦æµè§ˆå™¨æ”¯æŒ localStorage" + +msgid "Push Rules" msgstr "" msgid "Push events" @@ -1008,6 +1155,9 @@ msgstr "分支" msgid "RefSwitcher|Tags" msgstr "æ ‡ç¾" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相关的æ交" @@ -1062,6 +1212,9 @@ msgstr "ä¿å˜æµæ°´çº¿è®¡åˆ’" msgid "Schedule a new pipeline" msgstr "新建æµæ°´çº¿è®¡åˆ’" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´çº¿è®¡åˆ’" @@ -1101,6 +1254,12 @@ msgstr "设置密ç " msgid "Settings" msgstr "设置" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "显示 %d 个事件" @@ -1108,6 +1267,102 @@ msgstr[0] "显示 %d 个事件" msgid "Snippets" msgstr "代ç 片段" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "æºä»£ç " @@ -1120,6 +1375,9 @@ msgstr "在 Runner 设置时指定以下 URL:" msgid "StarProject|Star" msgstr "æ˜Ÿæ ‡" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ç”±æ¤æ›´æ”¹ %{new_merge_request}" @@ -1129,6 +1387,9 @@ msgstr "å¯åŠ¨ Runner!" msgid "Switch branch/tag" msgstr "切æ¢åˆ†æ”¯/æ ‡ç¾" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "æ ‡ç¾" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—ä¸æœ€ä¸é—´çš„值。例如在 3ã€5ã€9 之间ï msgid "There are problems accessing Git storage: " msgstr "访问 Git å˜å‚¨æ—¶å‡ºçŽ°é—®é¢˜ï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "在创建一个空的å˜å‚¨åº“或导入现有å˜å‚¨åº“之å‰ï¼Œå°†æ— 法推é€ä»£ç 。" @@ -1366,9 +1630,15 @@ msgstr "在安装过程ä¸ä½¿ç”¨ä»¥ä¸‹æ³¨å†Œä»¤ç‰Œï¼š" msgid "Use your global notification setting" msgstr "使用全局通知设置" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看待处ç†çš„åˆå¹¶è¯·æ±‚" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "内部" @@ -1441,6 +1711,12 @@ msgstr "在账å·ä¸ %{add_ssh_key_link} 之å‰å°†æ— 法通过 SSH 拉å–或推é msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po index 48b86508d1e..c3b6cc72aed 100644 --- a/locale/zh_HK/gitlab.po +++ b/locale/zh_HK/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Traditional, Hong Kong\n" "Language: zh_HK\n" @@ -27,6 +27,9 @@ msgstr[0] "為æ高é é¢åŠ 載速度åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} 次,GitLab å°‡é‡è©¦ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¢æµæ°´ç·š" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ" @@ -89,7 +95,7 @@ msgstr "æ·»åŠ æ–°ç›®éŒ„" msgid "All" msgstr "全部" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥ä»¤ç‰Œå—Žï¼Ÿ" msgid "Are you sure?" msgstr "確定嗎?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放文件到æ¤è™•æˆ–者 %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "分支" @@ -189,6 +222,90 @@ msgstr "切æ›åˆ†æ”¯" msgid "Branches" msgstr "分支" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ç€è¦½ç›®éŒ„" @@ -331,9 +448,6 @@ msgstr "æ交者:" msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "è²¢ç»æŒ‡å—" @@ -482,6 +596,9 @@ msgstr "編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "無法變更所有者" @@ -564,7 +684,28 @@ msgstr "è·³è½‰åˆ°æ´¾ç”Ÿé …ç›®" msgid "GoToYourFork|Fork" msgstr "è·³è½‰åˆ°æ´¾ç”Ÿé …ç›®" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ" msgid "HealthCheck|Unhealthy" msgstr "ä¸è‰¯" -msgid "Home" -msgstr "首é " - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "已開始ç¶è·" @@ -612,6 +747,9 @@ msgstr "è°é¡Œäº‹ä»¶ (issue event)" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "åˆä½µäº‹ä»¶ (merge event)" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "所有者" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,12 +1059,9 @@ msgstr "於階段" msgid "Preferences" msgstr "" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "專案" - msgid "Project '%{project_name}' queued for deletion." msgstr "é …ç›® '%{project_name}' 已進入刪除隊列。" @@ -942,12 +1092,6 @@ msgstr "é …ç›®å°Žå‡ºéˆæŽ¥å·²éŽæœŸã€‚è«‹å¾žé …ç›®è¨ç½®ä¸é‡æ–°ç”Ÿæˆé …目導 msgid "Project export started. A download link will be sent by email." msgstr "é …ç›®å°Žå‡ºå·²é–‹å§‹ã€‚ä¸‹è¼‰éˆæŽ¥å°‡é€šéŽé›»å郵件發é€ã€‚" -msgid "Project home" -msgstr "é …ç›®é¦–é " - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "訂閱" @@ -972,27 +1116,30 @@ msgstr "階段" msgid "ProjectNetworkGraph|Graph" msgstr "分支圖" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "推é€äº‹ä»¶ (push event) " @@ -1008,6 +1155,9 @@ msgstr "分支" msgid "RefSwitcher|Tags" msgstr "標籤" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相關的æ交" @@ -1062,6 +1212,9 @@ msgstr "ä¿å˜æµæ°´ç·šè¨ˆåŠƒ" msgid "Schedule a new pipeline" msgstr "新建æµæ°´ç·šè¨ˆåŠƒ" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´ç·šè¨ˆåŠƒ" @@ -1101,6 +1254,12 @@ msgstr "è¨ç½®å¯†ç¢¼" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "顯示 %d 個事件" @@ -1108,6 +1267,102 @@ msgstr[0] "顯示 %d 個事件" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "æºä»£ç¢¼" @@ -1120,6 +1375,9 @@ msgstr "在 Runner è¨ç½®æ™‚指定以下 URL:" msgid "StarProject|Star" msgstr "星標" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ç”±æ¤æ›´æ”¹ %{new_merge_request}" @@ -1129,6 +1387,9 @@ msgstr "é‹ä½œ Runner!" msgid "Switch branch/tag" msgstr "切æ›åˆ†æ”¯/標籤" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "標籤" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間ï msgid "There are problems accessing Git storage: " msgstr "è¨ªå• Git å˜å„²æ™‚出ç¾å•é¡Œï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "在創建壹個空的å˜å„²åº«æˆ–å°Žå…¥ç¾æœ‰å˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡æ³•æŽ¨é€ä»£ç¢¼ã€‚" @@ -1366,9 +1630,15 @@ msgstr "在安è£éŽç¨‹ä¸ä½¿ç”¨ä»¥ä¸‹è¨»å†Šä»¤ç‰Œï¼š" msgid "Use your global notification setting" msgstr "使用全局通知è¨ç½®" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看開啟的åˆä¸¦è«‹æ±‚" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "內部" @@ -1441,6 +1711,12 @@ msgstr "åœ¨è³¬è™Ÿä¸ %{add_ssh_key_link} 之å‰å°‡ç„¡æ³•é€šéŽ SSH 拉å–或推é msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index da6a98bdb5c..8a14cd01566 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" @@ -27,6 +27,9 @@ msgstr[0] "å› æ•ˆèƒ½è€ƒé‡ï¼Œä¸é¡¯ç¤º %s 個更動 (commit)。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} 在 %{commit_timeago} é€äº¤" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失敗 %{number_of_failures} 次,在失敗 %{maximum_failures} æ¬¡å‰ GitLab 會é‡è©¦ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¢æµæ°´ç·š" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒçºŒæ•´åˆ (CI) 相關的圖表" @@ -63,7 +69,7 @@ msgid "Access to failing storages has been temporarily disabled to allow the mou msgstr "已暫時åœç”¨å¤±æ•—çš„ Git 儲å˜ç©ºé–“。當儲å˜ç©ºé–“æ¢å¾©æ£å¸¸å¾Œï¼Œè«‹é‡ç½®å„²å˜ç©ºé–“å¥åº·æŒ‡æ•¸ã€‚" msgid "Account" -msgstr "" +msgstr "帳號" msgid "Active" msgstr "啟用" @@ -89,8 +95,8 @@ msgstr "新增目錄" msgid "All" msgstr "全部" -msgid "Appearances" -msgstr "外觀" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "應用程å¼" @@ -113,10 +119,34 @@ msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥å˜å–æ†‘è‰ (access token) 嗎?" msgid "Are you sure?" msgstr "確定嗎?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放檔案到æ¤è™•æˆ–者 %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "分支 (branch) " @@ -189,6 +222,90 @@ msgstr "切æ›åˆ†æ”¯ (branch)" msgid "Branches" msgstr "分支 (branch) " +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ç€è¦½ç›®éŒ„" @@ -331,9 +448,6 @@ msgstr "é€äº¤è€…為 " msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "å”作指å—" @@ -429,7 +543,7 @@ msgid_plural "Deploys" msgstr[0] "部署" msgid "Deploy Keys" -msgstr "" +msgstr "部署金鑰" msgid "Description" msgstr "æè¿°" @@ -482,6 +596,9 @@ msgstr "編輯 %{id} æµæ°´ç·š (pipeline) 排程" msgid "Emails" msgstr "é›»å郵件" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "顯示全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆä¸€æ—¥æ·©æ™¨å››é»žï¼‰" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯é€±åŸ·è¡Œï¼ˆé€±æ—¥æ·©æ™¨ 四點)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "無法變更所有權" @@ -547,7 +667,7 @@ msgid "From merge request merge until deploy to production" msgstr "從請求被åˆä½µå¾Œ (merge request merged) 直到部署至營é‹ç’°å¢ƒ" msgid "GPG Keys" -msgstr "" +msgstr "GPG 金鑰" msgid "Geo Nodes" msgstr "" @@ -564,8 +684,29 @@ msgstr "å‰å¾€æ‚¨çš„分支 (fork) " msgid "GoToYourFork|Fork" msgstr "å‰å¾€æ‚¨çš„分支 (fork) " -msgid "Group overview" -msgstr "群組總覽" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "å¥åº·æª¢æŸ¥" @@ -585,12 +726,6 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ" msgid "HealthCheck|Unhealthy" msgstr "ä¸è‰¯" -msgid "Home" -msgstr "首é " - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "已開始ç¶è·" @@ -612,6 +747,9 @@ msgstr "è°é¡Œ (issue) 事件" msgid "Issues" msgstr "è°é¡Œ" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -669,13 +807,16 @@ msgid "Members" msgstr "æˆå“¡" msgid "Merge Requests" -msgstr "" +msgstr "åˆä½µè«‹æ±‚ (merge request)" msgid "Merge events" msgstr "åˆä½µ (merge) 事件" +msgid "Merge request" +msgstr "" + msgid "Messages" -msgstr "訊æ¯" +msgstr "公告" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "新增 SSH 金鑰" @@ -801,6 +942,18 @@ msgstr "總覽" msgid "Owner" msgstr "所有權" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "密碼" @@ -904,14 +1057,11 @@ msgid "Pipeline|with stages" msgstr "於階段" msgid "Preferences" -msgstr "" +msgstr "å好è¨å®š" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "專案" - msgid "Project '%{project_name}' queued for deletion." msgstr "專案 '%{project_name}' å·²åŠ å…¥åˆªé™¤ä½‡åˆ—ã€‚" @@ -942,12 +1092,6 @@ msgstr "專案的匯出連çµå·²å¤±æ•ˆã€‚請到專案è¨å®šä¸ç”¢ç”Ÿæ–°çš„é€£çµ msgid "Project export started. A download link will be sent by email." msgstr "專案導出已開始。完æˆå¾Œä¸‹è¼‰é€£çµæœƒé€åˆ°æ‚¨çš„信箱。" -msgid "Project home" -msgstr "專案首é " - -msgid "Project overview" -msgstr "專案總覽" - msgid "ProjectActivityRSS|Subscribe" msgstr "訂閱" @@ -972,25 +1116,28 @@ msgstr "階段" msgid "ProjectNetworkGraph|Graph" msgstr "分支圖" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "æœå°‹æ‚¨çš„專案" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "抱æ‰ï¼Œæ²’有符åˆæœå°‹æ¢ä»¶çš„專案" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "æ¤åŠŸèƒ½éœ€è¦ç€è¦½å™¨æ”¯æ´ localStorage" + +msgid "Push Rules" msgstr "" msgid "Push events" @@ -1008,6 +1155,9 @@ msgstr "分支 (branch) " msgid "RefSwitcher|Tags" msgstr "標籤" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相關的更動記錄 (commit) " @@ -1054,7 +1204,7 @@ msgid "Revert this merge request" msgstr "還原æ¤åˆä½µè«‹æ±‚ (merge request) " msgid "SSH Keys" -msgstr "" +msgstr "SSH 金鑰" msgid "Save pipeline schedule" msgstr "儲å˜æµæ°´ç·š (pipeline) 排程" @@ -1062,6 +1212,9 @@ msgstr "儲å˜æµæ°´ç·š (pipeline) 排程" msgid "Schedule a new pipeline" msgstr "建立æµæ°´ç·š (pipeline) 排程" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´ç·š (pipeline) 排程" @@ -1101,6 +1254,12 @@ msgstr "è¨å®šå¯†ç¢¼" msgid "Settings" msgstr "è¨å®š" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "顯示 %d 個事件" @@ -1108,6 +1267,102 @@ msgstr[0] "顯示 %d 個事件" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "原始碼" @@ -1120,6 +1375,9 @@ msgstr "åœ¨å®‰è£ Runner 時指定以下 URL:" msgid "StarProject|Star" msgstr "收è—" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "以這些改動建立一個新的 %{new_merge_request} " @@ -1129,6 +1387,9 @@ msgstr "å•Ÿå‹• Runner!" msgid "Switch branch/tag" msgstr "切æ›åˆ†æ”¯ (branch) 或標籤" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "標籤" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間ï msgid "There are problems accessing Git storage: " msgstr "å˜å– Git 儲å˜ç©ºé–“時出ç¾å•é¡Œï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "這代表在您建立一個空的檔案庫 (repository) 或是匯入一個ç¾å˜çš„檔案庫之å‰ï¼Œæ‚¨å°‡ç„¡æ³•ä¸Šå‚³æ›´æ–° (push) 。" @@ -1366,9 +1630,15 @@ msgstr "在安è£éŽç¨‹ä¸ä½¿ç”¨æ¤è¨»å†Šæ†‘è‰ (registration token):" msgid "Use your global notification setting" msgstr "使用全域通知è¨å®š" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看æ¤åˆ†æ”¯çš„åˆä½µè«‹æ±‚ (merge request)" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "內部" @@ -1388,7 +1658,7 @@ msgid "We don't have enough data to show this stage." msgstr "å› è©²éšŽæ®µçš„è³‡æ–™ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š" msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "å–消權é™ç”³è«‹" @@ -1441,6 +1711,12 @@ msgstr "åœ¨å€‹äººå¸³è™Ÿä¸ %{add_ssh_key_link} 之å‰ï¼Œ 將無法使用 SSH 上 msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/qa/README.md b/qa/README.md index b6b5a76f1d3..e0ebb53a2e9 100644 --- a/qa/README.md +++ b/qa/README.md @@ -16,3 +16,22 @@ against any existing instance. 1. When we release a new version of GitLab, we build a Docker images for it. 1. Along with GitLab Docker Images we also build and publish GitLab QA images. 1. GitLab QA project uses these images to execute integration tests. + +## How can I use it? + +You can use GitLab QA to exercise tests on any live instance! For example, the +follow call would login to the local GitLab instance and run all specs in +`qa/specs/features`: + +``` +GITLAB_USERNAME='root' GITLAB_PASSWORD='5iveL!fe' bin/qa Test::Instance http://localhost +``` + +You can also supply a specific tests to run as another parameter. For example, to +test the EE license specs, you can run: + +``` +EE_LICENSE="<YOUR LICENSE KEY>" GITLAB_USERNAME='root' GITLAB_PASSWORD='5iveL!fe' bin/qa Test::Instance http://localhost qa/ee +``` + +All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa#supported-environment-variables). diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb index b01a4e10f93..f4619042e34 100644 --- a/qa/qa/page/admin/menu.rb +++ b/qa/qa/page/admin/menu.rb @@ -3,15 +3,10 @@ module QA module Admin class Menu < Page::Base def go_to_license - within_middle_menu { click_link 'License' } - end - - private - - def within_middle_menu - page.within('.nav-control') do - yield - end + link = find_link 'License' + # Click space to scroll this link into the view + link.send_keys(:space) + link.click end end end diff --git a/scripts/lint-changelog-yaml b/scripts/lint-changelog-yaml new file mode 100755 index 00000000000..cce5f1c7667 --- /dev/null +++ b/scripts/lint-changelog-yaml @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +require 'yaml' + +invalid_changelogs = Dir['changelogs/**/*'].reject do |changelog| + next true if changelog =~ /(archive\.md|unreleased(-ee)?)$/ + next false unless changelog.end_with?('.yml') + + begin + YAML.load_file(changelog) + rescue + end +end + +if invalid_changelogs.any? + puts "Invalid changelogs found!\n" + puts invalid_changelogs.sort + exit 1 +else + puts "All changelogs are valid YAML.\n" + exit 0 +end diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh index 54c1ef3dfdd..e5242fee32b 100755 --- a/scripts/lint-doc.sh +++ b/scripts/lint-doc.sh @@ -3,15 +3,19 @@ cd "$(dirname "$0")/.." # Use long options (e.g. --header instead of -H) for curl examples in documentation. -grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ +echo 'Checking for curl short options...' +grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ >/dev/null 2>&1 if [ $? == 0 ] then - echo '✖ ERROR: Short options should not be used in documentation!' >&2 + echo '✖ ERROR: Short options for curl should not be used in documentation! + Use long options (e.g., --header instead of -H):' >&2 + grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ exit 1 fi # Ensure that the CHANGELOG.md does not contain duplicate versions DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d) +echo 'Checking for CHANGELOG.md duplicate entries...' if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ] then echo '✖ ERROR: Duplicate versions in CHANGELOG.md:' >&2 @@ -19,5 +23,15 @@ then exit 1 fi +# Make sure no files in doc/ are executable +EXEC_PERM_COUNT=$(find doc/ app/ -type f -perm 755 | wc -l) +echo 'Checking for executable permissions...' +if [ "${EXEC_PERM_COUNT}" -ne 0 ] +then + echo '✖ ERROR: Executable permissions should not be used in documentation! Use `chmod 644` to the files in question:' >&2 + find doc/ app/ -type f -perm 755 + exit 1 +fi + echo "✔ Linting passed" exit 0 diff --git a/scripts/static-analysis b/scripts/static-analysis index 295b6f132c1..aeefb2bc96f 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -13,7 +13,8 @@ tasks = [ %w[yarn run eslint], %w[bundle exec rubocop --require rubocop-rspec], %w[scripts/lint-conflicts.sh], - %w[bundle exec rake gettext:lint] + %w[bundle exec rake gettext:lint], + %w[scripts/lint-changelog-yaml] ] failed_tasks = tasks.reduce({}) do |failures, task| diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb new file mode 100644 index 00000000000..e7966cee77b --- /dev/null +++ b/spec/factories/gitaly/commit.rb @@ -0,0 +1,17 @@ +FactoryGirl.define do + sequence(:gitaly_commit_id) { Digest::SHA1.hexdigest(Time.now.to_f.to_s) } + + factory :gitaly_commit, class: Gitaly::GitCommit do + skip_create + + id { generate(:gitaly_commit_id) } + parent_ids do + ids = [generate(:gitaly_commit_id), generate(:gitaly_commit_id)] + Google::Protobuf::RepeatedField.new(:string, ids) + end + subject { "My commit" } + body { subject + "\nMy body" } + author { build(:gitaly_commit_author) } + committer { build(:gitaly_commit_author) } + end +end diff --git a/spec/factories/gitaly/commit_author.rb b/spec/factories/gitaly/commit_author.rb new file mode 100644 index 00000000000..341873a2002 --- /dev/null +++ b/spec/factories/gitaly/commit_author.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + factory :gitaly_commit_author, class: Gitaly::CommitAuthor do + skip_create + + name { generate(:name) } + email { generate(:email) } + date { Google::Protobuf::Timestamp.new(seconds: Time.now.to_i) } + end +end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 7493b0a8b35..958d62181a2 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -143,6 +143,16 @@ FactoryGirl.define do end end + trait :wiki_repo do + after(:create) do |project| + raise 'Failed to create wiki repository!' unless project.create_wiki + end + end + + trait :readonly do + repository_read_only true + end + trait :broken_repo do after(:create) do |project| raise "Failed to create repository!" unless project.create_repository diff --git a/spec/factories/user_custom_attributes.rb b/spec/factories/user_custom_attributes.rb new file mode 100644 index 00000000000..278cf290d4f --- /dev/null +++ b/spec/factories/user_custom_attributes.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :user_custom_attribute do + user + sequence(:key) { |n| "key#{n}" } + sequence(:value) { |n| "value#{n}" } + end +end diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index b2229b44f99..a89dcdf41dc 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -405,7 +405,7 @@ feature 'Issues > Labels bulk assignment' do end def update_issues - click_button 'Update all' + find('.update-selected-issues').trigger('click') wait_for_requests end diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb index 5eeecaeda47..447281ed19d 100644 --- a/spec/features/issues/filtered_search/recent_searches_spec.rb +++ b/spec/features/issues/filtered_search/recent_searches_spec.rb @@ -104,6 +104,6 @@ describe 'Recent searches', js: true do set_recent_searches(project_1_local_storage_key, 'fail') visit project_issues_path(project_1) - expect(find('.flash-alert')).to have_text('An error occured while parsing recent searches') + expect(find('.flash-alert')).to have_text('An error occurred while parsing recent searches') end end diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index bcc6e9bab0f..1f57c110c11 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -118,7 +118,7 @@ feature 'Multiple issue updating from issues#index', :js do end def click_update_issues_button - find('.update-selected-issues').click + find('.update-selected-issues').trigger('click') wait_for_requests end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index a72fc821721..ac8e500a4f4 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -131,6 +131,14 @@ describe 'Issues' do end describe 'Issue info' do + it 'links to current issue in breadcrubs' do + issue = create(:issue, project: project) + + visit project_issue_path(project, issue) + + expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue)) + end + it 'excludes award_emoji from comment count' do issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar') create(:award_emoji, awardable: issue) diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb index e6dc284cba7..9cb8a357309 100644 --- a/spec/features/merge_requests/update_merge_requests_spec.rb +++ b/spec/features/merge_requests/update_merge_requests_spec.rb @@ -127,7 +127,7 @@ feature 'Multiple merge requests updating from merge_requests#index' do end def click_update_merge_requests_button - find('.update-selected-issues').click + find('.update-selected-issues').trigger('click') wait_for_requests end end diff --git a/spec/features/projects/commit/diff_notes_spec.rb b/spec/features/projects/commit/diff_notes_spec.rb new file mode 100644 index 00000000000..f0fe4e00acc --- /dev/null +++ b/spec/features/projects/commit/diff_notes_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +feature 'Commit diff', :js do + include RepoHelpers + + let(:user) { create(:user) } + let(:project) { create(:project, :public, :repository) } + + before do + project.add_master(user) + sign_in user + end + + %w(inline parallel).each do |view| + context "#{view} view" do + before do + visit project_commit_path(project, sample_commit.id, view: view) + end + + it "adds comment to diff" do + diff_line_num = first('.diff-line-num.new') + + diff_line_num.trigger('mouseover') + diff_line_num.find('.js-add-diff-note-button').trigger('click') + + page.within(first('.diff-viewer')) do + find('.js-note-text').set 'test comment' + + click_button 'Comment' + + expect(page).to have_content('test comment') + end + end + end + end +end diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb new file mode 100644 index 00000000000..e10d29e5eea --- /dev/null +++ b/spec/features/projects/fork_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe 'Project fork' do + let(:user) { create(:user) } + let(:project) { create(:project, :public, :repository) } + + before do + sign_in user + end + + it 'allows user to fork project' do + visit project_path(project) + + expect(page).not_to have_css('a.disabled', text: 'Fork') + end + + it 'disables fork button when user has exceeded project limit' do + user.projects_limit = 0 + user.save! + + visit project_path(project) + + expect(page).to have_css('a.disabled', text: 'Fork') + end + + context 'master in group' do + before do + group = create(:group) + group.add_master(user) + end + + it 'allows user to fork project to group or to user namespace' do + visit project_path(project) + + expect(page).not_to have_css('a.disabled', text: 'Fork') + + click_link 'Fork' + + expect(page).to have_css('.fork-thumbnail', count: 2) + expect(page).not_to have_css('.fork-thumbnail.disabled') + end + + it 'allows user to fork project to group and not user when exceeded project limit' do + user.projects_limit = 0 + user.save! + + visit project_path(project) + + expect(page).not_to have_css('a.disabled', text: 'Fork') + + click_link 'Fork' + + expect(page).to have_css('.fork-thumbnail', count: 2) + expect(page).to have_css('.fork-thumbnail.disabled') + end + end +end 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 1cf14204159..949d90a50ff 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -40,7 +40,7 @@ describe 'User updates wiki page' do expect(current_path).to include('one/two/three-test') expect(find('.wiki-pages')).to have_content('Three') - click_on('Three') + first(:link, text: 'Three').click expect(find('.nav-text')).to have_content('Three') diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index d201d4f6b98..49ba2969ef0 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -34,7 +34,7 @@ describe 'User views a wiki page' do it 'shows the history of a page that has a path', :js do expect(current_path).to include('one/two/three-test') - click_on('Three') + first(:link, text: 'Three').click click_on('Page history') expect(current_path).to include('one/two/three-test') @@ -48,7 +48,7 @@ describe 'User views a wiki page' do expect(current_path).to include('one/two/three-test') expect(find('.wiki-pages')).to have_content('Three') - click_on('Three') + first(:link, text: 'Three').click expect(find('.nav-text')).to have_content('Three') diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index b6367b88e17..917fad74ef1 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -24,6 +24,24 @@ feature 'Signup' do end end + context "when sigining up with different cased emails" do + it "creates the user successfully" do + user = build(:user) + + visit root_path + + fill_in 'new_user_name', with: user.name + fill_in 'new_user_username', with: user.username + fill_in 'new_user_email', with: user.email + fill_in 'new_user_email_confirmation', with: user.email.capitalize + fill_in 'new_user_password', with: user.password + click_button "Register" + + expect(current_path).to eq dashboard_projects_path + expect(page).to have_content("Welcome! You have signed up successfully.") + end + end + context "when not sending confirmation email" do before do stub_application_setting(send_user_confirmation_email: false) diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb index d6a6b8fc7d5..80750c904b5 100644 --- a/spec/features/tags/master_deletes_tag_spec.rb +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -35,15 +35,30 @@ feature 'Master deletes tag' do end context 'when pre-receive hook fails', js: true do - before do - allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) - .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags') + context 'when Gitaly operation_user_delete_tag feature is enabled' do + before do + allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag) + .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags') + end + + scenario 'shows the error message' do + delete_first_tag + + expect(page).to have_content('Do not delete tags') + end end - scenario 'shows the error message' do - delete_first_tag + context 'when Gitaly operation_user_delete_tag feature is disabled', skip_gitaly_mock: true do + before do + allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) + .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags') + end + + scenario 'shows the error message' do + delete_first_tag - expect(page).to have_content('Do not delete tags') + expect(page).to have_content('Do not delete tags') + end end end diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb index 1bab6d64388..4249c52c481 100644 --- a/spec/finders/users_finder_spec.rb +++ b/spec/finders/users_finder_spec.rb @@ -56,6 +56,15 @@ describe UsersFinder do expect(users.map(&:username)).not_to include([filtered_user_before.username, filtered_user_after.username]) end + + it 'does not filter by custom attributes' do + users = described_class.new( + user, + custom_attributes: { foo: 'bar' } + ).execute + + expect(users).to contain_exactly(user, user1, user2, omniauth_user) + end end context 'with an admin user' do @@ -72,6 +81,19 @@ describe UsersFinder do expect(users).to contain_exactly(admin, user1, user2, external_user, omniauth_user) end + + it 'filters by custom attributes' do + create :user_custom_attribute, user: user1, key: 'foo', value: 'foo' + create :user_custom_attribute, user: user1, key: 'bar', value: 'bar' + create :user_custom_attribute, user: user2, key: 'foo', value: 'foo' + + users = described_class.new( + admin, + custom_attributes: { foo: 'foo', bar: 'bar' } + ).execute + + expect(users).to contain_exactly(user1) + end end end end diff --git a/spec/fixtures/api/schemas/public_api/v4/commit/detail.json b/spec/fixtures/api/schemas/public_api/v4/commit/detail.json index b7b2535c204..88a3cad62f6 100644 --- a/spec/fixtures/api/schemas/public_api/v4/commit/detail.json +++ b/spec/fixtures/api/schemas/public_api/v4/commit/detail.json @@ -5,11 +5,18 @@ { "required" : [ "stats", - "status" + "status", + "last_pipeline" ], "properties": { "stats": { "$ref": "../commit_stats.json" }, - "status": { "type": ["string", "null"] } + "status": { "type": ["string", "null"] }, + "last_pipeline": { + "oneOf": [ + { "type": "null" }, + { "$ref": "../pipeline/basic.json" } + ] + } } } ] diff --git a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json new file mode 100644 index 00000000000..0d127dc5297 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "required" : [ + "id", + "sha", + "ref", + "status" + ], + "properties" : { + "id": { "type": "integer" }, + "sha": { "type": "string" }, + "ref": { "type": "string" }, + "status": { "type": "string" } + }, + "additionalProperties": false +} diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 7ded95d01af..641971485de 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -200,13 +200,13 @@ describe ProjectsHelper do end it 'returns image tag for member avatar' do - expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "" }) + expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "", "data-src" => anything }) helper.link_to_member_avatar(user) end it 'returns image tag with avatar class' do - expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "" }) + expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "", "data-src" => anything }) helper.link_to_member_avatar(user, avatar_class: "any-avatar-class") end diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js index 11f2a950678..c3e67550f05 100644 --- a/spec/javascripts/blob/notebook/index_spec.js +++ b/spec/javascripts/blob/notebook/index_spec.js @@ -117,7 +117,7 @@ describe('iPython notebook renderer', () => { it('shows error message', () => { expect( document.querySelector('.md').textContent.trim(), - ).toBe('An error occured whilst parsing the file.'); + ).toBe('An error occurred whilst parsing the file.'); }); }); @@ -153,7 +153,7 @@ describe('iPython notebook renderer', () => { it('shows error message', () => { expect( document.querySelector('.md').textContent.trim(), - ).toBe('An error occured whilst loading the file. Please try again later.'); + ).toBe('An error occurred whilst loading the file. Please try again later.'); }); }); }); diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js index bbeaf95e68d..51bf3086627 100644 --- a/spec/javascripts/blob/pdf/index_spec.js +++ b/spec/javascripts/blob/pdf/index_spec.js @@ -76,7 +76,7 @@ describe('PDF renderer', () => { it('shows error message', () => { expect( document.querySelector('.md').textContent.trim(), - ).toBe('An error occured whilst loading the file. Please try again later.'); + ).toBe('An error occurred whilst loading the file. Please try again later.'); }); }); }); diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/build_spec.js index 35149611095..d5b0f23e7b7 100644 --- a/spec/javascripts/build_spec.js +++ b/spec/javascripts/build_spec.js @@ -289,4 +289,18 @@ describe('Build', () => { }); }); }); + + describe('getBuildTrace', () => { + it('should request build trace with state parameter', (done) => { + spyOn(jQuery, 'ajax').and.callThrough(); + new Build(); + + setTimeout(() => { + expect(jQuery.ajax).toHaveBeenCalledWith( + { url: `${BUILD_URL}/trace.json`, data: { state: '' } }, + ); + done(); + }, 0); + }); + }); }); diff --git a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js index 2fb9eb0ca85..13e9fe00a00 100644 --- a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js +++ b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import Translate from '~/vue_shared/translate'; -import limitWarningComp from '~/cycle_analytics/components/limit_warning_component'; +import limitWarningComp from '~/cycle_analytics/components/limit_warning_component.vue'; Vue.use(Translate); diff --git a/spec/javascripts/issuable_context_spec.js b/spec/javascripts/issuable_context_spec.js new file mode 100644 index 00000000000..bcb2b7b24a0 --- /dev/null +++ b/spec/javascripts/issuable_context_spec.js @@ -0,0 +1,34 @@ +/* global IssuableContext */ +import '~/issuable_context'; +import $ from 'jquery'; + +describe('IssuableContext', () => { + describe('toggleHiddenParticipants', () => { + const event = jasmine.createSpyObj('event', ['preventDefault']); + + beforeEach(() => { + spyOn($.fn, 'data').and.returnValue('data'); + spyOn($.fn, 'text').and.returnValue('data'); + }); + + afterEach(() => { + gl.lazyLoader = undefined; + }); + + it('calls loadCheck if lazyLoader is set', () => { + gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']); + + IssuableContext.prototype.toggleHiddenParticipants(event); + + expect(gl.lazyLoader.loadCheck).toHaveBeenCalled(); + }); + + it('does not throw if lazyLoader is not defined', () => { + gl.lazyLoader = undefined; + + const toggle = IssuableContext.prototype.toggleHiddenParticipants.bind(null, event); + + expect(toggle).not.toThrow(); + }); + }); +}); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 787b405de47..f86f2f260c3 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -84,6 +84,62 @@ describe('common_utils', () => { expectGetElementIdToHaveBeenCalledWith('definição'); expectGetElementIdToHaveBeenCalledWith('user-content-definição'); }); + + it('scrolls element into view', () => { + document.body.innerHTML += ` + <div id="parent"> + <div style="height: 2000px;"></div> + <div id="test" style="height: 2000px;"></div> + </div> + `; + + window.history.pushState({}, null, '#test'); + commonUtils.handleLocationHash(); + + expectGetElementIdToHaveBeenCalledWith('test'); + expect(window.scrollY).toBe(document.getElementById('test').offsetTop); + + document.getElementById('parent').remove(); + }); + + it('scrolls user content element into view', () => { + document.body.innerHTML += ` + <div id="parent"> + <div style="height: 2000px;"></div> + <div id="user-content-test" style="height: 2000px;"></div> + </div> + `; + + window.history.pushState({}, null, '#test'); + commonUtils.handleLocationHash(); + + expectGetElementIdToHaveBeenCalledWith('test'); + expectGetElementIdToHaveBeenCalledWith('user-content-test'); + expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop); + + document.getElementById('parent').remove(); + }); + + it('scrolls to element with offset from navbar', () => { + spyOn(window, 'scrollBy').and.callThrough(); + document.body.innerHTML += ` + <div id="parent"> + <div class="navbar-gitlab" style="position: fixed; top: 0; height: 50px;"></div> + <div style="height: 2000px; margin-top: 50px;"></div> + <div id="user-content-test" style="height: 2000px;"></div> + </div> + `; + + window.history.pushState({}, null, '#test'); + commonUtils.handleLocationHash(); + + expectGetElementIdToHaveBeenCalledWith('test'); + expectGetElementIdToHaveBeenCalledWith('user-content-test'); + expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop - 50); + expect(window.scrollBy).toHaveBeenCalledWith(0, -50); + + document.getElementById('parent').remove(); + }); }); describe('setParamInURL', () => { diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js index c3ee3ef9825..b87c836654d 100644 --- a/spec/javascripts/lib/utils/sticky_spec.js +++ b/spec/javascripts/lib/utils/sticky_spec.js @@ -1,52 +1,79 @@ import { isSticky } from '~/lib/utils/sticky'; describe('sticky', () => { - const el = { - offsetTop: 0, - classList: {}, - }; + let el; beforeEach(() => { - el.offsetTop = 0; - el.classList.add = jasmine.createSpy('spy'); - el.classList.remove = jasmine.createSpy('spy'); + document.body.innerHTML += ` + <div class="parent"> + <div id="js-sticky"></div> + </div> + `; + + el = document.getElementById('js-sticky'); }); - describe('classList.remove', () => { - it('does not call classList.remove when stuck', () => { - isSticky(el, 0, 0); + afterEach(() => { + el.parentNode.remove(); + }); + + describe('when stuck', () => { + it('does not remove is-stuck class', () => { + isSticky(el, 0, el.offsetTop); + isSticky(el, 0, el.offsetTop); expect( - el.classList.remove, - ).not.toHaveBeenCalled(); + el.classList.contains('is-stuck'), + ).toBeTruthy(); }); - it('calls classList.remove when not stuck', () => { - el.offsetTop = 10; - isSticky(el, 0, 0); + it('adds is-stuck class', () => { + isSticky(el, 0, el.offsetTop); expect( - el.classList.remove, - ).toHaveBeenCalledWith('is-stuck'); + el.classList.contains('is-stuck'), + ).toBeTruthy(); + }); + + it('inserts placeholder element', () => { + isSticky(el, 0, el.offsetTop, true); + + expect( + document.querySelector('.sticky-placeholder'), + ).not.toBeNull(); }); }); - describe('classList.add', () => { - it('calls classList.add when stuck', () => { + describe('when not stuck', () => { + it('removes is-stuck class', () => { + spyOn(el.classList, 'remove').and.callThrough(); + + isSticky(el, 0, el.offsetTop); isSticky(el, 0, 0); expect( - el.classList.add, + el.classList.remove, ).toHaveBeenCalledWith('is-stuck'); + expect( + el.classList.contains('is-stuck'), + ).toBeFalsy(); }); - it('does not call classList.add when not stuck', () => { - el.offsetTop = 10; + it('does not add is-stuck class', () => { isSticky(el, 0, 0); expect( - el.classList.add, - ).not.toHaveBeenCalled(); + el.classList.contains('is-stuck'), + ).toBeFalsy(); + }); + + it('removes placeholder', () => { + isSticky(el, 0, el.offsetTop, true); + isSticky(el, 0, 0, true); + + expect( + document.querySelector('.sticky-placeholder'), + ).toBeNull(); }); }); }); diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js index aee274641e8..645664a5219 100644 --- a/spec/javascripts/line_highlighter_spec.js +++ b/spec/javascripts/line_highlighter_spec.js @@ -18,19 +18,25 @@ import '~/line_highlighter'; beforeEach(function() { loadFixtures('static/line_highlighter.html.raw'); this["class"] = new LineHighlighter(); - this.css = this["class"].highlightClass; + this.css = this["class"].highlightLineClass; return this.spies = { __setLocationHash__: spyOn(this["class"], '__setLocationHash__').and.callFake(function() {}) }; }); describe('behavior', function() { it('highlights one line given in the URL hash', function() { - new LineHighlighter('#L13'); + new LineHighlighter({ hash: '#L13' }); return expect($('#LC13')).toHaveClass(this.css); }); + it('highlights one line given in the URL hash with given CSS class name', function() { + const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' }); + expect(hiliter.highlightLineClass).toBe('hilite'); + expect($('#LC13')).toHaveClass('hilite'); + expect($('#LC13')).not.toHaveClass('hll'); + }); it('highlights a range of lines given in the URL hash', function() { var line, results; - new LineHighlighter('#L5-25'); + new LineHighlighter({ hash: '#L5-25' }); expect($("." + this.css).length).toBe(21); results = []; for (line = 5; line <= 25; line += 1) { @@ -41,7 +47,7 @@ import '~/line_highlighter'; it('scrolls to the first highlighted line on initial load', function() { var spy; spy = spyOn($, 'scrollTo'); - new LineHighlighter('#L5-25'); + new LineHighlighter({ hash: '#L5-25' }); return expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything()); }); it('discards click events', function() { @@ -50,10 +56,10 @@ import '~/line_highlighter'; clickLine(13); return expect(spy).toHaveBeenPrevented(); }); - return it('handles garbage input from the hash', function() { + it('handles garbage input from the hash', function() { var func; func = function() { - return new LineHighlighter('#blob-content-holder'); + return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' }); }; return expect(func).not.toThrow(); }); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index eadab738376..ccdbfcba692 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -416,5 +416,28 @@ import 'vendor/jquery.scrollTo'; }); }); }); + + describe('expandViewContainer', function () { + beforeEach(() => { + $('body').append('<div class="content-wrapper"><div class="container-fluid container-limited"></div></div>'); + }); + + afterEach(() => { + $('.content-wrapper').remove(); + }); + + it('removes container-limited from containers', function () { + this.class.expandViewContainer(); + + expect($('.content-wrapper')).not.toContainElement('.container-limited'); + }); + + it('does remove container-limited from breadcrumbs', function () { + $('.container-limited').addClass('breadcrumbs'); + this.class.expandViewContainer(); + + expect($('.content-wrapper')).toContainElement('.container-limited'); + }); + }); }); }).call(window); diff --git a/spec/javascripts/projects_dropdown/service/projects_service_spec.js b/spec/javascripts/projects_dropdown/service/projects_service_spec.js index d5dd8b3449a..cfd1bb7d24f 100644 --- a/spec/javascripts/projects_dropdown/service/projects_service_spec.js +++ b/spec/javascripts/projects_dropdown/service/projects_service_spec.js @@ -34,7 +34,7 @@ describe('ProjectsService', () => { const searchQuery = 'lab'; const queryParams = { - simple: false, + simple: true, per_page: 20, membership: true, order_by: 'last_activity_at', diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js index 29dc2d21e4b..411514009dc 100644 --- a/spec/javascripts/repo/components/repo_edit_button_spec.js +++ b/spec/javascripts/repo/components/repo_edit_button_spec.js @@ -21,13 +21,11 @@ describe('RepoEditButton', () => { expect(vm.$el.textContent).toMatch('Edit'); spyOn(vm, 'editCancelClicked').and.callThrough(); - spyOn(vm, 'toggleProjectRefsForm'); vm.$el.click(); Vue.nextTick(() => { expect(vm.editCancelClicked).toHaveBeenCalled(); - expect(vm.toggleProjectRefsForm).toHaveBeenCalled(); expect(vm.$el.textContent).toMatch('Cancel edit'); done(); }); diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js index 518a2d25ecf..f15633bd8b9 100644 --- a/spec/javascripts/repo/components/repo_file_spec.js +++ b/spec/javascripts/repo/components/repo_file_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import repoFile from '~/repo/components/repo_file.vue'; +import RepoStore from '~/repo/stores/repo_store'; describe('RepoFile', () => { const updated = 'updated'; @@ -12,8 +13,13 @@ describe('RepoFile', () => { level: 10, }; const activeFile = { + pageTitle: 'pageTitle', url: 'url', }; + const otherFile = { + html: '<p class="file-content">html</p>', + pageTitle: 'otherpageTitle', + }; function createComponent(propsData) { const RepoFile = Vue.extend(repoFile); @@ -60,6 +66,12 @@ describe('RepoFile', () => { expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy(); }); + it('sets the document title correctly', () => { + RepoStore.setActiveFiles(otherFile); + + expect(document.title.trim()).toEqual(otherFile.pageTitle); + }); + it('renders a spinner if the file is loading', () => { file.loading = true; const vm = createComponent({ diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/repo_sidebar_spec.js index abcff8e537e..db9911c7a2c 100644 --- a/spec/javascripts/repo/components/repo_sidebar_spec.js +++ b/spec/javascripts/repo/components/repo_sidebar_spec.js @@ -79,6 +79,20 @@ describe('RepoSidebar', () => { expect(Helper.getContent).toHaveBeenCalledWith(file1); }); + it('should not fetch data for already opened files', () => { + const file = { + id: 42, + url: 'foo', + }; + + spyOn(Helper, 'getFileFromPath').and.returnValue(file); + spyOn(RepoStore, 'setActiveFiles'); + const vm = createComponent(); + vm.fileClicked(file); + + expect(RepoStore.setActiveFiles).toHaveBeenCalledWith(file); + }); + it('should hide files in directory if already open', () => { spyOn(RepoStore, 'removeChildFilesOfTree').and.callThrough(); const file1 = { diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index f2072a6f350..5505f983d71 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -32,56 +32,86 @@ import '~/right_sidebar'; }; describe('RightSidebar', function() { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); - loadJSONFixtures('todos/todos.json'); - - beforeEach(function() { - loadFixtures(fixtureName); - this.sidebar = new Sidebar; - $aside = $('.right-sidebar'); - $page = $('.page-with-sidebar'); - $icon = $aside.find('i'); - $toggle = $aside.find('.js-sidebar-toggle'); - return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); - }); - it('should expand/collapse the sidebar when arrow is clicked', function() { - assertSidebarState('expanded'); - $toggle.click(); - assertSidebarState('collapsed'); - $toggle.click(); - assertSidebarState('expanded'); - }); - it('should float over the page and when sidebar icons clicked', function() { - $labelsIcon.click(); - return assertSidebarState('expanded'); - }); - it('should collapse when the icon arrow clicked while it is floating on page', function() { - $labelsIcon.click(); - assertSidebarState('expanded'); - $toggle.click(); - return assertSidebarState('collapsed'); + describe('fixture tests', () => { + var fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + loadJSONFixtures('todos/todos.json'); + + beforeEach(function() { + loadFixtures(fixtureName); + this.sidebar = new Sidebar; + $aside = $('.right-sidebar'); + $page = $('.page-with-sidebar'); + $icon = $aside.find('i'); + $toggle = $aside.find('.js-sidebar-toggle'); + return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); + }); + it('should expand/collapse the sidebar when arrow is clicked', function() { + assertSidebarState('expanded'); + $toggle.click(); + assertSidebarState('collapsed'); + $toggle.click(); + assertSidebarState('expanded'); + }); + it('should float over the page and when sidebar icons clicked', function() { + $labelsIcon.click(); + return assertSidebarState('expanded'); + }); + it('should collapse when the icon arrow clicked while it is floating on page', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + $toggle.click(); + return assertSidebarState('collapsed'); + }); + + it('should broadcast todo:toggle event when add todo clicked', function() { + var todos = getJSONFixture('todos/todos.json'); + spyOn(jQuery, 'ajax').and.callFake(function() { + var d = $.Deferred(); + var response = todos; + d.resolve(response); + return d.promise(); + }); + + var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + + $('.issuable-sidebar-header .js-issuable-todo').click(); + + expect(todoToggleSpy.calls.count()).toEqual(1); + }); + + it('should not hide collapsed icons', () => { + [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { + expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); + }); + }); }); - it('should broadcast todo:toggle event when add todo clicked', function() { - var todos = getJSONFixture('todos/todos.json'); - spyOn(jQuery, 'ajax').and.callFake(function() { - var d = $.Deferred(); - var response = todos; - d.resolve(response); - return d.promise(); + describe('sidebarToggleClicked', () => { + const event = jasmine.createSpyObj('event', ['preventDefault']); + + beforeEach(() => { + spyOn($.fn, 'hasClass').and.returnValue(false); + }); + + afterEach(() => { + gl.lazyLoader = undefined; }); - var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + it('calls loadCheck if lazyLoader is set', () => { + gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']); - $('.issuable-sidebar-header .js-issuable-todo').click(); + Sidebar.prototype.sidebarToggleClicked(event); - expect(todoToggleSpy.calls.count()).toEqual(1); - }); + expect(gl.lazyLoader.loadCheck).toHaveBeenCalled(); + }); + + it('does not throw if lazyLoader is not defined', () => { + gl.lazyLoader = undefined; + + const toggle = Sidebar.prototype.sidebarToggleClicked.bind(null, event); - it('should not hide collapsed icons', () => { - [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { - expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); + expect(toggle).not.toThrow(); }); }); }); diff --git a/spec/lib/github/client_spec.rb b/spec/lib/github/client_spec.rb new file mode 100644 index 00000000000..b846096fe25 --- /dev/null +++ b/spec/lib/github/client_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe Github::Client do + let(:connection) { spy } + let(:rate_limit) { double(get: [false, 1]) } + let(:client) { described_class.new({}) } + let(:results) { double } + let(:response) { double } + + before do + allow(Faraday).to receive(:new).and_return(connection) + allow(Github::RateLimit).to receive(:new).with(connection).and_return(rate_limit) + end + + describe '#get' do + before do + allow(Github::Response).to receive(:new).with(results).and_return(response) + end + + it 'uses a default per_page param' do + expect(connection).to receive(:get).with('/foo', per_page: 100).and_return(results) + + expect(client.get('/foo')).to eq(response) + end + + context 'with per_page given' do + it 'overwrites the default per_page' do + expect(connection).to receive(:get).with('/foo', per_page: 30).and_return(results) + + expect(client.get('/foo', per_page: 30)).to eq(response) + end + end + end +end diff --git a/spec/lib/github/import/legacy_diff_note_spec.rb b/spec/lib/github/import/legacy_diff_note_spec.rb new file mode 100644 index 00000000000..8c50b46cacb --- /dev/null +++ b/spec/lib/github/import/legacy_diff_note_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Github::Import::LegacyDiffNote do + describe '#type' do + it 'returns the original note type' do + expect(described_class.new.type).to eq('LegacyDiffNote') + end + end +end diff --git a/spec/lib/github/import/note_spec.rb b/spec/lib/github/import/note_spec.rb new file mode 100644 index 00000000000..fcdccd9e097 --- /dev/null +++ b/spec/lib/github/import/note_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Github::Import::Note do + describe '#type' do + it 'returns the original note type' do + expect(described_class.new.type).to eq('Note') + end + end +end diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index c0427639746..d2e7243ee05 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits do +describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :truncate do describe '#perform' do - set(:merge_request) { create(:merge_request) } - set(:merge_request_diff) { merge_request.merge_request_diff } + let(:merge_request) { create(:merge_request) } + let(:merge_request_diff) { merge_request.merge_request_diff } let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) } def diffs_to_hashes(diffs) @@ -70,8 +70,8 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits do before do merge_request.reload_diff(true) - convert_to_yaml(start_id, merge_request_diff.commits, merge_request_diff.diffs) - convert_to_yaml(stop_id, updated_merge_request_diff.commits, updated_merge_request_diff.diffs) + convert_to_yaml(start_id, merge_request_diff.commits, diffs_to_hashes(merge_request_diff.merge_request_diff_files)) + convert_to_yaml(stop_id, updated_merge_request_diff.commits, diffs_to_hashes(updated_merge_request_diff.merge_request_diff_files)) MergeRequestDiffCommit.delete_all MergeRequestDiffFile.delete_all @@ -80,10 +80,32 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits do context 'when BUFFER_ROWS is exceeded' do before do stub_const("#{described_class}::BUFFER_ROWS", 1) + + allow(Gitlab::Database).to receive(:bulk_insert).and_call_original + end + + it 'inserts commit rows in chunks of BUFFER_ROWS' do + # There are 29 commits in each diff, so we should have slices of 20 + 9 + 20 + 9. + stub_const("#{described_class}::BUFFER_ROWS", 20) + + expect(Gitlab::Database).to receive(:bulk_insert) + .with('merge_request_diff_commits', anything) + .exactly(4) + .times + .and_call_original + + subject.perform(start_id, stop_id) end - it 'updates and continues' do - expect(described_class::MergeRequestDiff).to receive(:transaction).twice + it 'inserts diff rows in chunks of DIFF_FILE_BUFFER_ROWS' do + # There are 20 files in each diff, so we should have slices of 20 + 20. + stub_const("#{described_class}::DIFF_FILE_BUFFER_ROWS", 20) + + expect(Gitlab::Database).to receive(:bulk_insert) + .with('merge_request_diff_files', anything) + .exactly(2) + .times + .and_call_original subject.perform(start_id, stop_id) end @@ -91,27 +113,87 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits do context 'when BUFFER_ROWS is not exceeded' do it 'only updates once' do - expect(described_class::MergeRequestDiff).to receive(:transaction).once + expect(Gitlab::Database).to receive(:bulk_insert) + .with('merge_request_diff_commits', anything) + .once + .and_call_original + + expect(Gitlab::Database).to receive(:bulk_insert) + .with('merge_request_diff_files', anything) + .once + .and_call_original subject.perform(start_id, stop_id) end end - end - context 'when the merge request diff update fails' do - before do - allow(described_class::MergeRequestDiff) - .to receive(:update_all).and_raise(ActiveRecord::Rollback) - end + context 'when some rows were already inserted due to a previous failure' do + before do + subject.perform(start_id, stop_id) - it 'does not add any diff commits' do - expect { subject.perform(merge_request_diff.id, merge_request_diff.id) } - .not_to change { MergeRequestDiffCommit.count } + convert_to_yaml(start_id, merge_request_diff.commits, diffs_to_hashes(merge_request_diff.merge_request_diff_files)) + convert_to_yaml(stop_id, updated_merge_request_diff.commits, diffs_to_hashes(updated_merge_request_diff.merge_request_diff_files)) + end + + it 'does not raise' do + expect { subject.perform(start_id, stop_id) }.not_to raise_exception + end + + it 'logs a message' do + expect(Rails.logger).to receive(:info) + .with( + a_string_matching(described_class.name).and(matching([start_id, stop_id].inspect)) + ) + .twice + + subject.perform(start_id, stop_id) + end + + it 'ends up with the correct rows' do + expect(updated_merge_request_diff.commits.count).to eq(29) + expect(updated_merge_request_diff.raw_diffs.count).to eq(20) + end end - it 'does not add any diff files' do - expect { subject.perform(merge_request_diff.id, merge_request_diff.id) } - .not_to change { MergeRequestDiffFile.count } + context 'when the merge request diff update fails' do + let(:exception) { ActiveRecord::RecordNotFound } + + let(:perform_ignoring_exceptions) do + begin + subject.perform(start_id, stop_id) + rescue described_class::Error + end + end + + before do + allow_any_instance_of(described_class::MergeRequestDiff::ActiveRecord_Relation) + .to receive(:update_all).and_raise(exception) + end + + it 'raises an error' do + expect { subject.perform(start_id, stop_id) } + .to raise_exception(described_class::Error) + end + + it 'logs the error' do + expect(Rails.logger).to receive(:info).with( + a_string_matching(described_class.name) + .and(matching([start_id, stop_id].inspect)) + .and(matching(exception.name)) + ) + + perform_ignoring_exceptions + end + + it 'still adds diff commits' do + expect { perform_ignoring_exceptions } + .to change { MergeRequestDiffCommit.count } + end + + it 'still adds diff files' do + expect { perform_ignoring_exceptions } + .to change { MergeRequestDiffFile.count } + end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb new file mode 100644 index 00000000000..f54e2326b06 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Create do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:pipeline) do + build(:ci_pipeline_with_one_job, project: project, + ref: 'master') + end + + let(:command) do + double('command', project: project, + current_user: user, + seeds_block: nil) + end + + let(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline is ready to be saved' do + it 'saves a pipeline' do + expect(pipeline).to be_persisted + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + + it 'creates stages' do + expect(pipeline.reload.stages).to be_one + end + end + + context 'when pipeline has validation errors' do + let(:pipeline) do + build(:ci_pipeline, project: project, ref: nil) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'appends validation error' do + expect(pipeline.errors.to_a) + .to include /Failed to persist the pipeline/ + end + end + + context 'when there is a seed block present' do + let(:seeds) { spy('pipeline seeds') } + + let(:command) do + double('command', project: project, + current_user: user, + seeds_block: seeds) + end + + it 'executes the block' do + expect(seeds).to have_received(:call).with(pipeline) + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb new file mode 100644 index 00000000000..e165e0fac2a --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Sequence do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:pipeline) { build_stubbed(:ci_pipeline) } + let(:command) { double('command' ) } + let(:first_step) { spy('first step') } + let(:second_step) { spy('second step') } + let(:sequence) { [first_step, second_step] } + + subject do + described_class.new(pipeline, command, sequence) + end + + context 'when one of steps breaks the chain' do + before do + allow(first_step).to receive(:break?).and_return(true) + end + + it 'does not process the second step' do + subject.build! do |pipeline, sequence| + expect(sequence).not_to be_complete + end + + expect(second_step).not_to have_received(:perform!) + end + + it 'returns a pipeline object' do + expect(subject.build!).to eq pipeline + end + end + + context 'when all chains are executed correctly' do + before do + sequence.each do |step| + allow(step).to receive(:break?).and_return(false) + end + end + + it 'iterates through entire sequence' do + subject.build! do |pipeline, sequence| + expect(sequence).to be_complete + end + + expect(first_step).to have_received(:perform!) + expect(second_step).to have_received(:perform!) + end + + it 'returns a pipeline object' do + expect(subject.build!).to eq pipeline + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb new file mode 100644 index 00000000000..32bd5de829b --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Skip do + set(:project) { create(:project) } + set(:user) { create(:user) } + set(:pipeline) { create(:ci_pipeline, project: project) } + + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: false, + save_incompleted: true) + end + + let(:step) { described_class.new(pipeline, command) } + + context 'when pipeline has been skipped by a user' do + before do + allow(pipeline).to receive(:git_commit_message) + .and_return('commit message [ci skip]') + + step.perform! + end + + it 'should break the chain' do + expect(step.break?).to be true + end + + it 'skips the pipeline' do + expect(pipeline.reload).to be_skipped + end + end + + context 'when pipeline has not been skipped' do + before do + step.perform! + end + + it 'should not break the chain' do + expect(step.break?).to be false + end + + it 'should not skip a pipeline chain' do + expect(pipeline.reload).not_to be_skipped + end + end + + context 'when [ci skip] should be ignored' do + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: true) + end + + it 'does not break the chain' do + step.perform! + + expect(step.break?).to be false + end + end + + context 'when pipeline should be skipped but not persisted' do + let(:command) do + double('command', project: project, + current_user: user, + ignore_skip_ci: false, + save_incompleted: false) + end + + before do + allow(pipeline).to receive(:git_commit_message) + .and_return('commit message [ci skip]') + + step.perform! + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'does not skip pipeline' do + expect(pipeline.reload).not_to be_skipped + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb new file mode 100644 index 00000000000..0bbdd23f4d6 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb @@ -0,0 +1,142 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do + set(:project) { create(:project, :repository) } + set(:user) { create(:user) } + + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: ref, project: project) + end + + let(:command) do + double('command', project: project, current_user: user) + end + + let(:step) { described_class.new(pipeline, command) } + + let(:ref) { 'master' } + + context 'when users has no ability to run a pipeline' do + before do + step.perform! + end + + it 'adds an error about insufficient permissions' do + expect(pipeline.errors.to_a) + .to include /Insufficient permissions/ + end + + it 'breaks the pipeline builder chain' do + expect(step.break?).to eq true + end + end + + context 'when user has ability to create a pipeline' do + before do + project.add_developer(user) + + step.perform! + end + + it 'does not invalidate the pipeline' do + expect(pipeline).to be_valid + end + + it 'does not break the chain' do + expect(step.break?).to eq false + end + end + + describe '#allowed_to_create?' do + subject { step.allowed_to_create? } + + 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/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb new file mode 100644 index 00000000000..3740df88f42 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Config do + set(:project) { create(:project) } + set(:user) { create(:user) } + + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: true) + end + + let!(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline has no YAML configuration' do + let(:pipeline) do + build_stubbed(:ci_pipeline, project: project) + end + + it 'appends errors about missing configuration' do + expect(pipeline.errors.to_a) + .to include 'Missing .gitlab-ci.yml file' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when YAML configuration contains errors' do + let(:pipeline) do + build(:ci_pipeline, project: project, config: 'invalid YAML') + end + + it 'appends errors about YAML errors' do + expect(pipeline.errors.to_a) + .to include 'Invalid configuration format' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + context 'when saving incomplete pipeline is allowed' do + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: true) + end + + it 'fails the pipeline' do + expect(pipeline.reload).to be_failed + end + end + + context 'when saving incomplete pipeline is not allowed' do + let(:command) do + double('command', project: project, + current_user: user, + save_incompleted: false) + end + + it 'does not drop pipeline' do + expect(pipeline).not_to be_failed + expect(pipeline).not_to be_persisted + end + end + end + + context 'when pipeline has no stages / jobs' do + let(:config) do + { rspec: { + script: 'ls', + only: ['something'] + } } + end + + let(:pipeline) do + build(:ci_pipeline, project: project, config: config) + end + + it 'appends an error about missing stages' do + expect(pipeline.errors.to_a) + .to include 'No stages / jobs for this pipeline.' + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when pipeline contains configuration validation errors' do + let(:config) { { rspec: {} } } + + let(:pipeline) do + build(:ci_pipeline, project: project, config: config) + end + + it 'appends configuration validation errors to pipeline errors' do + expect(pipeline.errors.to_a) + .to include "jobs:rspec config can't be blank" + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + end + + context 'when pipeline is correct and complete' do + let(:pipeline) do + build(:ci_pipeline_with_one_job, project: project) + end + + it 'does not invalidate the pipeline' do + expect(pipeline).to be_valid + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb new file mode 100644 index 00000000000..bb356efe9ad --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do + set(:project) { create(:project, :repository) } + set(:user) { create(:user) } + + let(:command) do + double('command', project: project, current_user: user) + end + + let!(:step) { described_class.new(pipeline, command) } + + before do + step.perform! + end + + context 'when pipeline ref and sha exists' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'master', sha: '123', project: project) + end + + it 'does not break the chain' do + expect(step.break?).to be false + end + + it 'does not append pipeline errors' do + expect(pipeline.errors).to be_empty + end + end + + context 'when pipeline ref does not exist' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'something', project: project) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'adds an error about missing ref' do + expect(pipeline.errors.to_a) + .to include 'Reference not found' + end + end + + context 'when pipeline does not have SHA set' do + let(:pipeline) do + build_stubbed(:ci_pipeline, ref: 'master', sha: nil, project: project) + end + + it 'breaks the chain' do + expect(step.break?).to be true + end + + it 'adds an error about missing SHA' do + expect(pipeline.errors.to_a) + .to include 'Commit not found' + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline_duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb index b26728a843c..7c9836e2da6 100644 --- a/spec/lib/gitlab/ci/pipeline_duration_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::PipelineDuration do +describe Gitlab::Ci::Pipeline::Duration do let(:calculated_duration) { calculate(data) } shared_examples 'calculating duration' do @@ -107,9 +107,9 @@ describe Gitlab::Ci::PipelineDuration do def calculate(data) periods = data.shuffle.map do |(first, last)| - Gitlab::Ci::PipelineDuration::Period.new(first, last) + described_class::Period.new(first, last) end - Gitlab::Ci::PipelineDuration.from_periods(periods.sort_by(&:first)) + described_class.from_periods(periods.sort_by(&:first)) end end diff --git a/spec/lib/gitlab/diff/diff_refs_spec.rb b/spec/lib/gitlab/diff/diff_refs_spec.rb index c73708d90a8..f9bfb4c469e 100644 --- a/spec/lib/gitlab/diff/diff_refs_spec.rb +++ b/spec/lib/gitlab/diff/diff_refs_spec.rb @@ -3,6 +3,61 @@ require 'spec_helper' describe Gitlab::Diff::DiffRefs do let(:project) { create(:project, :repository) } + describe '#==' do + let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') } + subject { commit.diff_refs } + + context 'when shas are missing' do + let(:other) { described_class.new(base_sha: subject.base_sha, start_sha: subject.start_sha, head_sha: nil) } + + it 'returns false' do + expect(subject).not_to eq(other) + end + end + + context 'when shas are equal' do + let(:other) { described_class.new(base_sha: subject.base_sha, start_sha: subject.start_sha, head_sha: subject.head_sha) } + + it 'returns true' do + expect(subject).to eq(other) + end + end + + context 'when shas are unequal' do + let(:other) { described_class.new(base_sha: subject.base_sha, start_sha: subject.start_sha, head_sha: subject.head_sha.reverse) } + + it 'returns false' do + expect(subject).not_to eq(other) + end + end + + context 'when shas are truncated' do + context 'when sha prefixes are too short' do + let(:other) { described_class.new(base_sha: subject.base_sha[0, 4], start_sha: subject.start_sha[0, 4], head_sha: subject.head_sha[0, 4]) } + + it 'returns false' do + expect(subject).not_to eq(other) + end + end + + context 'when sha prefixes are equal' do + let(:other) { described_class.new(base_sha: subject.base_sha[0, 10], start_sha: subject.start_sha[0, 10], head_sha: subject.head_sha[0, 10]) } + + it 'returns true' do + expect(subject).to eq(other) + end + end + + context 'when sha prefixes are unequal' do + let(:other) { described_class.new(base_sha: subject.base_sha[0, 10], start_sha: subject.start_sha[0, 10], head_sha: subject.head_sha[0, 10].reverse) } + + it 'returns false' do + expect(subject).not_to eq(other) + end + end + end + end + describe '#compare_in' do context 'with diff refs for the initial commit' do let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') } diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb index d4a2a852c12..7798736a4dc 100644 --- a/spec/lib/gitlab/diff/position_spec.rb +++ b/spec/lib/gitlab/diff/position_spec.rb @@ -429,6 +429,44 @@ describe Gitlab::Diff::Position do end end + describe '#==' do + let(:commit) { project.commit("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") } + + subject do + described_class.new( + old_path: "files/ruby/popen.rb", + new_path: "files/ruby/popen.rb", + old_line: nil, + new_line: 14, + diff_refs: commit.diff_refs + ) + end + + context 'when positions are equal' do + let(:other) { described_class.new(subject.to_h) } + + it 'returns true' do + expect(subject).to eq(other) + end + end + + context 'when positions are equal, except for truncated shas' do + let(:other) { described_class.new(subject.to_h.merge(start_sha: subject.start_sha[0, 10])) } + + it 'returns true' do + expect(subject).to eq(other) + end + end + + context 'when positions are unequal' do + let(:other) { described_class.new(subject.to_h.merge(start_sha: subject.start_sha.reverse)) } + + it 'returns false' do + expect(subject).not_to eq(other) + end + end + end + describe "#to_json" do let(:hash) do { diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index a3dff6d0d4b..3815055139a 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -65,34 +65,12 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe "Commit info from gitaly commit" do - let(:id) { 'f00' } - let(:parent_ids) { %w(b45 b46) } let(:subject) { "My commit".force_encoding('ASCII-8BIT') } let(:body) { subject + "My body".force_encoding('ASCII-8BIT') } - let(:committer) do - Gitaly::CommitAuthor.new( - name: generate(:name), - email: generate(:email), - date: Google::Protobuf::Timestamp.new(seconds: 123) - ) - end - let(:author) do - Gitaly::CommitAuthor.new( - name: generate(:name), - email: generate(:email), - date: Google::Protobuf::Timestamp.new(seconds: 456) - ) - end - let(:gitaly_commit) do - Gitaly::GitCommit.new( - id: id, - subject: subject, - body: body, - author: author, - committer: committer, - parent_ids: parent_ids - ) - end + let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body) } + let(:id) { gitaly_commit.id } + let(:committer) { gitaly_commit.committer } + let(:author) { gitaly_commit.author } let(:commit) { described_class.new(repository, gitaly_commit) } it { expect(commit.short_id).to eq(id[0..10]) } @@ -104,7 +82,7 @@ describe Gitlab::Git::Commit, seed_helper: true do it { expect(commit.author_name).to eq(author.name) } it { expect(commit.committer_name).to eq(committer.name) } it { expect(commit.committer_email).to eq(committer.email) } - it { expect(commit.parent_ids).to eq(parent_ids) } + it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) } context 'no body' do let(:body) { "".force_encoding('ASCII-8BIT') } diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index 3494f0cc98d..ee657101f4c 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -341,8 +341,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do end context 'when diff is quite large will collapse by default' do - let(:iterator) { [{ diff: 'a' * (Gitlab::Git::Diff.collapse_limit + 1) }] } - let(:max_files) { 100 } + let(:iterator) { [{ diff: 'a' * 20480 }] } context 'when no collapse is set' do let(:expanded) { true } diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index d39b33a0c05..4a7b06003fc 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -31,36 +31,6 @@ EOT [".gitmodules"]).patches.first end - describe 'size limit feature toggles' do - context 'when the feature gitlab_git_diff_size_limit_increase is enabled' do - before do - stub_feature_flags(gitlab_git_diff_size_limit_increase: true) - end - - it 'returns 200 KB for size_limit' do - expect(described_class.size_limit).to eq(200.kilobytes) - end - - it 'returns 100 KB for collapse_limit' do - expect(described_class.collapse_limit).to eq(100.kilobytes) - end - end - - context 'when the feature gitlab_git_diff_size_limit_increase is disabled' do - before do - stub_feature_flags(gitlab_git_diff_size_limit_increase: false) - end - - it 'returns 100 KB for size_limit' do - expect(described_class.size_limit).to eq(100.kilobytes) - end - - it 'returns 10 KB for collapse_limit' do - expect(described_class.collapse_limit).to eq(10.kilobytes) - end - end - end - describe '.new' do context 'using a Hash' do context 'with a small diff' do @@ -77,7 +47,7 @@ EOT context 'using a diff that is too large' do it 'prunes the diff' do - diff = described_class.new(diff: 'a' * (described_class.size_limit + 1)) + diff = described_class.new(diff: 'a' * 204800) expect(diff.diff).to be_empty expect(diff).to be_too_large @@ -115,8 +85,8 @@ EOT # The patch total size is 200, with lines between 21 and 54. # This is a quick-and-dirty way to test this. Ideally, a new patch is # added to the test repo with a size that falls between the real limits. - allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(150) - allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(100) + stub_const("#{described_class}::SIZE_LIMIT", 150) + stub_const("#{described_class}::COLLAPSE_LIMIT", 100) end it 'prunes the diff as a large diff instead of as a collapsed diff' do @@ -356,7 +326,7 @@ EOT describe '#collapsed?' do it 'returns true for a diff that is quite large' do - diff = described_class.new({ diff: 'a' * (described_class.collapse_limit + 1) }, expanded: false) + diff = described_class.new({ diff: 'a' * 20480 }, expanded: false) expect(diff).to be_collapsed end diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 0ff4f3bd105..2fe1f5603ce 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -14,6 +14,7 @@ describe Gitlab::Git::Hook do let(:repo_path) { repository.path } let(:user) { create(:user) } let(:gl_id) { Gitlab::GlId.gl_id(user) } + let(:gl_username) { user.username } def create_hook(name) FileUtils.mkdir_p(File.join(repo_path, 'hooks')) @@ -42,6 +43,7 @@ describe Gitlab::Git::Hook do let(:env) do { 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username, 'PWD' => repo_path, 'GL_PROTOCOL' => 'web', 'GL_REPOSITORY' => gl_repository @@ -59,7 +61,7 @@ describe Gitlab::Git::Hook do .with(env, hook_path, chdir: repo_path).and_call_original end - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be true expect(errors).to be_blank end @@ -72,7 +74,7 @@ describe Gitlab::Git::Hook do blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be false expect(errors).to eq("error message from the hook<br>error message from the hook line 2<br>") end @@ -86,7 +88,7 @@ describe Gitlab::Git::Hook do blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be true expect(errors).to be_nil end diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb index d4d75b66659..51e4e3fdad1 100644 --- a/spec/lib/gitlab/git/hooks_service_spec.rb +++ b/spec/lib/gitlab/git/hooks_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Git::HooksService, seed_helper: true do - let(:user) { Gitlab::Git::User.new('Jane Doe', 'janedoe@example.com', 'user-456') } + let(:user) { Gitlab::Git::User.new('janedoe', 'Jane Doe', 'janedoe@example.com', 'user-456') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, 'project-123') } let(:service) { described_class.new } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 4fc26c625a5..a0482e30a33 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -389,6 +389,40 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#has_local_branches?' do + shared_examples 'check for local branches' do + it { expect(repository.has_local_branches?).to eq(true) } + + context 'mutable' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + + after do + ensure_seeds + end + + it 'returns false when there are no branches' do + # Sanity check + expect(repository.has_local_branches?).to eq(true) + + FileUtils.rm_rf(File.join(repository.path, 'packed-refs')) + heads_dir = File.join(repository.path, 'refs/heads') + FileUtils.rm_rf(heads_dir) + FileUtils.mkdir_p(heads_dir) + + expect(repository.has_local_branches?).to eq(false) + end + end + end + + context 'with gitaly' do + it_behaves_like 'check for local branches' + end + + context 'without gitaly', skip_gitaly_mock: true do + it_behaves_like 'check for local branches' + end + end + describe "#delete_branch" do shared_examples "deleting a branch" do let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } @@ -1332,6 +1366,84 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#with_repo_branch_commit' do + context 'when comparing with the same repository' do + let(:start_repository) { repository } + + context 'when the branch exists' do + let(:start_branch_name) { 'master' } + + it 'yields the commit' do + expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) } + .to yield_with_args(an_instance_of(Gitlab::Git::Commit)) + end + end + + context 'when the branch does not exist' do + let(:start_branch_name) { 'definitely-not-master' } + + it 'yields nil' do + expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) } + .to yield_with_args(nil) + end + end + end + + context 'when comparing with another repository' do + let(:start_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + + context 'when the branch exists' do + let(:start_branch_name) { 'master' } + + it 'yields the commit' do + expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) } + .to yield_with_args(an_instance_of(Gitlab::Git::Commit)) + end + end + + context 'when the branch does not exist' do + let(:start_branch_name) { 'definitely-not-master' } + + it 'yields nil' do + expect { |b| repository.with_repo_branch_commit(start_repository, start_branch_name, &b) } + .to yield_with_args(nil) + end + end + end + end + + describe '#fetch_source_branch' do + let(:local_ref) { 'refs/merge-requests/1/head' } + + context 'when the branch exists' do + let(:source_branch) { 'master' } + + it 'writes the ref' do + expect(repository).to receive(:write_ref).with(local_ref, /\h{40}/) + + repository.fetch_source_branch(repository, source_branch, local_ref) + end + + it 'returns true' do + expect(repository.fetch_source_branch(repository, source_branch, local_ref)).to eq(true) + end + end + + context 'when the branch does not exist' do + let(:source_branch) { 'definitely-not-master' } + + it 'does not write the ref' do + expect(repository).not_to receive(:write_ref) + + repository.fetch_source_branch(repository, source_branch, local_ref) + end + + it 'returns false' do + expect(repository.fetch_source_branch(repository, source_branch, local_ref)).to eq(false) + end + end + end + 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 diff --git a/spec/lib/gitlab/git/user_spec.rb b/spec/lib/gitlab/git/user_spec.rb index 0ebcecb26c0..ab64b041187 100644 --- a/spec/lib/gitlab/git/user_spec.rb +++ b/spec/lib/gitlab/git/user_spec.rb @@ -1,22 +1,24 @@ require 'spec_helper' describe Gitlab::Git::User do + let(:username) { 'janedo' } let(:name) { 'Jane Doe' } let(:email) { 'janedoe@example.com' } let(:gl_id) { 'user-123' } - subject { described_class.new(name, email, gl_id) } + subject { described_class.new(username, name, email, gl_id) } describe '#==' do - def eq_other(name, email, gl_id) - eq(described_class.new(name, email, gl_id)) + def eq_other(username, name, email, gl_id) + eq(described_class.new(username, name, email, gl_id)) end - it { expect(subject).to eq_other(name, email, gl_id) } + it { expect(subject).to eq_other(username, name, email, gl_id) } - it { expect(subject).not_to eq_other(nil, nil, nil) } - it { expect(subject).not_to eq_other(name + 'x', email, gl_id) } - it { expect(subject).not_to eq_other(name, email + 'x', gl_id) } - it { expect(subject).not_to eq_other(name, email, gl_id + 'x') } + it { expect(subject).not_to eq_other(nil, nil, nil, nil) } + it { expect(subject).not_to eq_other(username + 'x', name, email, gl_id) } + it { expect(subject).not_to eq_other(username, name + 'x', email, gl_id) } + it { expect(subject).not_to eq_other(username, name, email + 'x', gl_id) } + it { expect(subject).not_to eq_other(username, name, email, gl_id + 'x') } end end diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb new file mode 100644 index 00000000000..769b14687ac --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::OperationService do + let(:project) { create(:project) } + let(:repository) { project.repository.raw } + let(:client) { described_class.new(repository) } + + describe '#user_create_branch' do + let(:user) { create(:user) } + let(:gitaly_user) { Gitlab::GitalyClient::Util.gitaly_user(user) } + let(:branch_name) { 'new' } + let(:start_point) { 'master' } + let(:request) do + Gitaly::UserCreateBranchRequest.new( + repository: repository.gitaly_repository, + branch_name: branch_name, + start_point: start_point, + user: gitaly_user + ) + end + let(:gitaly_commit) { build(:gitaly_commit) } + let(:commit_id) { gitaly_commit.id } + let(:gitaly_branch) do + Gitaly::Branch.new(name: branch_name, target_commit: gitaly_commit) + end + let(:response) { Gitaly::UserCreateBranchResponse.new(branch: gitaly_branch) } + let(:commit) { Gitlab::Git::Commit.new(repository, gitaly_commit) } + + subject { client.user_create_branch(branch_name, user, start_point) } + + it 'sends a user_create_branch message and returns a Gitlab::git::Branch' do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_create_branch).with(request, kind_of(Hash)) + .and_return(response) + + expect(subject.name).to eq(branch_name) + expect(subject.dereferenced_target).to eq(commit) + end + + context "when pre_receive_error is present" do + let(:response) do + Gitaly::UserCreateBranchResponse.new(pre_receive_error: "something failed") + end + + it "throws a PreReceive exception" do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_create_branch).with(request, kind_of(Hash)) + .and_return(response) + + expect { subject }.to raise_error( + Gitlab::Git::HooksService::PreReceiveError, "something failed") + end + end + end +end diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb new file mode 100644 index 00000000000..498f6886bee --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/util_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::Util do + describe '.repository' do + let(:repository_storage) { 'default' } + let(:relative_path) { 'my/repo.git' } + let(:gl_repository) { 'project-1' } + let(:git_object_directory) { '.git/objects' } + let(:git_alternate_object_directory) { '/dir/one:/dir/two' } + + subject do + described_class.repository(repository_storage, relative_path, gl_repository) + end + + it 'creates a Gitaly::Repository with the given data' do + expect(Gitlab::Git::Env).to receive(:[]).with('GIT_OBJECT_DIRECTORY') + .and_return(git_object_directory) + expect(Gitlab::Git::Env).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES') + .and_return(git_alternate_object_directory) + + expect(subject).to be_a(Gitaly::Repository) + expect(subject.storage_name).to eq(repository_storage) + expect(subject.relative_path).to eq(relative_path) + expect(subject.gl_repository).to eq(gl_repository) + expect(subject.git_object_directory).to eq(git_object_directory) + expect(subject.git_alternate_object_directories).to eq([git_alternate_object_directory]) + end + end + + describe '.gitaly_user' do + let(:user) { create(:user) } + let(:gl_id) { Gitlab::GlId.gl_id(user) } + + subject { described_class.gitaly_user(user) } + + it 'creates a Gitaly::User from a GitLab user' do + expect(subject).to be_a(Gitaly::User) + expect(subject.name).to eq(user.name) + expect(subject.email).to eq(user.email) + expect(subject.gl_id).to eq(gl_id) + end + end +end diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 8aaf320cbf5..db26e16e3b2 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -4,6 +4,7 @@ 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(:dn) { 'uid=user1,ou=People,dc=example' } let(:provider) { 'my-provider' } let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) } let(:info_hash) do @@ -197,7 +198,7 @@ describe Gitlab::OAuth::User do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } + allow(ldap_user).to receive(:dn) { dn } end context "and no account for the LDAP user" do @@ -213,7 +214,7 @@ describe Gitlab::OAuth::User do identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -221,7 +222,7 @@ describe Gitlab::OAuth::User do end context "and LDAP user has an account already" do - let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } it "adds the omniauth identity to the LDAP account" do allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) @@ -234,7 +235,7 @@ describe Gitlab::OAuth::User do identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -252,7 +253,7 @@ describe Gitlab::OAuth::User do expect(identities_as_hash) .to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -310,8 +311,8 @@ describe Gitlab::OAuth::User do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } - allow(oauth_user).to receive(:ldap_person).and_return(ldap_user) + allow(ldap_user).to receive(:dn) { dn } + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) end context "and no account for the LDAP user" do @@ -341,7 +342,7 @@ describe Gitlab::OAuth::User do end context 'and LDAP user has an account already' do - let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } context 'dont block on create (LDAP)' do before do diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 19710029224..59923bfb14d 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -1,9 +1,12 @@ require 'spec_helper' describe Gitlab::Saml::User do + include LdapHelpers + let(:saml_user) { described_class.new(auth_hash) } let(:gl_user) { saml_user.gl_user } let(:uid) { 'my-uid' } + let(:dn) { 'uid=user1,ou=People,dc=example' } let(:provider) { 'saml' } let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash, extra: { raw_info: OneLogin::RubySaml::Attributes.new({ 'groups' => %w(Developers Freelancers Designers) }) }) } let(:info_hash) do @@ -163,13 +166,17 @@ describe Gitlab::Saml::User do end context 'and a corresponding LDAP person' do + let(:adapter) { ldap_adapter('ldapmain') } + before do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } - allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) - allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user) + allow(ldap_user).to receive(:dn) { dn } + allow(Gitlab::LDAP::Adapter).to receive(:new).and_return(adapter) + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(uid, adapter).and_return(ldap_user) + allow(Gitlab::LDAP::Person).to receive(:find_by_dn).with(dn, adapter).and_return(ldap_user) + allow(Gitlab::LDAP::Person).to receive(:find_by_email).with('john@mail.com', adapter).and_return(ldap_user) end context 'and no account for the LDAP user' do @@ -181,20 +188,86 @@ describe Gitlab::Saml::User do expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, { provider: 'saml', extern_uid: uid }]) end end context 'and LDAP user has an account already' do + let(:auth_hash_base_attributes) do + { + uid: uid, + provider: provider, + info: info_hash, + extra: { + raw_info: OneLogin::RubySaml::Attributes.new( + { 'groups' => %w(Developers Freelancers Designers) } + ) + } + } + end + let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes) } + let(:uid_types) { %w(uid dn email) } + before do create(:omniauth_user, email: 'john@mail.com', - extern_uid: 'uid=user1,ou=People,dc=example', + extern_uid: dn, provider: 'ldapmain', username: 'john') end + shared_examples 'find LDAP person' do |uid_type, uid| + let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes.merge(uid: extern_uid)) } + + before do + nil_types = uid_types - [uid_type] + + nil_types.each do |type| + allow(Gitlab::LDAP::Person).to receive(:"find_by_#{type}").and_return(nil) + end + + allow(Gitlab::LDAP::Person).to receive(:"find_by_#{uid_type}").and_return(ldap_user) + end + + it 'adds the omniauth identity to the LDAP account' do + identities = [ + { provider: 'ldapmain', extern_uid: dn }, + { provider: 'saml', extern_uid: extern_uid } + ] + + identities_as_hash = gl_user.identities.map do |id| + { provider: id.provider, extern_uid: id.extern_uid } + end + + saml_user.save + + expect(gl_user).to be_valid + expect(gl_user.username).to eql 'john' + expect(gl_user.email).to eql 'john@mail.com' + expect(gl_user.identities.length).to be 2 + expect(identities_as_hash).to match_array(identities) + end + end + + context 'when uid is an uid' do + it_behaves_like 'find LDAP person', 'uid' do + let(:extern_uid) { uid } + end + end + + context 'when uid is a dn' do + it_behaves_like 'find LDAP person', 'dn' do + let(:extern_uid) { dn } + end + end + + context 'when uid is an email' do + it_behaves_like 'find LDAP person', 'email' do + let(:extern_uid) { 'john@mail.com' } + end + end + it 'adds the omniauth identity to the LDAP account' do saml_user.save @@ -203,7 +276,7 @@ describe Gitlab::Saml::User do expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, { provider: 'saml', extern_uid: uid }]) end @@ -219,17 +292,21 @@ describe Gitlab::Saml::User do context 'user has SAML user, and wants to add their LDAP identity' do it 'adds the LDAP identity to the existing SAML user' do - create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'saml', username: 'john') - local_hash = OmniAuth::AuthHash.new(uid: 'uid=user1,ou=People,dc=example', provider: provider, info: info_hash) + create(:omniauth_user, email: 'john@mail.com', extern_uid: dn, provider: 'saml', username: 'john') + + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(dn, adapter).and_return(ldap_user) + + local_hash = OmniAuth::AuthHash.new(uid: dn, provider: provider, info: info_hash) local_saml_user = described_class.new(local_hash) + local_saml_user.save local_gl_user = local_saml_user.gl_user expect(local_gl_user).to be_valid expect(local_gl_user.identities.length).to be 2 identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, + { provider: 'saml', extern_uid: dn }]) end end end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index c7930378240..9efdd7940ca 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -48,14 +48,35 @@ describe Gitlab::Shell do end end - describe '#add_key' do - it 'removes trailing garbage' do - allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) - expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( - [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] - ) - - gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage') + describe 'projects commands' do + let(:gitlab_shell_path) { File.expand_path('tmp/tests/gitlab-shell') } + let(:projects_path) { File.join(gitlab_shell_path, 'bin/gitlab-projects') } + let(:gitlab_shell_hooks_path) { File.join(gitlab_shell_path, 'hooks') } + + before do + allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(gitlab_shell_path) + allow(Gitlab.config.gitlab_shell).to receive(:hooks_path).and_return(gitlab_shell_hooks_path) + allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) + end + + describe '#mv_repository' do + it 'executes the command' do + expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( + [projects_path, 'mv-project', 'storage/path', 'project/path.git', 'new/path.git'] + ) + gitlab_shell.mv_repository('storage/path', 'project/path', 'new/path') + end + end + + describe '#add_key' do + it 'removes trailing garbage' do + allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) + expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with( + [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] + ) + + gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage') + end end end @@ -105,30 +126,42 @@ describe Gitlab::Shell do end describe '#add_repository' do - it 'creates a repository' do - created_path = File.join(TestEnv.repos_path, 'project', 'path.git') - hooks_path = File.join(created_path, 'hooks') + shared_examples '#add_repository' do + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } + let(:repo_name) { 'project/path' } + let(:created_path) { File.join(repository_storage_path, repo_name + '.git') } - begin - result = gitlab_shell.add_repository(TestEnv.repos_path, 'project/path') - - repo_stat = File.stat(created_path) rescue nil - hooks_stat = File.lstat(hooks_path) rescue nil - hooks_dir = File.realpath(hooks_path) - ensure + after do FileUtils.rm_rf(created_path) end - expect(result).to be_truthy - expect(repo_stat.mode & 0o777).to eq(0o770) - expect(hooks_stat.symlink?).to be_truthy - expect(hooks_dir).to eq(gitlab_shell_hooks_path) + it 'creates a repository' do + expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_truthy + + expect(File.stat(created_path).mode & 0o777).to eq(0o770) + + hooks_path = File.join(created_path, 'hooks') + expect(File.lstat(hooks_path)).to be_symlink + expect(File.realpath(hooks_path)).to eq(gitlab_shell_hooks_path) + end + + it 'returns false when the command fails' do + FileUtils.mkdir_p(File.dirname(created_path)) + # This file will block the creation of the repo's .git directory. That + # should cause #add_repository to fail. + FileUtils.touch(created_path) + + expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_falsy + end end - it 'returns false when the command fails' do - expect(FileUtils).to receive(:mkdir_p).and_raise(Errno::EEXIST) + context 'with gitaly' do + it_behaves_like '#add_repository' + end - expect(gitlab_shell.add_repository('current/storage', 'project/path')).to be_falsy + context 'without gitaly', skip_gitaly_mock: true do + it_behaves_like '#add_repository' end end @@ -136,7 +169,7 @@ describe Gitlab::Shell do it 'returns true when the command succeeds' do expect(Gitlab::Popen).to receive(:popen) .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], - nil, popen_vars).and_return([nil, 0]) + nil, popen_vars).and_return([nil, 0]) expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true end @@ -144,7 +177,7 @@ describe Gitlab::Shell do it 'returns false when the command fails' do expect(Gitlab::Popen).to receive(:popen) .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'], - nil, popen_vars).and_return(["error", 1]) + nil, popen_vars).and_return(["error", 1]) expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 699184ad9fe..72496e9a212 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -182,7 +182,12 @@ describe Gitlab::Workhorse do let(:repo_path) { repository.path_to_repo } let(:action) { 'info_refs' } let(:params) do - { GL_ID: "user-#{user.id}", GL_REPOSITORY: "project-#{project.id}", RepoPath: repo_path } + { + GL_ID: "user-#{user.id}", + GL_USERNAME: user.username, + GL_REPOSITORY: "project-#{project.id}", + RepoPath: repo_path + } end subject { described_class.git_http_ok(repository, false, user, action) } @@ -191,7 +196,12 @@ describe Gitlab::Workhorse do context 'when is_wiki' do let(:params) do - { GL_ID: "user-#{user.id}", GL_REPOSITORY: "wiki-#{project.id}", RepoPath: repo_path } + { + GL_ID: "user-#{user.id}", + GL_USERNAME: user.username, + GL_REPOSITORY: "wiki-#{project.id}", + RepoPath: repo_path + } end subject { described_class.git_http_ok(repository, true, user, action) } @@ -214,14 +224,13 @@ describe Gitlab::Workhorse do end it 'includes a Repository param' do - repo_param = { Repository: { + repo_param = { storage_name: 'default', relative_path: project.full_path + '.git', - git_object_directory: '', - git_alternate_object_directories: [] - } } + gl_repository: "project-#{project.id}" + } - expect(subject).to include(repo_param) + expect(subject[:Repository]).to include(repo_param) end context "when git_upload_pack action is passed" do diff --git a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb index 7125bfcab59..a0fb86345f3 100644 --- a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb +++ b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb @@ -16,7 +16,12 @@ describe SystemCheck::App::GitUserDefaultSSHConfigCheck do end it 'only whitelists safe files' do - expect(described_class::WHITELIST).to contain_exactly('authorized_keys', 'authorized_keys2', 'known_hosts') + expect(described_class::WHITELIST).to contain_exactly( + 'authorized_keys', + 'authorized_keys2', + 'authorized_keys.lock', + 'known_hosts' + ) end describe '#skip?' do diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb new file mode 100644 index 00000000000..4ab1bb67058 --- /dev/null +++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20170926150348_schedule_merge_request_diff_migrations_take_two') + +describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do + matcher :be_scheduled_migration do |time, *expected| + match do |migration| + BackgroundMigrationWorker.jobs.any? do |job| + job['args'] == [migration, expected] && + job['at'].to_i == time.to_i + end + end + + failure_message do |migration| + "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!" + end + end + + let(:merge_request_diffs) { table(:merge_request_diffs) } + let(:merge_requests) { table(:merge_requests) } + let(:projects) { table(:projects) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 1) + + projects.create!(id: 1, name: 'gitlab', path: 'gitlab') + + merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master') + + merge_request_diffs.create!(id: 1, merge_request_id: 1, st_commits: YAML.dump([]), st_diffs: nil) + merge_request_diffs.create!(id: 2, merge_request_id: 1, st_commits: nil, st_diffs: YAML.dump([])) + merge_request_diffs.create!(id: 3, merge_request_id: 1, st_commits: nil, st_diffs: nil) + merge_request_diffs.create!(id: 4, merge_request_id: 1, st_commits: YAML.dump([]), st_diffs: YAML.dump([])) + end + + it 'correctly schedules background migrations' do + Sidekiq::Testing.fake! do + Timecop.freeze do + migrate! + + expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes.from_now, 1, 1) + expect(described_class::MIGRATION).to be_scheduled_migration(20.minutes.from_now, 2, 2) + expect(described_class::MIGRATION).to be_scheduled_migration(30.minutes.from_now, 4, 4) + expect(BackgroundMigrationWorker.jobs.size).to eq 3 + end + end + end + + it 'migrates the data' do + Sidekiq::Testing.inline! do + non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL' + + expect(merge_request_diffs.where(non_empty).count).to eq 3 + + migrate! + + expect(merge_request_diffs.where(non_empty).count).to eq 0 + end + end +end diff --git a/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb b/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb new file mode 100644 index 00000000000..d625b60ff50 --- /dev/null +++ b/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20170927112318_update_legacy_diff_notes_type_for_import.rb') + +describe UpdateLegacyDiffNotesTypeForImport, :migration do + let(:notes) { table(:notes) } + + before do + notes.inheritance_column = nil + + notes.create(type: 'Note') + notes.create(type: 'LegacyDiffNote') + notes.create(type: 'Github::Import::Note') + notes.create(type: 'Github::Import::LegacyDiffNote') + end + + it 'updates the notes type' do + migrate! + + expect(notes.pluck(:type)) + .to contain_exactly('Note', 'Github::Import::Note', 'LegacyDiffNote', 'LegacyDiffNote') + end +end diff --git a/spec/migrations/update_notes_type_for_import_spec.rb b/spec/migrations/update_notes_type_for_import_spec.rb new file mode 100644 index 00000000000..06195d970d8 --- /dev/null +++ b/spec/migrations/update_notes_type_for_import_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20170927112319_update_notes_type_for_import.rb') + +describe UpdateNotesTypeForImport, :migration do + let(:notes) { table(:notes) } + + before do + notes.inheritance_column = nil + + notes.create(type: 'Note') + notes.create(type: 'LegacyDiffNote') + notes.create(type: 'Github::Import::Note') + notes.create(type: 'Github::Import::LegacyDiffNote') + end + + it 'updates the notes type' do + migrate! + + expect(notes.pluck(:type)) + .to contain_exactly('Note', 'Note', 'LegacyDiffNote', 'Github::Import::LegacyDiffNote') + end +end diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb index 2ce78e34b0c..889c243c8d8 100644 --- a/spec/models/ci/pipeline_variable_spec.rb +++ b/spec/models/ci/pipeline_variable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::PipelineVariable, models: true do +describe Ci::PipelineVariable do subject { build(:ci_pipeline_variable) } it { is_expected.to include_module(HasVariable) } diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 2e686e515c5..584dfe9a5c1 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -183,75 +183,42 @@ describe Ci::Runner do end end - context 'when runner is locked' do + context 'when runner is shared' do before do - runner.locked = true + runner.is_shared = true + build.project.runners = [] end - shared_examples 'locked build picker' do - context 'when runner cannot pick untagged jobs' do - before do - runner.run_untagged = false - end + it 'can handle builds' do + expect(runner.can_pick?(build)).to be_truthy + end - it 'cannot handle builds without tags' do - expect(runner.can_pick?(build)).to be_falsey - end + context 'when runner is locked' do + before do + runner.locked = true end - context 'when having runner tags' do - before do - runner.tag_list = %w(bb cc) - end - - it 'cannot handle it for builds without matching tags' do - build.tag_list = ['aa'] - - expect(runner.can_pick?(build)).to be_falsey - end + it 'can handle builds' do + expect(runner.can_pick?(build)).to be_truthy end end + end - context 'when serving the same project' do - it 'can handle it' do + context 'when runner is not shared' do + context 'when runner is assigned to a project' do + it 'can handle builds' do expect(runner.can_pick?(build)).to be_truthy end - - it_behaves_like 'locked build picker' - - context 'when having runner tags' do - before do - runner.tag_list = %w(bb cc) - build.tag_list = ['bb'] - end - - it 'can handle it for matching tags' do - expect(runner.can_pick?(build)).to be_truthy - end - end end - context 'serving a different project' do + context 'when runner is not assigned to a project' do before do - runner.runner_projects.destroy_all + build.project.runners = [] end - it 'cannot handle it' do + it 'cannot handle builds' do expect(runner.can_pick?(build)).to be_falsey end - - it_behaves_like 'locked build picker' - - context 'when having runner tags' do - before do - runner.tag_list = %w(bb cc) - build.tag_list = ['bb'] - end - - it 'cannot handle it for matching tags' do - expect(runner.can_pick?(build)).to be_falsey - end - end end end diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb index fadc8bfeb61..4a4d079b721 100644 --- a/spec/models/gpg_key_spec.rb +++ b/spec/models/gpg_key_spec.rb @@ -138,6 +138,14 @@ describe GpgKey do expect(gpg_key.verified?).to be_truthy expect(gpg_key.verified_and_belongs_to_email?('bette.cartwright@example.com')).to be_truthy end + + it 'returns true if one of the email addresses in the key belongs to the user and case-insensitively matches the provided 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.verified?).to be_truthy + expect(gpg_key.verified_and_belongs_to_email?('Bette.Cartwright@example.com')).to be_truthy + end end describe '#revoke' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 78226c6c3fa..c94481d53c3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1303,7 +1303,7 @@ describe Project do context 'using a regular repository' do it 'creates the repository' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(true) expect(project.repository).to receive(:after_create) @@ -1313,7 +1313,7 @@ describe Project do it 'adds an error if the repository could not be created' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(false) expect(project.repository).not_to receive(:after_create) @@ -1370,7 +1370,7 @@ describe Project do .and_return(false) expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(true) project.ensure_repository @@ -2363,10 +2363,22 @@ describe Project do describe '#legacy_storage?' do it 'returns true when storage_version is nil' do - project = build(:project) + project = build(:project, storage_version: nil) expect(project.legacy_storage?).to be_truthy end + + it 'returns true when the storage_version is 0' do + project = build(:project, storage_version: 0) + + expect(project.legacy_storage?).to be_truthy + end + end + + describe '#hashed_storage?' do + it 'returns false' do + expect(project.hashed_storage?).to be_falsey + end end describe '#rename_repo' do @@ -2425,6 +2437,38 @@ describe Project do expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) end end + + describe '#migrate_to_hashed_storage!' do + it 'returns true' do + expect(project.migrate_to_hashed_storage!).to be_truthy + end + + it 'flags as readonly' do + expect { project.migrate_to_hashed_storage! }.to change { project.repository_read_only }.to(true) + end + + it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do + Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase + + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + + project.migrate_to_hashed_storage! + end + + it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the wiki repo is in use' do + Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: true)).increase + + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + + project.migrate_to_hashed_storage! + end + + it 'schedules ProjectMigrateHashedStorageWorker' do + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).with(project.id) + + project.migrate_to_hashed_storage! + end + end end context 'hashed storage' do @@ -2438,6 +2482,18 @@ describe Project do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) end + describe '#legacy_storage?' do + it 'returns false' do + expect(project.legacy_storage?).to be_falsey + end + end + + describe '#hashed_storage?' do + it 'returns true' do + expect(project.hashed_storage?).to be_truthy + end + end + describe '#base_dir' do it 'returns base_dir based on hash of project id' do expect(project.base_dir).to eq('@hashed/6b/86') @@ -2508,6 +2564,26 @@ describe Project do expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) end end + + describe '#migrate_to_hashed_storage!' do + it 'returns nil' do + expect(project.migrate_to_hashed_storage!).to be_nil + end + + it 'does not flag as readonly' do + expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only } + end + end + end + + describe '#gl_repository' do + let(:project) { create(:project) } + + it 'delegates to Gitlab::GlRepository.gl_repository' do + expect(Gitlab::GlRepository).to receive(:gl_repository).with(project, true) + + project.gl_repository(is_wiki: true) + end end describe '#has_ci?' do @@ -2717,6 +2793,17 @@ describe Project do end end + describe '#check_repository_path_availability' do + let(:project) { build(:project) } + + it 'skips gitlab-shell exists?' do + project.skip_disk_validation = true + + expect(project.gitlab_shell).not_to receive(:exists?) + expect(project.check_repository_path_availability).to be_truthy + end + end + describe '#latest_successful_pipeline_for_default_branch' do let(:project) { build(:project) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 76bb658b10d..157a10edd73 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) @@ -815,45 +815,70 @@ describe Repository, models: true do end describe '#add_branch' do - context 'when pre hooks were successful' do - it 'runs without errors' do - hook = double(trigger: [true, nil]) - expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + let(:branch_name) { 'new_feature' } + let(:target) { 'master' } - expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error - end + subject { repository.add_branch(user, branch_name, target) } - it 'creates the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + context 'with Gitaly enabled' do + it "calls Gitaly's OperationService" do + expect_any_instance_of(Gitlab::GitalyClient::OperationService) + .to receive(:user_create_branch).with(branch_name, user, target) + .and_return(nil) - branch = repository.add_branch(user, 'new_feature', 'master') + subject + end - expect(branch.name).to eq('new_feature') + it 'creates_the_branch' do + expect(subject.name).to eq(branch_name) + expect(repository.find_branch(branch_name)).not_to be_nil end - it 'calls the after_create_branch hook' do - expect(repository).to receive(:after_create_branch) + context 'with a non-existing target' do + let(:target) { 'fake-target' } - repository.add_branch(user, 'new_feature', 'master') + it "returns false and doesn't create the branch" do + expect(subject).to be(false) + expect(repository.find_branch(branch_name)).to be_nil + end end end - context 'when pre hooks failed' do - it 'gets an error' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + context 'with Gitaly disabled', skip_gitaly_mock: true do + context 'when pre hooks were successful' do + it 'runs without errors' do + hook = double(trigger: [true, nil]) + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) - expect do - repository.add_branch(user, 'new_feature', 'master') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + expect { subject }.not_to raise_error + end + + it 'creates the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + + expect(subject.name).to eq(branch_name) + end + + it 'calls the after_create_branch hook' do + expect(repository).to receive(:after_create_branch) + + subject + end end - it 'does not create the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + context 'when pre hooks failed' do + it 'gets an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) - expect do - repository.add_branch(user, 'new_feature', 'master') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) - expect(repository.find_branch('new_feature')).to be_nil + expect { subject }.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end + + it 'does not create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + + expect { subject }.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + expect(repository.find_branch(branch_name)).to be_nil + end end end end @@ -1131,21 +1156,31 @@ describe Repository, models: true do end describe '#has_visible_content?' do - subject { repository.has_visible_content? } + before do + # If raw_repository.has_visible_content? gets called more than once then + # caching is broken. We don't want that. + expect(repository.raw_repository).to receive(:has_visible_content?) + .once + .and_return(result) + end - describe 'when there are no branches' do - before do - allow(repository.raw_repository).to receive(:branch_count).and_return(0) - end + context 'when true' do + let(:result) { true } - it { is_expected.to eq(false) } + it 'returns true and caches it' do + expect(repository.has_visible_content?).to eq(true) + # Second call hits the cache + expect(repository.has_visible_content?).to eq(true) + end end - describe 'when there are branches' do - it 'returns true' do - expect(repository.raw_repository).to receive(:branch_count).and_return(3) + context 'when false' do + let(:result) { false } - expect(subject).to eq(true) + it 'returns false and caches it' do + expect(repository.has_visible_content?).to eq(false) + # Second call hits the cache + expect(repository.has_visible_content?).to eq(false) end end end @@ -1262,6 +1297,7 @@ describe Repository, models: true do allow(repository).to receive(:empty?).and_return(true) expect(cache).to receive(:expire).with(:empty?) + expect(cache).to receive(:expire).with(:has_visible_content?) repository.expire_emptiness_caches end @@ -1270,6 +1306,7 @@ describe Repository, models: true do allow(repository).to receive(:empty?).and_return(false) expect(cache).not_to receive(:expire).with(:empty?) + expect(cache).not_to receive(:expire).with(:has_visible_content?) repository.expire_emptiness_caches end @@ -1599,7 +1636,7 @@ describe Repository, models: true do describe '#expire_branches_cache' do it 'expires the cache' do expect(repository).to receive(:expire_method_caches) - .with(%i(branch_names branch_count)) + .with(%i(branch_names branch_count has_visible_content?)) .and_call_original repository.expire_branches_cache @@ -1617,27 +1654,41 @@ describe Repository, models: true do end describe '#add_tag' do - context 'with a valid target' do - let(:user) { build_stubbed(:user) } + let(:user) { build_stubbed(:user) } - it 'creates the tag using rugged' do - expect(repository.rugged.tags).to receive(:create) - .with('8.5', repository.commit('master').id, - hash_including(message: 'foo', - tagger: hash_including(name: user.name, email: user.email))) - .and_call_original + shared_examples 'adding tag' do + context 'with a valid target' do + it 'creates the tag' do + repository.add_tag(user, '8.5', 'master', 'foo') - repository.add_tag(user, '8.5', 'master', 'foo') - end + tag = repository.find_tag('8.5') + expect(tag).to be_present + expect(tag.message).to eq('foo') + expect(tag.dereferenced_target.id).to eq(repository.commit('master').id) + end - it 'returns a Gitlab::Git::Tag object' do - tag = repository.add_tag(user, '8.5', 'master', 'foo') + it 'returns a Gitlab::Git::Tag object' do + tag = repository.add_tag(user, '8.5', 'master', 'foo') - expect(tag).to be_a(Gitlab::Git::Tag) + expect(tag).to be_a(Gitlab::Git::Tag) + end end - it 'passes commit SHA to pre-receive and update hooks,\ - and tag SHA to post-receive hook' do + context 'with an invalid target' do + it 'returns false' do + expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false + end + end + end + + context 'when Gitaly operation_user_add_tag feature is enabled' do + it_behaves_like 'adding tag' + end + + context 'when Gitaly operation_user_add_tag feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'adding tag' + + it 'passes commit SHA to pre-receive and update hooks and tag SHA to post-receive hook' do pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', project) update_hook = Gitlab::Git::Hook.new('update', project) post_receive_hook = Gitlab::Git::Hook.new('post-receive', project) @@ -1655,17 +1706,11 @@ describe Repository, models: true do tag_sha = tag.target expect(pre_receive_hook).to have_received(:trigger) - .with(anything, anything, commit_sha, anything) + .with(anything, anything, anything, commit_sha, anything) expect(update_hook).to have_received(:trigger) - .with(anything, anything, commit_sha, anything) + .with(anything, anything, anything, commit_sha, anything) expect(post_receive_hook).to have_received(:trigger) - .with(anything, anything, tag_sha, anything) - end - end - - context 'with an invalid target' do - it 'returns false' do - expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false + .with(anything, anything, anything, tag_sha, anything) end end end @@ -1682,12 +1727,22 @@ describe Repository, models: true do end describe '#rm_tag' do - it 'removes a tag' do - expect(repository).to receive(:before_remove_tag) + shared_examples 'removing tag' do + it 'removes a tag' do + expect(repository).to receive(:before_remove_tag) + + repository.rm_tag(build_stubbed(:user), 'v1.1.0') - repository.rm_tag(create(:user), 'v1.1.0') + expect(repository.find_tag('v1.1.0')).to be_nil + end + end + + context 'when Gitaly operation_user_delete_tag feature is enabled' do + it_behaves_like 'removing tag' + end - expect(repository.find_tag('v1.1.0')).to be_nil + context 'when Gitaly operation_user_delete_tag feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'removing tag' end end @@ -1860,6 +1915,15 @@ describe Repository, models: true do repository.expire_all_method_caches end + + it 'all cache_method definitions are in the lists of method caches' do + methods = repository.methods.map do |method| + match = /^_uncached_(.*)/.match(method) + match[1].to_sym if match + end.compact + + expect(methods).to match_array(Repository::CACHED_METHODS + Repository::MEMOIZED_CACHED_METHODS) + end end describe '#file_on_head' do diff --git a/spec/models/user_custom_attribute_spec.rb b/spec/models/user_custom_attribute_spec.rb new file mode 100644 index 00000000000..37fc3cb64f0 --- /dev/null +++ b/spec/models/user_custom_attribute_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe UserCustomAttribute do + describe 'assocations' do + it { is_expected.to belong_to(:user) } + end + + describe 'validations' do + subject { build :user_custom_attribute } + + it { is_expected.to validate_presence_of(:user_id) } + it { is_expected.to validate_presence_of(:key) } + it { is_expected.to validate_presence_of(:value) } + it { is_expected.to validate_uniqueness_of(:key).scoped_to(:user_id) } + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c1affa812aa..62890dd5002 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -39,6 +39,7 @@ describe User do it { is_expected.to have_many(:chat_names).dependent(:destroy) } it { is_expected.to have_many(:uploads).dependent(:destroy) } it { is_expected.to have_many(:reported_abuse_reports).dependent(:destroy).class_name('AbuseReport') } + it { is_expected.to have_many(:custom_attributes).class_name('UserCustomAttribute') } describe "#abuse_report" do let(:current_user) { create(:user) } diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb index a6bf70c1e09..5b8cf2e6ab5 100644 --- a/spec/policies/global_policy_spec.rb +++ b/spec/policies/global_policy_spec.rb @@ -51,4 +51,41 @@ describe GlobalPolicy do end end end + + describe "create fork" do + context "when user has not exceeded project limit" do + it { is_expected.to be_allowed(:create_fork) } + end + + context "when user has exceeded project limit" do + let(:current_user) { create(:user, projects_limit: 0) } + + it { is_expected.not_to be_allowed(:create_fork) } + end + + context "when user is a master in a group" do + let(:group) { create(:group) } + let(:current_user) { create(:user, projects_limit: 0) } + + before do + group.add_master(current_user) + end + + it { is_expected.to be_allowed(:create_fork) } + end + end + + describe 'custom attributes' do + context 'regular user' do + it { is_expected.not_to be_allowed(:read_custom_attribute) } + it { is_expected.not_to be_allowed(:update_custom_attribute) } + end + + context 'admin' do + let(:current_user) { create(:user, :admin) } + + it { is_expected.to be_allowed(:read_custom_attribute) } + it { is_expected.to be_allowed(:update_custom_attribute) } + end + end end diff --git a/spec/policies/namespace_policy_spec.rb b/spec/policies/namespace_policy_spec.rb new file mode 100644 index 00000000000..e52ff02e5f0 --- /dev/null +++ b/spec/policies/namespace_policy_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe NamespacePolicy do + let(:current_user) { create(:user) } + let(:namespace) { current_user.namespace } + + subject { described_class.new(current_user, namespace) } + + context "create projects" do + context "user namespace" do + it { is_expected.to be_allowed(:create_projects) } + end + + context "user who has exceeded project limit" do + let(:current_user) { create(:user, projects_limit: 0) } + + it { is_expected.not_to be_allowed(:create_projects) } + end + end +end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index f663719d28c..94462b4572d 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -491,6 +491,7 @@ describe API::Commits do expect(json_response['stats']['deletions']).to eq(commit.stats.deletions) expect(json_response['stats']['total']).to eq(commit.stats.total) expect(json_response['status']).to be_nil + expect(json_response['last_pipeline']).to be_nil end context 'when ref does not exist' do @@ -573,6 +574,10 @@ describe API::Commits do expect(response).to have_http_status(200) expect(response).to match_response_schema('public_api/v4/commit/detail') expect(json_response['status']).to eq('created') + expect(json_response['last_pipeline']['id']).to eq(pipeline.id) + expect(json_response['last_pipeline']['ref']).to eq(pipeline.ref) + expect(json_response['last_pipeline']['sha']).to eq(pipeline.sha) + expect(json_response['last_pipeline']['status']).to eq(pipeline.status) end context 'when pipeline succeeds' do diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index 2361809e0e1..f8cd529a06c 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -20,6 +20,7 @@ describe API::Environments do path path_with_namespace star_count forks_count created_at last_activity_at + avatar_url ) get api("/projects/#{project.id}/environments", user) diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index d4006fe71a2..060c8902471 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -159,18 +159,25 @@ describe API::Helpers do end describe "when authenticating using a user's private token" do - it "returns nil for an invalid token" do + it "returns a 401 response for an invalid token" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false } - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end - it "returns nil for a user without access" do + it "returns a 401 response for a user without access" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ + end + + it 'returns a 401 response for a user who is blocked' do + user.block! + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token + + expect { current_user }.to raise_error /401/ end it "leaves user as is when sudo not specified" do @@ -193,24 +200,31 @@ describe API::Helpers do allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false } end - it "returns nil for an invalid token" do + it "returns a 401 response for an invalid token" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token' - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end - it "returns nil for a user without access" do + it "returns a 401 response for a user without access" do env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false) - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end - it "returns nil for a token without the appropriate scope" do + it 'returns a 401 response for a user who is blocked' do + user.block! + env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token + + expect { current_user }.to raise_error /401/ + end + + it "returns a 401 response for a token without the appropriate scope" do personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user']) env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end it "leaves user as is when sudo not specified" do @@ -226,14 +240,14 @@ describe API::Helpers do personal_access_token.revoke! env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end it 'does not allow expired tokens' do personal_access_token.update_attributes!(expires_at: 1.day.ago) env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token - expect(current_user).to be_nil + expect { current_user }.to raise_error /401/ end end @@ -351,6 +365,18 @@ describe API::Helpers do end end end + + context 'when user is blocked' do + before do + user.block! + end + + it 'changes current_user to sudo' do + set_env(admin, user.id) + + expect(current_user).to eq(user) + end + end end context 'with regular user' do @@ -454,6 +480,27 @@ describe API::Helpers do handle_api_exception(exception) end + + context 'with a personal access token given' do + let(:token) { create(:personal_access_token, scopes: ['api'], user: user) } + + # Regression test for https://gitlab.com/gitlab-org/gitlab-ce/issues/38571 + it 'does not raise an additional exception because of missing `request`' do + # We need to stub at a lower level than #sentry_enabled? otherwise + # Sentry is not enabled when the request below is made, and the test + # would pass even without the fix + expect(Gitlab::Sentry).to receive(:enabled?).twice.and_return(true) + expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!') + + get api('/projects', personal_access_token: token) + + # The 500 status is expected as we're testing a case where an exception + # is raised, but Grape shouldn't raise an additional exception + expect(response).to have_gitlab_http_status(500) + expect(json_response['message']).not_to include("undefined local variable or method `request'") + expect(json_response['message']).to start_with("\nRuntimeError (Runtime Error!):") + end + end end describe '.authenticate_non_get!' do @@ -490,11 +537,10 @@ describe API::Helpers do context 'current_user is nil' do before do expect_any_instance_of(self.class).to receive(:current_user).and_return(nil) - allow_any_instance_of(self.class).to receive(:initial_current_user).and_return(nil) end it 'returns a 401 response' do - expect { authenticate! }.to raise_error '401 - {"message"=>"401 Unauthorized"}' + expect { authenticate! }.to raise_error /401/ end end @@ -502,35 +548,12 @@ describe API::Helpers do let(:user) { build(:user) } before do - expect_any_instance_of(self.class).to receive(:current_user).at_least(:once).and_return(user) - expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(user) + expect_any_instance_of(self.class).to receive(:current_user).and_return(user) end it 'does not raise an error' do expect { authenticate! }.not_to raise_error end end - - context 'current_user is blocked' do - let(:user) { build(:user, :blocked) } - - before do - expect_any_instance_of(self.class).to receive(:current_user).at_least(:once).and_return(user) - end - - it 'raises an error' do - expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(user) - - expect { authenticate! }.to raise_error '401 - {"message"=>"401 Unauthorized"}' - end - - it "doesn't raise an error if an admin user is impersonating a blocked user (via sudo)" do - admin_user = build(:user, :admin) - - expect_any_instance_of(self.class).to receive(:initial_current_user).and_return(admin_user) - - expect { authenticate! }.not_to raise_error - end - end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 508df990952..18f6f7df1fa 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -193,6 +193,7 @@ describe API::Projects do path path_with_namespace star_count forks_count created_at last_activity_at + avatar_url ) get api('/projects?simple=true', user) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 5b306ec6cbf..69c8aa4482a 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -125,6 +125,15 @@ describe API::Users do end context "when admin" do + context 'when sudo is defined' do + it 'does not return 500' do + admin_personal_access_token = create(:personal_access_token, user: admin).token + get api("/users?private_token=#{admin_personal_access_token}&sudo=#{user.id}", admin) + + expect(response).to have_http_status(:success) + end + end + it "returns an array of users" do get api("/users", admin) @@ -1896,4 +1905,8 @@ describe API::Users do expect(impersonation_token.reload.revoked).to be_truthy end end + + include_examples 'custom attributes endpoints', 'users' do + let(:attributable) { user } + end end diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index cae2c3118da..e5282c3311f 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -89,6 +89,7 @@ describe API::V3::Projects do path path_with_namespace star_count forks_count created_at last_activity_at + avatar_url ) get v3_api('/projects?simple=true', user) diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 4c2ff08039c..eb6e683cc23 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -133,6 +133,26 @@ describe Ci::CreatePipelineService do expect(merge_request.reload.head_pipeline).to eq head_pipeline end end + + context 'when pipeline has been skipped' do + before do + allow_any_instance_of(Ci::Pipeline) + .to receive(:git_commit_message) + .and_return('some commit [ci skip]') + end + + it 'updates merge request head pipeline' do + merge_request = create(:merge_request, source_branch: 'master', + target_branch: 'feature', + source_project: project) + + head_pipeline = execute_service + + expect(head_pipeline).to be_skipped + expect(head_pipeline).to be_persisted + expect(merge_request.reload.head_pipeline).to eq head_pipeline + end + end end context 'auto-cancel enabled' do @@ -481,104 +501,4 @@ describe Ci::CreatePipelineService do 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?) - 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/emails/create_service_spec.rb b/spec/services/emails/create_service_spec.rb index 641d5538de8..75812c17309 100644 --- a/spec/services/emails/create_service_spec.rb +++ b/spec/services/emails/create_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Emails::CreateService do let(:user) { create(:user) } - let(:opts) { { email: 'new@email.com' } } + let(:opts) { { email: 'new@email.com', user: user } } subject(:service) { described_class.new(user, opts) } diff --git a/spec/services/emails/destroy_service_spec.rb b/spec/services/emails/destroy_service_spec.rb index 1f4294dd905..7726fc0ef81 100644 --- a/spec/services/emails/destroy_service_spec.rb +++ b/spec/services/emails/destroy_service_spec.rb @@ -4,7 +4,7 @@ describe Emails::DestroyService do let!(:user) { create(:user) } let!(:email) { create(:email, user: user) } - subject(:service) { described_class.new(user, email: email.email) } + subject(:service) { described_class.new(user, user: user, email: email.email) } describe '#execute' do it 'removes an email' do diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index b60136064b7..80213d093f1 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -13,20 +13,21 @@ describe MergeRequests::MergeService do describe '#execute' do context 'MergeRequest#merge_jid' do + let(:service) do + described_class.new(project, user, commit_message: 'Awesome message') + end + before do merge_request.update_column(:merge_jid, 'hash-123') end it 'is cleaned when no error is raised' do - service = described_class.new(project, user, commit_message: 'Awesome message') - service.execute(merge_request) expect(merge_request.reload.merge_jid).to be_nil end it 'is cleaned when expected error is raised' do - service = described_class.new(project, user, commit_message: 'Awesome message') allow(service).to receive(:commit).and_raise(described_class::MergeError) service.execute(merge_request) @@ -34,6 +35,22 @@ describe MergeRequests::MergeService do expect(merge_request.reload.merge_jid).to be_nil end + it 'is cleaned when merge request is not mergeable' do + allow(merge_request).to receive(:mergeable?).and_return(false) + + service.execute(merge_request) + + expect(merge_request.reload.merge_jid).to be_nil + end + + it 'is cleaned when no source is found' do + allow(merge_request).to receive(:diff_head_sha).and_return(nil) + + service.execute(merge_request) + + expect(merge_request.reload.merge_jid).to be_nil + end + it 'is not cleaned when unexpected error is raised' do service = described_class.new(project, user, commit_message: 'Awesome message') allow(service).to receive(:commit).and_raise(StandardError) diff --git a/spec/services/merge_requests/post_merge_service_spec.rb b/spec/services/merge_requests/post_merge_service_spec.rb index a37cdab8928..d2bd05d921f 100644 --- a/spec/services/merge_requests/post_merge_service_spec.rb +++ b/spec/services/merge_requests/post_merge_service_spec.rb @@ -11,5 +11,16 @@ describe MergeRequests::PostMergeService do describe '#execute' do it_behaves_like 'cache counters invalidator' + + it 'refreshes the number of open merge requests for a valid MR', :use_clean_rails_memory_store_caching do + # Cache the counter before the MR changed state. + project.open_merge_requests_count + merge_request.update!(state: 'merged') + + service = described_class.new(project, user, {}) + + expect { service.execute(merge_request) } + .to change { project.open_merge_requests_count }.from(1).to(0) + end end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 5da634e2fb1..35f0c85b0ec 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -156,10 +156,11 @@ describe Projects::CreateService, '#execute' do } end - let(:repository_storage_path) { Gitlab.config.repositories.storages['default']['path'] } + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } before do - gitlab_shell.add_repository(repository_storage_path, "#{user.namespace.full_path}/existing") + gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") end after do @@ -208,6 +209,15 @@ describe Projects::CreateService, '#execute' do end end + context 'when skip_disk_validation is used' do + it 'sets the project attribute' do + opts[:skip_disk_validation] = true + project = create_project(user, opts) + + expect(project.skip_disk_validation).to be_truthy + end + end + def create_project(user, opts) Projects::CreateService.new(user, opts).execute end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index a6e0364d44c..fa9d6969830 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -76,10 +76,11 @@ describe Projects::ForkService do end context 'repository already exists' do - let(:repository_storage_path) { Gitlab.config.repositories.storages['default']['path'] } + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } before do - gitlab_shell.add_repository(repository_storage_path, "#{@to_user.namespace.full_path}/#{@from_project.path}") + gitlab_shell.add_repository(repository_storage, "#{@to_user.namespace.full_path}/#{@from_project.path}") end after do diff --git a/spec/services/projects/hashed_storage_migration_service_spec.rb b/spec/services/projects/hashed_storage_migration_service_spec.rb new file mode 100644 index 00000000000..1b61207b550 --- /dev/null +++ b/spec/services/projects/hashed_storage_migration_service_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe Projects::HashedStorageMigrationService do + let(:gitlab_shell) { Gitlab::Shell.new } + let(:project) { create(:project, :empty_repo, :wiki_repo) } + let(:service) { described_class.new(project) } + let(:legacy_storage) { Storage::LegacyProject.new(project) } + let(:hashed_storage) { Storage::HashedProject.new(project) } + + describe '#execute' do + before do + allow(service).to receive(:gitlab_shell) { gitlab_shell } + end + + context 'when succeeds' do + it 'renames project and wiki repositories' do + service.execute + + expect(gitlab_shell.exists?(project.repository_storage_path, "#{hashed_storage.disk_path}.git")).to be_truthy + expect(gitlab_shell.exists?(project.repository_storage_path, "#{hashed_storage.disk_path}.wiki.git")).to be_truthy + end + + it 'updates project to be hashed and not readonly' do + service.execute + + expect(project.hashed_storage?).to be_truthy + expect(project.repository_read_only).to be_falsey + end + + it 'move operation is called for both repositories' do + expect_move_repository(project.disk_path, hashed_storage.disk_path) + expect_move_repository("#{project.disk_path}.wiki", "#{hashed_storage.disk_path}.wiki") + + service.execute + end + end + + context 'when one move fails' do + it 'rollsback repositories to original name' do + from_name = project.disk_path + to_name = hashed_storage.disk_path + allow(service).to receive(:move_repository).and_call_original + allow(service).to receive(:move_repository).with(from_name, to_name).once { false } # will disable first move only + + expect(service).to receive(:rollback_folder_move).and_call_original + + service.execute + + expect(gitlab_shell.exists?(project.repository_storage_path, "#{hashed_storage.disk_path}.git")).to be_falsey + expect(gitlab_shell.exists?(project.repository_storage_path, "#{hashed_storage.disk_path}.wiki.git")).to be_falsey + end + + context 'when rollback fails' do + before do + from_name = legacy_storage.disk_path + to_name = hashed_storage.disk_path + + hashed_storage.ensure_storage_path_exists + gitlab_shell.mv_repository(project.repository_storage_path, from_name, to_name) + end + + it 'does not try to move nil repository over hashed' do + expect_move_repository("#{project.disk_path}.wiki", "#{hashed_storage.disk_path}.wiki") + + service.execute + end + end + end + + def expect_move_repository(from_name, to_name) + expect(gitlab_shell).to receive(:mv_repository).with(project.repository_storage_path, from_name, to_name).and_call_original + end + end +end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index a14ed526f68..2459f371a91 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -121,11 +121,14 @@ describe Projects::TransferService do end context 'namespace which contains orphan repository with same projects path name' do - let(:repository_storage_path) { Gitlab.config.repositories.storages['default']['path'] } + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } before do group.add_owner(user) - gitlab_shell.add_repository(repository_storage_path, "#{group.full_path}/#{project.path}") + unless gitlab_shell.add_repository(repository_storage, "#{group.full_path}/#{project.path}") + raise 'failed to add repository' + end @result = transfer_project(project, user, group) end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index c551083ac90..4873e967535 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -149,10 +149,11 @@ describe Projects::UpdateService, '#execute' do end context 'when renaming a project' do - let(:repository_storage_path) { Gitlab.config.repositories.storages['default']['path'] } + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } before do - gitlab_shell.add_repository(repository_storage_path, "#{user.namespace.full_path}/existing") + gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") end after do diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb index 57013b54560..e7e9080b6b0 100644 --- a/spec/services/tags/create_service_spec.rb +++ b/spec/services/tags/create_service_spec.rb @@ -28,7 +28,7 @@ describe Tags::CreateService do it 'returns an error' do expect(repository).to receive(:add_tag) .with(user, 'v1.1.0', 'master', 'Foo') - .and_raise(Rugged::TagError) + .and_raise(Gitlab::Git::Repository::TagExistsError) response = service.execute('v1.1.0', 'master', 'Foo') diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb index 6ee35a33b2d..f8d4a47b212 100644 --- a/spec/services/users/update_service_spec.rb +++ b/spec/services/users/update_service_spec.rb @@ -31,13 +31,13 @@ describe Users::UpdateService do end def update_user(user, opts) - described_class.new(user, opts).execute + described_class.new(user, opts.merge(user: user)).execute end end describe '#execute!' do it 'updates the name' do - service = described_class.new(user, name: 'New Name') + service = described_class.new(user, user: user, name: 'New Name') expect(service).not_to receive(:notify_new_user) result = service.execute! @@ -55,7 +55,7 @@ describe Users::UpdateService do it 'fires system hooks when a new user is saved' do system_hook_service = spy(:system_hook_service) user = build(:user) - service = described_class.new(user, name: 'John Doe') + service = described_class.new(user, user: user, name: 'John Doe') expect(service).to receive(:notify_new_user).and_call_original expect(service).to receive(:system_hook_service).and_return(system_hook_service) @@ -65,7 +65,7 @@ describe Users::UpdateService do end def update_user(user, opts) - described_class.new(user, opts).execute! + described_class.new(user, opts.merge(user: user)).execute! end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 92735336572..dbf05b7f004 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -64,8 +64,16 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! - config.define_derived_metadata(file_path: %r{/spec/requests/(ci/)?api/}) do |metadata| - metadata[:api] = true + config.define_derived_metadata(file_path: %r{/spec/}) do |metadata| + location = metadata[:location] + + metadata[:api] = true if location =~ %r{/spec/requests/api/} + + # do not overwrite type if it's already set + next if metadata.key?(:type) + + match = location.match(%r{/spec/([^/]+)/}) + metadata[:type] = match[1].singularize.to_sym if match end config.raise_errors_for_deprecations! diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb new file mode 100644 index 00000000000..c9302f7b750 --- /dev/null +++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb @@ -0,0 +1,103 @@ +shared_examples 'custom attributes endpoints' do |attributable_name| + let!(:custom_attribute1) { attributable.custom_attributes.create key: 'foo', value: 'foo' } + let!(:custom_attribute2) { attributable.custom_attributes.create key: 'bar', value: 'bar' } + + describe "GET /#{attributable_name} with custom attributes filter" do + let!(:other_attributable) { create attributable.class.name.underscore } + + context 'with an unauthorized user' do + it 'does not filter by custom attributes' do + get api("/#{attributable_name}", user), custom_attributes: { foo: 'foo', bar: 'bar' } + + expect(response).to have_http_status(200) + expect(json_response.size).to be 2 + end + end + + it 'filters by custom attributes' do + get api("/#{attributable_name}", admin), custom_attributes: { foo: 'foo', bar: 'bar' } + + expect(response).to have_http_status(200) + expect(json_response.size).to be 1 + expect(json_response.first['id']).to eq attributable.id + end + end + + describe "GET /#{attributable_name}/:id/custom_attributes" do + context 'with an unauthorized user' do + subject { get api("/#{attributable_name}/#{attributable.id}/custom_attributes", user) } + + it_behaves_like 'an unauthorized API user' + end + + it 'returns all custom attributes' do + get api("/#{attributable_name}/#{attributable.id}/custom_attributes", admin) + + expect(response).to have_http_status(200) + expect(json_response).to contain_exactly( + { 'key' => 'foo', 'value' => 'foo' }, + { 'key' => 'bar', 'value' => 'bar' } + ) + end + end + + describe "GET /#{attributable_name}/:id/custom_attributes/:key" do + context 'with an unauthorized user' do + subject { get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user) } + + it_behaves_like 'an unauthorized API user' + end + + it 'returns a single custom attribute' do + get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin) + + expect(response).to have_http_status(200) + expect(json_response).to eq({ 'key' => 'foo', 'value' => 'foo' }) + end + end + + describe "PUT /#{attributable_name}/:id/custom_attributes/:key" do + context 'with an unauthorized user' do + subject { put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user), value: 'new' } + + it_behaves_like 'an unauthorized API user' + end + + it 'creates a new custom attribute' do + expect do + put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), value: 'new' + end.to change { attributable.custom_attributes.count }.by(1) + + expect(response).to have_http_status(200) + expect(json_response).to eq({ 'key' => 'new', 'value' => 'new' }) + expect(attributable.custom_attributes.find_by(key: 'new').value).to eq 'new' + end + + it 'updates an existing custom attribute' do + expect do + put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), value: 'new' + end.not_to change { attributable.custom_attributes.count } + + expect(response).to have_http_status(200) + expect(json_response).to eq({ 'key' => 'foo', 'value' => 'new' }) + expect(custom_attribute1.reload.value).to eq 'new' + end + end + + describe "DELETE /#{attributable_name}/:id/custom_attributes/:key" do + context 'with an unauthorized user' do + subject { delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user) } + + it_behaves_like 'an unauthorized API user' + end + + it 'deletes an existing custom attribute' do + expect do + delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin) + end.to change { attributable.custom_attributes.count }.by(-1) + + expect(response).to have_http_status(204) + expect(attributable.custom_attributes.find_by(key: 'foo')).to be_nil + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 6e5b9700b54..b4e8b5ea67b 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -306,6 +306,9 @@ module TestEnv ensure_component_dir_name_is_correct!(component, install_dir) + # On CI, once installed, components never need update + return if File.exist?(install_dir) && ENV['CI'] + if component_needs_update?(install_dir, version) # Cleanup the component entirely to ensure we start fresh FileUtils.rm_rf(install_dir) diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb new file mode 100644 index 00000000000..f59792c3d36 --- /dev/null +++ b/spec/tasks/gitlab/storage_rake_spec.rb @@ -0,0 +1,52 @@ +require 'rake_helper' + +describe 'gitlab:storage rake tasks' do + before do + Rake.application.rake_require 'tasks/gitlab/storage' + + stub_warn_user_is_not_gitlab + end + + describe 'migrate_to_hashed rake task' do + context '0 legacy projects' do + it 'does nothing' do + expect(StorageMigratorWorker).not_to receive(:perform_async) + + run_rake_task('gitlab:storage:migrate_to_hashed') + end + end + + context '5 legacy projects' do + let(:projects) { create_list(:project, 5, storage_version: 0) } + + context 'in batches of 1' do + before do + stub_env('BATCH' => 1) + end + + it 'enqueues one StorageMigratorWorker per project' do + projects.each do |project| + expect(StorageMigratorWorker).to receive(:perform_async).with(project.id, project.id) + end + + run_rake_task('gitlab:storage:migrate_to_hashed') + end + end + + context 'in batches of 2' do + before do + stub_env('BATCH' => 2) + end + + it 'enqueues one StorageMigratorWorker per 2 projects' do + projects.map(&:id).sort.each_slice(2) do |first, last| + last ||= first + expect(StorageMigratorWorker).to receive(:perform_async).with(first, last) + end + + run_rake_task('gitlab:storage:migrate_to_hashed') + end + end + end + end +end diff --git a/spec/views/shared/issuable/_participants.html.haml.rb b/spec/views/shared/issuable/_participants.html.haml.rb new file mode 100644 index 00000000000..51059d4c0d7 --- /dev/null +++ b/spec/views/shared/issuable/_participants.html.haml.rb @@ -0,0 +1,26 @@ +require 'spec_helper' +require 'nokogiri' + +describe 'shared/issuable/_participants.html.haml' do + let(:project) { create(:project) } + let(:participants) { create_list(:user, 100) } + + before do + allow(view).to receive_messages(project: project, + participants: participants) + end + + it 'renders lazy loaded avatars' do + render 'shared/issuable/participants' + + html = Nokogiri::HTML(rendered) + + avatars = html.css('.participants-author img') + + avatars.each do |avatar| + expect(avatar[:class]).to include('lazy') + expect(avatar[:src]).to eql(LazyImageTagHelper.placeholder_image) + expect(avatar[:"data-src"]).to match('http://www.gravatar.com/avatar/') + end + end +end diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index f7b67b8efc6..ab656d619f4 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -32,7 +32,7 @@ describe GitGarbageCollectWorker do expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original expect_any_instance_of(Repository).to receive(:branch_names).and_call_original expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original - expect_any_instance_of(Gitlab::Git::Repository).to receive(:branch_count).and_call_original + expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original subject.perform(project.id, :gc, lease_key, lease_uuid) end @@ -47,7 +47,6 @@ describe GitGarbageCollectWorker do expect(subject).not_to receive(:command) expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original - expect_any_instance_of(Repository).not_to receive(:branch_count).and_call_original expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original subject.perform(project.id, :gc, lease_key, lease_uuid) @@ -78,7 +77,7 @@ describe GitGarbageCollectWorker do expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original expect_any_instance_of(Repository).to receive(:branch_names).and_call_original expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original - expect_any_instance_of(Gitlab::Git::Repository).to receive(:branch_count).and_call_original + expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original subject.perform(project.id) end @@ -93,7 +92,6 @@ describe GitGarbageCollectWorker do expect(subject).not_to receive(:command) expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original - expect_any_instance_of(Repository).not_to receive(:branch_count).and_call_original expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original subject.perform(project.id) diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index d3707a3cc11..05eecf5f0bb 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -70,12 +70,15 @@ describe PostReceive do context "creates a Ci::Pipeline for every change" do before do - allow_any_instance_of(Ci::CreatePipelineService).to receive(:commit) 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 + + # TODO, don't stub private methods + # + allow_any_instance_of(Ci::CreatePipelineService) + .to receive(:commit).and_return(OpenStruct.new(id: '123456')) + + allow_any_instance_of(Repository) + .to receive(:branch_exists?).and_return(true) end it { expect { subject }.to change { Ci::Pipeline.count }.by(2) } diff --git a/spec/workers/project_migrate_hashed_storage_worker_spec.rb b/spec/workers/project_migrate_hashed_storage_worker_spec.rb new file mode 100644 index 00000000000..f5226dee0ad --- /dev/null +++ b/spec/workers/project_migrate_hashed_storage_worker_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe ProjectMigrateHashedStorageWorker do + describe '#perform' do + let(:project) { create(:project, :empty_repo) } + let(:pending_delete_project) { create(:project, :empty_repo, pending_delete: true) } + + it 'skips when project no longer exists' do + nonexistent_id = 999999999999 + + expect(::Projects::HashedStorageMigrationService).not_to receive(:new) + subject.perform(nonexistent_id) + end + + it 'skips when project is pending delete' do + expect(::Projects::HashedStorageMigrationService).not_to receive(:new) + + subject.perform(pending_delete_project.id) + end + + it 'delegates removal to service class' do + service = double('service') + expect(::Projects::HashedStorageMigrationService).to receive(:new).with(project, subject.logger).and_return(service) + expect(service).to receive(:execute) + + subject.perform(project.id) + end + end +end diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb new file mode 100644 index 00000000000..8619ff2f7da --- /dev/null +++ b/spec/workers/storage_migrator_worker_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe StorageMigratorWorker do + subject(:worker) { described_class.new } + let(:projects) { create_list(:project, 2) } + + describe '#perform' do + let(:ids) { projects.map(&:id) } + + it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice + + worker.perform(ids.min, ids.max) + end + + it 'sets projects as read only' do + allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice + worker.perform(ids.min, ids.max) + + projects.each do |project| + expect(project.reload.repository_read_only?).to be_truthy + end + end + + it 'rescues and log exceptions' do + allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError) + expect { worker.perform(ids.min, ids.max) }.not_to raise_error + end + end +end diff --git a/yarn.lock b/yarn.lock index c95dd6433ca..ae86887630b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2714,7 +2714,7 @@ getpass@^0.1.1: "gitlab-svgs@https://gitlab.com/gitlab-org/gitlab-svgs.git": version "1.0.2" - resolved "https://gitlab.com/gitlab-org/gitlab-svgs.git#7f36f3951dd08904761780da48efcd639f34c3af" + resolved "https://gitlab.com/gitlab-org/gitlab-svgs.git#e7621d7b028607ae9c69f8b496cd49b42fe607e4" glob-base@^0.3.0: version "0.3.0" |