Commit 09a7da72 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into pipeline-emails

* upstream/master: (148 commits)
  Fix broken rspec in compare text
  Added logic to handle a revision input that does not exist in the menu
  Fix broken Spinach tests caused by changes in !6550
  Convert CHANGELOG to Markdown
  Fix active tab test for branches page
  Fix merge requests feature tests
  Wait for ajax call in merge request unsubscribe test
  Fix 500 error when creating mileston from group page
  Fix 404 when group path has dot in the name
  Add the tech writers usernames in the doc_sytleguide doc
  Fix broken SCSS linter errors due to missing newlines
  Fix Hash syntax to work for both Ruby 2.1 and 2.3
  Fix Spinach merge request diff failures
  remove ashley since she no longer works here
  Revert "Improve tabbing usability for sign in page"
  Don't use member properties in users_spec, and remove loading ref.
  Clean up stray Sign up ref.
  Back off the array spreading, bc poltergeist freaks out.
  Convert dispatcher to es6.
  Add exists to users routes and fix endpoint.
  ...
parents 43973223 cb8654e8
CHANGELOG merge=union
CHANGELOG.md merge=union
*.js.es6 gitlab-language=javascript
......@@ -99,7 +99,7 @@ update-knapsack:
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
- knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts:
expire_in: 31d
paths:
......
See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html.
See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html
## What does this MR do?
......
......@@ -61,7 +61,7 @@ linters:
# Separate rule, function, and mixin declarations with empty lines.
EmptyLineBetweenBlocks:
enabled: false
enabled: true
# Reports when you have an empty rule set.
EmptyRule:
......@@ -219,7 +219,7 @@ linters:
# Property values, @extend, @include, and @import directives, and variable
# declarations should always end with a semicolon.
TrailingSemicolon:
enabled: false
enabled: true
# Reports lines containing trailing whitespace.
TrailingWhitespace:
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.13.0 (unreleased)
## 8.13.0 (2016-10-22)
- Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
- Respond with 404 Not Found for non-existent tags (Linus Thiel)
- Truncate long labels with ellipsis in labels page
- Improve tabbing usability for sign in page (ClemMakesApps)
- Enforce TrailingSemicolon and EmptyLineBetweenBlocks in scss-lint
- Adding members no longer silently fails when there is extra whitespace
- Update runner version only when updating contacted_at
- Add link from system note to compare with previous version
......@@ -14,6 +17,7 @@ v 8.13.0 (unreleased)
- Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
- Updating verbiage on git basics to be more intuitive
- Clarify documentation for Runners API (Gennady Trafimenkov)
- The instrumentation for Banzai::Renderer has been restored
- Change user & group landing page routing from /u/:username to /:username
- Prevent running GfmAutocomplete setup for each diff note !6569
- Added documentation for .gitattributes files
......@@ -114,15 +118,20 @@ v 8.13.0 (unreleased)
- Fixes padding in all clipboard icons that have .btn class
- Fix a typo in doc/api/labels.md
- API: all unknown routing will be handled with 404 Not Found
- Add docs for request profiling
- Make guests unable to view MRs on private projects
v 8.12.7
## 8.12.7
- Use gitlab-markup gem instead of github-markup to fix `.rst` file rendering. !6659
- Fix GFM autocomplete setup being called several times
## 8.12.6
v 8.12.6
- Update mailroom to 0.8.1 in Gemfile.lock !6814
v 8.12.5
## 8.12.5
- Switch from request to env in ::API::Helpers. !6615
- Update the mail_room gem to 0.8.1 to fix a race condition with the mailbox watching thread. !6714
- Improve issue load time performance by avoiding ORDER BY in find_by call. !6724
......@@ -130,7 +139,8 @@ v 8.12.5
- Don't send Private-Token (API authentication) headers to Sentry
- Share projects via the API only with groups the authenticated user can access
v 8.12.4
## 8.12.4
- Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. !6294 (lukehowell)
- Fix padding in build sidebar. !6506
- Changed compare dropdowns to dropdowns with isolated search input. !6550
......@@ -145,10 +155,12 @@ v 8.12.4
- Set GitLab project exported file permissions to owner only
- Improve the way merge request versions are compared with each other
v 8.12.3
## 8.12.3
- Update Gitlab Shell to support low IO priority for storage moves
v 8.12.2
## 8.12.2
- Fix Import/Export not recognising correctly the imported services.
- Fix snippets pagination
- Fix "Create project" button layout when visibility options are restricted
......@@ -164,11 +176,13 @@ v 8.12.2
- Fix resolve discussion buttons endpoint path
- Refactor remnants of CoffeeScript destructured opts and super !6261
v 8.12.1
## 8.12.1
- Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
- Fix issue with search filter labels not displaying
v 8.12.0
## 8.12.0 (2016-09-22)
- Removes inconsistency regarding tagging immediatelly as merged once you create a new branch. !6408
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- Only check :can_resolve permission if the note is resolvable
......@@ -266,6 +280,7 @@ v 8.12.0
- Remove prefixes from transition CSS property (ClemMakesApps)
- Add Sentry logging to API calls
- Add BroadcastMessage API
- Merge request tabs are fixed when scrolling page
- Use 'git update-ref' for safer web commits !6130
- Sort pipelines requested through the API
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
......@@ -358,22 +373,27 @@ v 8.12.0
- Fix non-master branch readme display in tree view
- Add UX improvements for merge request version diffs
v 8.11.9
## 8.11.9
- Don't send Private-Token (API authentication) headers to Sentry
- Share projects via the API only with groups the authenticated user can access
v 8.11.8
## 8.11.8
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.11.7
## 8.11.7
- Avoid conflict with admin labels when importing GitHub labels. !6158
- Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
- Allow the Rails cookie to be used for API authentication.
- Login/Register UX upgrade !6328
## 8.11.6
v 8.11.6
- Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
- Make merge conflict file size limit 200 KB, to match the docs. !6052
- Fix an error where we were unable to create a CommitStatus for running state. !6107
......@@ -383,7 +403,8 @@ v 8.11.6
- Fix DB schema to match latest migration. !6256
- Exclude some pending or inactivated rows in Member scopes.
v 8.11.5
## 8.11.5
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
- Fix member expiration date picker after update. !6184
- Fix suggested colors options for new labels in the admin area. !6138
......@@ -396,7 +417,8 @@ v 8.11.5
- Fix confidential issues being exposed as public using gitlab.com export
- Use oj gem for faster JSON processing
v 8.11.4
## 8.11.4
- Fix resolving conflicts on forks. !6082
- Fix diff commenting on merge requests created prior to 8.10. !6029
- Fix pipelines tab layout regression. !5952
......@@ -413,7 +435,8 @@ v 8.11.4
- Remove gitorious. !5866
- Allow compare merge request versions
v 8.11.3
## 8.11.3
- Allow system info page to handle case where info is unavailable
- Label list shows all issues (opened or closed) with that label
- Don't show resolve conflicts link before MR status is updated
......@@ -424,17 +447,20 @@ v 8.11.3
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
- Issues filters reset button
v 8.11.2
## 8.11.2
- Show "Create Merge Request" widget for push events to fork projects on the source project. !5978
- Use gitlab-workhorse 0.7.11 !5983
- Does not halt the GitHub import process when an error occurs. !5763
- Fix file links on project page when default view is Files !5933
- Fixed enter key in search input not working !5888
v 8.11.1
## 8.11.1
- Pulled due to packaging error.
v 8.11.0
## 8.11.0 (2016-08-22)
- Use test coverage value from the latest successful pipeline in badge. !5862
- Add test coverage report badge. !5708
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
......@@ -587,48 +613,58 @@ v 8.11.0
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
v 8.10.12
## 8.10.12
- Don't send Private-Token (API authentication) headers to Sentry
- Share projects via the API only with groups the authenticated user can access
v 8.10.11
## 8.10.11
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.10.10
## 8.10.10
- Allow the Rails cookie to be used for API authentication.
v 8.10.9
## 8.10.9
- Exclude some pending or inactivated rows in Member scopes
v 8.10.8
## 8.10.8
- Fix information disclosure in issue boards.
- Fix privilege escalation in project import.
v 8.10.7
## 8.10.7
- Upgrade Hamlit to 2.6.1. !5873
- Upgrade Doorkeeper to 4.2.0. !5881
v 8.10.6
## 8.10.6
- Upgrade Rails to 4.2.7.1 for security fixes. !5781
- Restore "Largest repository" sort option on Admin > Projects page. !5797
- Fix privilege escalation via project export.
- Require administrator privileges to perform a project import.
v 8.10.5
## 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670
- Revert the "Defend against 'Host' header injection" change in the source NGINX templates. !5706
- Cache project count for 5 minutes to reduce DB load. !5746 & !5754
v 8.10.4
## 8.10.4
- Don't close referenced upstream issues from a forked project.
- Fixes issue with dropdowns `enter` key not working correctly. !5544
- Fix Import/Export project import not working in HA mode. !5618
- Fix Import/Export error checking versions. !5638
v 8.10.3
## 8.10.3
- Fix Import/Export issue importing milestones and labels not associated properly. !5426
- Fix timing problems running imports on production. !5523
- Add a log message when a project is scheduled for destruction for debugging. !5540
......@@ -639,7 +675,8 @@ v 8.10.3
- Trim extra displayed carriage returns in diffs and files with CRLFs. !5588
- Fix label already exist error message in the right sidebar.
v 8.10.2
## 8.10.2
- User can now search branches by name. !5144
- Page is now properly rendered after committing the first file and creating the first branch. !5399
- Add branch or tag icon to ref in builds page. !5434
......@@ -660,7 +697,8 @@ v 8.10.2
- Fix missing schema update for `20160722221922`. !5512
- Update `gitlab-shell` version to 3.2.1 in the 8.9->8.10 update guide. !5516
v 8.10.1
## 8.10.1
- Refactor repository storages documentation. !5428
- Gracefully handle case when keep-around references are corrupted or exist already. !5430
- Add detailed info on storage path mountpoints. !5437
......@@ -669,7 +707,8 @@ v 8.10.1
- Ignore invalid trusted proxies in X-Forwarded-For header. !5454
- Add links to the real markdown.md file for all GFM examples. !5458
v 8.10.0
## 8.10.0 (2016-07-22)
- Fix profile activity heatmap to show correct day name (eanplatter)
- Speed up ExternalWikiHelper#get_project_wiki_path
- Expose {should,force}_remove_source_branch (Ben Boeckel)
......@@ -833,26 +872,32 @@ v 8.10.0
- Show tooltip on GitLab export link in new project page
- Fix import_data wrongly saved as a result of an invalid import_url !5206
v 8.9.11
## 8.9.11
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.9.10
## 8.9.10
- Allow the Rails cookie to be used for API authentication.
v 8.9.9
## 8.9.9
- Exclude some pending or inactivated rows in Member scopes
v 8.9.8
## 8.9.8
- Upgrade Doorkeeper to 4.2.0. !5881
v 8.9.7
## 8.9.7
- Upgrade Rails to 4.2.7.1 for security fixes. !5781
- Require administrator privileges to perform a project import.
v 8.9.6
## 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
- Fix log statements in import/export. !5129
- Fix commit avatar alignment in compare view. !5128
......@@ -861,7 +906,8 @@ v 8.9.6
- Keeps issue number when importing from Gitlab.com
- Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
v 8.9.5
## 8.9.5
- Add more debug info to import/export and memory killer. !5108
- Fixed avatar alignment in new MR view. !5095
- Fix diff comments not showing up in activity feed. !5069
......@@ -876,7 +922,8 @@ v 8.9.5
- Update RedCloth to 4.3.2 for CVE-2012-6684. !4929 (Takuya Noguchi)
- Improve the request / withdraw access button. !4860
v 8.9.4
## 8.9.4
- Fix privilege escalation issue with OAuth external users.
- Ensure references to private repos aren't shown to logged-out users.
- Fixed search field blur not removing focus. !4704
......@@ -890,7 +937,8 @@ v 8.9.4
- Expiry date on pinned nav cookie. !5009
- Updated breakpoint for sidebar pinning. !5019
v 8.9.3
## 8.9.3
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963
- Fix rendering of commit notes. !4953
- Resolve "Pin should show up at 1280px min". !4947
......@@ -907,12 +955,14 @@ v 8.9.3
- Use update_columns to bypass all the dirty code on active_record. !4985
- Fix restore Rake task warning message output !4980
v 8.9.2
## 8.9.2
- Fix visibility of snippets when searching.
- Fix an information disclosure when requesting access to a group containing private projects.
- Update omniauth-saml to 1.6.0 !4951
v 8.9.1
## 8.9.1
- Refactor labels documentation. !3347
- Eager load award emoji on notes. !4628
- Fix some CI wording in documentation. !4660
......@@ -956,7 +1006,8 @@ v 8.9.1
- Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
- Remove duplicate 'New Page' button on edit wiki page
v 8.9.0
## 8.9.0 (2016-06-22)
- Fix group visibility form layout in application settings
- Fix builds API response not including commit data
- Fix error when CI job variables key specified but not defined
......@@ -1111,21 +1162,26 @@ v 8.9.0
- Add tooltip to pin/unpin navbar
- Add new sub nav style to Wiki and Graphs sub navigation
v 8.8.9
## 8.8.9
- Upgrade Doorkeeper to 4.2.0. !5881
v 8.8.8
## 8.8.8
- Upgrade Rails to 4.2.7.1 for security fixes. !5781
v 8.8.7
## 8.8.7
- Fix privilege escalation issue with OAuth external users.
- Ensure references to private repos aren't shown to logged-out users.
v 8.8.6
## 8.8.6
- Fix visibility of snippets when searching.
- Update omniauth-saml to 1.6.0 !4951
v 8.8.5
## 8.8.5
- Import GitHub repositories respecting the API rate limit !4166
- Fix todos page throwing errors when you have a project pending deletion !4300
- Disable Webhooks before proceeding with the GitHub import !4470
......@@ -1138,12 +1194,14 @@ v 8.8.5
- Banzai::Filter::UploadLinkFilter use XPath instead CSS expressions
- Banzai::Filter::ExternalLinkFilter use XPath instead CSS expressions
v 8.8.4
## 8.8.4
- Fix LDAP-based login for users with 2FA enabled. !4493
- Added descriptions to notification settings dropdown
- Due date can be removed from milestones
v 8.8.3
## 8.8.3
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
- Fixed JS error when trying to remove discussion form. !4303
- Fixed issue with button color when no CI enabled. !4287
......@@ -1162,7 +1220,8 @@ v 8.8.3
- Fix missing number on generated ordered list element. !4437
- Prevent disclosure of notes on confidential issues in search results.
v 8.8.2
## 8.8.2
- Added remove due date button. !4209
- Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242
- Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245
......@@ -1173,13 +1232,15 @@ v 8.8.2
- When creating a .gitignore file a dropdown with templates will be provided. !4075
- Fix concurrent request when updating build log in browser. !4183
v 8.8.1
## 8.8.1
- Add documentation for the "Health Check" feature
- Allow anonymous users to access a public project's pipelines !4233
- Fix MySQL compatibility in zero downtime migrations helpers
- Fix the CI login to Container Registry (the gitlab-ci-token user)
v 8.8.0
## 8.8.0 (2016-05-22)
- Implement GFM references for milestones (Alejandro Rodríguez)
- Snippets tab under user profile. !4001 (Long Nguyen)
- Fix error when using link to uploads in global snippets
......@@ -1255,34 +1316,40 @@ v 8.8.0
- When creating a .gitignore file a dropdown with templates will be provided
- Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
v 8.7.9
## 8.7.9
- Fix privilege escalation issue with OAuth external users.
- Ensure references to private repos aren't shown to logged-out users.
v 8.7.8
## 8.7.8
- Fix visibility of snippets when searching.
- Update omniauth-saml to 1.6.0 !4951
v 8.7.7
## 8.7.7
- Fix import by `Any Git URL` broken if the URL contains a space
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
- Only show notes through JSON on confidential issues that the user has access to
v 8.7.6
## 8.7.6
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
- Fix import from GitLab.com to a private instance failure. !4181
- Fix external imports not finding the import data. !4106
- Fix notification delay when changing status of an issue
- Bump Workhorse to 0.7.5 so it can serve raw diffs
v 8.7.5
## 8.7.5
- Fix relative links in wiki pages. !4050
- Fix always showing build notification message when switching between merge requests !4086
- Fix an issue when filtering merge requests with more than one label. !3886
- Fix short note for the default scope on build page (Takuya Noguchi)
v 8.7.4
## 8.7.4
- Links for Redmine issue references are generated correctly again !4048 (Benedikt Huss)
- Fix setting trusted proxies !3970
- Fix BitBucket importer bug when throwing exceptions !3941
......@@ -1291,20 +1358,23 @@ v 8.7.4
- Running rake gitlab:db:drop_tables uses "IF EXISTS" as a precaution !4100
- Use a case-insensitive comparison in sanitizing URI schemes
v 8.7.3
## 8.7.3
- Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
- Merge request widget displays TeamCity build state and code coverage correctly again.
- Fix the line code when importing PR review comments from GitHub. !4010
- Wikis are now initialized on legacy projects when checking repositories
- Remove animate.css in favor of a smaller subset of animations. !3937 (Connor Shea)
v 8.7.2
## 8.7.2
- The "New Branch" button is now loaded asynchronously
- Fix error 500 when trying to create a wiki page
- Updated spacing between notification label and button
- Label titles in filters are now escaped properly
v 8.7.1
## 8.7.1
- Throttle the update of `project.last_activity_at` to 1 minute. !3848
- Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
- Fix license detection to detect all license files, not only known licenses. !3878
......@@ -1314,7 +1384,8 @@ v 8.7.1
- Update width of search box to fix Safari bug. !3900 (Jedidiah)
- Use the `can?` helper instead of `current_user.can?`
v 8.7.0
## 8.7.0 (2016-04-22)
- Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
- Fix vulnerability that made it possible to gain access to private labels and milestones
- The number of InfluxDB points stored per UDP packet can now be configured
......@@ -1430,12 +1501,14 @@ v 8.7.0
- Add RAW build trace output and button on build page
- Add incremental build trace update into CI API
v 8.6.9
## 8.6.9
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
- Only show notes through JSON on confidential issues that the user has access to
v 8.6.8
## 8.6.8
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
......@@ -1448,12 +1521,14 @@ v 8.6.8
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.6.7
## 8.6.7
- Fix persistent XSS vulnerability in `commit_person_link` helper
- Fix persistent XSS vulnerability in Label and Milestone dropdowns
- Fix vulnerability that made it possible to enumerate private projects belonging to group
v 8.6.6
## 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
- Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
- Fix revoking of authorized OAuth applications (Connor Shea). !3690
......@@ -1461,7 +1536,8 @@ v 8.6.6
- Issuable header is consistent between issues and merge requests
- Improved spacing in issuable header on mobile
v 8.6.5
## 8.6.5
- Fix importing from GitHub Enterprise. !3529
- Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
- Check permissions when user attempts to import members from another project. !3535
......@@ -1470,11 +1546,13 @@ v 8.6.5
- Unblock user when active_directory is disabled and it can be found !3550
- Fix a 2FA authentication spoofing vulnerability.
v 8.6.4
## 8.6.4
- Don't attempt to fetch any tags from a forked repo (Stan Hu)
- Redesign the Labels page
v 8.6.3
## 8.6.3
- Mentions on confidential issues doesn't create todos for non-members. !3374
- Destroy related todos when an Issue/MR is deleted. !3376
- Fix error 500 when target is nil on todo list. !3376
......@@ -1487,7 +1565,8 @@ v 8.6.3
- Fix issue with dropdowns not selecting values. !3478
- Update gitlab-shell version and doc to 2.6.12. gitlab-org/gitlab-ee!280
v 8.6.2
## 8.6.2
- Fix dropdown alignment. !3298
- Fix issuable sidebar overlaps on tablet. !3299
- Make dropdowns pixel perfect. !3337
......@@ -1509,7 +1588,8 @@ v 8.6.2
- Gracefully handle notes on deleted commits in merge requests (Stan Hu). !3402
- Fixed issue with notification settings not saving. !3452
v 8.6.1
## 8.6.1
- Add option to reload the schema before restoring a database backup. !2807
- Display navigation controls on mobile. !3214
- Fixed bug where participants would not work correctly on merge requests. !3329
......@@ -1524,7 +1604,8 @@ v 8.6.1
- Fixes issue with assign milestone not loading milestone list. !3346
- Fix an issue causing the Dashboard/Milestones page to be blank. !3348
v 8.6.0
## 8.6.0 (2016-03-22)
- Add ability to move issue to another project
- Prevent tokens in the import URL to be showed by the UI
- Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
......@@ -1589,11 +1670,13 @@ v 8.6.0
- Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.13
## 8.5.13
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
v 8.5.12
## 8.5.12
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
......@@ -1604,41 +1687,51 @@ v 8.5.12
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.5.11
## 8.5.11
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.5.10
## 8.5.10
- Fix a 2FA authentication spoofing vulnerability.
v 8.5.9
## 8.5.9
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.5.8
## 8.5.8
- Bump Git version requirement to 2.7.4
v 8.5.7
## 8.5.7
- Bump Git version requirement to 2.7.3
v 8.5.6
## 8.5.6
- Obtain a lease before querying LDAP
v 8.5.5
## 8.5.5
- Ensure removing a project removes associated Todo entries
- Prevent a 500 error in Todos when author was removed
- Fix pagination for filtered dashboard and explore pages
- Fix "Show all" link behavior
v 8.5.4
## 8.5.4
- Do not cache requests for badges (including builds badge)
v 8.5.3
## 8.5.3
- Flush repository caches before renaming projects
- Sort starred projects on dashboard based on last activity by default
- Show commit message in JIRA mention comment
- Makes issue page and merge request page usable on mobile browsers.
- Improved UI for profile settings
v 8.5.2
## 8.5.2
- Fix sidebar overlapping content when screen width was below 1200px
- Don't repeat labels listed on Labels tab
- Bring the "branded appearance" feature from EE to CE
......@@ -1655,7 +1748,8 @@ v 8.5.2
- Don't show "Welcome to GitLab" when the search didn't return any projects
- Add Todos documentation
v 8.5.1
## 8.5.1
- Fix group projects styles
- Show Crowd login tab when sign in is disabled and Crowd is enabled (Peter Hudec)
- Fix a set of small UI glitches in project, profile, and wiki pages
......@@ -1675,7 +1769,8 @@ v 8.5.1
- Add build coverage in project's builds page (Steffen Köhler)
- Changed # to ! for merge requests in activity view
v 8.5.0
## 8.5.0 (2016-02-22)
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- Cache various Repository methods to improve performance
- Fix duplicated branch creation/deletion Webhooks/service notifications when using Web UI (Stan Hu)
......@@ -1754,11 +1849,13 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos
v 8.4.11
## 8.4.11
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
v 8.4.10
## 8.4.10
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
......@@ -1769,28 +1866,35 @@ v 8.4.10
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.4.9
## 8.4.9
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.4.8
## 8.4.8
- Fix a 2FA authentication spoofing vulnerability.
v 8.4.7
## 8.4.7
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.4.6
## 8.4.6
- Bump Git version requirement to 2.7.4
v 8.4.5
## 8.4.5
- No CE-specific changes
v 8.4.4
## 8.4.4
- Update omniauth-saml gem to 1.4.2
- Prevent long-running backup tasks from timing out the database connection
- Add a Project setting to allow guests to view build logs (defaults to true)
- Sort project milestones by due date including issue editor (Oliver Rogers / Orih)
v 8.4.3
## 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger
than 2.1GB
- Correctly highlight MR diff when MR has merge conflicts
......@@ -1801,7 +1905,8 @@ v 8.4.3
performance monitoring
- Allow autosize textareas to also be manually resized
v 8.4.2
## 8.4.2
- Bump required gitlab-workhorse version to bring in a fix for missing
artifacts in the build artifacts browser
- Get rid of those ugly borders on the file tree view
......@@ -1814,14 +1919,16 @@ v 8.4.2
- Fix method undefined when using external commit status in builds
- Fix highlighting in blame view.
v 8.4.1
## 8.4.1
- Apply security updates for Rails (4.2.5.1), rails-html-sanitizer (1.0.3),
and Nokogiri (1.6.7.2)
- Fix redirect loop during import
- Fix diff highlighting for all syntax themes
- Delete project and associations in a background worker
v 8.4.0
## 8.4.0 (2016-01-22)
- Allow LDAP users to change their email if it was not set by the LDAP server
- Ensure Gravatar host looks like an actual host
- Consider re-assign as a mention from a notification point of view
......@@ -1894,11 +2001,13 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.10
## 8.3.10
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
v 8.3.9
## 8.3.9
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
......@@ -1907,22 +2016,28 @@ v 8.3.9
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.3.8
## 8.3.8
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.3.7
## 8.3.7
- Fix a 2FA authentication spoofing vulnerability.
v 8.3.6
## 8.3.6
- Don't attempt to fetch any tags from a forked repo (Stan Hu).
v 8.3.5
## 8.3.5
- Bump Git version requirement to 2.7.4
v 8.3.4
## 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug)
v 8.3.3
## 8.3.3
- Preserve CE behavior with JIRA integration by only calling API if URL is set
- Fix duplicated branch creation/deletion events when using Web UI (Stan Hu)
- Add configurable LDAP server query timeout
......@@ -1938,17 +2053,20 @@ v 8.3.3
- Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells)
- Fix missing artifacts and build traces for build created before 8.3
v 8.3.2
## 8.3.2
- Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
- Add support for Google reCAPTCHA in user registration
v 8.3.1
## 8.3.1
- Fix Error 500 when global milestones have slashes (Stan Hu)
- Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
- Fix LDAP identity and user retrieval when special characters are used
- Move Sidekiq-cron configuration to gitlab.yml
v 8.3.0
## 8.3.0 (2015-12-22)
- Bump rack-attack to 4.3.1 for security fix (Stan Hu)
- API support for starred projects for authorized user (Zeger-Jan van de Weg)
- Add open_issues_count to project API (Stan Hu)
......@@ -2016,11 +2134,13 @@ v 8.3.0
- Expose Git's version in the admin area
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
v 8.2.6
## 8.2.6
- Prevent unauthorized access to other projects build traces
- Forbid scripting for wiki files
v 8.2.5
## 8.2.5
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
......@@ -2028,10 +2148,12 @@ v 8.2.5
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.2.4
## 8.2.4
- Bump Git version requirement to 2.7.4
v 8.2.3
## 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
- Update documentation for "Guest" permissions
......@@ -2040,7 +2162,8 @@ v 8.2.3
- Webhook payload has an added, modified and removed properties for each commit
- Fix 500 error when creating a merge request that removes a submodule
v 8.2.2
## 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
- Ensure cached application settings are refreshed at startup (Stan Hu)
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
......@@ -2050,11 +2173,13 @@ v 8.2.2
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- Fix: duplicate email notifications on issue comments
v 8.2.1
## 8.2.1
- Forcefully update builds that didn't want to update with state machine
- Fix: saving GitLabCiService as Admin Template
v 8.2.0
## 8.2.0 (2015-11-22)
- Improved performance of finding projects and groups in various places
- Improved performance of rendering user profile pages and Atom feeds
- Expose build artifacts path as config option
......@@ -2114,19 +2239,22 @@ v 8.2.0
- Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
- Add Award Emoji to issue and merge request pages
v 8.1.4
## 8.1.4
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- Prevent redirect loop when home_page_url is set to the root URL
- Fix incoming email config defaults
- Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
v 8.1.3
## 8.1.3
- Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
- Spread out runner contacted_at updates
- Use issue editor as cross reference comment author when issue is edited with a new mention
- Add Facebook authentication
v 8.1.2
## 8.1.2
- Fix cloning Wiki repositories via HTTP (Stan Hu)
- Add migration to remove satellites directory
- Fix specific runners visibility
......@@ -2136,10 +2264,12 @@ v 8.1.2
- Fix CI badge
- Allow developer to manage builds
v 8.1.1
## 8.1.1
- Removed, see 8.1.2
v 8.1.0
## 8.1.0 (2015-10-22)
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
- Fix duplicate repositories in GitHub import page (Stan Hu)
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
......@@ -2224,11 +2354,13 @@ v 8.1.0
- Fix padding of outdated discussion item.
- Animate the logo on hover
v 8.0.5
## 8.0.5
- Correct lookup-by-email for LDAP logins
- Fix loading spinner sometimes not being hidden on Merge Request tab switches
v 8.0.4
## 8.0.4
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
- Fix referrals for :back and relative URL installs
- Fix anchors to comments in diffs
......@@ -2237,13 +2369,15 @@ v 8.0.4
- Fix search in Files
- Add full project namespace to payload of system webhooks (Ricardo Band)
v 8.0.3
## 8.0.3
- Fix URL shown in Slack notifications
- Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
- Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
- Add work_in_progress key to MR webhooks (Ben Boeckel)
v 8.0.2
## 8.0.2
- Fix default avatar not rendering in network graph (Stan Hu)
- Skip check_initd_configured_correctly on omnibus installs
- Prevent double-prefixing of help page paths
......@@ -2257,10 +2391,12 @@ v 8.0.2
- Add option to use StartTLS with Reply by email IMAP server.
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
v 8.0.1
## 8.0.1
- Improve CI migration procedure and documentation
v 8.0.0
## 8.0.0 (2015-09-22)
- Fix Markdown links not showing up in dashboard activity feed (Stan Hu)
- Remove milestones from merge requests when milestones are deleted (Stan Hu)
- Fix HTML link that was improperly escaped in new user e-mail (Stan Hu)
......@@ -2325,5 +2461,6 @@ v 8.0.0
- Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
- Removed API calls from CE to CI
v 7.14.3 through 0.8.0
- See changelogs/archive.md
## 7.14.3 through 0.8.0
- See [changelogs/archive.md](changelogs/archive.md)
......@@ -247,7 +247,7 @@ request is as follows:
1. Fork the project into your personal space on GitLab.com
1. Create a feature branch, branch away from `master`
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG):
1. Add your changes to the [CHANGELOG.md](CHANGELOG.md):
1. If you are fixing a ~regression issue, you can add your entry to the next
patch release (e.g. `8.12.5` if current version is `8.12.4`)
1. Otherwise, add your entry to the next minor release (e.g. `8.13.0` if
......
......@@ -15,18 +15,17 @@
this.hideSidebar = bind(this.hideSidebar, this);
this.toggleSidebar = bind(this.toggleSidebar, this);
this.updateDropdown = bind(this.updateDropdown, this);
this.$document = $(document);
clearInterval(Build.interval);
// Init breakpoint checker
this.bp = Breakpoints.get();
$('.js-build-sidebar').niceScroll();
this.initSidebar();
this.populateJobs(this.build_stage);
this.updateStageDropdownText(this.build_stage);
this.hideSidebar();
$(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
$(window).off('resize.build').on('resize.build', this.hideSidebar);
$(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
$('#js-build-scroll > a').off('click').on('click', this.stepTrace);
this.updateArtifactRemoveDate();
if ($('#build-trace').length) {
......@@ -62,6 +61,21 @@
}
}
Build.prototype.initSidebar = function() {
this.$sidebar = $('.js-build-sidebar');
this.sidebarTranslationLimits = {
min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
}
this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
this.$sidebar.css({
top: this.sidebarTranslationLimits.max
});
this.$sidebar.niceScroll();
this.hideSidebar();
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
};
Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
......@@ -129,15 +143,23 @@
Build.prototype.toggleSidebar = function() {
if (this.shouldHideSidebar()) {
return $('.js-build-sidebar').toggleClass('right-sidebar-expanded right-sidebar-collapsed');
return this.$sidebar.toggleClass('right-sidebar-expanded right-sidebar-collapsed');
}
};
Build.prototype.translateSidebar = function(e) {
var newPosition = this.sidebarTranslationLimits.max - document.body.scrollTop;
if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
this.$sidebar.css({
top: newPosition
});
};
Build.prototype.hideSidebar = function() {
if (this.shouldHideSidebar()) {
return $('.js-build-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
return this.$sidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
} else {
return $('.js-build-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
return this.$sidebar.removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
}
};
......
......@@ -9,7 +9,10 @@
var $dropdown, selected;
$dropdown = $(this);
selected = $dropdown.data('selected');
return $dropdown.glDropdown({
const $dropdownContainer = $dropdown.closest('.dropdown');
const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({
data: function(term, callback) {
return $.ajax({
url: $dropdown.data('refs-url'),
......@@ -42,6 +45,14 @@
return $el.text().trim();
}
});
$filterInput.on('keyup', (e) => {
const keyCode = e.keyCode || e.which;
if (keyCode !== 13) return;
const text = $filterInput.val();
$fieldInput.val(text);
$('.dropdown-toggle-text', $dropdown).text(text);
$dropdownContainer.removeClass('open');
});
});
};
......
......@@ -8,6 +8,7 @@
Dispatcher = (function() {
function Dispatcher() {
this.initSearch();
this.initFieldErrors();
this.initPageScripts();
}
......@@ -20,6 +21,9 @@
path = page.split(':');
shortcut_handler = null;
switch (page) {
case 'sessions:new':
new UsernameValidator();
break;
case 'projects:boards:show':
case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation();
......@@ -140,12 +144,12 @@
break;
case 'groups:group_members:index':
new gl.MemberExpirationDate();
new GroupMembers();
new gl.Members();
new UsersSelect();
break;
case 'projects:project_members:index':
new gl.MemberExpirationDate();
new ProjectMembers();
new gl.Members();
new UsersSelect();
break;
case 'groups:new':
......@@ -291,6 +295,12 @@
}
};
Dispatcher.prototype.initFieldErrors = function() {
$('.show-gl-field-errors').each((i, form) => {
new gl.GlFieldErrors(form);
});
};
return Dispatcher;
})();
......
......@@ -25,7 +25,7 @@
return function(e) {
e.preventDefault();
e.stopPropagation();
return _this.input.val('').trigger('keyup').focus();
return _this.input.val('').trigger('input').focus();
};
})(this));
// Key events
......@@ -37,28 +37,16 @@
e.preventDefault()
}
})
.on('keyup', function(e) {
var keyCode;
keyCode = e.which;
if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) {
return;
}
.on('input', function() {
if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.addClass(HAS_VALUE_CLASS);
} else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.removeClass(HAS_VALUE_CLASS);
}
if (keyCode === 13 && !options.elIsInput) {
return false;
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
clearTimeout(timeout);
return timeout = setTimeout(function() {
var blurField = this.shouldBlur(keyCode);
if (blurField && this.filterInputBlur) {
this.input.blur();
}
return this.options.query(this.input.val(), function(data) {
return this.options.callback(data);
}.bind(this));
......@@ -255,7 +243,7 @@
_this.fullData = data;
_this.parseData(_this.fullData);
if (_this.options.filterable && _this.filter && _this.filter.input) {
return _this.filter.input.trigger('keyup');
return _this.filter.input.trigger('input');
}
};
// Remote data
......@@ -487,7 +475,7 @@
// Triggering 'keyup' will re-render the dropdown which is not always required
// specially if we want to keep the state of the dropdown needed for bulk-assignment
if (!this.options.persistWhenHide) {
$input.trigger("keyup");
$input.trigger("input");
}
if (this.dropdown.find(".dropdown-toggle-page").length) {
$('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS);
......@@ -500,14 +488,27 @@
// Render the full menu
GitLabDropdown.prototype.renderMenu = function(html) {
var menu_html;
menu_html = "";
if (this.options.renderMenu) {
menu_html = this.options.renderMenu(html);
return this.options.renderMenu(html);
} else {
menu_html = $('<ul />').append(html);
var ul = document.createElement('ul');
for (var i = 0; i < html.length; i++) {
var el = html[i];
if (el instanceof jQuery) {
el = el.get(0);
}
if (typeof el === 'string') {
ul.innerHTML += el;
} else {
ul.appendChild(el);
}
}
return ul;
}
return menu_html;
};
// Append the menu into the dropdown
......@@ -521,7 +522,7 @@
};
GitLabDropdown.prototype.renderItem = function(data, group, index) {
var cssClass, field, fieldName, groupAttrs, html, selected, text, url, value;
var field, fieldName, html, selected, text, url, value;
if (group == null) {
group = false;
}
......@@ -529,18 +530,16 @@
// Render the row
index = false;
}
html = "";
// Divider
if (data === "divider") {
return "<li class='divider'></li>";
}
// Separator is a full-width divider
if (data === "separator") {
return "<li class='separator'></li>";
html = document.createElement('li');
if (data === 'divider' || data === 'separator') {
html.className = data;
return html;
}
// Header
if (data.header != null) {
return _.template('<li class="dropdown-header"><%- header %></li>')({ header: data.header });
html.className = 'dropdown-header';
html.innerHTML = data.header;
return html;
}
if (this.options.renderRow) {
// Call the render function
......@@ -567,24 +566,25 @@
} else {
text = data.text != null ? data.text : '';
}
cssClass = "";
if (selected) {
cssClass = "is-active";
}
if (this.highlight) {
text = this.highlightTextMatches(text, this.filterInput.val());
}
// Create the list item & the link
var link = document.createElement('a');
link.href = url;
link.innerHTML = text;
if (selected) {
link.className = 'is-active';
}
if (group) {
groupAttrs = 'data-group=' + group + ' data-index=' + index;
} else {
groupAttrs = '';
link.dataset.group = group;
link.dataset.index = index;
}
html = _.template('<li><a href="<%- url %>" <%- groupAttrs %> class="<%- cssClass %>"><%= text %></a></li>')({
url: url,
groupAttrs: groupAttrs,
cssClass: cssClass,
text: text
});
html.appendChild(link);
}
return html;
};
......
((global) => {
/*
* This class overrides the browser's validation error bubbles, displaying custom
* error messages for invalid fields instead. To begin validating any form, add the
* class `show-gl-field-errors` to the form element, and ensure error messages are
* declared in each inputs' title attribute.
*
* Example:
*
* <form class='show-gl-field-errors'>
* <input type='text' name='username' title='Username is required.'/>
*</form>
*
* */
const errorMessageClass = 'gl-field-error';
const inputErrorClass = 'gl-field-error-outline';
class GlFieldError {
constructor({ input, formErrors }) {
this.inputElement = $(input);
this.inputDomElement = this.inputElement.get(0);
this.form = formErrors;
this.errorMessage = this.inputElement.attr('title') || 'This field is required.';
this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${ this.errorMessage }</p>`);
this.state = {
valid: false,
empty: true
};
this.initFieldValidation();
}
initFieldValidation() {
// hidden when injected into DOM
this.inputElement.after(this.fieldErrorElement);
this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
this.scopedSiblings = this.safelySelectSiblings();
}
safelySelectSiblings() {
// Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled with input validity
const ignoreSelector = '.validation-ignore';
const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreSelector})`);
const parentContainer = this.inputElement.parent('.form-group');
// Only select siblings when they're scoped within a form-group with one input
const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1;
return safelyScoped ? unignoredSiblings : this.fieldErrorElement;
}
renderValidity() {
this.renderClear();
if (this.state.valid) {
return this.renderValid();
}
if (this.state.empty) {
return this.renderEmpty();
}
if (!this.state.valid) {
return this.renderInvalid();
}
}
handleInvalidSubmit(event) {
event.preventDefault();
const currentValue = this.accessCurrentValue();
this.state.valid = false;
this.state.empty = currentValue === '';
this.renderValidity();
this.form.focusOnFirstInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
this.inputElement.off('keyup.field_validator')
.on('keyup.field_validator', this.updateValidity.bind(this));
}
/* Get or set current input value */
accessCurrentValue(newVal) {
return newVal ? this.inputElement.val(newVal) : this.inputElement.val();
}
getInputValidity() {
return this.inputDomElement.validity.valid;
}
updateValidity() {
const inputVal = this.accessCurrentValue();
this.state.empty = !inputVal.length;
this.state.valid = this.getInputValidity();
this.renderValidity();
}
renderValid() {
return this.renderClear();
}
renderEmpty() {
return this.renderInvalid();
}
renderInvalid() {
this.inputElement.addClass(inputErrorClass);
this.scopedSiblings.hide();
return this.fieldErrorElement.show();
}
renderClear() {
const inputVal = this.accessCurrentValue();
if (!inputVal.split(' ').length) {
const trimmedInput = inputVal.trim();
this.accessCurrentValue(trimmedInput);
}
this.inputElement.removeClass(inputErrorClass);
this.scopedSiblings.hide();
this.fieldErrorElement.hide();
}
}
const customValidationFlag = 'no-gl-field-errors';
class GlFieldErrors {
constructor(form) {
this.form = $(form);
this.state = {
inputs: [],
valid: false
};
this.initValidators();
}
initValidators () {
// select all non-hidden inputs in form
this.state.inputs = this.form.find(':input:not([type=hidden])').toArray()
.filter((input) => !input.classList.contains(customValidationFlag))
.map((input) => new GlFieldError({ input, formErrors: this }));
this.form.on('submit', this.catchInvalidFormSubmit);
}
/* Neccessary to prevent intercept and override invalid form submit
* because Safari & iOS quietly allow form submission when form is invalid
* and prevents disabling of invalid submit button by application.js */
catchInvalidFormSubmit (event) {
if (!event.currentTarget.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
}
focusOnFirstInvalid () {
const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0];
firstInvalid.inputElement.focus();
}
}
global.GlFieldErrors = GlFieldErrors;
})(window.gl || (window.gl = {}));
(function() {
this.GroupMembers = (function() {
function GroupMembers() {
$('li.group_member').bind('ajax:success', function() {
return $(this).fadeOut();
});
}
return GroupMembers;
})();
}).call(this);
......@@ -14,14 +14,18 @@
inputs.datepicker({
dateFormat: 'yy-mm-dd',
minDate: 1,
onSelect: toggleClearInput
onSelect: function () {
$(this).trigger('change');
toggleClearInput.call(this);
}
});
inputs.next('.js-clear-input').on('click', function(event) {
event.preventDefault();
var input = $(this).closest('.clearable-input').find('.js-access-expiration-date');
input.datepicker('setDate', null);
input.datepicker('setDate', null)
.trigger('change');
toggleClearInput.call(input);
});
......
((w) => {
w.gl = w.gl || {};
class Members {
constructor() {
this.addListeners();
}
addListeners() {
$('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
$('.js-member-update-control').off('change').on('change', this.formSubmit);
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
}
removeRow(e) {
const $target = $(e.target);
if ($target.hasClass('btn-remove')) {
$target.closest('.member')
.fadeOut(function () {
$(this).remove();
});
}
}
formSubmit() {
$(this).closest('form').trigger("submit.rails").end().disable();
}
formSuccess() {
$(this).find('.js-member-update-control').enable();
}
}
gl.Members = Members;
})(window);
......@@ -71,6 +71,7 @@
this._location = location;
this.bindEvents();
this.activateTab(this.opts.action);
this.initAffix();
}
MergeRequestTabs.prototype.bindEvents = function() {
......@@ -380,6 +381,46 @@
// Only when sidebar is collapsed
};
MergeRequestTabs.prototype.initAffix = function () {
var $tabs = $('.js-tabs-affix');
// Screen space on small screens is usually very sparse
// So we dont affix the tabs on these
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
var tabsWidth = $tabs.outerWidth(),
$diffTabs = $('#diff-notes-app'),
offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height());
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
top: offsetTop
}
}).on('affix.bs.affix', function () {
$tabs.css({
left: $tabs.offset().left,
width: tabsWidth
});
$diffTabs.css({
marginTop: $tabs.height()
});
}).on('affix-top.bs.affix', function () {
$tabs.css({
left: '',
width: ''
});
$diffTabs.css({
marginTop: ''
});
});
// Fix bug when reloading the page already scrolling
if ($tabs.hasClass('affix')) {
$tabs.trigger('affix.bs.affix');
}
};
return MergeRequestTabs;
})();
......
(function() {
this.ProjectMembers = (function() {
function ProjectMembers() {
$('li.project_member').bind('ajax:success', function() {
return $(this).fadeOut();
});
}
return ProjectMembers;
})();
}).call(this);
......@@ -16,7 +16,13 @@
if (initialQuery.name) this.requestFile(initialQuery);
$('.reset-template', this.dropdown.parent()).on('click', () => {
if (this.currentTemplate) this.setInputValueToTemplateContent(false);
this.setInputValueToTemplateContent();
});
$('.no-template', this.dropdown.parent()).on('click', () => {
this.currentTemplate = '';
this.setInputValueToTemplateContent();
$('.dropdown-toggle-text', this.dropdown).text('Choose a template');
});
}
......
((global) => {
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline';
const unavailableMessageSelector = '.username .validation-error';
const successMessageSelector = '.username .validation-success';
const pendingMessageSelector = '.username .validation-pending';
const invalidMessageSelector = '.username .gl-field-error';
class UsernameValidator {
constructor() {
this.inputElement = $('#new_user_username');
this.inputDomElement = this.inputElement.get(0);
this.state = {
available: false,
valid: false,
pending: false,
empty: true
};
const debounceTimeout = _.debounce((username) => {
this.validateUsername(username);
}, debounceTimeoutDuration);
this.inputElement.on('keyup.username_check', () => {
const username = this.inputElement.val();
this.state.valid = this.inputDomElement.validity.valid;
this.state.empty = !username.length;
if (this.state.valid) {
return debounceTimeout(username);
}
this.renderState();
});
// Override generic field validation
this.inputElement.on('invalid', this.interceptInvalid.bind(this));
}
renderState() {
// Clear all state
this.clearFieldValidationState();
if (this.state.valid && this.state.available) {
return this.setSuccessState();
}
if (this.state.empty) {
return this.clearFieldValidationState();
}
if (this.state.pending) {
return this.setPendingState();
}
if (!this.state.available) {
return this.setUnavailableState();
}
if (!this.state.valid) {
return this.setInvalidState();
}
}
interceptInvalid(event) {
event.preventDefault();
event.stopPropagation();
}
validateUsername(username) {
if (this.state.valid) {
this.state.pending = true;
this.state.available = false;
this.renderState();
return $.ajax({
type: 'GET',
url: `/u/${username}/exists`,
dataType: 'json',
success: (res) => this.setAvailabilityState(res.exists)
});
}
}
setAvailabilityState(usernameTaken) {
if (usernameTaken) {
this.state.valid = false;
this.state.available = false;
} else {
this.state.available = true;
}
this.state.pending = false;
this.renderState();
}
clearFieldValidationState() {
this.inputElement.siblings('p').hide();
this.inputElement.removeClass(invalidInputClass)
.removeClass(successInputClass);
}
setUnavailableState() {
const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
$usernameUnavailableMessage.show();
}
setSuccessState() {
const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
$usernameSuccessMessage.show();
}
setPendingState() {
const $usernamePendingMessage = $(pendingMessageSelector);
if (this.state.pending) {
$usernamePendingMessage.show();
} else {
$usernamePendingMessage.hide();
}
}
setInvalidState() {
const $inputErrorMessage = $(invalidMessageSelector);
this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
$inputErrorMessage.show();
}
}
global.UsernameValidator = UsernameValidator;
})(window);
......@@ -325,6 +325,10 @@
};
UsersSelect.prototype.user = function(user_id, callback) {
if(!/^\d+$/.test(user_id)) {
return false;
}
var url;
url = this.buildUrl(this.userPath);
url = url.replace(':id', user_id);
......
......@@ -5,6 +5,7 @@
display: none;
&.hide { display: block; }
}
&.open .content {
display: block;
&.hide { display: none; }
......
......@@ -19,6 +19,7 @@
&.diff-collapsed {
padding: 5px;
.click-to-expand {
cursor: pointer;
}
......@@ -203,6 +204,7 @@
}
}
}
&.user-cover-block {
padding: 24px 0 0;
}
......
......@@ -25,7 +25,7 @@
&:focus {
background-color: $hover-background;
color: $hover-text;
border-color: $hover-border;;
border-color: $hover-border;
}
}
......@@ -152,7 +152,8 @@
@include btn-blue-medium;
}
&.btn-info {
&.btn-info,
&.btn-register {
@include btn-blue;
}
......@@ -240,6 +241,7 @@
width: 100%;
margin: 0;
margin-bottom: 15px;
&.btn {
padding: 6px 0;
}
......@@ -321,6 +323,7 @@
.btn-build {
margin-left: 10px;
i {
color: $gl-icon-color;
}
......@@ -328,6 +331,7 @@
.clone-dropdown-btn a {
color: $dropdown-link-color;
&:hover {
text-decoration: none;
}
......@@ -337,6 +341,7 @@
background-color: $background-color !important;
border: 1px solid lightgrey;
cursor: default;
&:active {
-moz-box-shadow: inset 0 0 0 white;
-webkit-box-shadow: inset 0 0 0 white;
......
......@@ -13,10 +13,12 @@
color: $text-color;
background: $background-color;
}
.bs-callout h4 {
margin-top: 0;
margin-bottom: 5px;
}
.bs-callout p:last-child {
margin-bottom: 0;
}
......@@ -27,16 +29,19 @@
border-color: #eed3d7;
color: #b94a48;
}
.bs-callout-warning {
background-color: #faf8f0;
border-color: #faebcc;
color: #8a6d3b;
}
.bs-callout-info {
background-color: #f4f8fa;
border-color: #bce8f1;
color: #34789a;
}
.bs-callout-success {
background-color: #dff0d8;
border-color: #5ca64d;
......
/** COLORS **/
.cgray { color: $gl-gray; }
.clgray { color: #bbb }
.clgray { color: #bbb; }
.cred { color: $gl-text-red; }
.cgreen { color: $gl-text-green; }
.cdark { color: #444 }
.cdark { color: #444; }
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; }
.prepend-top-10 { margin-top: 10px }
.prepend-top-10 { margin-top: 10px; }
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top: 20px }
.prepend-left-5 { margin-left: 5px }
.prepend-left-10 { margin-left: 10px }
.prepend-top-20 { margin-top: 20px; }
.prepend-left-5 { margin-left: 5px; }
.prepend-left-10 { margin-left: 10px; }
.prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px }
.append-right-5 { margin-right: 5px }
.append-right-10 { margin-right: 10px }
.prepend-left-20 { margin-left: 20px; }
.append-right-5 { margin-right: 5px; }
.append-right-10 { margin-right: 10px; }
.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px }
.append-bottom-0 { margin-bottom: 0 }
.append-bottom-10 { margin-bottom: 10px }
.append-bottom-15 { margin-bottom: 15px }
.append-bottom-20 { margin-bottom: 20px }
.append-right-20 { margin-right: 20px; }
.append-bottom-0 { margin-bottom: 0; }
.append-bottom-10 { margin-bottom: 10px; }
.append-bottom-15 { margin-bottom: 15px; }
.append-bottom-20 { margin-bottom: 20px; }
.append-bottom-default { margin-bottom: $gl-padding; }
.inline { display: inline-block }
.center { text-align: center }
.inline { display: inline-block; }
.center { text-align: center; }
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: #999; }
......@@ -97,6 +97,7 @@ span.update-author {
color: #999;
font-weight: normal;
font-style: italic;
strong {
font-weight: bold;
font-style: normal;
......@@ -128,7 +129,7 @@ p.time {
// Fix issue with notes & lists creating a bunch of bottom borders.
li.note {
img { max-width: 100% }
img { max-width: 100%; }
.note-title {
li {
border-bottom: none !important;
......@@ -172,6 +173,7 @@ li.note {
@extend .col-md-6;
text-align: left;
margin-top: 40px;
pre {
background: white;
border: none;
......@@ -197,6 +199,7 @@ li.note {
background: #c67;
color: #fff;
font-weight: bold;
a {
color: #fff;
text-decoration: underline;
......@@ -227,6 +230,7 @@ li.note {
&.milestone-closed {
background: $gray-light;
}
.progress {
margin-bottom: 0;
margin-top: 4px;
......@@ -286,6 +290,7 @@ table {
.footer-links {
margin-bottom: 20px;
a {
margin-right: 15px;
}
......
......@@ -12,6 +12,7 @@
.dropdown-menu,
.dropdown-menu-nav {
display: block;
@media (max-width: $screen-xs-max) {
width: 100%;
}
......@@ -48,6 +49,7 @@
margin-top: -6px;
color: $dropdown-toggle-icon-color;
font-size: 10px;
&.fa-spinner {
font-size: 16px;
margin-top: -8px;
......
......@@ -26,15 +26,6 @@
padding: 10px $gl-padding;
word-wrap: break-word;
border-radius: 3px 3px 0 0;
cursor: pointer;
&:hover {
background-color: $dark-background-color;
}
.diff-toggle-caret {
padding-right: 6px;
}
&.file-title-clear {
padding-left: 0;
......@@ -66,6 +57,7 @@
margin-top: -3px;
}
}
.file-content {
background: #fff;
......@@ -105,22 +97,27 @@
border: none;
margin: 0;
}
tr {
border-bottom: 1px solid #eee;
}
td {
&:first-child {
border-left: none;
}
&:last-child {
border-right: none;
}
}
td.blame-commit {
padding: 0 10px;
min-width: 400px;
background: $gray-light;
}
td.line-numbers {
float: none;
border-left: 1px solid #ddd;
......@@ -130,6 +127,7 @@
margin-right: 0;
}
}
td.lines {
padding: 0;
}
......@@ -146,8 +144,10 @@
border-left: 1px solid $border-color;
margin-bottom: 0;
background: white;
li {
color: #888;
p {
margin: 0;
color: #333;
......
......@@ -9,7 +9,7 @@ input {
input[type='text'].danger {
background: #f2dede!important;
border-color: #d66;
text-shadow: 0 1px 1px #fff
text-shadow: 0 1px 1px #fff;
}
.datetime-controls {
......@@ -73,8 +73,8 @@ label {
}
.form-control {
box-shadow: none;
border-radius: 3px;
@include box-shadow(none);
border-radius: 2px;
padding: $gl-vert-padding $gl-input-padding;
}
......@@ -117,9 +117,11 @@ label {
display: table-cell;
width: 200px !important;
}
.input-group-addon {
background-color: #f7f8fa;
}
.input-group-addon:not(:first-child):not(:last-child) {
border-left: 0;
border-right: 0;
......@@ -129,3 +131,8 @@ label {
.help-block {
margin-bottom: 0;
}
.gl-field-error {
color: $red-normal;
}
......@@ -62,7 +62,7 @@
}
i {
color: $white-light
color: $white-light;
}
path,
......
......@@ -168,6 +168,7 @@ header {
a {
color: $gl-text-color;
&:hover {
text-decoration: underline;
}
......
......@@ -60,6 +60,7 @@
padding-top: 1px;
margin: 0;
color: $gray-dark;
img {
position: relative;
top: 3px;
......@@ -128,6 +129,10 @@ ul.content-list {
color: $gl-dark-link-color;
}
.member-group-link {
color: $blue-normal;
}
.description {
p {
@include str-truncated;
......@@ -168,6 +173,14 @@ ul.content-list {
}
}
.member-controls {
float: none;
@media (min-width: $screen-sm-min) {
float: right;
}
}
// When dragging a list item
&.ui-sortable-helper {
border-bottom: none;
......
......@@ -37,6 +37,7 @@
0%, 10%, 100% {
fill: lighten($tanuki-yellow, 25%);
}
90% {
fill: $tanuki-yellow;
}
......@@ -48,6 +49,7 @@
10%, 80% {
fill: $tanuki-orange;
}
20%, 90% {
fill: lighten($tanuki-orange, 25%);
}
......@@ -59,6 +61,7 @@
10%, 80% {
fill: $tanuki-red;
}
20%, 90% {
fill: lighten($tanuki-red, 25%);
}
......@@ -70,6 +73,7 @@
20%, 70% {
fill: $tanuki-red;
}
30%, 80% {
fill: lighten($tanuki-red, 25%);
}
......@@ -81,6 +85,7 @@
30%, 60% {
fill: $tanuki-orange;
}
40%, 70% {
fill: lighten($tanuki-orange, 25%);
}
......@@ -92,6 +97,7 @@
30%, 60% {
fill: $tanuki-red;
}
40%, 70% {
fill: lighten($tanuki-red, 25%);
}
......@@ -103,6 +109,7 @@
40% {
fill: $tanuki-yellow;
}
60% {
fill: lighten($tanuki-yellow, 25%);
}
......
......@@ -34,6 +34,7 @@
&.active {
background: $gray-light;
a {
font-weight: 600;
}
......
......@@ -210,6 +210,7 @@
@media (max-width: $screen-xs-max) {
padding-bottom: 0;
width: 100%;
.btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
margin: 0 0 10px;
display: block;
......
......@@ -13,6 +13,11 @@
.dropdown-menu-toggle {
line-height: 20px;
}
.badge {
margin-top: -2px;
margin-left: 5px;
}
}
.panel-body {
......
......@@ -93,7 +93,7 @@
background: none;
.select2-search-field input {
padding: $gl-padding / 2;
padding: 5px $gl-padding / 2;
font-size: 13px;
height: auto;
font-family: inherit;
......@@ -101,7 +101,7 @@
}
.select2-search-choice {
margin: 8px 0 0 8px;
margin: 5px 0 0 8px;
box-shadow: none;
border-color: $input-border;
color: $gl-text-color;
......@@ -137,6 +137,7 @@
.select2-results {
max-height: 350px;
.select2-highlighted {
background: $gl-primary;
}
......@@ -212,9 +213,11 @@
.group-image {
float: left;
}
.group-name {
font-weight: bold;
}
.group-path {
color: #999;
}
......@@ -239,6 +242,7 @@
color: #aaa;
font-weight: normal;
}
.namespace-path {
margin-left: 10px;
font-weight: bolder;
......
......@@ -48,6 +48,7 @@
&:before {
background: none;
}
.timeline-entry .timeline-entry-inner {
.timeline-icon {
display: none;
......
......@@ -48,31 +48,40 @@
.clearfix {
@include clearfix();
}
.center-block {
@include center-block();
}
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}
.hide {
display: none;
}
.show {
display: block !important;
}
.invisible {
visibility: hidden;
}
.text-hide {
@include text-hide();
}
.hidden {
display: none !important;
visibility: hidden !important;
}
.affix {
position: fixed;
}
......@@ -146,6 +155,7 @@
padding: 6px 15px;
font-size: 13px;
font-weight: normal;
a {
color: #777;
}
......
......@@ -106,6 +106,7 @@
@extend .table-bordered;
margin: 12px 0;
color: #5c5d5e;
th {
background: #f8fafc;
}
......
......@@ -55,68 +55,68 @@
color: #000 !important;
}
.hll { background-color: #373b41 }
.c { color: #969896 } /* Comment */
.err { color: #c66 } /* Error */
.k { color: #b294bb } /* Keyword */
.l { color: #de935f } /* Literal */
.n { color: #c5c8c6 } /* Name */
.o { color: #8abeb7 } /* Operator */
.p { color: #c5c8c6 } /* Punctuation */
.cm { color: #969896 } /* Comment.Multiline */
.cp { color: #969896 } /* Comment.Preproc */
.c1 { color: #969896 } /* Comment.Single */
.cs { color: #969896 } /* Comment.Special */
.gd { color: #c66 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gh { color: #c5c8c6; font-weight: bold } /* Generic.Heading */
.gi { color: #b5bd68 } /* Generic.Inserted */
.gp { color: #969896; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #8abeb7; font-weight: bold } /* Generic.Subheading */
.kc { color: #b294bb } /* Keyword.Constant */
.kd { color: #b294bb } /* Keyword.Declaration */
.kn { color: #8abeb7 } /* Keyword.Namespace */
.kp { color: #b294bb } /* Keyword.Pseudo */
.kr { color: #b294bb } /* Keyword.Reserved */
.kt { color: #f0c674 } /* Keyword.Type */
.ld { color: #b5bd68 } /* Literal.Date */
.m { color: #de935f } /* Literal.Number */
.s { color: #b5bd68 } /* Literal.String */
.na { color: #81a2be } /* Name.Attribute */
.nb { color: #c5c8c6 } /* Name.Builtin */
.nc { color: #f0c674 } /* Name.Class */
.no { color: #c66 } /* Name.Constant */
.nd { color: #8abeb7 } /* Name.Decorator */
.ni { color: #c5c8c6 } /* Name.Entity */
.ne { color: #c66 } /* Name.Exception */
.nf { color: #81a2be } /* Name.Function */
.nl { color: #c5c8c6 } /* Name.Label */
.nn { color: #f0c674 } /* Name.Namespace */
.nx { color: #81a2be } /* Name.Other */
.py { color: #c5c8c6 } /* Name.Property */
.nt { color: #8abeb7 } /* Name.Tag */
.nv { color: #c66 } /* Name.Variable */
.ow { color: #8abeb7 } /* Operator.Word */
.w { color: #c5c8c6 } /* Text.Whitespace */
.mf { color: #de935f } /* Literal.Number.Float */
.mh { color: #de935f } /* Literal.Number.Hex */
.mi { color: #de935f } /* Literal.Number.Integer */
.mo { color: #de935f } /* Literal.Number.Oct */
.sb { color: #b5bd68 } /* Literal.String.Backtick */
.sc { color: #c5c8c6 } /* Literal.String.Char */
.sd { color: #969896 } /* Literal.String.Doc */
.s2 { color: #b5bd68 } /* Literal.String.Double */
.se { color: #de935f } /* Literal.String.Escape */
.sh { color: #b5bd68 } /* Literal.String.Heredoc */
.si { color: #de935f } /* Literal.String.Interpol */
.sx { color: #b5bd68 } /* Literal.String.Other */
.sr { color: #b5bd68 } /* Literal.String.Regex */
.s1 { color: #b5bd68 } /* Literal.String.Single */
.ss { color: #b5bd68 } /* Literal.String.Symbol */
.bp { color: #c5c8c6 } /* Name.Builtin.Pseudo */
.vc { color: #c66 } /* Name.Variable.Class */
.vg { color: #c66 } /* Name.Variable.Global */
.vi { color: #c66 } /* Name.Variable.Instance */
.il { color: #de935f } /* Literal.Number.Integer.Long */
.hll { background-color: #373b41; }
.c { color: #969896; } /* Comment */
.err { color: #c66; } /* Error */
.k { color: #b294bb; } /* Keyword */
.l { color: #de935f; } /* Literal */
.n { color: #c5c8c6; } /* Name */
.o { color: #8abeb7; } /* Operator */
.p { color: #c5c8c6; } /* Punctuation */
.cm { color: #969896; } /* Comment.Multiline */
.cp { color: #969896; } /* Comment.Preproc */
.c1 { color: #969896; } /* Comment.Single */
.cs { color: #969896; } /* Comment.Special */
.gd { color: #c66; } /* Generic.Deleted */
.ge { font-style: italic; } /* Generic.Emph */
.gh { color: #c5c8c6; font-weight: bold; } /* Generic.Heading */
.gi { color: #b5bd68; } /* Generic.Inserted */
.gp { color: #969896; font-weight: bold; } /* Generic.Prompt */
.gs { font-weight: bold; } /* Generic.Strong */
.gu { color: #8abeb7; font-weight: bold; } /* Generic.Subheading */
.kc { color: #b294bb; } /* Keyword.Constant */
.kd { color: #b294bb; } /* Keyword.Declaration */
.kn { color: #8abeb7; } /* Keyword.Namespace */
.kp { color: #b294bb; } /* Keyword.Pseudo */
.kr { color: #b294bb; } /* Keyword.Reserved */
.kt { color: #f0c674; } /* Keyword.Type */
.ld { color: #b5bd68; } /* Literal.Date */
.m { color: #de935f; } /* Literal.Number */
.s { color: #b5bd68; } /* Literal.String */
.na { color: #81a2be; } /* Name.Attribute */
.nb { color: #c5c8c6; } /* Name.Builtin */
.nc { color: #f0c674; } /* Name.Class */
.no { color: #c66; } /* Name.Constant */
.nd { color: #8abeb7; } /* Name.Decorator */
.ni { color: #c5c8c6; } /* Name.Entity */
.ne { color: #c66; } /* Name.Exception */
.nf { color: #81a2be; } /* Name.Function */
.nl { color: #c5c8c6; } /* Name.Label */
.nn { color: #f0c674; } /* Name.Namespace */
.nx { color: #81a2be; } /* Name.Other */
.py { color: #c5c8c6; } /* Name.Property */
.nt { color: #8abeb7; } /* Name.Tag */
.nv { color: #c66; } /* Name.Variable */
.ow { color: #8abeb7; } /* Operator.Word */
.w { color: #c5c8c6; } /* Text.Whitespace */
.mf { color: #de935f; } /* Literal.Number.Float */
.mh { color: #de935f; } /* Literal.Number.Hex */
.mi { color: #de935f; } /* Literal.Number.Integer */
.mo { color: #de935f; } /* Literal.Number.Oct */
.sb { color: #b5bd68; } /* Literal.String.Backtick */
.sc { color: #c5c8c6; } /* Literal.String.Char */
.sd { color: #969896; } /* Literal.String.Doc */
.s2 { color: #b5bd68; } /* Literal.String.Double */
.se { color: #de935f; } /* Literal.String.Escape */
.sh { color: #b5bd68; } /* Literal.String.Heredoc */
.si { color: #de935f; } /* Literal.String.Interpol */
.sx { color: #b5bd68; } /* Literal.String.Other */
.sr { color: #b5bd68; } /* Literal.String.Regex */
.s1 { color: #b5bd68; } /* Literal.String.Single */
.ss { color: #b5bd68; } /* Literal.String.Symbol */
.bp { color: #c5c8c6; } /* Name.Builtin.Pseudo */
.vc { color: #c66; } /* Name.Variable.Class */
.vg { color: #c66; } /* Name.Variable.Global */
.vi { color: #c66; } /* Name.Variable.Instance */
.il { color: #de935f; } /* Literal.Number.Integer.Long */
}
......@@ -55,65 +55,65 @@
color: #000 !important;
}
.hll { background-color: #49483e }
.c { color: #75715e } /* Comment */
.err { color: #960050; background-color: #1e0010 } /* Error */
.k { color: #66d9ef } /* Keyword */
.l { color: #ae81ff } /* Literal */
.n { color: #f8f8f2 } /* Name */
.o { color: #f92672 } /* Operator */
.p { color: #f8f8f2 } /* Punctuation */
.cm { color: #75715e } /* Comment.Multiline */
.cp { color: #75715e } /* Comment.Preproc */
.c1 { color: #75715e } /* Comment.Single */
.cs { color: #75715e } /* Comment.Special */
.ge { font-style: italic } /* Generic.Emph */
.gs { font-weight: bold } /* Generic.Strong */
.kc { color: #66d9ef } /* Keyword.Constant */
.kd { color: #66d9ef } /* Keyword.Declaration */
.kn { color: #f92672 } /* Keyword.Namespace */
.kp { color: #66d9ef } /* Keyword.Pseudo */
.kr { color: #66d9ef } /* Keyword.Reserved */
.kt { color: #66d9ef } /* Keyword.Type */
.ld { color: #e6db74 } /* Literal.Date */
.m { color: #ae81ff } /* Literal.Number */
.s { color: #e6db74 } /* Literal.String */
.na { color: #a6e22e } /* Name.Attribute */
.nb { color: #f8f8f2 } /* Name.Builtin */
.nc { color: #a6e22e } /* Name.Class */
.no { color: #66d9ef } /* Name.Constant */
.nd { color: #a6e22e } /* Name.Decorator */
.ni { color: #f8f8f2 } /* Name.Entity */
.ne { color: #a6e22e } /* Name.Exception */
.nf { color: #a6e22e } /* Name.Function */
.nl { color: #f8f8f2 } /* Name.Label */
.nn { color: #f8f8f2 } /* Name.Namespace */
.nx { color: #a6e22e } /* Name.Other */
.py { color: #f8f8f2 } /* Name.Property */
.nt { color: #f92672 } /* Name.Tag */
.nv { color: #f8f8f2 } /* Name.Variable */
.ow { color: #f92672 } /* Operator.Word */
.w { color: #f8f8f2 } /* Text.Whitespace */
.mf { color: #ae81ff } /* Literal.Number.Float */
.mh { color: #ae81ff } /* Literal.Number.Hex */
.mi { color: #ae81ff } /* Literal.Number.Integer */
.mo { color: #ae81ff } /* Literal.Number.Oct */
.sb { color: #e6db74 } /* Literal.String.Backtick */
.sc { color: #e6db74 } /* Literal.String.Char */
.sd { color: #e6db74 } /* Literal.String.Doc */
.s2 { color: #e6db74 } /* Literal.String.Double */
.se { color: #ae81ff } /* Literal.String.Escape */
.sh { color: #e6db74 } /* Literal.String.Heredoc */
.si { color: #e6db74 } /* Literal.String.Interpol */
.sx { color: #e6db74 } /* Literal.String.Other */
.sr { color: #e6db74 } /* Literal.String.Regex */
.s1 { color: #e6db74 } /* Literal.String.Single */
.ss { color: #e6db74 } /* Literal.String.Symbol */
.bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.vc { color: #f8f8f2 } /* Name.Variable.Class */
.vg { color: #f8f8f2 } /* Name.Variable.Global */
.vi { color: #f8f8f2 } /* Name.Variable.Instance */
.il { color: #ae81ff } /* Literal.Number.Integer.Long */
.hll { background-color: #49483e; }
.c { color: #75715e; } /* Comment */
.err { color: #960050; background-color: #1e0010; } /* Error */
.k { color: #66d9ef; } /* Keyword */
.l { color: #ae81ff; } /* Literal */
.n { color: #f8f8f2; } /* Name */
.o { color: #f92672; } /* Operator */
.p { color: #f8f8f2; } /* Punctuation */
.cm { color: #75715e; } /* Comment.Multiline */
.cp { color: #75715e; } /* Comment.Preproc */
.c1 { color: #75715e; } /* Comment.Single */
.cs { color: #75715e; } /* Comment.Special */
.ge { font-style: italic; } /* Generic.Emph */
.gs { font-weight: bold; } /* Generic.Strong */
.kc { color: #66d9ef; } /* Keyword.Constant */
.kd { color: #66d9ef; } /* Keyword.Declaration */
.kn { color: #f92672; } /* Keyword.Namespace */
.kp { color: #66d9ef; } /* Keyword.Pseudo */
.kr { color: #66d9ef; } /* Keyword.Reserved */
.kt { color: #66d9ef; } /* Keyword.Type */
.ld { color: #e6db74; } /* Literal.Date */
.m { color: #ae81ff; } /* Literal.Number */
.s { color: #e6db74; } /* Literal.String */
.na { color: #a6e22e; } /* Name.Attribute */
.nb { color: #f8f8f2; } /* Name.Builtin */
.nc { color: #a6e22e; } /* Name.Class */
.no { color: #66d9ef; } /* Name.Constant */
.nd { color: #a6e22e; } /* Name.Decorator */
.ni { color: #f8f8f2; } /* Name.Entity */
.ne { color: #a6e22e; } /* Name.Exception */
.nf { color: #a6e22e; } /* Name.Function */
.nl { color: #f8f8f2; } /* Name.Label */
.nn { color: #f8f8f2; } /* Name.Namespace */
.nx { color: #a6e22e; } /* Name.Other */
.py { color: #f8f8f2; } /* Name.Property */
.nt { color: #f92672; } /* Name.Tag */
.nv { color: #f8f8f2; } /* Name.Variable */
.ow { color: #f92672; } /* Operator.Word */
.w { color: #f8f8f2; } /* Text.Whitespace */
.mf { color: #ae81ff; } /* Literal.Number.Float */
.mh { color: #ae81ff; } /* Literal.Number.Hex */
.mi { color: #ae81ff; } /* Literal.Number.Integer */
.mo { color: #ae81ff; } /* Literal.Number.Oct */
.sb { color: #e6db74; } /* Literal.String.Backtick */
.sc { color: #e6db74; } /* Literal.String.Char */
.sd { color: #e6db74; } /* Literal.String.Doc */
.s2 { color: #e6db74; } /* Literal.String.Double */
.se { color: #ae81ff; } /* Literal.String.Escape */
.sh { color: #e6db74; } /* Literal.String.Heredoc */
.si { color: #e6db74; } /* Literal.String.Interpol */
.sx { color: #e6db74; } /* Literal.String.Other */
.sr { color: #e6db74; } /* Literal.String.Regex */
.s1 { color: #e6db74; } /* Literal.String.Single */
.ss { color: #e6db74; } /* Literal.String.Symbol */
.bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */
.vc { color: #f8f8f2; } /* Name.Variable.Class */
.vg { color: #f8f8f2; } /* Name.Variable.Global */
.vi { color: #f8f8f2; } /* Name.Variable.Instance */
.il { color: #ae81ff; } /* Literal.Number.Integer.Long */
.gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
.gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
.gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
......
......@@ -72,72 +72,72 @@
green #859900 operators, other keywords
*/
.c { color: #586e75 } /* Comment */
.err { color: #93a1a1 } /* Error */
.g { color: #93a1a1 } /* Generic */
.k { color: #859900 } /* Keyword */
.l { color: #93a1a1 } /* Literal */
.n { color: #93a1a1 } /* Name */
.o { color: #859900 } /* Operator */
.x { color: #cb4b16 } /* Other */
.p { color: #93a1a1 } /* Punctuation */
.cm { color: #586e75 } /* Comment.Multiline */
.cp { color: #859900 } /* Comment.Preproc */
.c1 { color: #586e75 } /* Comment.Single */
.cs { color: #859900 } /* Comment.Special */
.gd { color: #2aa198 } /* Generic.Deleted */
.ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
.gr { color: #dc322f } /* Generic.Error */
.gh { color: #cb4b16 } /* Generic.Heading */
.gi { color: #859900 } /* Generic.Inserted */
.go { color: #93a1a1 } /* Generic.Output */
.gp { color: #93a1a1 } /* Generic.Prompt */
.gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
.gu { color: #cb4b16 } /* Generic.Subheading */
.gt { color: #93a1a1 } /* Generic.Traceback */
.kc { color: #cb4b16 } /* Keyword.Constant */
.kd { color: #268bd2 } /* Keyword.Declaration */
.kn { color: #859900 } /* Keyword.Namespace */
.kp { color: #859900 } /* Keyword.Pseudo */
.kr { color: #268bd2 } /* Keyword.Reserved */
.kt { color: #dc322f } /* Keyword.Type */
.ld { color: #93a1a1 } /* Literal.Date */
.m { color: #2aa198 } /* Literal.Number */
.s { color: #2aa198 } /* Literal.String */
.na { color: #93a1a1 } /* Name.Attribute */
.nb { color: #b58900 } /* Name.Builtin */
.nc { color: #268bd2 } /* Name.Class */
.no { color: #cb4b16 } /* Name.Constant */
.nd { color: #268bd2 } /* Name.Decorator */
.ni { color: #cb4b16 } /* Name.Entity */
.ne { color: #cb4b16 } /* Name.Exception */
.nf { color: #268bd2 } /* Name.Function */
.nl { color: #93a1a1 } /* Name.Label */
.nn { color: #93a1a1 } /* Name.Namespace */
.nx { color: #93a1a1 } /* Name.Other */
.py { color: #93a1a1 } /* Name.Property */
.nt { color: #268bd2 } /* Name.Tag */
.nv { color: #268bd2 } /* Name.Variable */
.ow { color: #859900 } /* Operator.Word */
.w { color: #93a1a1 } /* Text.Whitespace */
.mf { color: #2aa198 } /* Literal.Number.Float */
.mh { color: #2aa198 } /* Literal.Number.Hex */
.mi { color: #2aa198 } /* Literal.Number.Integer */
.mo { color: #2aa198 } /* Literal.Number.Oct */
.sb { color: #586e75 } /* Literal.String.Backtick */
.sc { color: #2aa198 } /* Literal.String.Char */
.sd { color: #93a1a1 } /* Literal.String.Doc */
.s2 { color: #2aa198 } /* Literal.String.Double */
.se { color: #cb4b16 } /* Literal.String.Escape */
.sh { color: #93a1a1 } /* Literal.String.Heredoc */
.si { color: #2aa198 } /* Literal.String.Interpol */
.sx { color: #2aa198 } /* Literal.String.Other */
.sr { color: #dc322f } /* Literal.String.Regex */
.s1 { color: #2aa198 } /* Literal.String.Single */
.ss { color: #2aa198 } /* Literal.String.Symbol */
.bp { color: #268bd2 } /* Name.Builtin.Pseudo */
.vc { color: #268bd2 } /* Name.Variable.Class */
.vg { color: #268bd2 } /* Name.Variable.Global */
.vi { color: #268bd2 } /* Name.Variable.Instance */
.il { color: #2aa198 } /* Literal.Number.Integer.Long */
.c { color: #586e75; } /* Comment */
.err { color: #93a1a1; } /* Error */
.g { color: #93a1a1; } /* Generic */
.k { color: #859900; } /* Keyword */
.l { color: #93a1a1; } /* Literal */
.n { color: #93a1a1; } /* Name */
.o { color: #859900; } /* Operator */
.x { color: #cb4b16; } /* Other */
.p { color: #93a1a1; } /* Punctuation */
.cm { color: #586e75; } /* Comment.Multiline */
.cp { color: #859900; } /* Comment.Preproc */
.c1 { color: #586e75; } /* Comment.Single */
.cs { color: #859900; } /* Comment.Special */
.gd { color: #2aa198; } /* Generic.Deleted */
.ge { color: #93a1a1; font-style: italic; } /* Generic.Emph */
.gr { color: #dc322f; } /* Generic.Error */
.gh { color: #cb4b16; } /* Generic.Heading */
.gi { color: #859900; } /* Generic.Inserted */
.go { color: #93a1a1; } /* Generic.Output */
.gp { color: #93a1a1; } /* Generic.Prompt */
.gs { color: #93a1a1; font-weight: bold; } /* Generic.Strong */
.gu { color: #cb4b16; } /* Generic.Subheading */
.gt { color: #93a1a1; } /* Generic.Traceback */
.kc { color: #cb4b16; } /* Keyword.Constant */
.kd { color: #268bd2; } /* Keyword.Declaration */
.kn { color: #859900; } /* Keyword.Namespace */
.kp { color: #859900; } /* Keyword.Pseudo */
.kr { color: #268bd2; } /* Keyword.Reserved */
.kt { color: #dc322f; } /* Keyword.Type */
.ld { color: #93a1a1; } /* Literal.Date */
.m { color: #2aa198; } /* Literal.Number */
.s { color: #2aa198; } /* Literal.String */
.na { color: #93a1a1; } /* Name.Attribute */
.nb { color: #b58900; } /* Name.Builtin */
.nc { color: #268bd2; } /* Name.Class */
.no { color: #cb4b16; } /* Name.Constant */
.nd { color: #268bd2; } /* Name.Decorator */
.ni { color: #cb4b16; } /* Name.Entity */
.ne { color: #cb4b16; } /* Name.Exception */
.nf { color: #268bd2; } /* Name.Function */
.nl { color: #93a1a1; } /* Name.Label */
.nn { color: #93a1a1; } /* Name.Namespace */
.nx { color: #93a1a1; } /* Name.Other */
.py { color: #93a1a1; } /* Name.Property */
.nt { color: #268bd2; } /* Name.Tag */
.nv { color: #268bd2; } /* Name.Variable */
.ow { color: #859900; } /* Operator.Word */
.w { color: #93a1a1; } /* Text.Whitespace */
.mf { color: #2aa198; } /* Literal.Number.Float */
.mh { color: #2aa198; } /* Literal.Number.Hex */
.mi { color: #2aa198; } /* Literal.Number.Integer */
.mo { color: #2aa198; } /* Literal.Number.Oct */
.sb { color: #586e75; } /* Literal.String.Backtick */
.sc { color: #2aa198; } /* Literal.String.Char */
.sd { color: #93a1a1; } /* Literal.String.Doc */
.s2 { color: #2aa198; } /* Literal.String.Double */
.se { color: #cb4b16; } /* Literal.String.Escape */
.sh { color: #93a1a1; } /* Literal.String.Heredoc */
.si { color: #2aa198; } /* Literal.String.Interpol */
.sx { color: #2aa198; } /* Literal.String.Other */
.sr { color: #dc322f; } /* Literal.String.Regex */
.s1 { color: #2aa198; } /* Literal.String.Single */
.ss { color: #2aa198; } /* Literal.String.Symbol */
.bp { color: #268bd2; } /* Name.Builtin.Pseudo */
.vc { color: #268bd2; } /* Name.Variable.Class */
.vg { color: #268bd2; } /* Name.Variable.Global */
.vi { color: #268bd2; } /* Name.Variable.Instance */
.il { color: #2aa198; } /* Literal.Number.Integer.Long */
}
......@@ -78,72 +78,72 @@
green #859900 operators, other keywords
*/
.c { color: #93a1a1 } /* Comment */
.err { color: #586e75 } /* Error */
.g { color: #586e75 } /* Generic */
.k { color: #859900 } /* Keyword */
.l { color: #586e75 } /* Literal */
.n { color: #586e75 } /* Name */
.o { color: #859900 } /* Operator */
.x { color: #cb4b16 } /* Other */
.p { color: #586e75 } /* Punctuation */
.cm { color: #93a1a1 } /* Comment.Multiline */
.cp { color: #859900 } /* Comment.Preproc */
.c1 { color: #93a1a1 } /* Comment.Single */
.cs { color: #859900 } /* Comment.Special */
.gd { color: #2aa198 } /* Generic.Deleted */
.ge { color: #586e75; font-style: italic } /* Generic.Emph */
.gr { color: #dc322f } /* Generic.Error */
.gh { color: #cb4b16 } /* Generic.Heading */
.gi { color: #859900 } /* Generic.Inserted */
.go { color: #586e75 } /* Generic.Output */
.gp { color: #586e75 } /* Generic.Prompt */
.gs { color: #586e75; font-weight: bold } /* Generic.Strong */
.gu { color: #cb4b16 } /* Generic.Subheading */
.gt { color: #586e75 } /* Generic.Traceback */
.kc { color: #cb4b16 } /* Keyword.Constant */
.kd { color: #268bd2 } /* Keyword.Declaration */
.kn { color: #859900 } /* Keyword.Namespace */
.kp { color: #859900 } /* Keyword.Pseudo */
.kr { color: #268bd2 } /* Keyword.Reserved */
.kt { color: #dc322f } /* Keyword.Type */
.ld { color: #586e75 } /* Literal.Date */
.m { color: #2aa198 } /* Literal.Number */
.s { color: #2aa198 } /* Literal.String */
.na { color: #586e75 } /* Name.Attribute */
.nb { color: #b58900 } /* Name.Builtin */
.nc { color: #268bd2 } /* Name.Class */
.no { color: #cb4b16 } /* Name.Constant */
.nd { color: #268bd2 } /* Name.Decorator */
.ni { color: #cb4b16 } /* Name.Entity */
.ne { color: #cb4b16 } /* Name.Exception */
.nf { color: #268bd2 } /* Name.Function */
.nl { color: #586e75 } /* Name.Label */
.nn { color: #586e75 } /* Name.Namespace */
.nx { color: #586e75 } /* Name.Other */
.py { color: #586e75 } /* Name.Property */
.nt { color: #268bd2 } /* Name.Tag */
.nv { color: #268bd2 } /* Name.Variable */
.ow { color: #859900 } /* Operator.Word */
.w { color: #586e75 } /* Text.Whitespace */
.mf { color: #2aa198 } /* Literal.Number.Float */
.mh { color: #2aa198 } /* Literal.Number.Hex */
.mi { color: #2aa198 } /* Literal.Number.Integer */
.mo { color: #2aa198 } /* Literal.Number.Oct */
.sb { color: #93a1a1 } /* Literal.String.Backtick */
.sc { color: #2aa198 } /* Literal.String.Char */
.sd { color: #586e75 } /* Literal.String.Doc */
.s2 { color: #2aa198 } /* Literal.String.Double */
.se { color: #cb4b16 } /* Literal.String.Escape */
.sh { color: #586e75 } /* Literal.String.Heredoc */
.si { color: #2aa198 } /* Literal.String.Interpol */
.sx { color: #2aa198 } /* Literal.String.Other */
.sr { color: #dc322f } /* Literal.String.Regex */
.s1 { color: #2aa198 } /* Literal.String.Single */
.ss { color: #2aa198 } /* Literal.String.Symbol */
.bp { color: #268bd2 } /* Name.Builtin.Pseudo */
.vc { color: #268bd2 } /* Name.Variable.Class */
.vg { color: #268bd2 } /* Name.Variable.Global */
.vi { color: #268bd2 } /* Name.Variable.Instance */
.il { color: #2aa198 } /* Literal.Number.Integer.Long */
.c { color: #93a1a1; } /* Comment */
.err { color: #586e75; } /* Error */
.g { color: #586e75; } /* Generic */
.k { color: #859900; } /* Keyword */
.l { color: #586e75; } /* Literal */
.n { color: #586e75; } /* Name */
.o { color: #859900; } /* Operator */
.x { color: #cb4b16; } /* Other */
.p { color: #586e75; } /* Punctuation */
.cm { color: #93a1a1; } /* Comment.Multiline */
.cp { color: #859900; } /* Comment.Preproc */
.c1 { color: #93a1a1; } /* Comment.Single */
.cs { color: #859900; } /* Comment.Special */
.gd { color: #2aa198; } /* Generic.Deleted */
.ge { color: #586e75; font-style: italic; } /* Generic.Emph */
.gr { color: #dc322f; } /* Generic.Error */
.gh { color: #cb4b16; } /* Generic.Heading */
.gi { color: #859900; } /* Generic.Inserted */
.go { color: #586e75; } /* Generic.Output */
.gp { color: #586e75; } /* Generic.Prompt */
.gs { color: #586e75; font-weight: bold; } /* Generic.Strong */
.gu { color: #cb4b16; } /* Generic.Subheading */
.gt { color: #586e75; } /* Generic.Traceback */
.kc { color: #cb4b16; } /* Keyword.Constant */
.kd { color: #268bd2; } /* Keyword.Declaration */
.kn { color: #859900; } /* Keyword.Namespace */
.kp { color: #859900; } /* Keyword.Pseudo */
.kr { color: #268bd2; } /* Keyword.Reserved */
.kt { color: #dc322f; } /* Keyword.Type */
.ld { color: #586e75; } /* Literal.Date */
.m { color: #2aa198; } /* Literal.Number */
.s { color: #2aa198; } /* Literal.String */
.na { color: #586e75; } /* Name.Attribute */
.nb { color: #b58900; } /* Name.Builtin */
.nc { color: #268bd2; } /* Name.Class */
.no { color: #cb4b16; } /* Name.Constant */
.nd { color: #268bd2; } /* Name.Decorator */
.ni { color: #cb4b16; } /* Name.Entity */
.ne { color: #cb4b16; } /* Name.Exception */
.nf { color: #268bd2; } /* Name.Function */
.nl { color: #586e75; } /* Name.Label */
.nn { color: #586e75; } /* Name.Namespace */
.nx { color: #586e75; } /* Name.Other */
.py { color: #586e75; } /* Name.Property */
.nt { color: #268bd2; } /* Name.Tag */
.nv { color: #268bd2; } /* Name.Variable */
.ow { color: #859900; } /* Operator.Word */
.w { color: #586e75; } /* Text.Whitespace */
.mf { color: #2aa198; } /* Literal.Number.Float */
.mh { color: #2aa198; } /* Literal.Number.Hex */
.mi { color: #2aa198; } /* Literal.Number.Integer */
.mo { color: #2aa198; } /* Literal.Number.Oct */
.sb { color: #93a1a1; } /* Literal.String.Backtick */
.sc { color: #2aa198; } /* Literal.String.Char */
.sd { color: #586e75; } /* Literal.String.Doc */
.s2 { color: #2aa198; } /* Literal.String.Double */
.se { color: #cb4b16; } /* Literal.String.Escape */
.sh { color: #586e75; } /* Literal.String.Heredoc */
.si { color: #2aa198; } /* Literal.String.Interpol */
.sx { color: #2aa198; } /* Literal.String.Other */
.sr { color: #dc322f; } /* Literal.String.Regex */
.s1 { color: #2aa198; } /* Literal.String.Single */
.ss { color: #2aa198; } /* Literal.String.Symbol */
.bp { color: #268bd2; } /* Name.Builtin.Pseudo */
.vc { color: #268bd2; } /* Name.Variable.Class */
.vg { color: #268bd2; } /* Name.Variable.Global */
.vi { color: #268bd2; } /* Name.Variable.Instance */
.il { color: #2aa198; } /* Literal.Number.Integer.Long */
}
......@@ -86,7 +86,7 @@
background-color: #fafe3d !important;
}
.hll { background-color: #f8f8f8 }
.hll { background-color: #f8f8f8; }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
......
......@@ -78,7 +78,7 @@ span.highlight_word {
background-color: #fafe3d !important;
}
.hll { background-color: #f8f8f8 }
.hll { background-color: #f8f8f8; }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
......
......@@ -2,22 +2,28 @@ img {
max-width: 100%;
height: auto;
}
p.details {
font-style: italic;
color: #777
color: #777;
}
.footer > p {
font-size: small;
color: #777
color: #777;
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats > a {
text-decoration: none;
> .new-file {
color: #090;
}
> .deleted-file {
color: #b00;
}
......
......@@ -22,7 +22,7 @@
.admin-filter form {
.select2-container {
width: 100%
width: 100%;
}
.controls {
......@@ -31,7 +31,7 @@
.form-actions {
padding-left: 130px;
background: #fff
background: #fff;
}
.visibility-levels {
......@@ -106,26 +106,33 @@
.table {
table-layout: fixed;
}
.subheading {
padding-bottom: $gl-padding;
}
.message {
word-wrap: break-word;
}
.btn {
white-space: normal;
padding: $gl-btn-padding;
}
th {
width: 15%;
&.wide {
width: 55%;
}
}
@media (max-width: $screen-sm-max) {
th {
width: 100%;
}
td {
width: 100%;
float: left;
......@@ -137,6 +144,7 @@
margin-left: $btn-side-margin;
margin-top: 3px;
}
span {
font-size: 19px;
}
......
......@@ -137,6 +137,7 @@
.retry-link {
color: $gl-link-color;
&:hover {
text-decoration: underline;
}
......@@ -218,6 +219,7 @@
.build-detail-row {
margin-bottom: 5px;
&:last-of-type {
margin-bottom: 0;
}
......@@ -233,3 +235,9 @@
right: 0;
margin-top: -17px;
}
@media (min-width: $screen-md-min) {
.sub-nav.build {
width: calc(100% + #{$gutter_width});
}
}
......@@ -51,6 +51,7 @@
margin-left: 4px;
}
}
.commit-committer-link,
.commit-author-link {
color: $gl-gray;
......@@ -108,21 +109,25 @@
line-height: 20px;
}
}
.new-file {
a {
color: $gl-text-green;
}
}
.renamed-file {
a {
color: $gl-text-orange;
}
}
.deleted-file {
a {
color: $gl-text-red;
}
}
.edit-file {
a {
color: $gl-text-color;
......@@ -158,6 +163,7 @@
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
......
......@@ -161,6 +161,7 @@
.branch-commit {
color: $gl-gray;
.commit-id, .commit-row-message {
color: $gl-gray;
}
......
......@@ -5,6 +5,7 @@
background: $background-color;
border-top-left-radius: 0;
}
border-top-left-radius: 0;
}
}
......@@ -17,6 +18,7 @@
float: left;
@extend .col-md-2;
}
.btn {
margin-left: 5px;
float: left;
......
......@@ -33,6 +33,19 @@
font-size: smaller;
}
}
.file-title {
cursor: pointer;
&:hover {
background-color: $dark-background-color;
}
.diff-toggle-caret {
padding-right: 6px;
}
}
.diff-content {
overflow: auto;
overflow-y: hidden;
......@@ -123,15 +136,18 @@
max-width: 50px;
width: 35px;
@include user-select(none);
a {
float: left;
width: 35px;
font-weight: normal;
&:hover {
text-decoration: underline;
}
}
}
.line_content {
display: block;
margin: 0;
......@@ -151,10 +167,12 @@
white-space: pre-wrap;
}
}
.image {
background: #ddd;
text-align: center;
padding: 30px;
.wrap {
display: inline-block;
}
......@@ -163,6 +181,7 @@
display: inline-block;
background-color: #fff;
line-height: 0;
img {
border: 1px solid #fff;
background-image: linear-gradient(45deg, #e5e5e5 25%, transparent 25%, transparent 75%, #e5e5e5 75%, #e5e5e5 100%),
......@@ -171,6 +190,7 @@
background-position: 0 0, 5px 5px;
max-width: 100%;
}
&.deleted {
border: 1px solid $deleted;
}
......@@ -179,6 +199,7 @@
border: 1px solid $added;
}
}
.image-info {
font-size: 12px;
margin: 5px 0 0;
......@@ -193,6 +214,7 @@
margin: auto;
position: relative;
}
.swipe-wrap {
overflow: hidden;
border-left: 1px solid #999;
......@@ -201,10 +223,12 @@
top: 13px;
right: 7px;
}
.frame {
top: 0;
right: 0;
position: absolute;
&.deleted {
margin: 0;
display: block;
......@@ -212,6 +236,7 @@
right: 7px;
}
}
.swipe-bar {
display: block;
height: 100%;
......@@ -219,14 +244,17 @@
z-index: 100;
position: absolute;
cursor: pointer;
&:hover {
.top-handle {
background-position: -15px 3px;
}
.bottom-handle {
background-position: -15px -11px;
}
}
.top-handle {
display: block;
height: 14px;
......@@ -235,6 +263,7 @@
top: 0;
background: image-url('swipemode_sprites.gif') 0 3px no-repeat;
}
.bottom-handle {
display: block;
height: 14px;
......@@ -252,12 +281,14 @@
margin: auto;
position: relative;
}
.frame.added, .frame.deleted {
position: absolute;
display: block;
top: 0;
left: 0;
}
.controls {
display: block;
height: 14px;
......@@ -311,6 +342,7 @@
}
//.view.onion-skin
}
.view-modes {
padding: 10px;
text-align: center;
......@@ -328,19 +360,24 @@
border-left: 1px solid #c1c1c1;
padding: 0 12px 0 16px;
cursor: pointer;
&:first-child {
border-left: none;
}
&:hover {
text-decoration: underline;
}
&.active {
&:hover {
text-decoration: none;
}
cursor: default;
color: #333;
}
&.disabled {
display: none;
}
......
......@@ -15,6 +15,7 @@
.cancel-btn {
color: #b94a48;
&:hover {
color: #b94a48;
}
......@@ -70,16 +71,20 @@
.soft-wrap-toggle {
margin: 0 $btn-side-margin;
.soft-wrap {
display: block;
}
.no-wrap {
display: none;
}
&.soft-wrap-active {
.soft-wrap {
display: none;
}
.no-wrap {
display: block;
}
......
......@@ -52,7 +52,7 @@
}
}
.table.builds.environments {
.table.ci-table.environments {
.icon-container {
width: 20px;
......
......@@ -78,6 +78,7 @@
margin-bottom: 0;
}
}
.event-note-icon {
color: #777;
float: left;
......@@ -86,6 +87,7 @@
margin-right: 5px;
}
}
.event_icon {
position: relative;
float: right;
......@@ -95,12 +97,13 @@
background: $gray-light;
margin-left: 10px;
top: -6px;
img {
width: 20px;
}
}
&:last-child { border: none }
&:last-child { border: none; }
.event_commits {
li {
......@@ -109,6 +112,7 @@
padding: 3px;
padding-left: 0;
border: none;
.commit-row-title {
font-size: $gl-font-size;
}
......@@ -117,6 +121,7 @@
&.commits-stat {
display: block;
padding: 0 3px 0 0;
&:hover {
background: none;
}
......@@ -158,6 +163,7 @@
overflow: visible;
max-width: 100%;
}
.avatar {
display: none;
}
......
.member-search-form {
float: left;
input[type='search'] {
width: 225px;
vertical-align: bottom;
@media (max-width: $screen-xs-max) {
width: 100px;
vertical-align: bottom;
}
}
}
.milestone-row {
@include str-truncated(90%);
}
......@@ -48,6 +34,7 @@
.group-right-buttons {
position: absolute;
right: 16px;
.btn {
@include btn-gray;
padding: 3px 10px;
......
......@@ -23,28 +23,28 @@
color: #555;
tbody:first-child tr:first-child {
padding-top: 0
padding-top: 0;
}
th {
padding-top: 15px;
line-height: 1.5;
color: #333;
text-align: left
text-align: left;
}
td {
padding-top: 3px;
padding-bottom: 3px;
vertical-align: top;
line-height: 20px
line-height: 20px;
}
.shortcut {
padding-right: 10px;
color: #999;
text-align: right;
white-space: nowrap
white-space: nowrap;
}
.key {
......
......@@ -27,6 +27,7 @@
margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
padding: 6px 10px;
}
......@@ -128,7 +129,7 @@
}
.selectbox {
display: none
display: none;
}
.btn-clipboard {
......@@ -199,7 +200,7 @@
display: none;
/* Small devices (tablets, 768px and up) */
@media (min-width: $screen-sm-min) {
display: block
display: block;
}
width: $sidebar_collapsed_width;
......@@ -276,7 +277,7 @@
}
&.btn-primary {
@extend .btn-primary
@extend .btn-primary;
}
}
......@@ -400,6 +401,7 @@
.js-issuable-selector {
width: 100%;
}
@media (max-width: $screen-sm-max) {
margin-bottom: $gl-padding;
}
......
......@@ -37,6 +37,7 @@ ul.related-merge-requests > li {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
.merge-request-id {
flex-shrink: 0;
}
......
.suggest-colors {
margin-top: 5px;
a {
border-radius: 4px;
width: 30px;
......
......@@ -3,6 +3,7 @@
font-size: 19px;
color: red;
}
.correct-syntax {
font-size: 19px;
color: #47a447;
......
......@@ -17,6 +17,7 @@
line-height: 1.5;
p {
font-size: 18px;
color: #888;
}
......@@ -36,10 +37,14 @@
}
}
.login-box {
background: #fafafa;
border-radius: 10px;
box-shadow: 0 0 2px #ccc;
p {
font-size: 13px;
}
.login-box, .omniauth-container {
box-shadow: 0 0 0 1px $border-color;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 2px;
padding: 15px;
.login-heading h3 {
......@@ -58,42 +63,127 @@
a.forgot {
float: right;
padding-top: 6px
padding-top: 6px;
}
.nav .active a {
background: transparent;
}
// Styles the glowing border of focused input for username async validation
.login-body {
font-size: 13px;
input + p {
margin-top: 5px;
}
.form-control {
font-size: 14px;
padding: 10px 8px;
width: 100%;
height: auto;
.gl-field-success-outline {
border: 1px solid $green-normal;
&.top {
border-radius: 5px 5px 0 0;
margin-bottom: 0;
&:focus {
box-shadow: 0 0 0 1px $green-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 $green-normal;
border: 0 none;
}
}
&.bottom {
border-radius: 0 0 5px 5px;
border-top: 0;
margin-bottom: 20px;
.gl-field-error-outline {
border: 1px solid $red-normal;
&:focus {
box-shadow: 0 0 0 1px $red-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 rgba(210, 40, 82, 0.6);
border: 0 none;
}
}
&.middle {
border-top: 0;
margin-bottom: 0;
border-radius: 0;
.username .validation-success,
.gl-field-success-message {
color: $green-normal;
}
.username .validation-error,
.gl-field-error-message {
color: $red-normal;
}
.gl-field-hint {
color: $gl-text-color;
}
}
}
.omniauth-container {
p {
margin: 0;
}
}
.new-session-tabs {
display: -webkit-flex;
display: flex;
box-shadow: 0 0 0 1px $border-color;
border-top-right-radius: 2px;
border-top-left-radius: 2px;
li {
flex: 1;
text-align: center;
&:last-of-type {
border-left: 1px solid $border-color;
}
&:not(.active) {
background-color: $gray-light;
}
a {
width: 100%;
font-size: 18px;
&:hover {
border: 1px solid transparent;
}
}
&.active {
border-bottom: 1px solid $border-color;
a {
border: none;
border-bottom: 2px solid $link-underline-blue;
color: $black;
&:hover {
border-bottom: 2px solid $link-underline-blue;
}
}
}
}
}
.form-control {
&:active, &:focus {
background-color: #fff;
}
}
label {
font-weight: normal;
}
.submit-container {
margin-top: 16px;
}
input[type="submit"] {
@extend .btn-block;
margin-bottom: 0;
}
.devise-errors {
h2 {
margin-top: 0;
......@@ -101,14 +191,6 @@
color: #a00;
}
}
.remember-me {
margin-top: -10px;
label {
font-weight: normal;
}
}
}
@media (max-width: $screen-xs-max) {
......@@ -127,3 +209,35 @@
height: 32px;
}
}
.devise-layout-html {
margin: 0;
padding: 0;
height: 100%;
}
// Fixes footer container to bottom of viewport
.devise-layout-html body {
// offset height of fixed header + 1 to avoid scroll
height: calc(100% - 51px);
margin: 0;
padding: 0;
.page-wrap {
min-height: 100%;
position: relative;
}
.footer-container, hr.footer-fixed {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 40px;
background: $white-light;
}
.navless-container {
padding: 65px; // height of footer + bottom padding of email confirmation link
}
}
.project-members-title {
padding-bottom: 10px;
border-bottom: 1px solid $border-color;
}
.member {
.list-item-name {
@media (min-width: $screen-sm-min) {
float: left;
width: 50%;
}
strong {
font-weight: 600;
}
}
.controls {
@media (min-width: $screen-sm-min) {
display: -webkit-flex;
display: flex;
width: 400px;
max-width: 50%;
}
}
.form-horizontal {
margin-top: 5px;
@media (min-width: $screen-sm-min) {
display: -webkit-flex;
display: flex;
width: 100%;
margin-top: 3px;
}
}
.btn-remove {
width: 100%;
@media (min-width: $screen-sm-min) {
width: auto;
}
}
}
.member-form-control {
@media (max-width: $screen-xs-max) {
padding: 5px 0;
margin-left: 0;
margin-right: 0;
}
@media (min-width: $screen-sm-min) {
width: 50%;
}
}
.member-access-text {
margin-left: auto;
line-height: 43px;
}
.member.existing-title {
@media (min-width: $screen-sm-min) {
float: left;
}
}
.member-search-form {
position: relative;
@media (min-width: $screen-sm-min) {
float: right;
}
.form-control {
width: 100%;
padding-right: 35px;
@media (min-width: $screen-sm-min) {
width: 350px;
}
}
}
.member-search-btn {
position: absolute;
right: 0;
top: 0;
height: 35px;
padding-left: 10px;
padding-right: 10px;
color: $gray-darkest;
background: transparent;
border: 0;
outline: 0;
}
......@@ -131,6 +131,7 @@ $colors: (
}
}
}
&.head {
background-color: map-get($colors, #{$color}_header_head_neutral);
border-color: map-get($colors, #{$color}_header_head_neutral);
......@@ -174,6 +175,7 @@ $colors: (
background-color: map-get($colors, #{$color}_line_not_chosen);
}
}
&.head {
background-color: map-get($colors, #{$color}_line_head_neutral);
......
......@@ -10,6 +10,7 @@
form {
margin-bottom: 0;
.clearfix {
margin-bottom: 0;
}
......@@ -46,6 +47,7 @@
&.right {
float: right;
a {
color: $gl-gray;
}
......@@ -192,6 +194,7 @@
padding-top: 2px;
padding-bottom: 2px;
list-style: none;
&:hover {
background: none;
}
......@@ -215,6 +218,7 @@
padding-top: 20px;
padding-bottom: 10px;
}
svg {
width: 230px;
}
......@@ -280,12 +284,6 @@
line-height: 31px;
}
.builds {
.table-holder {
overflow-x: auto;
}
}
.panel-new-merge-request {
.panel-heading {
padding: 5px 10px;
......@@ -373,7 +371,7 @@
}
.table-holder {
.builds {
.ci-table {
th {
background-color: $white-light;
......@@ -437,3 +435,12 @@
margin-bottom: 20px;
}
}
.merge-request-tabs {
background-color: #fff;
&.affix {
top: 100px;
z-index: 9;
}
}
......@@ -59,6 +59,7 @@
color: $gl-placeholder-color;
margin-right: 5px;
}
.avatar {
float: none;
}
......
......@@ -11,6 +11,7 @@
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new-note {
......@@ -194,6 +195,7 @@
min-height: 140px;
max-height: 500px;
}
.note-form-actions {
background: transparent;
}
......
......@@ -147,9 +147,18 @@ ul.notes {
// Diff code in discussion view
.discussion-body .diff-file {
.file-title {
cursor: default;
&:hover {
background-color: $gray-light;
}
}
.diff-header > span {
margin-right: 10px;
}
.line_content {
white-space: pre-wrap;
}
......@@ -345,6 +354,7 @@ ul.notes {
width: 32px;
// "hide" it by default
display: none;
&:hover {
background: $gl-info;
color: #fff;
......
......@@ -20,7 +20,7 @@
margin: 4px;
}
.table.builds {
.table.ci-table {
min-width: 1200px;
.branch-commit {
......@@ -44,13 +44,20 @@
overflow: auto;
}
.table.builds {
.table.ci-table {
min-width: 900px;
&.pipeline {
min-width: 650px;
}
&.builds-page {
tr {
height: 71px;
}
}
tr {
th {
padding: 16px 8px;
......@@ -176,7 +183,7 @@
&::after {
content: '';
width: 8px;
position: absolute;;
position: absolute;
right: -7px;
bottom: 8px;
border-bottom: 2px solid $border-color;
......@@ -353,6 +360,7 @@
&:hover {
background-color: $gray-lighter;
.dropdown-menu-toggle {
background-color: transparent;
}
......@@ -536,6 +544,7 @@
height: 29px;
top: -9px;
}
.curve {
display: block;
}
......@@ -621,7 +630,9 @@
}
}
.pipelines.tab-pane {
.tab-pane {
&.pipelines {
.content-list.pipelines {
overflow: auto;
......@@ -635,6 +646,16 @@
.pipeline-actions {
min-width: initial;
}
}
&.builds {
.ci-table {
tr {
height: 71px;
}
}
}
}
.ci-status-icon-created {
......
......@@ -243,6 +243,7 @@
.btn {
-webkit-flex-grow: 1;
flex-grow: 1;
&:first-child {
margin-left: 0;
}
......
......@@ -17,34 +17,43 @@
&.features .control-label {
font-weight: normal;
}
.form-group {
margin-bottom: 5px;
}
&> .form-group {
padding-left: 0;
}
}
.help-block {
margin-bottom: 10px;
}
.project-path {
padding-right: 0;
.form-control {
border-radius: $border-radius-base;
}
}
.input-group > div {
&:last-child {
padding-right: 0;
}
}
@media (max-width: $screen-xs-max) {
.input-group > div {
margin-bottom: 14px;
&:last-child {
margin-bottom: 0;
}
}
fieldset > .form-group:first-child {
padding-right: 0;
}
......@@ -56,6 +65,7 @@
border-radius: 3px;
border: 1px solid #e5e5e5;
}
&+ .select2 a {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
......@@ -201,6 +211,7 @@
pointer-events: none;
}
}
.count {
@include btn-gray;
display: inline-block;
......@@ -361,10 +372,12 @@ a.deploy-project-label {
margin: $gl-padding;
text-align: center;
width: 169px;
&:hover, &.forked {
background-color: $row-hover;
border-color: $row-hover-border;
}
.no-avatar {
width: 100px;
height: 100px;
......@@ -372,17 +385,20 @@ a.deploy-project-label {
border: 1px solid $gray-dark;
margin: 0 auto;
border-radius: 50%;
i {
font-size: 100px;
color: $gray-dark;
}
}
a {
display: block;
width: 100%;
height: 100%;
padding-top: $gl-padding;
color: $gl-gray;
.caption {
min-height: 30px;
padding: $gl-padding 0;
......@@ -644,6 +660,7 @@ pre.light-well {
.clone-options {
display: table-cell;
a.btn {
width: 100%;
}
......@@ -832,6 +849,7 @@ pre.light-well {
.form-control {
min-width: 100px;
}
.select2-choice {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
......
......@@ -6,6 +6,7 @@
&.runner-state-shared {
background: #32b186;
}
&.runner-state-specific {
background: #3498db;
}
......
......@@ -65,6 +65,7 @@
.ci-status-icon-success {
color: $gl-success;
}
.ci-status-icon-failed {
color: $gl-danger;
}
......@@ -77,6 +78,7 @@
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
......
......@@ -5,6 +5,7 @@
.file-finder {
width: 50%;
.file-finder-input {
width: 95%;
display: inline-block;
......
......@@ -23,15 +23,19 @@
.term-bold {
font-weight: bold;
}
.term-italic {
font-style: italic;
}
.term-conceal {
visibility: hidden;
}
.term-underline {
text-decoration: underline;
}
.term-cross {
text-decoration: line-through;
}
......@@ -39,48 +43,63 @@
.term-fg-black {
color: $black;
}
.term-fg-red {
color: $red;
}
.term-fg-green {
color: $green;
}
.term-fg-yellow {
color: $yellow;
}
.term-fg-blue {
color: $blue;
}
.term-fg-magenta {
color: $magenta;
}
.term-fg-cyan {
color: $cyan;
}
.term-fg-white {
color: $white;
}
.term-fg-l-black {
color: $l-black;
}
.term-fg-l-red {
color: $l-red;
}
.term-fg-l-green {
color: $l-green;
}
.term-fg-l-yellow {
color: $l-yellow;
}
.term-fg-l-blue {
color: $l-blue;
}
.term-fg-l-magenta {
color: $l-magenta;
}
.term-fg-l-cyan {
color: $l-cyan;
}
.term-fg-l-white {
color: $l-white;
}
......@@ -88,818 +107,1087 @@
.term-bg-black {
background-color: $black;
}
.term-bg-red {
background-color: $red;
}
.term-bg-green {
background-color: $green;
}
.term-bg-yellow {
background-color: $yellow;
}
.term-bg-blue {
background-color: $blue;
}
.term-bg-magenta {
background-color: $magenta;
}
.term-bg-cyan {
background-color: $cyan;
}
.term-bg-white {
background-color: $white;
}
.term-bg-l-black {
background-color: $l-black;
}
.term-bg-l-red {
background-color: $l-red;
}
.term-bg-l-green {
background-color: $l-green;
}
.term-bg-l-yellow {
background-color: $l-yellow;
}
.term-bg-l-blue {
background-color: $l-blue;
}
.term-bg-l-magenta {
background-color: $l-magenta;
}
.term-bg-l-cyan {
background-color: $l-cyan;
}
.term-bg-l-white {
background-color: $l-white;
}
.xterm-fg-0 {
color: #000;
}
.xterm-fg-1 {
color: #800000;
}
.xterm-fg-2 {
color: #008000;
}
.xterm-fg-3 {
color: #808000;
}
.xterm-fg-4 {
color: #000080;
}
.xterm-fg-5 {
color: #800080;
}
.xterm-fg-6 {
color: #008080;
}
.xterm-fg-7 {
color: #c0c0c0;
}
.xterm-fg-8 {
color: #808080;
}
.xterm-fg-9 {
color: #f00;
}
.xterm-fg-10 {
color: #0f0;
}
.xterm-fg-11 {
color: #ff0;
}
.xterm-fg-12 {
color: #00f;
}
.xterm-fg-13 {
color: #f0f;
}
.xterm-fg-14 {
color: #0ff;
}
.xterm-fg-15 {
color: #fff;
}
.xterm-fg-16 {
color: #000;
}
.xterm-fg-17 {
color: #00005f;
}
.xterm-fg-18 {
color: #000087;
}
.xterm-fg-19 {
color: #0000af;
}
.xterm-fg-20 {
color: #0000d7;
}
.xterm-fg-21 {
color: #00f;
}
.xterm-fg-22 {
color: #005f00;
}
.xterm-fg-23 {
color: #005f5f;
}
.xterm-fg-24 {
color: #005f87;
}
.xterm-fg-25 {
color: #005faf;
}
.xterm-fg-26 {
color: #005fd7;
}
.xterm-fg-27 {
color: #005fff;
}
.xterm-fg-28 {
color: #008700;
}
.xterm-fg-29 {
color: #00875f;
}
.xterm-fg-30 {
color: #008787;
}
.xterm-fg-31 {
color: #0087af;
}
.xterm-fg-32 {
color: #0087d7;
}
.xterm-fg-33 {
color: #0087ff;
}
.xterm-fg-34 {
color: #00af00;
}
.xterm-fg-35 {
color: #00af5f;
}
.xterm-fg-36 {
color: #00af87;
}
.xterm-fg-37 {
color: #00afaf;
}
.xterm-fg-38 {
color: #00afd7;
}
.xterm-fg-39 {
color: #00afff;
}
.xterm-fg-40 {
color: #00d700;
}
.xterm-fg-41 {
color: #00d75f;
}
.xterm-fg-42 {
color: #00d787;
}
.xterm-fg-43 {
color: #00d7af;
}
.xterm-fg-44 {
color: #00d7d7;
}
.xterm-fg-45 {
color: #00d7ff;
}
.xterm-fg-46 {
color: #0f0;
}
.xterm-fg-47 {
color: #00ff5f;
}
.xterm-fg-48 {
color: #00ff87;
}
.xterm-fg-49 {
color: #00ffaf;
}
.xterm-fg-50 {
color: #00ffd7;
}
.xterm-fg-51 {
color: #0ff;
}
.xterm-fg-52 {
color: #5f0000;
}
.xterm-fg-53 {
color: #5f005f;
}
.xterm-fg-54 {
color: #5f0087;
}
.xterm-fg-55 {
color: #5f00af;
}
.xterm-fg-56 {
color: #5f00d7;
}
.xterm-fg-57 {
color: #5f00ff;
}
.xterm-fg-58 {
color: #5f5f00;
}
.xterm-fg-59 {
color: #5f5f5f;
}
.xterm-fg-60 {
color: #5f5f87;
}
.xterm-fg-61 {
color: #5f5faf;
}
.xterm-fg-62 {
color: #5f5fd7;
}
.xterm-fg-63 {
color: #5f5fff;
}
.xterm-fg-64 {
color: #5f8700;
}
.xterm-fg-65 {
color: #5f875f;
}
.xterm-fg-66 {
color: #5f8787;
}
.xterm-fg-67 {
color: #5f87af;
}
.xterm-fg-68 {
color: #5f87d7;
}
.xterm-fg-69 {
color: #5f87ff;
}
.xterm-fg-70 {
color: #5faf00;
}
.xterm-fg-71 {
color: #5faf5f;
}
.xterm-fg-72 {
color: #5faf87;
}
.xterm-fg-73 {
color: #5fafaf;
}
.xterm-fg-74 {
color: #5fafd7;
}
.xterm-fg-75 {
color: #5fafff;
}
.xterm-fg-76 {
color: #5fd700;
}
.xterm-fg-77 {
color: #5fd75f;
}
.xterm-fg-78 {
color: #5fd787;
}
.xterm-fg-79 {
color: #5fd7af;
}
.xterm-fg-80 {
color: #5fd7d7;
}
.xterm-fg-81 {
color: #5fd7ff;
}
.xterm-fg-82 {
color: #5fff00;
}
.xterm-fg-83 {
color: #5fff5f;
}
.xterm-fg-84 {
color: #5fff87;
}
.xterm-fg-85 {
color: #5fffaf;
}
.xterm-fg-86 {
color: #5fffd7;
}
.xterm-fg-87 {
color: #5fffff;
}
.xterm-fg-88 {
color: #870000;
}
.xterm-fg-89 {
color: #87005f;
}
.xterm-fg-90 {
color: #870087;
}
.xterm-fg-91 {
color: #8700af;
}
.xterm-fg-92 {
color: #8700d7;
}
.xterm-fg-93 {
color: #8700ff;
}
.xterm-fg-94 {
color: #875f00;
}
.xterm-fg-95 {
color: #875f5f;
}
.xterm-fg-96 {
color: #875f87;
}
.xterm-fg-97 {
color: #875faf;
}
.xterm-fg-98 {
color: #875fd7;
}
.xterm-fg-99 {
color: #875fff;
}
.xterm-fg-100 {
color: #878700;
}
.xterm-fg-101 {
color: #87875f;
}
.xterm-fg-102 {
color: #878787;
}
.xterm-fg-103 {
color: #8787af;
}
.xterm-fg-104 {
color: #8787d7;
}
.xterm-fg-105 {
color: #8787ff;
}
.xterm-fg-106 {
color: #87af00;
}
.xterm-fg-107 {
color: #87af5f;
}
.xterm-fg-108 {
color: #87af87;
}
.xterm-fg-109 {
color: #87afaf;
}
.xterm-fg-110 {
color: #87afd7;
}
.xterm-fg-111 {
color: #87afff;
}
.xterm-fg-112 {
color: #87d700;
}
.xterm-fg-113 {
color: #87d75f;
}
.xterm-fg-114 {
color: #87d787;
}
.xterm-fg-115 {
color: #87d7af;
}
.xterm-fg-116 {
color: #87d7d7;
}
.xterm-fg-117 {
color: #87d7ff;
}
.xterm-fg-118 {
color: #87ff00;
}
.xterm-fg-119 {
color: #87ff5f;
}
.xterm-fg-120 {
color: #87ff87;
}
.xterm-fg-121 {
color: #87ffaf;
}
.xterm-fg-122 {
color: #87ffd7;
}
.xterm-fg-123 {
color: #87ffff;
}
.xterm-fg-124 {
color: #af0000;
}
.xterm-fg-125 {
color: #af005f;
}
.xterm-fg-126 {
color: #af0087;
}
.xterm-fg-127 {
color: #af00af;
}
.xterm-fg-128 {
color: #af00d7;
}
.xterm-fg-129 {
color: #af00ff;
}
.xterm-fg-130 {
color: #af5f00;
}
.xterm-fg-131 {
color: #af5f5f;
}
.xterm-fg-132 {
color: #af5f87;
}
.xterm-fg-133 {
color: #af5faf;
}
.xterm-fg-134 {
color: #af5fd7;
}
.xterm-fg-135 {
color: #af5fff;
}
.xterm-fg-136 {
color: #af8700;
}
.xterm-fg-137 {
color: #af875f;
}
.xterm-fg-138 {
color: #af8787;
}
.xterm-fg-139 {
color: #af87af;
}
.xterm-fg-140 {
color: #af87d7;
}
.xterm-fg-141 {
color: #af87ff;
}
.xterm-fg-142 {
color: #afaf00;
}
.xterm-fg-143 {
color: #afaf5f;
}
.xterm-fg-144 {
color: #afaf87;
}
.xterm-fg-145 {
color: #afafaf;
}
.xterm-fg-146 {
color: #afafd7;
}
.xterm-fg-147 {
color: #afafff;
}
.xterm-fg-148 {
color: #afd700;
}
.xterm-fg-149 {
color: #afd75f;
}
.xterm-fg-150 {
color: #afd787;
}
.xterm-fg-151 {
color: #afd7af;
}
.xterm-fg-152 {
color: #afd7d7;
}
.xterm-fg-153 {
color: #afd7ff;
}
.xterm-fg-154 {
color: #afff00;
}
.xterm-fg-155 {
color: #afff5f;
}
.xterm-fg-156 {
color: #afff87;
}
.xterm-fg-157 {
color: #afffaf;
}
.xterm-fg-158 {
color: #afffd7;
}
.xterm-fg-159 {
color: #afffff;
}
.xterm-fg-160 {
color: #d70000;
}
.xterm-fg-161 {
color: #d7005f;
}
.xterm-fg-162 {
color: #d70087;
}
.xterm-fg-163 {
color: #d700af;
}
.xterm-fg-164 {
color: #d700d7;
}
.xterm-fg-165 {
color: #d700ff;
}
.xterm-fg-166 {
color: #d75f00;
}
.xterm-fg-167 {
color: #d75f5f;
}
.xterm-fg-168 {
color: #d75f87;
}
.xterm-fg-169 {
color: #d75faf;
}
.xterm-fg-170 {
color: #d75fd7;
}
.xterm-fg-171 {
color: #d75fff;
}
.xterm-fg-172 {
color: #d78700;
}
.xterm-fg-173 {
color: #d7875f;
}
.xterm-fg-174 {
color: #d78787;
}
.xterm-fg-175 {
color: #d787af;
}
.xterm-fg-176 {
color: #d787d7;
}
.xterm-fg-177 {
color: #d787ff;
}
.xterm-fg-178 {
color: #d7af00;
}
.xterm-fg-179 {
color: #d7af5f;
}
.xterm-fg-180 {
color: #d7af87;
}
.xterm-fg-181 {
color: #d7afaf;
}
.xterm-fg-182 {
color: #d7afd7;
}
.xterm-fg-183 {
color: #d7afff;
}
.xterm-fg-184 {
color: #d7d700;
}
.xterm-fg-185 {
color: #d7d75f;
}
.xterm-fg-186 {
color: #d7d787;
}
.xterm-fg-187 {
color: #d7d7af;
}
.xterm-fg-188 {
color: #d7d7d7;
}
.xterm-fg-189 {
color: #d7d7ff;
}
.xterm-fg-190 {
color: #d7ff00;
}
.xterm-fg-191 {
color: #d7ff5f;
}
.xterm-fg-192 {
color: #d7ff87;
}
.xterm-fg-193 {
color: #d7ffaf;
}
.xterm-fg-194 {
color: #d7ffd7;
}
.xterm-fg-195 {
color: #d7ffff;
}
.xterm-fg-196 {
color: #f00;
}
.xterm-fg-197 {
color: #ff005f;
}
.xterm-fg-198 {
color: #ff0087;
}
.xterm-fg-199 {
color: #ff00af;
}
.xterm-fg-200 {
color: #ff00d7;
}
.xterm-fg-201 {
color: #f0f;
}
.xterm-fg-202 {
color: #ff5f00;
}
.xterm-fg-203 {
color: #ff5f5f;
}
.xterm-fg-204 {
color: #ff5f87;
}
.xterm-fg-205 {
color: #ff5faf;
}
.xterm-fg-206 {
color: #ff5fd7;
}
.xterm-fg-207 {
color: #ff5fff;
}
.xterm-fg-208 {
color: #ff8700;
}
.xterm-fg-209 {
color: #ff875f;
}
.xterm-fg-210 {
color: #ff8787;
}
.xterm-fg-211 {
color: #ff87af;
}
.xterm-fg-212 {
color: #ff87d7;
}
.xterm-fg-213 {
color: #ff87ff;
}
.xterm-fg-214 {
color: #ffaf00;
}
.xterm-fg-215 {
color: #ffaf5f;
}
.xterm-fg-216 {
color: #ffaf87;
}
.xterm-fg-217 {
color: #ffafaf;
}
.xterm-fg-218 {
color: #ffafd7;
}
.xterm-fg-219 {
color: #ffafff;
}
.xterm-fg-220 {
color: #ffd700;
}
.xterm-fg-221 {
color: #ffd75f;
}
.xterm-fg-222 {
color: #ffd787;
}
.xterm-fg-223 {
color: #ffd7af;
}
.xterm-fg-224 {
color: #ffd7d7;
}
.xterm-fg-225 {
color: #ffd7ff;
}
.xterm-fg-226 {
color: #ff0;
}
.xterm-fg-227 {
color: #ffff5f;
}
.xterm-fg-228 {
color: #ffff87;
}
.xterm-fg-229 {
color: #ffffaf;
}
.xterm-fg-230 {
color: #ffffd7;
}
.xterm-fg-231 {
color: #fff;
}
.xterm-fg-232 {
color: #080808;
}
.xterm-fg-233 {
color: #121212;
}
.xterm-fg-234 {
color: #1c1c1c;
}
.xterm-fg-235 {
color: #262626;
}
.xterm-fg-236 {
color: #303030;
}
.xterm-fg-237 {
color: #3a3a3a;
}
.xterm-fg-238 {
color: #444;
}
.xterm-fg-239 {
color: #4e4e4e;
}
.xterm-fg-240 {
color: #585858;
}
.xterm-fg-241 {
color: #626262;
}
.xterm-fg-242 {
color: #6c6c6c;
}
.xterm-fg-243 {
color: #767676;
}
.xterm-fg-244 {
color: #808080;
}
.xterm-fg-245 {
color: #8a8a8a;
}
.xterm-fg-246 {
color: #949494;
}
.xterm-fg-247 {
color: #9e9e9e;
}
.xterm-fg-248 {
color: #a8a8a8;
}
.xterm-fg-249 {
color: #b2b2b2;
}
.xterm-fg-250 {
color: #bcbcbc;
}
.xterm-fg-251 {
color: #c6c6c6;
}
.xterm-fg-252 {
color: #d0d0d0;
}
.xterm-fg-253 {
color: #dadada;
}
.xterm-fg-254 {
color: #e4e4e4;
}
.xterm-fg-255 {
color: #eee;
}
......
......@@ -45,6 +45,10 @@ class ApplicationController < ActionController::Base
redirect_to request.referer.present? ? :back : default, options
end
def not_found
render_404
end
protected
# This filter handles both private tokens and personal access tokens
......
class Projects::GroupLinksController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!
before_action :authorize_admin_project_member!, only: [:update]
def index
@group_links = project.project_group_links.all
......@@ -27,9 +28,26 @@ class Projects::GroupLinksController < Projects::ApplicationController
redirect_to namespace_project_group_links_path(project.namespace, project)
end
def update
@group_link = @project.project_group_links.find(params[:id])
@group_link.update_attributes(group_link_params)
end
def destroy
project.project_group_links.find(params[:id]).destroy
respond_to do |format|
format.html do
redirect_to namespace_project_group_links_path(project.namespace, project)
end
format.js { head :ok }
end
end
protected
def group_link_params
params.require(:group_link).permit(:group_access, :expires_at)
end
end
......@@ -5,34 +5,23 @@ class Projects::ProjectMembersController < Projects::ApplicationController
before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
def index
@group_links = @project.project_group_links
@project_members = @project.project_members
@project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
if params[:search].present?
users = @project.users.search(params[:search]).to_a
@project_members = @project_members.where(user_id: users)
end
@project_members = @project_members.order('access_level DESC')
@group = @project.group
if @group
@group_members = @group.group_members
@group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group)
if params[:search].present?
users = @group.users.search(params[:search]).to_a
@group_members = @group_members.where(user_id: users)
@group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
end
@group_members = @group_members.order('access_level DESC')
end
@project_members = @project_members.order(access_level: :desc).page(params[:page])
@requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new
@project_group_links = @project.project_group_links
end
def create
......@@ -43,6 +32,21 @@ class Projects::ProjectMembersController < Projects::ApplicationController
current_user: current_user
)
if params[:group_ids].present?
group_ids = params[:group_ids].split(',')
groups = Group.where(id: group_ids)
groups.each do |group|
next unless can?(current_user, :read_group, group)
project.project_group_links.create(
group: group,
group_access: params[:access_level],
expires_at: params[:expires_at]
)
end
end
redirect_to namespace_project_project_members_path(@project.namespace, @project)
end
......
class UsersController < ApplicationController
skip_before_action :authenticate_user!
before_action :user
before_action :user, except: [:exists]
before_action :authorize_read_user!, only: [:show]
def show
......@@ -85,6 +85,10 @@ class UsersController < ApplicationController
render 'calendar_activities', layout: false
end
def exists
render json: { exists: Namespace.where(path: params[:username].downcase).any? }
end
private
def authorize_read_user!
......
module Ci
class Build < CommitStatus
include TokenAuthenticatable
include AfterCommitQueue
belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
......@@ -75,25 +76,20 @@ module Ci
state_machine :status do
after_transition pending: :running do |build|
build.execute_hooks
build.run_after_commit do
BuildHooksWorker.perform_async(id)
end
end
after_transition any => [:success, :failed, :canceled] do |build|
build.update_coverage
build.execute_hooks
build.run_after_commit do
BuildFinishedWorker.perform_async(id)
end
end
after_transition any => [:success] do |build|
if build.environment.present?
service = CreateDeploymentService.new(
build.project, build.user,
environment: build.environment,
sha: build.sha,
ref: build.ref,
tag: build.tag,
options: build.options.to_h[:environment],
variables: build.variables)
service.execute(build)
build.run_after_commit do
BuildSuccessWorker.perform_async(id)
end
end
end
......
......@@ -76,7 +76,11 @@ module Ci
end
after_transition do |pipeline, transition|
pipeline.execute_hooks unless transition.loopback?
next if transition.loopback?
pipeline.run_after_commit do
PipelineHooksWorker.perform_async(id)
end
end
end
......
......@@ -491,7 +491,7 @@ class Project < ActiveRecord::Base
end
def import_url
if import_data && super
if import_data && super.present?
import_url = Gitlab::UrlSanitizer.new(super, credentials: import_data.credentials)
import_url.full_url
else
......
......@@ -2,25 +2,35 @@ require_relative 'base_service'
class CreateDeploymentService < BaseService
def execute(deployable = nil)
environment = find_or_create_environment
return unless executable?
deployment = project.deployments.create(
environment: environment,
ref: params[:ref],
tag: params[:tag],
sha: params[:sha],
user: current_user,
deployable: deployable
)
ActiveRecord::Base.transaction do
@deployable = deployable
@environment = prepare_environment
deploy.tap do |deployment|
deployment.update_merge_request_metrics!
deployment
end
end
end
private
def find_or_create_environment
def executable?
project && name.present?
end
def deploy
project.deployments.create(
environment: @environment,
ref: params[:ref],
tag: params[:tag],
sha: params[:sha],
user: current_user,
deployable: @deployable)
end
def prepare_environment
project.environments.find_or_create_by(name: expanded_name) do |environment|
environment.external_url = expanded_url
end
......
- page_title "Preview | Appearance"
= render 'devise/shared/tab_single', tab_title: 'Sign in preview'
.login-box
.login-heading
%h3 Existing user? Sign in
%form
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
%form.show-gl-field-errors
.form-group
= label_tag :login
= text_field_tag :login, nil, class: "form-control top", title: 'Please provide your username or email address.'
.form-group
= label_tag :password
= password_field_tag :password, nil, class: "form-control bottom", title: 'This field is required.'
.form-group
= button_tag "Sign in", class: "btn-create btn"
......@@ -71,7 +71,7 @@
.col-md-6
%h4 Recent builds served by this Runner
%table.table.builds.runner-builds
%table.table.ci-table.runner-builds
%thead
%tr
%th Build
......
= render 'devise/shared/tab_single', tab_title: 'Resend confirmation instructions'
.login-box
.login-heading
%h3 Resend confirmation instructions
.login-body
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
.devise-errors
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: 'Email', class: "form-control", required: true
.form-group
= f.label :email
= f.email_field :email, class: "form-control", required: true, title: 'Please provide a valid email address.'
.clearfix
= f.submit "Resend confirmation instructions", class: 'btn btn-success'
= f.submit "Resend", class: 'btn btn-success'
.clearfix.prepend-top-20
= render 'devise/shared/sign_in_link'
= render 'devise/shared/tab_single', tab_title:'Change your password'
.login-box
.login-heading
%h3 Change your password
.login-body
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'show-gl-field-errors' }) do |f|
.devise-errors
= devise_error_messages!
= f.hidden_field :reset_password_token
%div
= f.password_field :password, class: "form-control top", placeholder: "New password", required: true
%div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
.form-group
= f.label 'New password', for: :password
= f.password_field :password, class: "form-control top", required: true, title: 'This field is required'
.form-group
= f.label 'Confirm new password', for: :password_confirmation
= f.password_field :password_confirmation, class: "form-control bottom", title: 'This field is required', required: true
.clearfix
= f.submit "Change your password", class: "btn btn-primary"
.clearfix.prepend-top-20
%p
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
= render 'devise/shared/sign_in_link'
%span.light Didn't receive a confirmation email?
= link_to "Request a new one", new_confirmation_path(resource_name)
= render 'devise/shared/sign_in_link'
= render 'devise/shared/tab_single', tab_title: 'Reset Password'
.login-box
.login-heading
%h3 Reset password
.login-body
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
.devise-errors
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: "Email", class: "form-control", required: true, value: params[:user_email], autofocus: true
.form-group
= f.label :email
= f.email_field :email, class: "form-control", required: true, value: params[:user_email], autofocus: true, title: 'Please provide a valid email address.'
.clearfix
= f.submit "Reset password", class: "btn-primary btn"
......
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off"
= f.password_field :password, class: "form-control bottom", placeholder: "Password"
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user show-gl-field-errors', 'aria-live' => 'assertive'}) do |f|
%div.form-group
= f.label "Username or email", for: :login
= f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
%div.form-group
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, title: "This field is required."
- if devise_mapping.rememberable?
.remember-me.checkbox
%label{for: "user_remember_me"}
......@@ -8,5 +12,5 @@
%span Remember me
.pull-right
= link_to "Forgot your password?", new_password_path(resource_name)
%div
%div.submit-container
= f.submit "Sign in", class: "btn btn-save"
= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user' ) do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "Username", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'show-gl-field-errors') do
.form-group
= label_tag 'Username or email', for: :username
= text_field_tag :username, nil, {class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true }
.form-group
= label_tag :password
= password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
- if devise_mapping.rememberable?
.remember-me.checkbox
%label{for: "remember_me"}
......
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user') do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "#{server['label']} Login", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "show-gl-field-errors") do
.form-group
= label_tag "#{server['label']} Login", for: :username
= text_field_tag :username, nil, {class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
.form-group
= label_tag :password
= password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
- if devise_mapping.rememberable?
.remember-me.checkbox
%label{for: "remember_me"}
......
- page_title "Sign in"
%div
- if form_based_providers.any?
= render 'devise/shared/tabs_ldap'
- else
= render 'devise/shared/tabs_normal'
.tab-content
- if signin_enabled? || ldap_enabled? || crowd_enabled?
= render 'devise/shared/signin_box'
-# Omniauth fits between signin/ldap signin and signup and does not have a surrounding box
- if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix.prepend-top-20
= render 'devise/shared/omniauth_box'
-# Signup only makes sense if you can also sign-in
- if signin_enabled? && signup_enabled?
.prepend-top-20
= render 'devise/shared/signup_box'
-# Show a message if none of the mechanisms above are enabled
- if !signin_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?)
%div
No authentication methods configured.
- if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix
= render 'devise/shared/omniauth_box'
......@@ -3,20 +3,19 @@
= page_specific_javascript_tag('u2f.js')
%div
= render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication'
.login-box
.login-heading
%h3 Two-Factor Authentication
.login-body
- if @user.two_factor_otp_enabled?
%h5 Authenticate via Two-Factor App
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f|
- resource_params = params[resource_name].presence || params
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off'
.form-group
= f.label 'Two-Factor Authentication code', name: :otp_attempt
= f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20
= f.submit "Verify code", class: "btn btn-save"
- if @user.two_factor_u2f_enabled?
%hr
= render "u2f/authenticate", locals: { params: params, resource: resource, resource_name: resource_name }
%p
%div.omniauth-container
%p
%span.light
Sign in with &nbsp;
- providers = enabled_button_based_providers
......
%p
%span.light
Already have login and password?
%strong
= link_to "Sign in", new_session_path(resource_name)
.login-box
- if signup_enabled?
.login-heading
%h3 Existing user? Sign in
- else
.login-heading
%h3 Sign in
#login-pane.login-box{ role: 'tabpanel', class: 'tab-pane active' }
.login-body
- if form_based_providers.any?
%ul.nav-links
- if crowd_enabled?
%li.active
= link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i|
%li{class: (:active if i.zero? && !crowd_enabled?)}
= link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
- if signin_enabled?
%li
= link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
.tab-content
- if crowd_enabled?
%div.tab-pane.active{id: "tab-crowd"}
= render 'devise/sessions/new_crowd'
......
.login-box
- if signin_enabled?
.login-heading
%h3 New user? Create an account
- else
.login-heading
%h3 Create an account
#register-pane.login-box{ role: 'tabpanel', class: 'tab-pane' }
.login-body
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name)) do |f|
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user show-gl-field-errors", "aria-live" => "assertive" }) do |f|
.devise-errors
= devise_error_messages!
%div
= f.text_field :name, class: "form-control top", placeholder: "Name", required: true
%div
= f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
%div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
%div.form-group
= f.label :name
= f.text_field :name, class: "form-control top", required: true, title: "This field is required."
%div.username.form-group
= f.label :username
= f.text_field :username, class: "form-control middle no-gl-field-error", pattern: "[a-zA-Z0-9]+", required: true, title: 'Please create a username with only alphanumeric characters.'
%p.validation-error.hide Username is already taken.
%p.validation-success.hide Username is available.
%p.validation-pending.hide Checking username availability...
%div.form-group
= f.label :email
= f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address."
.form-group.append-bottom-20#password-strength
= f.password_field :password, class: "form-control bottom", placeholder: "Password - minimum length #{@minimum_password_length} characters", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters"
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
%p.gl-field-hint Minimum length is #{@minimum_password_length} characters
%div
- if current_application_settings.recaptcha_enabled
= recaptcha_tags
%div
= f.submit "Sign up", class: "btn-create btn"
.clearfix.prepend-top-20
= f.submit "Register", class: "btn-register btn"
.clearfix.submit-container
%p
%span.light Didn't receive a confirmation email?
= succeed '.' do
......
%ul.nav-links.nav-tabs.new-session-tabs.single-tab
%li.active
%a= tab_title
%ul.new-session-tabs.nav-links.nav-tabs
- if crowd_enabled?
%li.active
= link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i|
%li{class: (:active if i.zero? && !crowd_enabled?)}
= link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
- if signin_enabled?
%li
= link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'}
%li.active{ role: 'presentation' }
%a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in
%li{ role: 'presentation'}
%a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register
= render 'devise/shared/tab_single', tab_title: 'Resend unlock instructions'
.login-box
.login-heading
%h3 Resend unlock email
.login-body
= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
.devise-errors
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off'
.form-group.append-bottom-20
= f.label :email
= f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: 'Please provide a valid email address.'
.clearfix
= f.submit 'Resend unlock instructions', class: 'btn btn-success'
......
= form_for @group_member, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
.col-sm-10
= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
.help-block
= form_for @group_member, url: group_group_members_path(@group), html: { class: 'users-project-form users-group-form' } do |f|
.row
.col-md-4.col-lg-6
= users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true)
.help-block.append-bottom-10
Search for users by name, username, or email, or invite new ones using their email address.
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
.col-sm-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2"
.help-block
Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.col-md-3.col-lg-2
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select"
.help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.col-md-3.col-lg-2
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input
.help-block
.help-block.append-bottom-10
On this date, the user(s) will automatically lose access to this group and all of its projects.
.form-actions
= f.submit 'Add users to group', class: "btn btn-create"
.col-md-2
= f.submit 'Add to group', class: "btn btn-create btn-block"
- page_title "Members"
.group-members-page.prepend-top-default
.project-members-page.prepend-top-default
%h4
Members
%hr
- if can?(current_user, :admin_group_member, @group)
.panel.panel-default
.panel-heading
Add new user to group
.panel-body
%p.light
Members of group have access to all group projects.
.new-group-member-holder
.project-members-new.append-bottom-default
%p.clearfix
Add new user to
%strong= @group.name
= render "new_group_member"
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
.append-bottom-default.clearfix
%h5.member.existing-title
Existing users
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
.panel.panel-default
.panel-heading
Users with access to
%strong #{@group.name}
group members
%span.badge= @members.total_count
.controls
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false }
= button_tag class: 'btn', title: 'Search' do
= icon("search")
%ul.content-list
= render partial: 'shared/members/member', collection: @members, as: :member
= paginate @members, theme: 'gitlab'
:javascript
$('form.member-search-form').on('submit', function(event) {
event.preventDefault();
Turbolinks.visit(this.action + '?' + $(this).serialize());
});
:plain
$("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
new gl.MemberExpirationDate();
var $listItem = $('#{escape_javascript(render('shared/members/member', member: @group_member))}');
$("##{dom_id(@group_member)} .list-item-name").replaceWith($listItem.find('.list-item-name'));
- project = @target_project || @project
- noteable_type = @noteable.class if @noteable.present?
:javascript
- if project
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_type, type_id: params[:id])}"
GitLab.GfmAutoComplete.cachedData = undefined;
GitLab.GfmAutoComplete.setup();
......@@ -11,3 +11,4 @@
= render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body
= render "layouts/init_auto_complete" if @gfm_form
!!! 5
%html{ lang: "en"}
%html{ lang: "en", class: "devise-layout-html"}
= render "layouts/head"
%body.ui_charcoal.login-page.application.navless
%body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page }}
.page-wrap
= Gon::Base.render_data
= render "layouts/header/empty"
= render "layouts/broadcast"
......@@ -9,7 +10,7 @@
.content
= render "layouts/flash"
.row
.col-sm-5.pull-right
.col-sm-5.pull-right.new-session-forms-container
= yield
.col-sm-7.brand-holder.pull-left
%h1
......@@ -21,15 +22,15 @@
%h3 Open source software to collaborate on code
%p
Manage git repositories with fine grained access controls that keep your code secure.
Manage Git repositories with fine-grained access controls that keep your code secure.
Perform code reviews and enhance collaboration with merge requests.
Each project can also have an issue tracker and a wiki.
- if current_application_settings.sign_in_text.present?
= markdown_field(current_application_settings, :sign_in_text)
%hr
.container
%hr.footer-fixed
.container.footer-container
.footer-links
= link_to "Explore", explore_root_path
= link_to "Help", help_path
......
......@@ -86,11 +86,11 @@
= f.label :username, "Path", class: "label-light"
.input-group
.input-group-addon
= "#{root_url}u/"
= root_url
= f.text_field :username, required: true, class: 'form-control'
.help-block
Current path:
= "#{root_url}u/#{current_user.username}"
= "#{root_url}#{current_user.username}"
.prepend-top-default
= f.button class: "btn btn-warning", type: "submit" do
= icon "spinner spin", class: "hidden loading-username"
......
- @gfm_form = true
- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
.zen-backdrop
- classes << ' js-gfm-input js-autosize markdown-area'
......@@ -7,6 +8,3 @@
= text_area_tag attr, nil, class: classes, placeholder: placeholder
%a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
= icon('compress')
- content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user && (@target_project || @project)
- @no_container = true
- page_title "Blame", @blob.path, @ref
= render "projects/commits/head"
%h3.page-title Blame view
%div{ class: container_class }
%h3.page-title Blame view
#blob-content-holder.tree-holder
#blob-content-holder.tree-holder
.file-holder
.file-title
= blob_icon @blob.mode, @blob.name
......
- @no_container = true
- page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
= page_specific_javascript_tag('blob_edit/blob_edit_bundle.js')
= render "projects/commits/head"
- if @conflict
%div{ class: container_class }
- if @conflict
.alert.alert-danger
Someone edited the file the same time you did. Please check out
= link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank"
and make sure your changes will not unintentionally remove theirs.
.file-editor
.file-editor
%ul.nav-links.no-bottom.js-edit-mode
%li.active
= link_to '#editor' do
......
......@@ -5,7 +5,7 @@
.nothing-here-block No builds to show
- else
.table-holder
%table.table.builds
%table.table.ci-table.builds-page
%thead
%tr
%th Status
......
- @no_container = true
- page_title "#{@build.name} (##{@build.id})", "Builds"
- trace_with_state = @build.trace_with_state
- header_title project_title(@project, "Builds", project_builds_path(@project))
= render "projects/pipelines/head", build_subnav: true
.build-page
%div{ class: container_class }
.build-page
= render "header"
- if @build.stuck?
......@@ -44,9 +47,9 @@
#down-build-trace
= render "sidebar"
= render "sidebar"
:javascript
:javascript
new Build({
page_url: "#{namespace_project_build_url(@project.namespace, @project, @build)}",
build_url: "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}",
......
......@@ -56,7 +56,7 @@
\.gitlab-ci.yml not found in this commit
.table-holder.pipeline-holder
%table.table.builds.pipeline
%table.table.ci-table.pipeline
%thead
%tr
%th Status
......
......@@ -4,7 +4,7 @@
.nothing-here-block No pipelines to show
- else
.table-holder
%table.table.builds
%table.table.ci-table
%tbody
%th Status
%th Pipeline
......
- @no_container = true
- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "projects/commits/head"
.prepend-top-default
%div{ class: container_class }
.prepend-top-default
= render "commit_box"
= render "ci_menu"
= render "builds"
= render "ci_menu"
= render "builds"
- @no_container = true
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description
= render "projects/commits/head"
.prepend-top-default
%div{ class: container_class }
.prepend-top-default
= render "commit_box"
- if @commit.status
- if @commit.status
= render "ci_menu"
- else
- else
%div.block-connector
= render "projects/diffs/diffs", diffs: @diffs
= render "projects/notes/notes_with_form"
- if can_collaborate_with_project?
= render "projects/diffs/diffs", diffs: @diffs
= render "projects/notes/notes_with_form"
- if can_collaborate_with_project?
- %w(revert cherry-pick).each do |type|
= render "projects/commit/change", type: type, commit: @commit, title: @commit.title
.dropdown-menu.dropdown-menu-selectable
= dropdown_title "Select branch/tag"
= dropdown_filter "Filter by branch/tag"
= dropdown_title "Select Git revision"
= dropdown_filter "Filter by Git revision"
= dropdown_content
= dropdown_loading
- @no_container = true
- page_title "Edit", @environment.name, "Environments"
= render "projects/pipelines/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
Edit environment
%hr
= render 'form'
%hr
= render 'form'
......@@ -24,7 +24,7 @@
New environment
- else
.table-holder
%table.table.builds.environments
%table.table.ci-table.environments
%tbody
%th Environment
%th Last Deployment
......
- @no_container = true
- page_title 'New Environment'
= render "projects/pipelines/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
New environment
%hr
= render 'form'
%hr
= render 'form'
......@@ -24,7 +24,7 @@
= link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
- else
.table-holder
%table.table.builds.environments
%table.table.ci-table.environments
%thead
%tr
%th ID
......
:plain
var $listItem = $('#{escape_javascript(render('shared/members/group', group_link: @group_link))}');
$("#group_member_#{@group_link.id} .list-item-name").replaceWith($listItem.find('.list-item-name'));
- @no_container = true
- page_title "Edit", @label.name, "Labels"
= render "projects/issues/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
Edit Label
%hr
= render 'form'
%hr
= render 'form'
- @no_container = true
- page_title "New Label"
= render "projects/issues/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
New Label
%hr
= render 'form'
%hr
= render 'form'
......@@ -47,7 +47,7 @@
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- if @commits_count.nonzero?
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
Discussion
......
- @no_container = true
- page_title "Edit", @milestone.title, "Milestones"
= render "projects/issues/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
Edit Milestone ##{@milestone.iid}
%hr
%hr
= render "form"
= render "form"
- @no_container = true
- page_title "New Milestone"
= render "projects/issues/head"
%h3.page-title
%div{ class: container_class }
%h3.page-title
New Milestone
%hr
%hr
= render "form"
= render "form"
- @no_container = true
- page_title @milestone.title, "Milestones"
- page_description @milestone.description
= render "projects/issues/head"
.detail-page-header
%div{ class: container_class }
.detail-page-header
.status-box{ class: status_box_class(@milestone) }
- if @milestone.closed?
Closed
......@@ -28,7 +31,7 @@
= link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
Delete
.detail-page-description.milestone-detail
.detail-page-description.milestone-detail
%h2.title
= markdown_field(@milestone, :title)
%div
......@@ -38,12 +41,12 @@
= preserve do
= markdown_field(@milestone, :description)
- if @milestone.total_items_count(current_user).zero?
- if @milestone.total_items_count(current_user).zero?
.alert.alert-success.prepend-top-default
%span Assign some issues to this milestone.
- elsif @milestone.complete?(current_user) && @milestone.active?
- elsif @milestone.complete?(current_user) && @milestone.active?
.alert.alert-success.prepend-top-default
%span All issues for this milestone are closed. You may close this milestone now.
= render 'shared/milestones/summary', milestone: @milestone, project: @project
= render 'shared/milestones/tabs', milestone: @milestone
= render 'shared/milestones/summary', milestone: @milestone, project: @project
= render 'shared/milestones/tabs', milestone: @milestone
......@@ -14,7 +14,7 @@
.disabled-comment.text-center
.disabled-comment-text.inline
Please
= link_to "sign up", new_session_path(:user, redirect_to_referer: 'yes')
= link_to "register", new_session_path(:user, redirect_to_referer: 'yes')
or
= link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
to post a comment
......
= content_for :sub_nav do
.scrolling-tabs-container.sub-nav-scroll
= render 'shared/nav_scroll'
.nav-links.sub-nav.scrolling-tabs
.nav-links.sub-nav.scrolling-tabs{ class: ('build' if local_assigns.fetch(:build_subnav, false)) }
%ul{ class: (container_class) }
- if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do
......
......@@ -43,7 +43,7 @@
.nothing-here-block No pipelines to show
- else
.table-holder
%table.table.builds
%table.table.ci-table
%thead
%th.col-xs-1.col-sm-1 Status
%th.col-xs-2.col-sm-4 Pipeline
......
- @no_container = true
- page_title "Pipeline"
= render "projects/pipelines/head"
.prepend-top-default
%div{ class: container_class }
.prepend-top-default
- if @commit
= render "projects/pipelines/info"
%div.block-connector
= render "projects/commit/pipeline", pipeline: @pipeline
= render "projects/commit/pipeline", pipeline: @pipeline
.panel.panel-default
.panel-heading
Group members with access to
%strong #{@group.name}
group members
%span.badge= members.size
- if can?(current_user, :admin_group_member, @group)
.controls
......
.panel.panel-default.project-members-groups
.panel-heading
Groups with access to
%strong #{@project.name}
%span.badge= group_links.size
%ul.content-list
= render partial: 'shared/members/group', collection: group_links, as: :group_link
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
.form-group
= f.label :user_ids, "People", class: 'control-label'
.col-sm-10
= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
.help-block
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f|
.row
.col-md-4.col-lg-6
= users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true)
.help-block.append-bottom-10
Search for users by name, username, or email, or invite new ones using their email address.
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
.col-sm-10
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "project-access-select select2"
.help-block
Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.col-md-3.col-lg-2
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
.help-block.append-bottom-10
= link_to "Read more", help_page_path("user/permissions"), class: "vlink"
about role permissions
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.col-md-3.col-lg-2
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
%i.clear-icon.js-clear-input
.help-block
.help-block.append-bottom-10
On this date, the user(s) will automatically lose access to this project.
.form-actions
= f.submit 'Add users to project', class: "btn btn-create"
.col-md-2
= f.submit "Add to project", class: "btn btn-create btn-block"
.panel.panel-default
.panel-heading
Users with access to
%strong #{@project.name}
project members
%span.badge= members.size
.controls
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false }
= button_tag class: 'btn', title: 'Search' do
= icon("search")
%span.badge= @project_members.total_count
%ul.content-list
= render partial: 'shared/members/member', collection: members, as: :member
:javascript
$('form.member-search-form').on('submit', function (event) {
event.preventDefault();
Turbolinks.visit(this.action + '?' + $(this).serialize());
});
- page_title "Members"
.project-members-page.js-project-members-page.prepend-top-default
.project-members-page.prepend-top-default
%h4.project-members-title.clearfix
Members
= link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default pull-right hidden-xs", title: "Import members from another project"
- if can?(current_user, :admin_project_member, @project)
.panel.panel-default
.panel-heading
Add new user to project
.controls
= link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
Import members
.panel-body
%p.light
Users with access to this project are listed below.
.project-members-new.append-bottom-default
%p.clearfix
Add new user to
%strong= @project.name
= render "new_project_member"
= render 'shared/members/requests', membership_source: @project, requesters: @requesters
= render 'team', members: @project_members
- if @group
= render "group_members", members: @group_members
.append-bottom-default.clearfix
%h5.member.existing-title
Existing users and groups
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
- if @group_links.any?
= render 'groups', group_links: @group_links
- if @project_group_links.any? && @project.allowed_to_share_with_group?
= render "shared_group_members"
= render 'team', members: @project_members
= paginate @project_members, theme: "gitlab"
:plain
$("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}');
new gl.MemberExpirationDate();
var $listItem = $('#{escape_javascript(render('shared/members/member', member: @project_member))}');
$("##{dom_id(@project_member)} .list-item-name").replaceWith($listItem.find('.list-item-name'));
......@@ -23,6 +23,8 @@
data: { data: issuable_template_names, field_name: 'issuable_template', selected: selected_template(issuable), project_path: ref_project.path, namespace_path: ref_project.namespace.path } } ) do
%ul.dropdown-footer-list
%li
%a.no-template
No template
%a.reset-template
Reset template
%div{ class: issuable_template_names.any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
......
- group_link = local_assigns[:group_link]
- group = group_link.group
- can_admin_member = can?(current_user, :admin_project_member, @project)
%li.member.group_member{ id: "group_member_#{group_link.id}" }
%span{ class: "list-item-name" }
= image_tag group_icon(group), class: "avatar s40", alt: ''
%strong
= link_to group.name, group_path(group)
.cgray
Joined #{time_ago_with_tooltip(group.created_at)}
- if group_link.expires?
·
%span{ class: ('text-warning' if group_link.expires_soon?) }
Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
.controls.member-controls
= form_tag namespace_project_group_link_path(@project.namespace, @project, group_link), method: :put, remote: true, class: 'form-horizontal js-edit-member-form' do
= select_tag 'group_link[group_access]', options_for_select(ProjectGroupLink.access_options, group_link.group_access), class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{group.id}", disabled: !can_admin_member
.prepend-left-5.clearable-input.member-form-control
= text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{group.id}", disabled: !can_admin_member
%i.clear-icon.js-clear-input
- if can_admin_member
= link_to namespace_project_group_link_path(@project.namespace, @project, group_link),
remote: true,
method: :delete,
data: { confirm: "Are you sure you want to remove #{group.name}?" },
class: 'btn btn-remove prepend-left-10' do
%span.visible-xs-block
Delete
= icon('trash', class: 'hidden-xs')
- show_roles = local_assigns.fetch(:show_roles, true)
- show_controls = local_assigns.fetch(:show_controls, true)
- user = member.user
- user = local_assigns.fetch(:user, member.user)
- source = member.source
- can_admin_member = can?(current_user, action_member_permission(:update, member), member)
%li.js-toggle-container{ class: dom_class(member), id: dom_id(member) }
- if show_roles
.controls
%strong.control-text= member.human_access
- if show_controls
- if !user && can?(current_user, action_member_permission(:admin, member), member.source)
= link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
method: :post,
class: 'btn'
- if can?(current_user, action_member_permission(:update, member), member)
= button_tag icon('pencil'),
type: 'button',
class: 'btn inline js-toggle-button',
title: 'Edit'
- if member.request?
= link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
method: :post,
class: 'btn btn-success',
title: 'Grant access'
- if can?(current_user, action_member_permission(:destroy, member), member)
- if current_user == user
= link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]),
method: :delete,
data: { confirm: leave_confirmation_message(member.source) },
class: 'btn btn-remove'
- else
= link_to icon('trash'), member,
remote: true,
method: :delete,
data: { confirm: remove_member_message(member) },
class: 'btn btn-remove',
title: remove_member_title(member)
%span{ class: ("list-item-name" if show_controls) }
%li.member{ class: dom_class(member), id: dom_id(member) }
%span.list-item-name
- if user
= image_tag avatar_icon(user, 40), class: "avatar s40", alt: ''
%strong
= link_to user.name, user_path(user)
%span.cgray= user.username
%span.cgray= user.to_reference
- if user == current_user
%span.label.label-success It's you
%span.label.label-success.prepend-left-5 It's you
- if user.blocked?
%label.label.label-danger
%strong Blocked
.cgray
- if source.instance_of?(Group) && !@group
= link_to source, class: "member-group-link prepend-left-5" do
= #{source.name}"
.hidden-xs.cgray
- if member.request?
Requested
= time_ago_with_tooltip(member.requested_at)
......@@ -73,20 +43,44 @@
by
= link_to member.created_by.name, user_path(member.created_by)
= time_ago_with_tooltip(member.created_at)
- if show_roles
.edit-member.hide.js-toggle-content
%br
= form_for member, remote: true, html: { class: 'form-horizontal' } do |f|
.form-group
= label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label'
.col-sm-10
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}"
.form-group
= label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}"
.controls.member-controls
- if show_controls
- if user != current_user
= form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f|
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member
.prepend-left-5.clearable-input.member-form-control
= f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member
%i.clear-icon.js-clear-input
.prepend-top-10
= f.submit 'Save', class: 'btn btn-save btn-sm'
- else
%span.member-access-text= member.human_access
- if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source)
= link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
method: :post,
class: 'btn btn-default prepend-left-10'
- elsif member.request? && can_admin_member
= link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
method: :post,
class: 'btn btn-success prepend-left-10',
title: 'Grant access'
- if can?(current_user, action_member_permission(:destroy, member), member)
- if current_user == user
= link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]),
method: :delete,
data: { confirm: leave_confirmation_message(member.source) },
class: 'btn btn-remove prepend-left-10'
- else
= link_to member,
remote: true,
method: :delete,
data: { confirm: remove_member_message(member) },
class: 'btn btn-remove prepend-left-10',
title: remove_member_title(member) do
%span.visible-xs-block
Delete
= icon('trash', class: 'hidden-xs')
- else
%span.member-access-text= member.human_access
- if requesters.any?
.panel.panel-default
.panel-heading
Users requesting access to
%strong= membership_source.name
access requests
%span.badge= requesters.size
%ul.content-list
= render partial: 'shared/members/member', collection: requesters, as: :member
......@@ -6,7 +6,7 @@
%script#js-authenticate-u2f-setup{ type: "text/template" }
%div
%p Insert your security key (if you haven't already), and press the button below.
%a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Login Via U2F Device
%a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Sign in via U2F device
%script#js-authenticate-u2f-in-progress{ type: "text/template" }
%p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
......
class BuildCoverageWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(build_id)
Ci::Build.find_by(id: build_id)
.try(:update_coverage)
end
end
class BuildFinishedWorker
include Sidekiq::Worker
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
BuildCoverageWorker.new.perform(build.id)
BuildHooksWorker.new.perform(build.id)
end
end
end
class BuildHooksWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(build_id)
Ci::Build.find_by(id: build_id)
.try(:execute_hooks)
end
end
class BuildSuccessWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
create_deployment(build)
end
end
private
def create_deployment(build)
return if build.environment.blank?
service = CreateDeploymentService.new(
build.project, build.user,
environment: build.environment,
sha: build.sha,
ref: build.ref,
tag: build.tag,
options: build.options.to_h[:environment],
variables: build.variables)
service.execute(build)
end
end
class PipelineHooksWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:execute_hooks)
end
end
......@@ -88,4 +88,6 @@ Rails.application.routes.draw do
get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: /.*/ }
root to: "root#index"
get '*unmatched_route', to: 'application#not_found'
end
require 'constraints/group_url_constrainer'
constraints(GroupUrlConstrainer.new) do
scope(path: ':id', as: :group, controller: :groups) do
scope(path: ':id',
as: :group,
constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ },
controller: :groups) do
get '/', action: :show
patch '/', action: :update
put '/', action: :update
delete '/', action: :destroy
end
end
......
......@@ -408,7 +408,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end
end
resources :group_links, only: [:index, :create, :destroy], constraints: { id: /\d+/ }
resources :group_links, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ }
resources :notes, only: [:index, :create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do
member do
......
......@@ -33,5 +33,6 @@ scope(path: 'u/:username',
get :projects
get :contributed, as: :contributed_projects
get :snippets
get :exists
get '/', to: redirect('/%{username}')
end
......@@ -49,6 +49,7 @@
- [Git LFS configuration](workflow/lfs/lfs_administration.md)
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
- [GitLab Performance Monitoring](administration/monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
- [Request Profiling](administration/monitoring/performance/request_profiling.md) Get a detailed profile on slow requests.
- [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint.
- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
......
# Request Profiling
## Procedure
1. Grab the profiling token from `Monitoring > Requests Profiles` admin page
(highlighted in a blue in the image below).
![Profile token](img/request_profiling_token.png)
1. Pass the header `X-Profile-Token: <token>` to the request you want to profile. You can use any of these tools
* [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) Chrome extension
* [Modify Headers](https://addons.mozilla.org/en-US/firefox/addon/modify-headers/) Firefox extension
* `curl --header 'X-Profile-Token: <token>' https://gitlab.example.com/group/project`
1. Once request is finished (which will take a little longer than usual), you can
view the profiling output from `Monitoring > Requests Profiles` admin page.
![Profiling output](img/request_profile_result.png)
## Cleaning up
Profiling output will be cleared out every day via a Sidekiq worker.
......@@ -159,7 +159,8 @@ Variables can be also defined on [job level](#job-variables).
> Introduced in GitLab Runner v0.7.0.
`cache` is used to specify a list of files and directories which should be
cached between builds.
cached between builds. You can only use paths that are within the project
workspace.
**By default the caching is enabled per-job and per-branch.**
......@@ -606,8 +607,8 @@ You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-n
> - Build artifacts are only collected for successful builds by default.
`artifacts` is used to specify a list of files and directories which should be
attached to the build after success. To pass artifacts between different builds,
see [dependencies](#dependencies).
attached to the build after success. You can only use paths that are within the
project workspace. To pass artifacts between different builds, see [dependencies](#dependencies).
Below are some examples.
......
......@@ -95,10 +95,10 @@ merge request.
someone in the Merge Request
- When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`,
`@dblessing`, `@ashleys`. This is to ensure that no document
with wrong heading is going live without an audit, thus preventing dead links
and redirection issues when corrected
of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`,
`@SeanPackham`. This is to ensure that no document with wrong heading is going
live without an audit, thus preventing dead links and redirection issues when
corrected
- Leave exactly one newline after a heading
## Links
......
......@@ -34,10 +34,11 @@ graphs/dashboards.
## Tooling
GitLab provides two built-in tools to aid the process of improving performance:
GitLab provides built-in tools to aid the process of improving performance:
* [Sherlock](profiling.md#sherlock)
* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
GitLab employees can use GitLab.com's performance monitoring systems located at
<http://performance.gitlab.net>, this requires you to log in using your
......
......@@ -400,7 +400,7 @@ If you are not using Linux you may have to run `gmake` instead of
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
sudo -u git -H git checkout v0.8.4
sudo -u git -H git checkout v0.8.5
sudo -u git -H make
### Initialize Database and Activate Advanced Features
......
......@@ -64,6 +64,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [Making GitLab Great for Everyone - Video](https://www.youtube.com/watch?v=GGC40y4vMx0) - Response to "Dear GitHub" letter
1. [Using Innersourcing to Improve Collaboration](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/)
1. [The Software Development Market and GitLab - Video](https://www.youtube.com/watch?v=sXlhgPK1NTY&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e&index=6) - [Slides](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit)
1. [The GitLab Book Club](bookclub/index.md)
#### 1.7 Community and Support
......
# Books
List of books and resources, that may be worth reading.
## Papers
1. **The Humble Programmer**
Edsger W. Dijkstra, 1972 ([paper](http://dl.acm.org/citation.cfm?id=361591))
## Programming
1. **Design Patterns: Elements of Reusable Object-Oriented Software**
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1994 ([amazon](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612))
1. **Clean Code: A Handbook of Agile Software Craftsmanship**
Robert C. "Uncle Bob" Martin, 2008 ([amazon](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882))
1. **Code Complete: A Practical Handbook of Software Construction**, 2nd Edition
Steve McConnell, 2004 ([amazon](http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670))
1. **The Pragmatic Programmer: From Journeyman to Master**
Andrew Hunt, David Thomas, 1999 ([amazon](http://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X))
1. **Working Effectively with Legacy Code**
Michael Feathers, 2004 ([amazon](http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052))
1. **Eloquent Ruby**
Russ Olsen, 2011 ([amazon](http://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104))
1. **Domain-Driven Design: Tackling Complexity in the Heart of Software**
Eric Evans, 2003 ([amazon](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215))
1. **How to Solve It: A New Aspect of Mathematical Method**
Polya G. 1957 ([amazon](http://www.amazon.com/How-Solve-Mathematical-Princeton-Science/dp/069116407X))
1. **Software Creativity 2.0**
Robert L. Glass, 2006 ([amazon](http://www.amazon.com/Software-Creativity-2-0-Robert-Glass/dp/0977213315))
1. **Object-Oriented Software Construction**
Bertrand Meyer, 1997 ([amazon](http://www.amazon.com/Object-Oriented-Software-Construction-Book-CD-ROM/dp/0136291554))
1. **Refactoring: Improving the Design of Existing Code**
Martin Fowler, Kent Beck, 1999 ([amazon](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672))
1. **Test Driven Development: By Example**
Kent Beck, 2002 ([amazon](http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530))
1. **Algorithms in C++: Fundamentals, Data Structure, Sorting, Searching**
Robert Sedgewick, 1990 ([amazon](http://www.amazon.com/Algorithms-Parts-1-4-Fundamentals-Structure/dp/0201350882))
1. **Effective C++**
Scott Mayers, 1996 ([amazon](http://www.amazon.com/Effective-Specific-Improve-Programs-Designs/dp/0321334876))
1. **Extreme Programming Explained: Embrace Change**
Kent Beck, 1999 ([amazon](http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658))
1. **The Art of Computer Programming**
Donald E. Knuth, 1997 ([amazon](http://www.amazon.com/Computer-Programming-Volumes-1-4A-Boxed/dp/0321751043))
1. **Writing Efficient Programs**
Jon Louis Bentley, 1982 ([amazon](http://www.amazon.com/Writing-Efficient-Programs-Prentice-Hall-Software/dp/013970244X))
1. **The Mythical Man-Month: Essays on Software Engineering**
Frederick Phillips Brooks, 1975 ([amazon](http://www.amazon.com/Mythical-Man-Month-Essays-Software-Engineering/dp/0201006502))
1. **Peopleware: Productive Projects and Teams** 3rd Edition
Tom DeMarco, Tim Lister, 2013 ([amazon](http://www.amazon.com/Peopleware-Productive-Projects-Teams-3rd/dp/0321934113))
1. **Principles Of Software Engineering Management**
Tom Gilb, 1988 ([amazon](http://www.amazon.com/Principles-Software-Engineering-Management-Gilb/dp/0201192462))
## Other
1. **Thinking, Fast and Slow**
Daniel Kahneman, 2013 ([amazon](http://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555))
1. **The Social Animal** 11th Edition
Elliot Aronson, 2011 ([amazon](http://www.amazon.com/Social-Animal-Elliot-Aronson/dp/1429233419))
1. **Influence: Science and Practice** 5th Edition
Robert B. Cialdini, 2008 ([amazon](http://www.amazon.com/Influence-Practice-Robert-B-Cialdini/dp/0205609996))
1. **Getting to Yes: Negotiating Agreement Without Giving In**
Roger Fisher, William L. Ury, Bruce Patton, 2011 ([amazon](http://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757))
1. **How to Win Friends & Influence People**
Dale Carnegie, 1981 ([amazon](http://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034))
# The GitLab Book Club
The Book Club is a casual meet-up to read and discuss books we like.
We'll find a time that suits most, if not all.
See the [book list](booklist.md) for additional recommendations.
## Currently reading : Books about remote work
1. **Remote: Office not required**
David Heinemeier Hansson and Jason Fried, 2013
([amazon](http://www.amazon.co.uk/Remote-Required-David-Heinemeier-Hansson/dp/0091954673))
1. **The Year Without Pants**
Scott Berkun, 2013 ([ScottBerkun.com](http://scottberkun.com/yearwithoutpants/))
Any other books you'd like to suggest? Edit this page and add them to the queue.
......@@ -84,7 +84,7 @@ GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
sudo -u git -H git checkout v0.8.4
sudo -u git -H git checkout v0.8.5
sudo -u git -H make
```
......
......@@ -228,7 +228,7 @@ We'll discuss the three reasons to merge in master: leveraging code, merge confl
If you need to leverage some code that was introduced in master after you created the feature branch you can sometimes solve this by just cherry-picking a commit.
If your feature branch has a merge conflict, creating a merge commit is a normal way of solving this.
You can prevent some merge conflicts by using [gitattributes](http://git-scm.com/docs/gitattributes) for files that can be in a random order.
For example in GitLab our changelog file is specified in .gitattributes as `CHANGELOG merge=union` so that there are fewer merge conflicts in it.
For example in GitLab our changelog file is specified in .gitattributes as `CHANGELOG.md merge=union` so that there are fewer merge conflicts in it.
The last reason for creating merge commits is having long lived branches that you want to keep up to date with the latest state of the project.
Martin Fowler, in [his article about feature branches](http://martinfowler.com/bliki/FeatureBranch.html) talks about this Continuous Integration (CI).
At GitLab we are guilty of confusing CI with branch testing. Quoting Martin Fowler: "I've heard people say they are doing CI because they are running builds, perhaps using a CI server, on every branch with every commit.
......
......@@ -2,6 +2,6 @@
* The official GitLab Community Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ce/).
* The official GitLab Enterprise Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ee/).
* The complete usage guide can be found in [Using GitLab Docker images](http://doc.gitlab.com/omnibus/docker/)
* The complete usage guide can be found in [Using GitLab Docker images](https://docs.gitlab.com/omnibus/docker/)
* The Dockerfile used for building public images is in [Omnibus Repository](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/docker)
* Check the guide for [creating Omnibus-based Docker Image](http://doc.gitlab.com/omnibus/build/README.html#build-docker-image)
* Check the guide for [creating Omnibus-based Docker Image](https://docs.gitlab.com/omnibus/build/README.html#build-docker-image)
......@@ -39,11 +39,6 @@ Feature: Groups
When I visit group "Owned" merge requests page
Then I should not see merge requests from the archived project
Scenario: I should see edit group "Owned" page
When I visit group "Owned" settings page
And I change group "Owned" name to "new-name"
Then I should see new group "Owned" name
Scenario: I edit group "Owned" avatar
When I visit group "Owned" settings page
And I change group "Owned" avatar
......
......@@ -105,7 +105,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
select "Developer", from: "access_level"
end
click_button "Add users to group"
click_button "Add to group"
end
step 'I should see current user as "Developer"' do
......
......@@ -70,7 +70,7 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
select "Developer", from: "access_level"
end
click_button "Add users to project"
click_button "Add to project"
end
step 'I should see current user as "Developer"' do
......
class Spinach::Features::GroupMembers < Spinach::FeatureSteps
include WaitForAjax
include SharedAuthentication
include SharedPaths
include SharedGroup
......@@ -13,7 +14,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
select "Reporter", from: "access_level"
end
click_button "Add users to group"
click_button "Add to group"
end
step 'I select "Mike" as "Master"' do
......@@ -24,7 +25,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
select "Master", from: "access_level"
end
click_button "Add users to group"
click_button "Add to group"
end
step 'I should see "Mike" in team list as "Reporter"' do
......@@ -47,7 +48,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
select "Reporter", from: "access_level"
end
click_button "Add users to group"
click_button "Add to group"
end
step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
......@@ -66,7 +67,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
select "Reporter", from: "access_level"
end
click_button "Add users to group"
click_button "Add to group"
end
step 'I should see user "John Doe" in team list' do
......@@ -108,7 +109,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
step 'I search for \'Mary\' member' do
page.within '.member-search-form' do
fill_in 'search', with: 'Mary'
click_button 'Search'
find('.member-search-btn').click
end
end
......@@ -116,9 +117,8 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
member = mary_jane_member
page.within "#group_member_#{member.id}" do
click_button 'Edit'
select 'Developer', from: "member_access_level_#{member.id}"
click_on 'Save'
wait_for_ajax
end
end
......
......@@ -73,18 +73,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
author: current_user
end
step 'I change group "Owned" name to "new-name"' do
fill_in 'group_name', with: 'new-name'
fill_in 'group_path', with: 'new-name'
click_button "Save group"
end
step 'I should see new group "Owned" name' do
page.within ".navbar-gitlab" do
expect(page).to have_content "new-name"
end
end
step 'I change group "Owned" avatar' do
attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
click_button "Save group"
......
......@@ -54,7 +54,7 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end
step 'I click the "Branches" tab' do
page.within '.content' do
page.within '.sub-nav' do
click_link('Branches')
end
end
......
......@@ -42,15 +42,16 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I fill compare fields with branches' do
fill_in 'from', with: 'feature'
fill_in 'to', with: 'master'
select_using_dropdown('from', 'feature')
select_using_dropdown('to', 'master')
click_button 'Compare'
end
step 'I fill compare fields with refs' do
fill_in "from", with: sample_commit.parent_id
fill_in "to", with: sample_commit.id
select_using_dropdown('from', sample_commit.parent_id, true)
select_using_dropdown('to', sample_commit.id, true)
click_button "Compare"
end
......@@ -97,8 +98,8 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I fill compare fields with branches' do
fill_in 'from', with: 'master'
fill_in 'to', with: 'feature'
select_using_dropdown('from', 'master')
select_using_dropdown('to', 'feature')
click_button 'Compare'
end
......@@ -182,4 +183,15 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "More submodules"
expect(page).not_to have_content "Change some files"
end
def select_using_dropdown(dropdown_type, selection, is_commit = false)
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
dropdown.fill_in("Filter by Git revision", with: selection)
if is_commit
dropdown.find('input[type="search"]').send_keys(:return)
else
find_link(selection, visible: true).click
end
end
end
......@@ -19,8 +19,8 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
end
step 'page should have languages graphs' do
expect(page).to have_content "Ruby 66.63 %"
expect(page).to have_content "JavaScript 22.96 %"
expect(page).to have_content /Ruby 66.* %/
expect(page).to have_content /JavaScript 22.* %/
end
step 'page should have commits graphs' do
......
......@@ -7,6 +7,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
include SharedMarkdown
include SharedDiffNote
include SharedUser
include WaitForAjax
step 'I click link "New Merge Request"' do
click_link "New Merge Request"
......@@ -90,6 +91,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click button "Unsubscribe"' do
click_on "Unsubscribe"
wait_for_ajax
end
step 'I click link "Close"' do
......@@ -114,7 +116,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
source_project: project,
target_project: project,
source_branch: 'fix',
target_branch: 'master',
target_branch: 'merge-test',
author: project.users.first,
description: "# Description header"
)
......@@ -137,7 +139,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
title: "Bug NS-05",
source_project: project,
target_project: project,
author: project.users.first)
author: project.users.first,
source_branch: 'merge-test')
end
step 'project "Shop" have "Feature NS-05" merged merge request' do
......@@ -508,7 +511,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I should see new target branch changes' do
expect(page).to have_content 'Request to merge fix into feature'
expect(page).to have_content 'Target branch changed from master to feature'
expect(page).to have_content 'Target branch changed from merge-test to feature'
end
step 'I click on "Email Patches"' do
......
......@@ -22,7 +22,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
end
click_button "Add users to project"
click_button "Add to project"
end
step 'I should see "Mike" in team list as "Reporter"' do
......@@ -36,10 +36,10 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
step 'I select "sjobs@apple.com" as "Reporter"' do
page.within ".users-project-form" do
select2("sjobs@apple.com", from: "#user_ids", multiple: true)
find('#user_ids', visible: false).set('sjobs@apple.com')
select "Reporter", from: "access_level"
end
click_button "Add users to project"
click_button "Add to project"
end
step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
......@@ -65,9 +65,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
user = User.find_by(name: 'Dmitriy')
project_member = project.project_members.find_by(user_id: user.id)
page.within "#project_member_#{project_member.id}" do
click_button 'Edit'
select "Reporter", from: "member_access_level_#{project_member.id}"
click_button "Save"
end
end
......@@ -112,7 +110,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end
step 'I click link "Import team from another project"' do
click_link "Import members from another project"
click_link "Import"
end
When 'I submit "Website" project for import team' do
......@@ -144,8 +142,9 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
end
step 'I should see "Opensource" group user listing' do
expect(page).to have_content("Shared with OpenSource group, members with Master role (2)")
expect(page).to have_content(@os_user1.name)
expect(page).to have_content(@os_user2.name)
page.within '.project-members-groups' do
expect(page).to have_content('OpenSource')
expect(find('select').value).to eq('40')
end
end
end
require Rails.root.join('features/support/wait_for_ajax')
module SharedNote
include Spinach::DSL
include WaitForAjax
step 'I delete a comment' do
page.within('.main-notes-list') do
......@@ -116,8 +119,9 @@ module SharedNote
page.within(".js-main-target-form") do
fill_in "note[note]", with: "# Comment with a header"
click_button "Comment"
sleep 0.05
end
wait_for_ajax
end
step 'The comment with the header should not have an ID' do
......
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
DatabaseCleaner[:active_record].strategy = :truncation
Spinach.hooks.before_scenario do
DatabaseCleaner.start
......
# The spinach-rerun-reporter doesn't define the on_undefined_step
# See it here: https://github.com/javierav/spinach-rerun-reporter/blob/master/lib/spinach/reporter/rerun.rb
require 'spinach-rerun-reporter'
module Spinach
class Reporter
class Rerun
......
module Banzai
module Renderer
extend self
module_function
# Convert a Markdown String into an HTML-safe String of HTML
#
......@@ -141,8 +141,6 @@ module Banzai
end.html_safe
end
private
def cacheless_render(text, context = {})
Gitlab::Metrics.measure(:banzai_cacheless_render) do
result = render_result(text, context)
......
......@@ -113,18 +113,19 @@ module ExtractsPath
@id = get_id
@ref, @path = extract_ref(@id)
@repo = @project.repository
if @options[:extended_sha1].blank?
@commit = @repo.commit(@ref)
else
if @options[:extended_sha1].present?
@commit = @repo.commit(@options[:extended_sha1])
end
else
@commit = @repo.commit(@ref)
if @path.empty? && !@commit
if @path.empty? && !@commit && @id.ends_with?('.atom')
@id = @ref = extract_ref_without_atom(@id)
@commit = @repo.commit(@ref)
request.format = :atom if @commit
end
end
raise InvalidPathError unless @commit
......
......@@ -10,11 +10,11 @@ then
exit 1
fi
# Ensure that the CHANGELOG does not contain duplicate versions
DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^v [0-9.]+' CHANGELOG | sed 's| (unreleased)||' | sort | uniq -d)
# 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)
if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ]
then
echo '✖ ERROR: Duplicate versions in CHANGELOG:' >&2
echo '✖ ERROR: Duplicate versions in CHANGELOG.md:' >&2
echo "${DUPLICATE_CHANGELOG_VERSIONS}" >&2
exit 1
fi
......
......@@ -44,7 +44,7 @@ describe "Compare", js: true do
def select_using_dropdown(dropdown_type, selection)
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
dropdown.fill_in("Filter by branch/tag", with: selection)
click_link selection
dropdown.fill_in("Filter by Git revision", with: selection)
find_link(selection, visible: true).click
end
end
......@@ -41,7 +41,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
def expect_visible_access_request(group, user)
expect(group.requesters.exists?(user_id: user)).to be_truthy
expect(page).to have_content "#{group.name} access requests 1"
expect(page).to have_content "Users requesting access to #{group.name} 1"
expect(page).to have_content user.name
end
end
......@@ -11,11 +11,12 @@ feature 'Group', feature: true do
end
end
describe 'creating a group with space in group path' do
describe 'create a group' do
before { visit new_group_path }
describe 'with space in group path' do
it 'renders new group form with validation errors' do
visit new_group_path
fill_in 'Group path', with: 'space group'
click_button 'Create group'
expect(current_path).to eq(groups_path)
......@@ -23,11 +24,9 @@ feature 'Group', feature: true do
end
end
describe 'creating a group with .atom at end of group path' do
describe 'with .atom at end of group path' do
it 'renders new group form with validation errors' do
visit new_group_path
fill_in 'Group path', with: 'atom_group.atom'
click_button 'Create group'
expect(current_path).to eq(groups_path)
......@@ -35,43 +34,76 @@ feature 'Group', feature: true do
end
end
describe 'creating a group with .git at end of group path' do
describe 'with .git at end of group path' do
it 'renders new group form with validation errors' do
visit new_group_path
fill_in 'Group path', with: 'git_group.git'
click_button 'Create group'
expect(current_path).to eq(groups_path)
expect(page).to have_namespace_error_message
end
end
end
describe 'description' do
describe 'group edit' do
let(:group) { create(:group) }
let(:path) { edit_group_path(group) }
let(:new_name) { 'new-name' }
before { visit path }
it 'saves new settings' do
fill_in 'group_name', with: new_name
click_button 'Save group'
expect(page).to have_content 'successfully updated'
expect(find('#group_name').value).to eq(new_name)
page.within ".navbar-gitlab" do
expect(page).to have_content new_name
end
end
it 'removes group' do
click_link 'Remove Group'
expect(page).to have_content "scheduled for deletion"
end
end
describe 'group page with markdown description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group')
visit path
expect(page).to have_css('.description > p > strong')
end
it 'passes through html-pipeline' do
group.update_attribute(:description, 'This group is the :poop:')
visit path
expect(page).to have_css('.description > p > img')
end
it 'sanitizes unwanted tags' do
group.update_attribute(:description, '# Group Description')
visit path
expect(page).not_to have_css('.description h1')
end
it 'permits `rel` attribute on links' do
group.update_attribute(:description, 'https://google.com/')
visit path
expect(page).to have_css('.description a[rel]')
end
end
......
......@@ -29,7 +29,7 @@ feature 'Login', feature: true do
describe 'with two-factor authentication' do
def enter_code(code)
fill_in 'Two-Factor Authentication code', with: code
fill_in 'user_otp_attempt', with: code
click_button 'Verify code'
end
......
......@@ -45,7 +45,7 @@ feature 'Merge request created from fork' do
page.within('.merge-request-tabs') { click_link 'Builds' }
wait_for_ajax
page.within('table.builds') do
page.within('table.ci-table') do
expect(page).to have_content 'rspec'
expect(page).to have_content 'spinach'
end
......
......@@ -15,6 +15,7 @@ feature 'issuable templates', feature: true, js: true do
let(:template_content) { 'this is a test "bug" template' }
let(:longtemplate_content) { %Q(this\n\n\n\n\nis\n\n\n\n\na\n\n\n\n\nbug\n\n\n\n\ntemplate) }
let(:issue) { create(:issue, author: user, assignee: user, project: project) }
let(:description_addition) { ' appending to description' }
background do
project.repository.commit_file(user, '.gitlab/issue_templates/bug.md', template_content, 'added issue template', 'master', false)
......@@ -26,7 +27,26 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "bug" template' do
select_template 'bug'
wait_for_ajax
preview_template(template_content)
preview_template
save_changes
end
scenario 'user selects "bug" template and then "no template"' do
select_template 'bug'
wait_for_ajax
select_option 'No template'
wait_for_ajax
preview_template('')
save_changes('')
end
scenario 'user selects "bug" template, edits description and then selects "reset template"' do
select_template 'bug'
wait_for_ajax
find_field('issue_description').send_keys(description_addition)
preview_template(template_content + description_addition)
select_option 'Reset template'
preview_template
save_changes
end
......@@ -75,7 +95,7 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects "feature-proposal" template' do
select_template 'feature-proposal'
wait_for_ajax
preview_template(template_content)
preview_template
save_changes
end
end
......@@ -102,25 +122,31 @@ feature 'issuable templates', feature: true, js: true do
scenario 'user selects template' do
select_template 'feature-proposal'
wait_for_ajax
preview_template(template_content)
preview_template
save_changes
end
end
end
end
def preview_template(expected_content)
def preview_template(expected_content = template_content)
click_link 'Preview'
expect(page).to have_content expected_content
click_link 'Write'
end
def save_changes
def save_changes(expected_content = template_content)
click_button "Save changes"
expect(page).to have_content template_content
expect(page).to have_content expected_content
end
def select_template(name)
first('.js-issuable-selector').click
first('.js-issuable-selector-wrap .dropdown-content a', text: name).click
end
def select_option(name)
first('.js-issuable-selector').click
first('.js-issuable-selector-wrap .dropdown-footer-list a', text: name).click
end
end
require 'spec_helper'
feature 'Projects > Members > Anonymous user sees members', feature: true, js: true do
include WaitForAjax
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:empty_project, :public) }
background do
project.team << [user, :master]
@group_link = create(:project_group_link, project: project, group: group)
login_as(user)
visit namespace_project_project_members_path(project.namespace, project)
end
it 'updates group access level' do
select 'Guest', from: "member_access_level_#{group.id}"
wait_for_ajax
visit namespace_project_project_members_path(project.namespace, project)
expect(page).to have_select("member_access_level_#{group.id}", selected: 'Guest')
end
it 'updates expiry date' do
tomorrow = Date.today + 3
fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F")
wait_for_ajax
page.within(find('li.group_member')) do
expect(page).to have_content('Expires in')
end
end
it 'deletes group link' do
page.within(first('.group_member')) do
find('.btn-remove').click
end
wait_for_ajax
expect(page).not_to have_selector('.group_member')
end
context 'search' do
it 'finds no results' do
page.within '.member-search-form' do
fill_in 'search', with: 'testing 123'
find('.member-search-btn').click
end
expect(page).not_to have_selector('.group_member')
end
it 'finds results' do
page.within '.member-search-form' do
fill_in 'search', with: group.name
find('.member-search-btn').click
end
expect(page).to have_selector('.group_member', count: 1)
end
end
end
require 'spec_helper'
feature 'Projects > Members > Master adds member with expiration date', feature: true, js: true do
include WaitForAjax
include Select2Helper
include ActiveSupport::Testing::TimeHelpers
......@@ -20,7 +21,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
page.within '.users-project-form' do
select2(new_member.id, from: '#user_ids', multiple: true)
fill_in 'expires_at', with: '2016-08-10'
click_on 'Add users to project'
click_on 'Add to project'
end
page.within '.project_member:first-child' do
......@@ -35,9 +36,8 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
visit namespace_project_project_members_path(project.namespace, project)
page.within '.project_member:first-child' do
click_on 'Edit'
fill_in 'Access expiration date', with: '2016-08-09'
click_on 'Save'
find('.js-access-expiration-date').set '2016-08-09'
wait_for_ajax
expect(page).to have_content('Expires in 3 days')
end
end
......
......@@ -41,7 +41,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
def expect_visible_access_request(project, user)
expect(project.requesters.exists?(user_id: user)).to be_truthy
expect(page).to have_content "#{project.name} access requests 1"
expect(page).to have_content "Users requesting access to #{project.name} 1"
expect(page).to have_content user.name
end
end
......@@ -14,7 +14,7 @@ feature 'Signup', feature: true do
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: user.password
click_button "Sign up"
click_button "Register"
expect(current_path).to eq users_almost_there_path
expect(page).to have_content("Please check your email to confirm your account")
......@@ -33,7 +33,7 @@ feature 'Signup', feature: true do
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: user.password
click_button "Sign up"
click_button "Register"
expect(current_path).to eq dashboard_projects_path
expect(page).to have_content("Welcome! You have signed up successfully.")
......@@ -52,7 +52,7 @@ feature 'Signup', feature: true do
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: existing_user.email
fill_in 'new_user_password', with: user.password
click_button "Sign up"
click_button "Register"
expect(current_path).to eq user_registration_path
expect(page).to have_content("error prohibited this user from being saved")
......@@ -69,7 +69,7 @@ feature 'Signup', feature: true do
fill_in 'new_user_username', with: user.username
fill_in 'new_user_email', with: existing_user.email
fill_in 'new_user_password', with: user.password
click_button "Sign up"
click_button "Register"
expect(current_path).to eq user_registration_path
expect(page.body).not_to match(/#{user.password}/)
......
......@@ -160,7 +160,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user)
@u2f_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......@@ -174,7 +174,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user)
@u2f_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......@@ -186,7 +186,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user, remember: true)
@u2f_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
within 'div#js-authenticate-u2f' do
......@@ -209,7 +209,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Try authenticating user with the old U2F device
login_as(current_user)
@u2f_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......@@ -230,7 +230,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Try authenticating user with the same U2F device
login_as(current_user)
@u2f_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......@@ -244,7 +244,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
unregistered_device = FakeU2fDevice.new(page, FFaker::Name.first_name)
login_as(user)
unregistered_device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......@@ -271,7 +271,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
[first_device, second_device].each do |device|
login_as(user)
device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
click_on "Sign in via U2F device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
......
require 'spec_helper'
feature 'Users', feature: true do
feature 'Users', feature: true, js: true do
let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') }
scenario 'GET /users/sign_in creates a new user account' do
visit new_user_session_path
click_link 'Register'
fill_in 'new_user_name', with: 'Name Surname'
fill_in 'new_user_username', with: 'Great'
fill_in 'new_user_email', with: 'name@mail.com'
fill_in 'new_user_password', with: 'password1234'
expect { click_button 'Sign up' }.to change { User.count }.by(1)
expect { click_button 'Register' }.to change { User.count }.by(1)
end
scenario 'Successful user signin invalidates password reset token' do
......@@ -31,11 +32,12 @@ feature 'Users', feature: true do
scenario 'Should show one error if email is already taken' do
visit new_user_session_path
click_link 'Register'
fill_in 'new_user_name', with: 'Another user name'
fill_in 'new_user_username', with: 'anotheruser'
fill_in 'new_user_email', with: user.email
fill_in 'new_user_password', with: '12341234'
expect { click_button 'Sign up' }.to change { User.count }.by(0)
expect { click_button 'Register' }.to change { User.count }.by(0)
expect(page).to have_text('Email has already been taken')
expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}'
end
......@@ -51,6 +53,28 @@ feature 'Users', feature: true do
end
end
feature 'username validation' do
include WaitForAjax
let(:loading_icon) { '.fa.fa-spinner' }
let(:username_input) { 'new_user_username' }
before(:each) do
visit new_user_session_path
click_link 'Register'
end
scenario 'shows an error border if the username already exists' do
fill_in username_input, with: user.username
wait_for_ajax
expect(find('.username')).to have_css '.gl-field-error-outline'
end
scenario 'doesn\'t show an error border if the username is available' do
fill_in username_input, with: 'new-user'
wait_for_ajax
expect(find('#new_user_username')).not_to have_css '.gl-field-error-outline'
end
end
def errors_on_page(page)
page.find('#error_explanation').find('ul').all('li').map{ |item| item.text }.join("\n")
end
......
%form.show-gl-field-errors{action: 'submit', method: 'post'}
.form-group
%input.required-text{required: true, type: 'text'} Text
.form-group
%input.email{type: 'email', title: 'Please provide a valid email address.', required: true } Email
.form-group
%input.password{type: 'password', required: true} Password
.form-group
%input.alphanumeric{type: 'text', pattern: '[a-zA-Z0-9]', required: true} Alphanumeric
.form-group
%input.hidden{ type:'hidden' }
.form-group
%input.custom.no-gl-field-errors{ type:'text' } Custom, do not validate
.form-group
%input.submit{type: 'submit'} Submit
//= require jquery
//= require gl_field_errors
((global) => {
fixture.preload('gl_field_errors.html');
describe('GL Style Field Errors', function() {
beforeEach(function() {
fixture.load('gl_field_errors.html');
const $form = this.$form = $('form.show-gl-field-errors');
this.fieldErrors = new global.GlFieldErrors($form);
});
it('should properly initialize the form', function() {
expect(this.$form).toBeDefined();
expect(this.$form.length).toBe(1);
expect(this.fieldErrors).toBeDefined();
const inputs = this.fieldErrors.state.inputs;
expect(inputs.length).toBe(5);
});
it('should ignore elements with custom error handling', function() {
const customErrorFlag = 'no-gl-field-errors';
const customErrorElem = $(`.${customErrorFlag}`);
expect(customErrorElem.length).toBe(1);
const customErrors = this.fieldErrors.state.inputs.filter((input) => {
return input.inputElement.hasClass(customErrorFlag);
});
expect(customErrors.length).toBe(0);
});
it('should not show any errors before submit attempt', function() {
this.$form.find('.email').val('not-a-valid-email').keyup();
this.$form.find('.text-required').val('').keyup();
this.$form.find('.alphanumberic').val('?---*').keyup();
const errorsShown = this.$form.find('.gl-field-error-outline');
expect(errorsShown.length).toBe(0);
});
it('should show errors when input valid is submitted', function() {
this.$form.find('.email').val('not-a-valid-email').keyup();
this.$form.find('.text-required').val('').keyup();
this.$form.find('.alphanumberic').val('?---*').keyup();
this.$form.submit();
const errorsShown = this.$form.find('.gl-field-error-outline');
expect(errorsShown.length).toBe(4);
});
it('should properly track validity state on input after invalid submission attempt', function() {
this.$form.submit();
const emailInputModel = this.fieldErrors.state.inputs[1];
const fieldState = emailInputModel.state;
const emailInputElement = emailInputModel.inputElement;
// No input
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(true);
expect(fieldState.valid).toBe(false);
// Then invalid input
emailInputElement.val('not-a-valid-email').keyup();
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(false);
// Then valid input
emailInputElement.val('email@gitlab.com').keyup();
expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(true);
// Then invalid input
emailInputElement.val('not-a-valid-email').keyup();
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(false);
// Then empty input
emailInputElement.val('').keyup();
expect(emailInputElement).toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(true);
expect(fieldState.valid).toBe(false);
// Then valid input
emailInputElement.val('email@gitlab.com').keyup();
expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
expect(fieldState.empty).toBe(false);
expect(fieldState.valid).toBe(true);
});
it('should properly infer error messages', function() {
this.$form.submit();
const trackedInputs = this.fieldErrors.state.inputs;
const inputHasTitle = trackedInputs[1];
const hasTitleErrorElem = inputHasTitle.inputElement.siblings('.gl-field-error');
const inputNoTitle = trackedInputs[2];
const noTitleErrorElem = inputNoTitle.inputElement.siblings('.gl-field-error');
expect(noTitleErrorElem.text()).toBe('This field is required.');
expect(hasTitleErrorElem.text()).toBe('Please provide a valid email address.');
});
});
})(window.gl || (window.gl = {}));
/*= require merge_request_tabs */
//= require breakpoints
(function() {
describe('MergeRequestTabs', function() {
......
......@@ -21,7 +21,7 @@
setupButton = this.container.find("#js-login-u2f-device");
setupMessage = this.container.find("p");
expect(setupMessage.text()).toContain('Insert your security key');
expect(setupButton.text()).toBe('Login Via U2F Device');
expect(setupButton.text()).toBe('Sign in via U2F device');
setupButton.trigger('click');
inProgressMessage = this.container.find("p");
expect(inProgressMessage.text()).toContain("Trying to communicate with your device");
......
......@@ -412,9 +412,10 @@ describe 'Git HTTP requests', lib: true do
context "when the params are anything else" do
let(:params) { { service: 'git-implode-pack' } }
before { get path, params }
it "fails to find a route" do
expect { get(path, params) }.to raise_error(ActionController::RoutingError)
it "redirects to the sign-in page" do
expect(response).to redirect_to(new_user_session_path)
end
end
end
......
......@@ -270,6 +270,12 @@ describe "Groups", "routing" do
expect(get('/1')).to route_to('groups#show', id: '1')
end
it "also display group#show with dot in the path" do
allow(Group).to receive(:find_by_path).and_return(true)
expect(get('/group.with.dot')).to route_to('groups#show', id: 'group.with.dot')
end
end
describe HealthCheckController, 'routing' do
......
......@@ -84,6 +84,17 @@ describe CreateDeploymentService, services: true do
expect(subject).to be_persisted
end
end
context 'when project was removed' do
let(:project) { nil }
it 'does not create deployment or environment' do
expect { subject }.not_to raise_error
expect(Environment.count).to be_zero
expect(Deployment.count).to be_zero
end
end
end
describe 'processing of builds' do
......
......@@ -17,6 +17,7 @@ module TestEnv
'markdown' => '0ed8c6c',
'lfs' => 'be93687',
'master' => 'b83d6e3',
'merge-test' => '5937ac0',
"'test'" => 'e56497b',
'orphaned-branch' => '45127a9',
'binary-encoding' => '7b1cf43',
......
require 'spec_helper'
describe BuildCoverageWorker do
describe '#perform' do
context 'when build exists' do
let!(:build) { create(:ci_build) }
it 'updates code coverage' do
expect_any_instance_of(Ci::Build)
.to receive(:update_coverage)
described_class.new.perform(build.id)
end
end
context 'when build does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
end
end
end
end
require 'spec_helper'
describe BuildFinishedWorker do
describe '#perform' do
context 'when build exists' do
let(:build) { create(:ci_build) }
it 'calculates coverage and calls hooks' do
expect(BuildCoverageWorker)
.to receive(:new).ordered.and_call_original
expect(BuildHooksWorker)
.to receive(:new).ordered.and_call_original
expect_any_instance_of(BuildCoverageWorker)
.to receive(:perform)
expect_any_instance_of(BuildHooksWorker)
.to receive(:perform)
described_class.new.perform(build.id)
end
end
context 'when build does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
end
end
end
end
require 'spec_helper'
describe BuildHooksWorker do
describe '#perform' do
context 'when build exists' do
let!(:build) { create(:ci_build) }
it 'calls build hooks' do
expect_any_instance_of(Ci::Build)
.to receive(:execute_hooks)
described_class.new.perform(build.id)
end
end
context 'when build does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
end
end
end
end
require 'spec_helper'
describe BuildSuccessWorker do
describe '#perform' do
context 'when build exists' do
context 'when build belogs to the environment' do
let!(:build) { create(:ci_build, environment: 'production') }
it 'executes deployment service' do
expect_any_instance_of(CreateDeploymentService)
.to receive(:execute)
described_class.new.perform(build.id)
end
end
context 'when build is not associated with project' do
let!(:build) { create(:ci_build, project: nil) }
it 'does not create deployment' do
expect_any_instance_of(CreateDeploymentService)
.not_to receive(:execute)
described_class.new.perform(build.id)
end
end
end
context 'when build does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
end
end
end
end
require 'spec_helper'
describe PipelineHooksWorker do
describe '#perform' do
context 'when pipeline exists' do
let(:pipeline) { create(:ci_pipeline) }
it 'executes hooks for the pipeline' do
expect_any_instance_of(Ci::Pipeline)
.to receive(:execute_hooks)
described_class.new.perform(pipeline.id)
end
end
context 'when pipeline does not exist' do
it 'does not raise exception' do
expect { described_class.new.perform(123) }
.not_to raise_error
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment