diff options
104 files changed, 1438 insertions, 488 deletions
diff --git a/.gitignore b/.gitignore index 91ea81bfc4e..1eb785451f4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ config/gitlab.yml config/gitlab_ci.yml config/initializers/rack_attack.rb config/initializers/smtp_settings.rb +config/initializers/relative_url.rb config/resque.yml config/unicorn.rb config/secrets.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e98b7a68ee..c477721f9da 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -89,6 +89,7 @@ spec:other: spinach:project:half: stage: test script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half tags: - ruby @@ -97,6 +98,7 @@ spinach:project:half: spinach:project:rest: stage: test script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest tags: - ruby @@ -105,6 +107,7 @@ spinach:project:rest: spinach:other: stage: test script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other tags: - ruby @@ -275,6 +278,7 @@ spinach:project:half:ruby22: only: - master script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half cache: key: "ruby22" @@ -290,6 +294,7 @@ spinach:project:rest:ruby22: only: - master script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest cache: key: "ruby22" @@ -305,6 +310,7 @@ spinach:other:ruby22: only: - master script: + - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other cache: key: "ruby22" @@ -318,7 +324,7 @@ spinach:other:ruby22: notify:slack: stage: notifications script: - - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Check <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>" + - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>" when: on_failure only: - master@gitlab-org/gitlab-ce diff --git a/CHANGELOG b/CHANGELOG index f526d60c3d8..8de86f53129 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,15 +1,22 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.6.0 (unreleased) + - Contributions to forked projects are included in calendar - Improve the formatting for the user page bio (Connor Shea) + - Fix issue when pushing to projects ending in .wiki - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Don't load all of GitLab in mail_room - Strip leading and trailing spaces in URL validator (evuez) + - Return empty array instead of 404 when commit has no statuses in commit status API - Update documentation to reflect Guest role not being enforced on internal projects + - Allow search for logged out users v 8.5.2 - Fix sidebar overlapping content when screen width was below 1200px - Fix error 500 when commenting on a commit + - Fix broken icons on installations with relative URL (Artem Sidorenko) + - Fix import from gitlab.com (KazSawada) + - Fix help keyboard shortcut on relative URL setups (Artem Sidorenko) v 8.5.1 - Fix group projects styles @@ -29,6 +36,7 @@ v 8.5.1 - Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu) - Update sentry-raven gem to 0.15.6 - Add build coverage in project's builds page (Steffen Köhler) + - Changed # to ! for merge requests in activity view v 8.5.0 - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b13f36af214..714701e7345 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,24 +3,27 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Contribute to GitLab](#contribute-to-gitlab) - - [Contributor license agreement](#contributor-license-agreement) - - [Security vulnerability disclosure](#security-vulnerability-disclosure) - - [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests) - - [Helping others](#helping-others) - - [I want to contribute!](#i-want-to-contribute) - - [Issue tracker](#issue-tracker) - - [Feature proposals](#feature-proposals) - - [Issue tracker guidelines](#issue-tracker-guidelines) - - [Issue weight](#issue-weight) - - [Regression issues](#regression-issues) - - [Merge requests](#merge-requests) - - [Merge request guidelines](#merge-request-guidelines) - - [Merge request description format](#merge-request-description-format) - - [Contribution acceptance criteria](#contribution-acceptance-criteria) - - [Changes for Stable Releases](#changes-for-stable-releases) - - [Definition of done](#definition-of-done) - - [Style guides](#style-guides) - - [Code of conduct](#code-of-conduct) + - [Contributor license agreement](#contributor-license-agreement) + - [Security vulnerability disclosure](#security-vulnerability-disclosure) + - [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests) + - [Helping others](#helping-others) + - [I want to contribute!](#i-want-to-contribute) + - [Implement design & UI elements](#implement-design-ui-elements) + - [Design reference](#design-reference) + - [UI development kit](#ui-development-kit) + - [Issue tracker](#issue-tracker) + - [Feature proposals](#feature-proposals) + - [Issue tracker guidelines](#issue-tracker-guidelines) + - [Issue weight](#issue-weight) + - [Regression issues](#regression-issues) + - [Merge requests](#merge-requests) + - [Merge request guidelines](#merge-request-guidelines) + - [Merge request description format](#merge-request-description-format) + - [Contribution acceptance criteria](#contribution-acceptance-criteria) + - [Changes for Stable Releases](#changes-for-stable-releases) + - [Definition of done](#definition-of-done) + - [Style guides](#style-guides) + - [Code of conduct](#code-of-conduct) <!-- END doctoc generated TOC please keep comment here to allow auto update --> @@ -83,6 +86,22 @@ GitLab. This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. +## Implement design & UI elements + +### Design reference + +The GitLab design reference can be found in the [gitlab-design] project. +The designs are made using Antetype (`.atype` files). You can use the +[free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design +(the PNG is 1:1). + +The current designs can be found in the [`gitlab1.atype` file]. + +### UI development kit + +Implemented UI elements can also be found at https://gitlab.com/help/ui. Please +note that this page isn't comprehensive at this time. + ## Issue tracker To get support for your particular problem please use the @@ -299,13 +318,14 @@ to us than having a minimal commit log. The smaller an MR is the more likely it is it will be merged (quickly). After that you can send more MRs to enhance it. For examples of feedback on merge requests please look at already -[closed merge requests][closed-merge-requests]. If you would like quick feedback on your merge -request feel free to mention one of the Merge Marshalls of the [core team][core-team]. +[closed merge requests][closed-merge-requests]. If you would like quick feedback +on your merge request feel free to mention one of the Merge Marshalls in the +[core team][core-team] or one of the +[Merge request coaches](https://about.gitlab.com/team/). Please ensure that your merge request meets the contribution acceptance criteria. When having your code reviewed and when reviewing merge requests please take the -[thoughtbot code review guidelines](https://github.com/thoughtbot/guides/tree/master/code-review) -into account. +[Thoughtbot code review guide] into account. ### Merge request description format @@ -473,3 +493,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout [rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" +[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design +[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 +[`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/ +[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review @@ -1,14 +1,14 @@ source "https://rubygems.org" -gem 'rails', '4.2.5.1' +gem 'rails', '4.2.5.2' gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with gem 'responders', '~> 2.0' -# Specify a sprockets version due to security issue -# See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY -gem 'sprockets', '~> 2.12.3' +# Specify a sprockets version due to increased performance +# See https://gitlab.com/gitlab-org/gitlab-ce/issues/6069 +gem 'sprockets', '~> 3.3.5' # Default values for AR models gem "default_value_for", "~> 3.0.0" @@ -213,7 +213,6 @@ gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-rails', '~> 4.0.0' gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-ui-rails', '~> 5.0.0' -gem 'nprogress-rails', '~> 0.1.6.7' gem 'raphael-rails', '~> 2.1.2' gem 'request_store', '~> 1.2.0' gem 'select2-rails', '~> 3.5.9' diff --git a/Gemfile.lock b/Gemfile.lock index d0f780e9519..e048e2f5a56 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,41 +4,41 @@ GEM CFPropertyList (2.3.2) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.2.5.1) - actionpack (= 4.2.5.1) - actionview (= 4.2.5.1) - activejob (= 4.2.5.1) + actionmailer (4.2.5.2) + actionpack (= 4.2.5.2) + actionview (= 4.2.5.2) + activejob (= 4.2.5.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.5.1) - actionview (= 4.2.5.1) - activesupport (= 4.2.5.1) + actionpack (4.2.5.2) + actionview (= 4.2.5.2) + activesupport (= 4.2.5.2) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.5.1) - activesupport (= 4.2.5.1) + actionview (4.2.5.2) + activesupport (= 4.2.5.2) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (4.2.5.1) - activesupport (= 4.2.5.1) + activejob (4.2.5.2) + activesupport (= 4.2.5.2) globalid (>= 0.3.0) - activemodel (4.2.5.1) - activesupport (= 4.2.5.1) + activemodel (4.2.5.2) + activesupport (= 4.2.5.2) builder (~> 3.1) - activerecord (4.2.5.1) - activemodel (= 4.2.5.1) - activesupport (= 4.2.5.1) + activerecord (4.2.5.2) + activemodel (= 4.2.5.2) + activesupport (= 4.2.5.2) arel (~> 6.0) activerecord-deprecated_finders (1.0.4) activerecord-session_store (0.1.2) actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) - activesupport (4.2.5.1) + activesupport (4.2.5.2) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -407,7 +407,6 @@ GEM railties (>= 4.0.1) hashie (3.4.3) highline (1.7.8) - hike (1.2.3) hipchat (1.5.2) httparty mimemagic @@ -483,7 +482,6 @@ GEM newrelic_rpm (3.14.1.311) nokogiri (1.6.7.2) mini_portile2 (~> 2.0.0.rc2) - nprogress-rails (0.1.6.7) oauth (0.4.7) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) @@ -587,16 +585,16 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.5.1) - actionmailer (= 4.2.5.1) - actionpack (= 4.2.5.1) - actionview (= 4.2.5.1) - activejob (= 4.2.5.1) - activemodel (= 4.2.5.1) - activerecord (= 4.2.5.1) - activesupport (= 4.2.5.1) + rails (4.2.5.2) + actionmailer (= 4.2.5.2) + actionpack (= 4.2.5.2) + actionview (= 4.2.5.2) + activejob (= 4.2.5.2) + activemodel (= 4.2.5.2) + activerecord (= 4.2.5.2) + activesupport (= 4.2.5.2) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.5.1) + railties (= 4.2.5.2) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -606,9 +604,9 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (4.2.5.1) - actionpack (= 4.2.5.1) - activesupport (= 4.2.5.1) + railties (4.2.5.2) + actionpack (= 4.2.5.2) + activesupport (= 4.2.5.2) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) @@ -772,11 +770,8 @@ GEM spring (>= 0.9.1) spring-commands-teaspoon (0.0.2) spring (>= 0.9.1) - sprockets (2.12.4) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) + sprockets (3.3.5) + rack (> 1, < 3) sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) @@ -808,7 +803,7 @@ GEM rack (~> 1.0) thor (0.19.1) thread_safe (0.3.5) - tilt (1.4.1) + tilt (2.0.2) timfel-krb5-auth (0.8.3) tinder (1.10.1) eventmachine (~> 1.0) @@ -964,7 +959,6 @@ DEPENDENCIES net-ssh (~> 3.0.1) newrelic_rpm (~> 3.14) nokogiri (~> 1.6.7, >= 1.6.7.2) - nprogress-rails (~> 0.1.6.7) oauth2 (~> 1.0.0) octokit (~> 3.8.0) omniauth (~> 1.3.1) @@ -989,7 +983,7 @@ DEPENDENCIES rack-attack (~> 4.3.1) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) - rails (= 4.2.5.1) + rails (= 4.2.5.2) rails-deprecated_sanitizer (~> 1.0.3) raphael-rails (~> 2.1.2) rblineprof @@ -1026,7 +1020,7 @@ DEPENDENCIES spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.0.0) spring-commands-teaspoon (~> 0.0.2) - sprockets (~> 2.12.3) + sprockets (~> 3.3.5) state_machines-activerecord (~> 0.3.0) task_list (~> 1.0.2) teaspoon (~> 1.0.0) diff --git a/PROCESS.md b/PROCESS.md index 5f4d67bc10e..cad45d23df9 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -2,23 +2,39 @@ ## Purpose of describing the contributing process -Below we describe the contributing process to GitLab for two reasons. So that contributors know what to expect from maintainers (possible responses, friendly treatment, etc.). And so that maintainers know what to expect from contributors (use the latest version, ensure that the issue is addressed, friendly treatment, etc.). +Below we describe the contributing process to GitLab for two reasons. So that +contributors know what to expect from maintainers (possible responses, friendly +treatment, etc.). And so that maintainers know what to expect from contributors +(use the latest version, ensure that the issue is addressed, friendly treatment, +etc.). ## Common actions ### Issue team -- Looks for issues without [workflow labels](#how-we-handle-issues) and triages issue -- Closes invalid issues with a comment (duplicates, [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) -- Asks for feedback from issue reporter ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) -- Monitors all issues for feedback (but especially ones commented on since automatically watching them) + +- Looks for issues without [workflow labels](#how-we-handle-issues) and triages + issue +- Closes invalid issues with a comment (duplicates, + [fixed in newer version](#issue-fixed-in-newer-version), + [issue report for old version](#issue-report-for-old-version), not a problem + in GitLab, etc.) +- Asks for feedback from issue reporter + ([invalid issue reports](#improperly-formatted-issue), + [format code](#code-format), etc.) +- Monitors all issues for feedback (but especially ones commented on since + automatically watching them) - Closes issues with no feedback from the reporter for two weeks -### Merge marshal +### Merge marshall & merge request coach -- Responds to merge requests the issue team mentions them in and monitors for new merge requests -- Provides feedback to the merge request submitter to improve the merge request (style, tests, etc.) -- Mark merge requests 'ready-for-merge' when they meet the contribution guidelines -- Mention developer(s) based on the [list of members and their specialities](https://about.gitlab.com/core-team/) +- Responds to merge requests the issue team mentions them in and monitors for + new merge requests +- Provides feedback to the merge request submitter to improve the merge request + (style, tests, etc.) +- Mark merge requests `Ready for Merge` when they meet the + [contribution acceptance criteria] +- Mention developer(s) based on the + [list of members and their specialities][team] - Closes merge requests with no feedback from the reporter for two weeks ## Priorities of the issue team @@ -30,29 +46,40 @@ Below we describe the contributing process to GitLab for two reasons. So that co ## Mentioning people -The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](https://about.gitlab.com/core-team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. +The most important thing is making sure valid issues receive feedback from the +development team. Therefore the priority is mentioning developers that can help +on those issue. Please select someone with relevant experience from +[GitLab core team][core-team]. If there is nobody mentioned with that expertise +look in the commit history for the affected files to find someone. Avoid +mentioning the lead developer, this is the person that is least likely to give a +timely response. If the involvement of the lead developer is needed the other +core team members will mention this person. ## Workflow labels -Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. - -- *Awaiting feedback*: Feedback pending from the reporter -- *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away) -- *Attached MR*: There is a MR attached and the discussion should happen there - - We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay. -- *Developer*: needs help from a developer -- *UX* needs needs help from a UX designer -- *Frontend* needs help from a Front-end engineer -- *Graphics* needs help from a Graphics designer -- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. -- *feature proposal* is a proposal for a new feature for GitLab. People are encouraged to vote +Workflow labels are purposely not very detailed since that would be hard to keep +updated as you would need to re-evaluate them after every comment. We optionally +use functional labels on demand when want to group related issues to get an +overview (for example all issues related to RVM, to tackle them in one go) and +to add details to the issue. + +- ~"Awaiting Feedback" Feedback pending from the reporter +- ~UX needs help from a UX designer +- ~Frontend needs help from a Front-end engineer. Please follow the + ["Implement design & UI elements" guidelines]. +- ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. +- ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote in support or comment for further detail. Do not use `feature request`. - +- ~bug is an issue reporting undesirable or incorrect behavior. +- ~customer is an issue reported by enterprise subscribers. This label should +be accompanied by *bug* or *feature proposal* labels. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. ## Functional labels -These labels describe what development specialities are involved such as: PostgreSQL, UX, LDAP. +These labels describe what development specialities are involved such as: `CI`, +`Core`, `Documentation`, `Frontend`, `Issues`, `Merge Requests`, `Omnibus`, +`Release`, `Repository`, `UX`. ## Assigning issues @@ -60,21 +87,29 @@ If an issue is complex and needs the attention of a specific person, assignment ## Label colors -- Light orange `#fef2c0`: workflow labels for issue team members (awaiting feedback, awaiting confirmation of fix) -- Bright orange `#eb6420`: workflow labels for core team members (attached MR, awaiting developer action/feedback) -- Light blue `#82C5FF`: functional labels -- Green labels `#009800`: issues that can generally be ignored. For example, issues given the following labels normally can be closed immediately: - - Support (see copy & paste response: [Support requests and configuration questions](#support-requests-and-configuration-questions) +- Light orange `#fef2c0`: workflow labels for issue team members (awaiting + feedback, awaiting confirmation of fix) +- Bright orange `#eb6420`: workflow labels for core team members (attached MR, + awaiting developer action/feedback) +- Light blue `#82C5FF`: functional labels +- Green labels `#009800`: issues that can generally be ignored. For example, + issues given the following labels normally can be closed immediately: + - Support (see copy & paste response: + [Support requests and configuration questions](#support-requests-and-configuration-questions) ## Be kind -Be kind to people trying to contribute. Be aware that people may be a non-native English speaker, they might not understand things or they might be very sensitive as to how you word things. Use Emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review). +Be kind to people trying to contribute. Be aware that people may be a non-native +English speaker, they might not understand things or they might be very +sensitive as to how you word things. Use Emoji to express your feelings (heart, +star, smile, etc.). Some good tips about giving feedback to merge requests is in +the [Thoughtbot code review guide]. ## Copy & paste responses ### Improperly formatted issue -Thanks for the issue report. Please reformat your issue to conform to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). +Thanks for the issue report. Please reformat your issue to conform to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). ### Issue report for old version @@ -110,11 +145,11 @@ This merge request has been closed because a request for more information has no ### Accepting merge requests -Is there an issue on the [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) -that is similar to this? -Could you please link it here? +Is there an issue on the +\[issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues) that is +similar to this? Could you please link it here? Please be aware that new functionality that is not marked -[accepting merge requests](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) +\[accepting merge requests\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) might not make it into GitLab. ### Only accepting merge requests with green tests @@ -129,4 +164,10 @@ rebase with master to see if that solves the issue. We are currently in the process of closing down the issue tracker on GitHub, to prevent duplication with the GitLab.com issue tracker. Since this is an older issue I'll be closing this for now. If you think this is -still an issue I encourage you to open it on the \[GitLab.com issue tracker\](https://gitlab.com/gitlab-org/gitlab-ce/issues). +still an issue I encourage you to open it on the \[GitLab.com issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues). + +[core-team]: https://about.gitlab.com/core-team/ +[team]: https://about.gitlab.com/team/ +[contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria +["Implement design & UI elements" guidelines]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#implement-design-ui-elements +[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review @@ -4,4 +4,7 @@ require File.expand_path('../config/application', __FILE__) +relative_url_conf = File.expand_path('../config/initializers/relative_url', __FILE__) +require relative_url_conf if File.exist?("#{relative_url_conf}.rb") + Gitlab::Application.load_tasks diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 5463397f475..c17d2186e29 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -31,8 +31,6 @@ #= require ace/ace #= require ace/ext-searchbox #= require underscore -#= require nprogress -#= require nprogress-turbolinks #= require dropzone #= require mousetrap #= require mousetrap/pause diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee index 35b2fbbba07..d14b7139237 100644 --- a/app/assets/javascripts/logo.js.coffee +++ b/app/assets/javascripts/logo.js.coffee @@ -1,4 +1,4 @@ -NProgress.configure(showSpinner: false) +Turbolinks.enableProgressBar(); defaultClass = 'tanuki-shape' pieces = [ diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 40cfa59a229..23a218b4c7d 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -146,6 +146,7 @@ class @MergeRequestTabs url: "#{source}.json" + @_location.search success: (data) => document.querySelector("div#diffs").innerHTML = data.html + $('.js-timeago').timeago() $('div#diffs .js-syntax-highlight').syntaxHighlight() @expandViewContainer() if @diffViewType() is 'parallel' @diffsLoaded = true diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee index f141fb69c3e..9c7c2474aa4 100644 --- a/app/assets/javascripts/shortcuts.js.coffee +++ b/app/assets/javascripts/shortcuts.js.coffee @@ -13,8 +13,10 @@ class @Shortcuts if $('#modal-shortcuts').length > 0 $('#modal-shortcuts').modal('show') else + url = '/help/shortcuts' + url = gon.relative_url_root + url if gon.relative_url_root? $.ajax( - url: '/help/shortcuts', + url: url, dataType: 'script', success: (e) -> if location and location.length > 0 diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index f51054f13dc..e2d590f4df4 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -26,12 +26,6 @@ @import "framework"; /* - * NProgress load bar css - */ -@import 'nprogress'; -@import 'nprogress-bootstrap'; - -/* * Font icons */ @import "font-awesome"; diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index fa7641b1676..e2a30f5ed34 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -26,6 +26,7 @@ @import "framework/mobile.scss"; @import "framework/nav.scss"; @import "framework/pagination.scss"; +@import "framework/progress.scss"; @import "framework/panels.scss"; @import "framework/selects.scss"; @import "framework/sidebar.scss"; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 9ecb547b64f..9e8506b2a81 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -305,7 +305,7 @@ table { } .btn-sign-in { - margin-top: 8px; + margin-top: 10px; text-shadow: none; } diff --git a/app/assets/stylesheets/framework/progress.scss b/app/assets/stylesheets/framework/progress.scss new file mode 100644 index 00000000000..e9800bd24b5 --- /dev/null +++ b/app/assets/stylesheets/framework/progress.scss @@ -0,0 +1,5 @@ +html.turbolinks-progress-bar::before { + background-color: $progress-color!important; + height: 2px!important; + box-shadow: 0 0 10px $progress-color, 0 0 5px $progress-color; +} diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index e0ccd6f100f..de947c89c19 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -13,6 +13,19 @@ transition-duration: .3s; } + .gitlab-text-container-link { + z-index: 1; + position: absolute; + left: 0px; + } + + #logo { + z-index: 2; + position: absolute; + width: 58px; + cursor: pointer; + } + &.right-sidebar-expanded { padding-right: $gutter_width; } @@ -74,7 +87,7 @@ width: 158px; float: left; margin: 0; - margin-left: 14px; + margin-left: 50px; font-size: 19px; line-height: 41px; font-weight: normal; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 2706d031d7b..7834cb0bfa5 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -7,7 +7,7 @@ $gl-header-color: #323232; $gl-link-color: #333c48; $md-text-color: #444; $md-link-color: #3084bb; -$nprogress-color: #c0392b; +$progress-color: #c0392b; $gl-font-size: 15px; $list-font-size: 15px; $sidebar_collapsed_width: 62px; diff --git a/app/assets/stylesheets/pages/appearances.scss b/app/assets/stylesheets/pages/appearances.scss new file mode 100644 index 00000000000..e2070f17c3b --- /dev/null +++ b/app/assets/stylesheets/pages/appearances.scss @@ -0,0 +1,11 @@ +.appearance-logo-preview { + max-width: 400px; + margin-bottom: 20px; +} + +.appearance-light-logo-preview { + background-color: $background-color; + max-width: 72px; + padding: 10px; + margin-bottom: 10px; +} diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index f9c6f1b39f9..61bec02f6c5 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -8,6 +8,10 @@ max-width: none; } + .flash-container { + margin-bottom: $gl-padding; + } + .brand-holder { font-size: 18px; line-height: 1.5; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 19ead07c06a..d5f9852ebed 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -14,6 +14,18 @@ ul.notes { margin: 0px; padding: 0px; + .timeline-icon { + float: left; + } + + .timeline-content { + margin-left: 55px; + } + + .note_created_ago, .note-updated-at { + white-space: nowrap; + } + .system-note { font-size: 14px; padding-top: 10px; @@ -151,6 +163,7 @@ ul.notes { border-left: none; &.notes_line { + vertical-align: middle; text-align: center; padding: 10px 0; background: #FFF; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 3aaa96da609..84234b15c65 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -1,8 +1,12 @@ .search-results { .search-result-row { - border-bottom: 1px solid #DDD; - padding-bottom: 15px; - margin-bottom: 15px; + border-bottom: 1px solid $border-color; + padding-bottom: $gl-padding; + margin-bottom: $gl-padding; + + &:last-child { + border-bottom: none; + } } } diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index 2f57f21963d..0dc5a905f99 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -12,29 +12,10 @@ } } -.todos { - .panel { - border-top: none; - margin-bottom: 0; - } -} - .todo-item { font-size: $gl-font-size; - padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top); - border-bottom: 1px solid $table-border-color; - color: #7f8fa4; - - &.todo-inline { - .avatar { - position: relative; - top: -2px; - } - - .todo-title { - line-height: 40px; - } - } + padding-left: $gl-avatar-size + $gl-padding-top; + color: $secondary-text; a { color: #4c4e54; @@ -48,7 +29,7 @@ @include str-truncated(calc(100% - 174px)); font-weight: 600; - .author_name { + .author-name { color: #333; } } @@ -88,17 +69,7 @@ margin-bottom: 0; } } - - .todo-note-icon { - color: #777; - float: left; - font-size: $gl-font-size; - line-height: 16px; - margin-right: 5px; - } } - - &:last-child { border:none } } @media (max-width: $screen-xs-max) { diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb new file mode 100644 index 00000000000..26cf74e4849 --- /dev/null +++ b/app/controllers/admin/appearances_controller.rb @@ -0,0 +1,57 @@ +class Admin::AppearancesController < Admin::ApplicationController + before_action :set_appearance, except: :create + + def show + end + + def preview + end + + def create + @appearance = Appearance.new(appearance_params) + + if @appearance.save + redirect_to admin_appearances_path, notice: 'Appearance was successfully created.' + else + render action: 'show' + end + end + + def update + if @appearance.update(appearance_params) + redirect_to admin_appearances_path, notice: 'Appearance was successfully updated.' + else + render action: 'show' + end + end + + def logo + @appearance.remove_logo! + + @appearance.save + + redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.' + end + + def header_logos + @appearance.remove_header_logo! + @appearance.save + + redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.' + end + + private + + # Use callbacks to share common setup or constraints between actions. + def set_appearance + @appearance = Appearance.last || Appearance.new + end + + # Only allow a trusted parameter "white list" through. + def appearance_params + params.require(:appearance).permit( + :title, :description, :logo, :logo_cache, :header_logo, :header_logo_cache, + :updated_by + ) + end +end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index f3bfede4354..8f83fdd02bc 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -12,11 +12,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController current_user.save! if current_user.changed? - if two_factor_grace_period_expired? - flash.now[:alert] = 'You must enable Two-factor Authentication for your account.' - else - grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours - flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}." + if two_factor_authentication_required? + if two_factor_grace_period_expired? + flash.now[:alert] = 'You must enable Two-factor Authentication for your account.' + else + grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours + flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}." + end end @qr_code = build_qr_code diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index f7e6bb34443..b64dbbd89ce 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -1,4 +1,6 @@ class Projects::AvatarsController < Projects::ApplicationController + include BlobHelper + before_action :project def show @@ -7,7 +9,7 @@ class Projects::AvatarsController < Projects::ApplicationController headers['X-Content-Type-Options'] = 'nosniff' headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers['Content-Disposition'] = 'inline' - headers['Content-Type'] = @blob.content_type + headers['Content-Type'] = safe_content_type(@blob) head :ok # 'render nothing: true' messes up the Content-Type else render_404 diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 0c551501ca4..a0835c9aad0 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -4,12 +4,22 @@ class Projects::ForksController < Projects::ApplicationController before_action :authorize_download_code! def index - @sort = params[:sort] || 'id_desc' - @all_forks = project.forks.includes(:creator).order_by(@sort) - - @public_forks, @protected_forks = @all_forks.partition do |project| - can?(current_user, :read_project, project) - end + base_query = project.forks.includes(:creator) + + @forks = if current_user + base_query.where('projects.visibility_level IN (?) OR projects.id IN (?)', + Project.public_and_internal_levels, + current_user.authorized_projects.pluck(:id)) + else + base_query.where('projects.visibility_level = ?', Project::PUBLIC) + end + + @total_forks_count = base_query.size + @private_forks_count = @total_forks_count - @forks.size + @public_forks_count = @total_forks_count - @private_forks_count + + @sort = params[:sort] || 'id_desc' + @forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE) end def new diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 87b4d08da0e..d9723acb1d9 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a file's raw class Projects::RawController < Projects::ApplicationController include ExtractsPath + include BlobHelper before_action :require_non_empty_project before_action :assign_ref_vars @@ -17,7 +18,7 @@ class Projects::RawController < Projects::ApplicationController else headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers['Content-Disposition'] = 'inline' - headers['Content-Type'] = get_blob_type + headers['Content-Type'] = safe_content_type(@blob) head :ok # 'render nothing: true' messes up the Content-Type end else @@ -27,16 +28,6 @@ class Projects::RawController < Projects::ApplicationController private - def get_blob_type - if @blob.text? - 'text/plain; charset=utf-8' - elsif @blob.image? - @blob.content_type - else - 'application/octet-stream' - end - end - def send_lfs_object lfs_object = find_lfs_object diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 9bb42ec86b3..e42d2d73947 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,4 +1,6 @@ class SearchController < ApplicationController + skip_before_action :authenticate_user!, :reject_blocked + include SearchHelper layout 'search' diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 868b05929d7..509f4f412ca 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -55,14 +55,15 @@ class UploadsController < ApplicationController "user" => User, "project" => Project, "note" => Note, - "group" => Group + "group" => Group, + "appearance" => Appearance } upload_models[params[:model]] end def upload_mount - upload_mounts = %w(avatar attachment file) + upload_mounts = %w(avatar attachment file logo header_logo) if upload_mounts.include?(params[:mounted_as]) params[:mounted_as] diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6055b606086..626213c6728 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -57,7 +57,7 @@ class UsersController < ApplicationController def contributions_calendar @contributions_calendar ||= Gitlab::ContributionsCalendar. - new(contributed_projects.reject(&:forked?), @user) + new(contributed_projects, @user) end def load_events diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index c5820bf4c50..e0abc3a2869 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -1,21 +1,33 @@ module AppearancesHelper - def brand_item - nil - end - def brand_title - 'GitLab Community Edition' + if brand_item && brand_item.title + brand_item.title + else + 'GitLab Community Edition' + end end def brand_image - nil + if brand_item.logo? + image_tag brand_item.logo + else + nil + end end def brand_text - nil + markdown(brand_item.description) + end + + def brand_item + @appearance ||= Appearance.first end def brand_header_logo - render 'shared/logo.svg' + if brand_item && brand_item.header_logo? + image_tag brand_item.header_logo + else + render 'shared/logo.svg' + end end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 7143a744869..7f63a2e2cb4 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -134,4 +134,22 @@ module BlobHelper blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml blob end + + # If we blindly set the 'real' content type when serving a Git blob we + # are enabling XSS attacks. An attacker could upload e.g. a Javascript + # file to a Git repository, trick the browser of a victim into + # downloading the blob, and then the 'application/javascript' content + # type would tell the browser to execute the attacker's Javascript. By + # overriding the content type and setting it to 'text/plain' (in the + # example of Javascript) we tell the browser of the victim not to + # execute untrusted data. + def safe_content_type(blob) + if blob.text? + 'text/plain; charset=utf-8' + elsif blob.image? + blob.content_type + else + 'application/octet-stream' + end + end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 31bf45baeb7..e5fcaab9551 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -168,11 +168,11 @@ module EventsHelper link_to(namespace_project_snippet_path(event.project.namespace, event.project, event.note_target)) do - "#{event.note_target_type} ##{truncate event.note_target_id}" + "#{event.note_target_type} #{truncate event.note_target.to_reference}" end else link_to event_note_target_path(event) do - "#{event.note_target_type} ##{truncate event.note_target_iid}" + "#{event.note_target_type} #{truncate event.note_target.to_reference}" end end else diff --git a/app/models/appearance.rb b/app/models/appearance.rb new file mode 100644 index 00000000000..4cf8dd9a8ce --- /dev/null +++ b/app/models/appearance.rb @@ -0,0 +1,9 @@ +class Appearance < ActiveRecord::Base + validates :title, presence: true + validates :description, presence: true + validates :logo, file_size: { maximum: 1.megabyte } + validates :header_logo, file_size: { maximum: 1.megabyte } + + mount_uploader :logo, AttachmentUploader + mount_uploader :header_logo, AttachmentUploader +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index cbe65d70997..8f99e3bef9b 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -27,7 +27,7 @@ class Milestone < ActiveRecord::Base belongs_to :project has_many :issues - has_many :labels, through: :issues + has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests has_many :participants, through: :issues, source: :assignee diff --git a/app/models/user.rb b/app/models/user.rb index 6baf2468ade..3098d49d58a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -169,7 +169,7 @@ class User < ActiveRecord::Base validates :avatar_crop_x, :avatar_crop_y, :avatar_crop_size, numericality: { only_integer: true }, presence: true, - if: ->(user) { user.avatar? } + if: ->(user) { user.avatar? && user.avatar_changed? } before_validation :generate_password, on: :create before_validation :restricted_signup_domains, on: :create @@ -362,17 +362,19 @@ class User < ActiveRecord::Base def disable_two_factor! update_attributes( - two_factor_enabled: false, - encrypted_otp_secret: nil, - encrypted_otp_secret_iv: nil, - encrypted_otp_secret_salt: nil, - otp_backup_codes: nil + two_factor_enabled: false, + encrypted_otp_secret: nil, + encrypted_otp_secret_iv: nil, + encrypted_otp_secret_salt: nil, + otp_grace_period_started_at: nil, + otp_backup_codes: nil ) end def namespace_uniq # Return early if username already failed the first uniqueness validation - return if self.errors[:username].include?('has already been taken') + return if self.errors.key?(:username) && + self.errors[:username].include?('has already been taken') namespace_name = self.username existing_namespace = Namespace.by_path(namespace_name) diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml new file mode 100644 index 00000000000..6f325914d14 --- /dev/null +++ b/app/views/admin/appearances/_form.html.haml @@ -0,0 +1,58 @@ += form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f| + - if @appearance.errors.any? + .alert.alert-danger + - @appearance.errors.full_messages.each do |msg| + %p= msg + + %fieldset.sign-in + %legend + Sign in/Sign up pages: + .form-group + = f.label :title, class: 'control-label' + .col-sm-10 + = f.text_field :title, class: "form-control" + .form-group + = f.label :description, class: 'control-label' + .col-sm-10 + = f.text_area :description, class: "form-control", rows: 10 + .hint + Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown', 'markdown'), target: '_blank'}. + .form-group + = f.label :logo, class: 'control-label' + .col-sm-10 + - if @appearance.logo? + = image_tag @appearance.logo_url, class: 'appearance-logo-preview' + - if @appearance.persisted? + %br + = link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo" + %hr + = f.hidden_field :logo_cache + = f.file_field :logo, class: "" + .hint + Maximum file size is 1MB. Pages are optimized for a 640x360 px logo. + + %fieldset.app_logo + %legend + Navigation bar: + .form-group + = f.label :header_logo, 'Header logo', class: 'control-label' + .col-sm-10 + - if @appearance.header_logo? + = image_tag @appearance.header_logo_url, class: 'appearance-light-logo-preview' + - if @appearance.persisted? + %br + = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo" + %hr + = f.hidden_field :header_logo_cache + = f.file_field :header_logo, class: "" + .hint + Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo + + .form-actions + = f.submit 'Save', class: 'btn btn-save' + - if @appearance.persisted? + = link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank' + + - if @appearance.updated_at + %span.pull-right + Last edit #{time_ago_with_tooltip(@appearance.updated_at)} diff --git a/app/views/admin/appearances/preview.html.haml b/app/views/admin/appearances/preview.html.haml new file mode 100644 index 00000000000..dd4a64e80bc --- /dev/null +++ b/app/views/admin/appearances/preview.html.haml @@ -0,0 +1,29 @@ +- page_title "Preview | Appearance" +%h3.page-title + Appearance settings - Preview +%hr + +.ui-box + .title + Sign-in page + %div + .login-page + .container + .content + .login-title + %h1= brand_title + %hr + .container + .content + .row + .col-sm-7 + .brand-image + = brand_image + .brand_text + = brand_text + .col-sm-4 + .login-box + %h3.page-title Sign in + = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email" + = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password" + = button_tag "Sign in", class: "btn-create btn" diff --git a/app/views/admin/appearances/show.html.haml b/app/views/admin/appearances/show.html.haml new file mode 100644 index 00000000000..089e8e4cb7a --- /dev/null +++ b/app/views/admin/appearances/show.html.haml @@ -0,0 +1,7 @@ +- page_title "Appearance" +%h3.page-title + Appearance settings +%p.light + You can modify the look and feel of GitLab here + += render 'form' diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index 6975f6ed0db..f878d36e739 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -1,11 +1,11 @@ %li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) } - .todo-item{class: 'todo-block'} + .todo-item.todo-block = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' .todo-title - %span.author_name + %span.author-name = link_to_author todo - %span.todo_label + %span.todo-label = todo_action_name(todo) = todo_target_link(todo) diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index 4ecf1c33d2a..e9e16a7646f 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -4,7 +4,7 @@ = event_action_name(event) - if event.target - %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target] = event_preposition(event) diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index e53d5b07801..c799e9c588d 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -4,7 +4,7 @@ .header-logo %a#logo = brand_header_logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do + = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do .gitlab-text-container %h3 GitLab diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 678ed3c2c1f..babfb032236 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -5,11 +5,7 @@ -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body. = yield :scripts_body_top - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - + = render "layouts/header/default", title: header_title = render 'layouts/page', sidebar: sidebar = yield :scripts_body diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml index 3cfd36720f0..a13241bebee 100644 --- a/app/views/layouts/ci/_page.html.haml +++ b/app/views/layouts/ci/_page.html.haml @@ -4,7 +4,7 @@ .header-logo %a#logo = brand_header_logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do + = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do .gitlab-text-container %h3 GitLab diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 4781ff23507..77d01a7736c 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -13,30 +13,35 @@ %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('search') - - if session[:impersonator_id] - %li.impersonation - = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do - = icon('user-secret fw') - - if current_user.is_admin? + - if current_user + - if session[:impersonator_id] + %li.impersonation + = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do + = icon('user-secret fw') + - if current_user.is_admin? + %li + = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = icon('wrench fw') %li - = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('wrench fw') - %li - = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - %span.badge.todos-pending-count - = todos_pending_count - - if current_user.can_create_project? + = link_to dashboard_todos_path, title: 'Todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + %span.badge.todos-pending-count + = todos_pending_count + - if current_user.can_create_project? + %li + = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = icon('plus fw') + - if Gitlab::Sherlock.enabled? + %li + = link_to sherlock_transactions_path, title: 'Sherlock Transactions', + data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = icon('tachometer fw') %li - = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('plus fw') - - if Gitlab::Sherlock.enabled? - %li - = link_to sherlock_transactions_path, title: 'Sherlock Transactions', - data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('tachometer fw') - %li - = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do - = icon('sign-out') + = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do + = icon('sign-out') + - else + .pull-right + = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' + %h1.title= title diff --git a/app/views/layouts/header/_public.html.haml b/app/views/layouts/header/_public.html.haml deleted file mode 100644 index a6a26518a0e..00000000000 --- a/app/views/layouts/header/_public.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } - %div{ class: fluid_layout ? "container-fluid" : "container-fluid" } - .header-content - - unless current_controller?('sessions') - .pull-right - = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' - - %h1.title= title - -= render 'shared/outdated_browser' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index ac1d5429382..280a1b93729 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -56,6 +56,11 @@ = icon('cog fw') %span Background Jobs + = nav_link(controller: :appearances) do + = link_to admin_appearances_path, title: 'Appearances' do + = icon('image') + %span + Appearance = nav_link(controller: :applications) do = link_to admin_applications_path, title: 'Applications' do diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml index f99bc9a85eb..63ede71e6f1 100644 --- a/app/views/projects/diffs/_warning.html.haml +++ b/app/views/projects/diffs/_warning.html.haml @@ -3,17 +3,16 @@ Too many changes to show. .pull-right - unless diff_hard_limit_enabled? - = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm btn-warning" + = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm" - if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) - = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-warning btn-sm" - = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-warning btn-sm" + = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm" + = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-sm" - elsif @merge_request && @merge_request.persisted? - = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-warning btn-sm" - = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-warning btn-sm" + = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-sm" + = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm" %p To preserve performance only %strong #{shown_files_count} of #{diffs.size} files are displayed. - diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 42fa6fdb782..ace22625d1d 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -1,9 +1,7 @@ .top-area .nav-text - - public_count = @public_forks.size - - protected_count = @protected_forks.size - - full_count_title = "#{public_count} public and #{protected_count} private" - == #{pluralize(@all_forks.size, 'fork')}: #{full_count_title} + - full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private" + == #{pluralize(@total_forks_count, 'fork')}: #{full_count_title} .nav-controls = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short', @@ -41,17 +39,17 @@ .projects-list-holder - - if @public_forks.blank? + - if @forks.blank? %ul.content-list %li .nothing-here-block No forks to show - else - = render 'shared/projects/list', projects: @public_forks, use_creator_avatar: true, + = render 'shared/projects/list', projects: @forks, use_creator_avatar: true, forks: true, show_last_commit_as_description: true - - if protected_count > 0 + - if @private_forks_count > 0 %ul.projects-list.private-forks-notice %li.project-row = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon') - %strong= pluralize(protected_count, 'private fork') + %strong= pluralize(@private_forks_count, 'private fork') %span you have no access to. diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index b9d5982a56f..18cf3f14f0b 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -48,7 +48,7 @@ = note_count .merge-request-info - \##{merge_request.iid} · + #{merge_request.to_reference} · opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)} - if merge_request.target_project.default_branch != merge_request.target_branch diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 648512e5379..d7bc26e24b9 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" +- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 14ea7b17786..b634a4af8d2 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -2,7 +2,7 @@ .status-box{ class: status_box_class(@merge_request) } = @merge_request.state_human_name %span.identifier - Merge Request ##{@merge_request.iid} + Merge Request #{@merge_request.to_reference} %span.creator · by #{link_to_member(@project, @merge_request.author, size: 24)} diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index e858c412836..52972576aff 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -28,7 +28,7 @@ %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'} = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago') - if note.updated_at != note.created_at - %span + %span.note-updated-at · = icon('edit', title: 'edited') = time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago') diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml index 17b0981f073..a9dbc84da29 100644 --- a/app/views/search/_form.html.haml +++ b/app/views/search/_form.html.haml @@ -11,4 +11,4 @@ = button_tag 'Search', class: "btn btn-primary" - unless params[:snippets].eql? 'true' %br - = render 'filter' + = render 'filter' if current_user diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index d0e64537621..eef7b7b5efd 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -18,7 +18,7 @@ = render 'shared/projects/list', projects: @objects - else = render partial: "search/results/#{@scope.singularize}", collection: @objects - = paginate @objects, theme: 'gitlab' + = paginate @objects, theme: 'gitlab' :javascript $(".search-results .term").highlight("#{escape_javascript(params[:search])}"); diff --git a/config/application.rb b/config/application.rb index b905f1a3e90..28684a3e578 100644 --- a/config/application.rb +++ b/config/application.rb @@ -54,14 +54,6 @@ module Gitlab config.action_view.sanitized_allowed_protocols = %w(smb) - # Relative URL support - # WARNING: We recommend using an FQDN to host GitLab in a root path instead - # of using a relative URL. - # Documentation: http://doc.gitlab.com/ce/install/relative_url.html - # Uncomment and customize the following line to run in a non-root path - # - # config.relative_url_root = "/gitlab" - config.middleware.use Rack::Attack # Allow access to GitLab API from other domains diff --git a/config/initializers/relative_url.rb.sample b/config/initializers/relative_url.rb.sample new file mode 100644 index 00000000000..125297d5385 --- /dev/null +++ b/config/initializers/relative_url.rb.sample @@ -0,0 +1,10 @@ +# Relative URL support +# WARNING: We recommend using an FQDN to host GitLab in a root path instead +# of using a relative URL. +# Documentation: http://doc.gitlab.com/ce/install/relative_url.html +# Copy this file to relative_url.rb and customize it to run in a non-root path +# + +Rails.application.configure do + config.relative_url_root = "/gitlab" +end diff --git a/config/routes.rb b/config/routes.rb index 1485b64da19..a2acf170a6b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -156,6 +156,11 @@ Rails.application.routes.draw do to: "uploads#show", constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ } + # Appearance + get ":model/:mounted_as/:id/:filename", + to: "uploads#show", + constraints: { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ } + # Project markdown uploads get ":namespace_id/:project_id/:secret/:filename", to: "projects/uploads#show", @@ -253,6 +258,14 @@ Rails.application.routes.draw do end end + resource :appearances, path: 'appearance' do + member do + get :preview + delete :logo + delete :header_logos + end + end + resource :application_settings, only: [:show, :update] do resources :services put :reset_runners_token diff --git a/db/migrate/20160222153918_create_appearances_ce.rb b/db/migrate/20160222153918_create_appearances_ce.rb new file mode 100644 index 00000000000..bec66bcc71e --- /dev/null +++ b/db/migrate/20160222153918_create_appearances_ce.rb @@ -0,0 +1,14 @@ +class CreateAppearancesCe < ActiveRecord::Migration + def change + unless table_exists?(:appearances) + create_table :appearances do |t| + t.string :title + t.text :description + t.string :header_logo + t.string :logo + + t.timestamps null: false + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4708c29d9ae..53a941d30de 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160220123949) do +ActiveRecord::Schema.define(version: 20160222153918) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,15 @@ ActiveRecord::Schema.define(version: 20160220123949) do t.datetime "updated_at" end + create_table "appearances", force: :cascade do |t| + t.string "title" + t.text "description" + t.string "header_logo" + t.string "logo" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "application_settings", force: :cascade do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" diff --git a/doc/README.md b/doc/README.md index 5089e1e70f6..be6c5f96ea1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -16,40 +16,42 @@ - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. -## CI Documentation +## CI User documentation -- [Quick Start](ci/quick_start/README.md) -- [Enable or disable GitLab CI](ci/enable_or_disable_ci.md) -- [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) -- [Configuring runner](ci/runners/README.md) -- [Configuring deployment](ci/deployment/README.md) -- [Using Docker Images](ci/docker/using_docker_images.md) -- [Using Docker Build](ci/docker/using_docker_build.md) -- [Using Variables](ci/variables/README.md) -- [Using SSH keys](ci/ssh_keys/README.md) +- [Get started with GitLab CI](ci/quick_start/README.md) +- [Learn how to enable or disable GitLab CI](ci/enable_or_disable_ci.md) +- [Learn how `.gitlab-ci.yml` works](ci/yaml/README.md) +- [Configure a Runner, the application that runs your builds](ci/runners/README.md) +- [Use Docker images with GitLab Runner](ci/docker/using_docker_images.md) +- [Use CI to build Docker images](ci/docker/using_docker_build.md) +- [Use variables in your `.gitlab-ci.yml`](ci/variables/README.md) +- [Use SSH keys in your build environment](ci/ssh_keys/README.md) +- [Trigger builds through the API](ci/triggers/README.md) +- [Build artifacts](ci/build_artifacts/README.md) - [User permissions](ci/permissions/README.md) - [API](ci/api/README.md) -- [Triggering builds through the API](ci/triggers/README.md) -- [Build artifacts](ci/build_artifacts/README.md) -### CI Languages +### CI Examples -- [Testing PHP](ci/languages/php.md) +- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) +- [Test your PHP applications](ci/examples/php.md) +- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) +- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md) +- [Test Clojure applications](ci/examples/test-clojure-application.md) +- [Using `dpl` as deployment tool](ci/deployment/README.md) +- Help your favorite programming language and GitLab by sending a merge request + with a guide for that language. ### CI Services +GitLab CI uses the `services` keyword to define what docker containers should +be linked with your base image. Below is a list of examples you may use: + - [Using MySQL](ci/services/mysql.md) - [Using PostgreSQL](ci/services/postgres.md) - [Using Redis](ci/services/redis.md) - [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services) -### CI Examples - -- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) -- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md) -- [Test Clojure applications](ci/examples/test-clojure-application.md) -- Help your favorite programming language and GitLab by sending a merge request with a guide for that language. - ## Administrator documentation - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough. diff --git a/doc/ci/README.md b/doc/ci/README.md index 5886829be51..2120b5b2850 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -1,39 +1,37 @@ ## GitLab CI Documentation -### User documentation - -* [Quick Start](quick_start/README.md) -* [Enable or disable GitLab CI](enable_or_disable_ci.md) -* [Configuring project (.gitlab-ci.yml)](yaml/README.md) -* [Configuring runner](runners/README.md) -* [Configuring deployment](deployment/README.md) -* [Using Docker Images](docker/using_docker_images.md) -* [Using Docker Build](docker/using_docker_build.md) -* [Using Variables](variables/README.md) -* [Using SSH keys](ssh_keys/README.md) -* [Triggering builds through the API](triggers/README.md) -* [Build artifacts](build_artifacts/README.md) - -### Languages - -* [Testing PHP](languages/php.md) - -### Services - -* [Using MySQL](services/mysql.md) -* [Using PostgreSQL](services/postgres.md) -* [Using Redis](services/redis.md) -* [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services) - -### Examples - -+ [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) -+ [Test and deploy Ruby applications to Heroku](examples/test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy Python applications to Heroku](examples/test-and-deploy-python-application-to-heroku.md) -+ [Test Clojure applications](examples/test-clojure-application.md) -+ Help your favorite programming language and GitLab by sending a merge request with a guide for that language. - -### Administrator documentation - -* [User permissions](permissions/README.md) -* [API](api/README.md) +### CI User documentation + +- [Get started with GitLab CI](quick_start/README.md) +- [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md) +- [Learn how `.gitlab-ci.yml` works](yaml/README.md) +- [Configure a Runner, the application that runs your builds](runners/README.md) +- [Use Docker images with GitLab Runner](docker/using_docker_images.md) +- [Use CI to build Docker images](docker/using_docker_build.md) +- [Use variables in your `.gitlab-ci.yml`](variables/README.md) +- [Use SSH keys in your build environment](ssh_keys/README.md) +- [Trigger builds through the API](triggers/README.md) +- [Build artifacts](build_artifacts/README.md) +- [User permissions](permissions/README.md) +- [API](api/README.md) + +### CI Examples + +- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) +- [Test your PHP applications](examples/php.md) +- [Test and deploy Ruby applications to Heroku](examples/test-and-deploy-ruby-application-to-heroku.md) +- [Test and deploy Python applications to Heroku](examples/test-and-deploy-python-application-to-heroku.md) +- [Test Clojure applications](examples/test-clojure-application.md) +- [Using `dpl` as deployment tool](deployment/README.md) +- Help your favorite programming language and GitLab by sending a merge request + with a guide for that language. + +### CI Services + +GitLab CI uses the `services` keyword to define what docker containers should +be linked with your base image. Below is a list of examples you may use: + +- [Using MySQL](services/mysql.md) +- [Using PostgreSQL](services/postgres.md) +- [Using Redis](services/redis.md) +- [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index 1cf41aea391..31f29f4a082 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -1,5 +1,13 @@ -# Build script examples +## Build script examples -+ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) -+ [Test a Clojure application](test-clojure-application.md) +- [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) +- [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) +- [Test a Clojure application](test-clojure-application.md) + +## Languages + +This is a list of languages you can test with GitLab CI. Each section has +comprehensive documentation and comes with a test repository hosted on +GitLab.com. + +- [Testing PHP](php.md) diff --git a/doc/ci/languages/php.md b/doc/ci/examples/php.md index aeadd6a448e..aeadd6a448e 100644 --- a/doc/ci/languages/php.md +++ b/doc/ci/examples/php.md diff --git a/doc/ci/languages/README.md b/doc/ci/languages/README.md deleted file mode 100644 index 54b2343e08b..00000000000 --- a/doc/ci/languages/README.md +++ /dev/null @@ -1,7 +0,0 @@ -### Languages - -This is a list of languages you can test with GitLab CI. Each section has -comprehensive documentation and comes with a test repository hosted on -GitLab.com - -+ [Testing PHP](php.md) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 327c83bef72..5af7be5581e 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -201,6 +201,11 @@ You can access a builds badge image using following link: http://example.gitlab.com/namespace/project/badges/branch/build.svg ``` +## Examples + +Visit the [examples README][examples] to see a list of examples using GitLab +CI with various languages. + ## Next steps Awesome! You started using CI in GitLab! @@ -212,3 +217,4 @@ Visit our various languages examples at <https://gitlab.com/groups/gitlab-exampl [runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ +[examples]: ../examples/README.md diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md index 1ebb0a4a250..4b79461d55c 100644 --- a/doc/ci/services/README.md +++ b/doc/ci/services/README.md @@ -1,9 +1,9 @@ ## GitLab CI Services -GitLab CI uses the `services` keyword to define what docker containers should be -linked with your base image. Below is a list of examples you may use. +GitLab CI uses the `services` keyword to define what docker containers should +be linked with your base image. Below is a list of examples you may use. -+ [Using MySQL](mysql.md) -+ [Using PostgreSQL](postgres.md) -+ [Using Redis](redis.md) -+ [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services) +- [Using MySQL](mysql.md) +- [Using PostgreSQL](postgres.md) +- [Using Redis](redis.md) +- [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 9e89e6e395e..b0e53cbc261 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -30,7 +30,7 @@ The API_TOKEN will take the Secure Variable value: `SECURE`. | **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built | | **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally | | **CI_BUILD_REPO** | all | The URL to clone the Git repository | -| **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was triggered | +| **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was [triggered] | | **CI_PROJECT_ID** | all | The unique id of the current project that GitLab CI uses internally | | **CI_PROJECT_DIR** | all | The full path where the repository is cloned and where the build is ran | @@ -104,3 +104,5 @@ job_name: script: - export ``` + +[triggered]: ../triggers/README.md diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 0edb56dc20e..051eaa04152 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -518,3 +518,10 @@ You can find the link under `/ci/lint` of your gitlab instance. If your commit message contains `[ci skip]`, the commit will be created but the builds will be skipped. + +## Examples + +Visit the [examples README][examples] to see a list of examples using GitLab +CI with various languages. + +[examples]: ../examples/README.md diff --git a/doc/customization/branded_login_page.md b/doc/customization/branded_login_page.md new file mode 100644 index 00000000000..d4d9f5f7b5e --- /dev/null +++ b/doc/customization/branded_login_page.md @@ -0,0 +1,19 @@ +# Changing the appearance of the login page + +GitLab Community Edition offers a way to put your company's identity on the login page of your GitLab server and make it a branded login page. + +By default, the page shows the GitLab logo and description. + +![default_login_page](branded_login_page/default_login_page.png) + +## Changing the appearance of the login page + +Navigate to the **Admin** area and go to the **Appearance** page. + +Fill in the required details like Title, Description and upload the company logo. + +![appearance](branded_login_page/appearance.png) + +After saving the page, your GitLab login page will have the details you filled in: + +![company_login_page](branded_login_page/custom_sign_in.png) diff --git a/doc/customization/branded_login_page/appearance.png b/doc/customization/branded_login_page/appearance.png Binary files differnew file mode 100644 index 00000000000..6bce1f0a287 --- /dev/null +++ b/doc/customization/branded_login_page/appearance.png diff --git a/doc/customization/branded_login_page/custom_sign_in.png b/doc/customization/branded_login_page/custom_sign_in.png Binary files differnew file mode 100644 index 00000000000..d6020b029a2 --- /dev/null +++ b/doc/customization/branded_login_page/custom_sign_in.png diff --git a/doc/customization/branded_login_page/default_login_page.png b/doc/customization/branded_login_page/default_login_page.png Binary files differnew file mode 100644 index 00000000000..795c7954d8e --- /dev/null +++ b/doc/customization/branded_login_page/default_login_page.png diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md index e993230bb88..a0cb234bea0 100644 --- a/doc/customization/welcome_message.md +++ b/doc/customization/welcome_message.md @@ -1,12 +1,12 @@ -# Customize the complete sign-in page (GitLab Enterprise Edition only) +# Customize the complete sign-in page -Please see [Branded login page](http://doc.gitlab.com/ee/customization/branded_login_page.html) +Please see [Branded login page](branded_login_page.md) # Add a welcome message to the sign-in page (GitLab Community Edition) It is possible to add a markdown-formatted welcome message to your GitLab sign-in page. Users of GitLab Enterprise Edition should use the [branded login -page feature](/ee/customization/branded_login_page.html) instead. +page feature](branded_login_page.md) instead. The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI. -Admin area > Settings
\ No newline at end of file +Admin area > Settings diff --git a/doc/development/README.md b/doc/development/README.md index d5bf166ad32..b9a0d81e5ba 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -1,11 +1,12 @@ # Development - [Architecture](architecture.md) of GitLab -- [Shell commands](shell_commands.md) in the GitLab codebase -- [Rake tasks](rake_tasks.md) for development +- [Benchmarking](benchmarking.md) - [CI setup](ci_setup.md) for testing GitLab +- [Gotchas](gotchas.md) to avoid +- [How to dump production data to staging](db_dump.md) +- [Migration Style Guide](migration_style_guide.md) for creating safe migrations +- [Rake tasks](rake_tasks.md) for development +- [Shell commands](shell_commands.md) in the GitLab codebase - [Sidekiq debugging](sidekiq_debugging.md) - [UI guide](ui_guide.md) for building GitLab with existing css styles and elements -- [Migration Style Guide](migration_style_guide.md) for creating safe migrations -- [How to dump production data to staging](dump_db.md) -- [Benchmarking](benchmarking.md) diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 96d1dffbc52..187ec9e7b75 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -85,9 +85,19 @@ Inside the document: ## Notes -- Notes should be in italics with the word `Note:` being bold. Use this form: - `_**Note:** This is something to note._`. If the note spans across multiple - lines it's OK to split the line. +- Notes should be quoted with the word `Note:` being bold. Use this form: + + ``` + >**Note:** + This is something to note. + ``` + + which renders to: + + >**Note:** + This is something to note. + + If the note spans across multiple lines it's OK to split the line. ## New features diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md new file mode 100644 index 00000000000..21078c8d6f9 --- /dev/null +++ b/doc/development/gotchas.md @@ -0,0 +1,103 @@ +# Gotchas + +The purpose of this guide is to document potential "gotchas" that contributors +might encounter or should avoid during development of GitLab CE and EE. + +## Don't `describe` symbols + +Consider the following model spec: + +```ruby +require 'rails_helper' + +describe User do + describe :to_param do + it 'converts the username to a param' do + user = described_class.new(username: 'John Smith') + + expect(user.to_param).to eq 'john-smith' + end + end +end +``` + +When run, this spec doesn't do what we might expect: + +```sh +spec/models/user_spec.rb|6 error| Failure/Error: u = described_class.new NoMethodError: undefined method `new' for :to_param:Symbol +``` + +### Solution + +Except for the top-level `describe` block, always provide a String argument to +`describe`. + +## Don't `rescue Exception` + +See ["Why is it bad style to `rescue Exception => e` in Ruby?"][Exception]. + +_**Note:** This rule is [enforced automatically by +Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L911-914)._ + +[Exception]: http://stackoverflow.com/q/10048173/223897 + +## Don't use inline CoffeeScript in views + +Using the inline `:coffee` or `:coffeescript` Haml filters comes with a +performance overhead. + +_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/initializers/haml.rb) +in an initializer._ + +### Further reading + +- Pull Request: [Replace CoffeeScript block into JavaScript in Views](https://git.io/vztMu) +- Stack Overflow: [Performance implications of using :coffescript filter inside HAML templates?](http://stackoverflow.com/a/17571242/223897) + +## ID-based CSS selectors need to be a bit more specific + +Normally, because HTML `id` attributes need to be unique to the page, it's +perfectly fine to write some JavaScript like the following: + +```javascript +$('#js-my-selector').hide(); +``` + +However, there's a feature of GitLab's Markdown processing that [automatically +adds anchors to header elements][ToC Processing], with the `id` attribute being +automatically generated based on the content of the header. + +Unfortunately, this feature makes it possible for user-generated content to +create a header element with the same `id` attribute we're using in our +selector, potentially breaking the JavaScript behavior. A user could break the +above example with the following Markdown: + +```markdown +## JS My Selector +``` + +Which gets converted to the following HTML: + +```html +<h2> + <a id="js-my-selector" class="anchor" href="#js-my-selector" aria-hidden="true"></a> + JS My Selector +</h2> +``` + +[ToC Processing]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/lib/banzai/filter/table_of_contents_filter.rb#L31-37 + +### Solution + +The current recommended fix for this is to make our selectors slightly more +specific: + +```javascript +$('div#js-my-selector').hide(); +``` + +### Further reading + +- Issue: [Merge request ToC anchor conflicts with tabs](https://gitlab.com/gitlab-org/gitlab-ce/issues/3908) +- Merge Request: [Make tab target selectors less naive](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2023) +- Merge Request: [Make cross-project reference's clipboard target less naive](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2024) diff --git a/doc/install/relative_url.md b/doc/install/relative_url.md index b341b6311c6..0245febfcd8 100644 --- a/doc/install/relative_url.md +++ b/doc/install/relative_url.md @@ -25,7 +25,7 @@ that points to your GitLab instance. The TL;DR list of configuration files that you need to change in order to serve GitLab under a relative URL is: -- `/home/git/gitlab/config/application.rb` +- `/home/git/gitlab/config/initializers/relative_url.rb` - `/home/git/gitlab/config/gitlab.yml` - `/home/git/gitlab/config/unicorn.rb` - `/home/git/gitlab-shell/config.yml` @@ -66,8 +66,14 @@ Make sure to follow all steps below: sudo service gitlab stop ``` -1. Edit `/home/git/gitlab/config/application.rb` and uncomment/change the - following line: +1. Create `/home/git/gitlab/config/initializers/relative_url.rb` + + ```shell + cp /home/git/gitlab/config/initializers/relative_url.rb.sample \ + /home/git/gitlab/config/initializers/relative_url.rb + ``` + + and change the following line: ```ruby config.relative_url_root = "/gitlab" @@ -119,8 +125,12 @@ Make sure to follow all steps below: ### Disable relative URL in GitLab -To disable the relative URL, follow the same steps as above and set up the -GitLab URL to one that doesn't contain a relative path. +To disable the relative URL: + +1. Remove `/home/git/gitlab/config/initializers/relative_url.rb` + +1. Follow the same as above starting from 2. and set up the + GitLab URL to one that doesn't contain a relative path. [omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab" [restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab" diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 8e6627b2be5..ba47cb16265 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -1,8 +1,11 @@ # OmniAuth -GitLab leverages OmniAuth to allow users to sign in using Twitter, GitHub, and other popular services. +GitLab leverages OmniAuth to allow users to sign in using Twitter, GitHub, and +other popular services. -Configuring OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) from continuing to work. Users can choose to sign in using any of the configured mechanisms. +Configuring OmniAuth does not prevent standard GitLab authentication or LDAP +(if configured) from continuing to work. Users can choose to sign in using any +of the configured mechanisms. - [Initial OmniAuth Configuration](#initial-omniauth-configuration) - [Supported Providers](#supported-providers) @@ -28,17 +31,25 @@ contains some settings that are common for all providers. ## Initial OmniAuth Configuration -Before configuring individual OmniAuth providers there are a few global settings that are in common for all providers that we need to consider. +Before configuring individual OmniAuth providers there are a few global settings +that are in common for all providers that we need to consider. - Omniauth needs to be enabled, see details below for example. -- `allow_single_sign_on` defaults to `false`. If `false` users must be created manually or they will not be able to -sign in via OmniAuth. -- `block_auto_created_users` defaults to `true`. If `true` auto created users will be blocked by default and will -have to be unblocked by an administrator before they are able to sign in. -- **Note:** If you set `allow_single_sign_on` to `true` and `block_auto_created_users` to `false` please be aware -that any user on the Internet will be able to successfully sign in to your GitLab without administrative approval. - -If you want to change these settings: +- `allow_single_sign_on` allows you to specify the providers you want to allow to + automatically create an account. It defaults to `false`. If `false` users must + be created manually or they will not be able to sign in via OmniAuth. +- `block_auto_created_users` defaults to `true`. If `true` auto created users will + be blocked by default and will have to be unblocked by an administrator before + they are able to sign in. + +>**Note:** +If you set `block_auto_created_users` to `false`, make sure to only +define providers under `allow_single_sign_on` that you are able to control, like +SAML, Shibboleth, Crowd or Google, or set it to `false` otherwise any user on +the Internet will be able to successfully sign in to your GitLab without +administrative approval. + +To change these settings: * **For omnibus package** @@ -48,11 +59,16 @@ If you want to change these settings: sudo editor /etc/gitlab/gitlab.rb ``` - and change + and change: - ``` + ```ruby gitlab_rails['omniauth_enabled'] = true - gitlab_rails['omniauth_allow_single_sign_on'] = false + + # CAUTION! + # This allows users to login without having a user account first. Define the allowed providers + # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. + # User accounts will be created automatically when authentication was successful. + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'twitter'] gitlab_rails['omniauth_block_auto_created_users'] = true ``` @@ -66,43 +82,57 @@ If you want to change these settings: sudo -u git -H editor config/gitlab.yml ``` - and change the following section + and change the following section: - ``` + ```yaml ## OmniAuth settings omniauth: # Allow login via Twitter, Google, etc. using OmniAuth providers enabled: true # CAUTION! - # This allows users to login without having a user account first (default: false). + # This allows users to login without having a user account first. Define the allowed providers + # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. # User accounts will be created automatically when authentication was successful. - allow_single_sign_on: false + allow_single_sign_on: ["saml", "twitter"] + # Locks down those users until they have been cleared by the admin (default: true). block_auto_created_users: true ``` -Now we can choose one or more of the Supported Providers below to continue configuration. +Now we can choose one or more of the Supported Providers listed above to continue +the configuration process. ## Enable OmniAuth for an Existing User -Existing users can enable OmniAuth for specific providers after the account is created. For example, if the user originally signed in with LDAP an OmniAuth provider such as Twitter can be enabled. Follow the steps below to enable an OmniAuth provider for an existing user. +Existing users can enable OmniAuth for specific providers after the account is +created. For example, if the user originally signed in with LDAP, an OmniAuth +provider such as Twitter can be enabled. Follow the steps below to enable an +OmniAuth provider for an existing user. 1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider. 1. Go to profile settings (the silhouette icon in the top right corner). 1. Select the "Account" tab. 1. Under "Connected Accounts" select the desired OmniAuth provider, such as Twitter. -1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab. +1. The user will be redirected to the provider. Once the user authorized GitLab + they will be redirected back to GitLab. The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on. ## Using Custom Omniauth Providers -GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. +>**Note:** +The following information only applies for installations from source. + +GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships +with a few providers pre-installed (e.g. LDAP, GitHub, Twitter). But sometimes that +is not enough and you need to integrate with other authentication solutions. For +these cases you can use the Omniauth provider. ### Steps -These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. +These steps are fairly general and you will need to figure out the exact details +from the Omniauth provider's documentation. - Stop GitLab: @@ -128,8 +158,12 @@ These steps are fairly general and you will need to figure out the exact details ### Examples -If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. +If you have successfully set up a provider that is not shipped with GitLab itself, +please let us know. -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations). +You can help others by reporting successful configurations and probably share a +few insights or provide warnings for common errors or pitfalls by sharing your +experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations). -While we can't officially support every possible authentication mechanism out there, we'd like to at least help those with specific needs. +While we can't officially support every possible authentication mechanism out there, +we'd like to at least help those with specific needs. diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 8841dbdb7c6..c84113556cd 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -1,10 +1,14 @@ # SAML OmniAuth Provider -GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as Microsoft ADFS to authenticate users. +GitLab can be configured to act as a SAML 2.0 Service Provider (SP). This allows +GitLab to consume assertions from a SAML 2.0 Identity Provider (IdP) such as +Microsoft ADFS to authenticate users. -First configure SAML 2.0 support in GitLab, then register the GitLab application in your SAML IdP: +First configure SAML 2.0 support in GitLab, then register the GitLab application +in your SAML IdP: -1. Make sure GitLab is configured with HTTPS. See [Using HTTPS](../install/installation.md#using-https) for instructions. +1. Make sure GitLab is configured with HTTPS. + See [Using HTTPS](../install/installation.md#using-https) for instructions. 1. On your GitLab server, open the configuration file. @@ -22,7 +26,40 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application sudo -u git -H editor config/gitlab.yml ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) + for initial settings. + +1. To allow your users to use SAML to sign up without having to manually create + an account first, don't forget to add the following values to your configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] + gitlab_rails['omniauth_block_auto_created_users'] = false + ``` + + For installations from source: + + ```yaml + allow_single_sign_on: ["saml"] + block_auto_created_users: false + ``` + +1. You can also automatically link SAML users with existing GitLab users if their + email addresses match by adding the following setting: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_auto_link_saml_user'] = true + ``` + + For installations from source: + + ```yaml + auto_link_saml_user: true + ``` 1. Add the provider configuration: @@ -31,15 +68,15 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application ```ruby gitlab_rails['omniauth_providers'] = [ { - "name" => "saml", - args: { + name: 'saml', + args: { assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' }, - "label" => "Company Login" # optional label for SAML login button, defaults to "Saml" + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" } ] ``` @@ -47,34 +84,52 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application For installations from source: ```yaml - - { name: 'saml', - args: { - assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', - idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', - idp_sso_target_url: 'https://login.example.com/idp', - issuer: 'https://gitlab.example.com', - name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - } } + - { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', + idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', + idp_sso_target_url: 'https://login.example.com/idp', + issuer: 'https://gitlab.example.com', + name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' + }, + label: 'Company Login' # optional label for SAML login button, defaults to "Saml" + } ``` -1. Change the value for 'assertion_consumer_service_url' to match the HTTPS endpoint of GitLab (append 'users/auth/saml/callback' to the HTTPS URL of your GitLab installation to generate the correct value). +1. Change the value for `assertion_consumer_service_url` to match the HTTPS endpoint + of GitLab (append `users/auth/saml/callback` to the HTTPS URL of your GitLab + installation to generate the correct value). -1. Change the values of 'idp_cert_fingerprint', 'idp_sso_target_url', 'name_identifier_format' to match your IdP. Check [the omniauth-saml documentation](https://github.com/PracticallyGreen/omniauth-saml) for details on these options. +1. Change the values of `idp_cert_fingerprint`, `idp_sso_target_url`, + `name_identifier_format` to match your IdP. Check + [the omniauth-saml documentation](https://github.com/omniauth/omniauth-saml) + for details on these options. -1. Change the value of 'issuer' to a unique name, which will identify the application to the IdP. +1. Change the value of `issuer` to a unique name, which will identify the application + to the IdP. 1. Restart GitLab for the changes to take effect. -1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified in 'issuer'. +1. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified + in `issuer`. -To ease configuration, most IdP accept a metadata URL for the application to provide configuration information to the IdP. To build the metadata URL for GitLab, append 'users/auth/saml/metadata' to the HTTPS URL of your GitLab installation, for instance: +To ease configuration, most IdP accept a metadata URL for the application to provide +configuration information to the IdP. To build the metadata URL for GitLab, append +`users/auth/saml/metadata` to the HTTPS URL of your GitLab installation, for instance: ``` https://gitlab.example.com/users/auth/saml/metadata ``` -At a minimum the IdP *must* provide a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. GitLab will also use claims with name 'name', 'first_name', 'last_name' (see [the omniauth-saml gem](https://github.com/PracticallyGreen/omniauth-saml/blob/master/lib/omniauth/strategies/saml.rb) for supported claims). +At a minimum the IdP *must* provide a claim containing the user's email address, using +claim name `email` or `mail`. The email will be used to automatically generate the GitLab +username. GitLab will also use claims with name `name`, `first_name`, `last_name` +(see [the omniauth-saml gem](https://github.com/omniauth/omniauth-saml/blob/master/lib/omniauth/strategies/saml.rb) +for supported claims). -On the sign in page there should now be a SAML button below the regular sign in form. Click the icon to begin the authentication process. If everything goes well the user will be returned to GitLab and will be signed in. +On the sign in page there should now be a SAML button below the regular sign in form. +Click the icon to begin the authentication process. If everything goes well the user +will be returned to GitLab and will be signed in. ## Troubleshooting @@ -82,7 +137,7 @@ If you see a "500 error" in GitLab when you are redirected back from the SAML si this likely indicates that GitLab could not get the email address for the SAML user. Make sure the IdP provides a claim containing the user's email address, using claim name -'email' or 'mail'. The email will be used to automatically generate the GitLab username. +`email` or `mail`. If after signing in into your SAML server you are redirected back to the sign in page and no error is displayed, check your `production.log` file. It will most likely contain the diff --git a/features/admin/appearance.feature b/features/admin/appearance.feature new file mode 100644 index 00000000000..5c1dd7531c1 --- /dev/null +++ b/features/admin/appearance.feature @@ -0,0 +1,37 @@ +Feature: Admin Appearance + Scenario: Create new appearance + Given I sign in as an admin + And I visit admin appearance page + When submit form with new appearance + Then I should be redirected to admin appearance page + And I should see newly created appearance + + Scenario: Preview appearance + Given application has custom appearance + And I sign in as an admin + When I visit admin appearance page + And I click preview button + Then I should see a customized appearance + + Scenario: Custom sign-in page + Given application has custom appearance + When I visit login page + Then I should see a customized appearance + + Scenario: Appearance logo + Given application has custom appearance + And I sign in as an admin + And I visit admin appearance page + When I attach a logo + Then I should see a logo + And I remove the logo + Then I should see logo removed + + Scenario: Header logos + Given application has custom appearance + And I sign in as an admin + And I visit admin appearance page + When I attach header logos + Then I should see header logos + And I remove the header logos + Then I should see header logos removed diff --git a/features/project/milestone.feature b/features/project/milestone.feature index e0f4c0e9d7c..713f0f3b979 100644 --- a/features/project/milestone.feature +++ b/features/project/milestone.feature @@ -13,6 +13,7 @@ Feature: Project Milestone Given I visit project "Shop" milestones page And I click link "v2.2" Then I should see the labels "bug", "enhancement" and "feature" + And I should see the "bug" label listed only once @javascript Scenario: Listing labels from labels tab diff --git a/features/search.feature b/features/search.feature index a9234c1a611..3cd52810e59 100644 --- a/features/search.feature +++ b/features/search.feature @@ -65,3 +65,25 @@ Feature: Search And I search for "Wiki content" And I click "Wiki" link Then I should see "test_wiki" link in the search results + + Scenario: I logout and should see project I am looking for + Given project "Shop" is public + And I logout + And I search for "Sho" + Then I should see "Shop" project link + + Scenario: I logout and should see issues I am looking for + Given project "Shop" is public + And I logout + And project has issues + When I search for "Foo" + And I click "Issues" link + Then I should see "Foo" link in the search results + And I should not see "Bar" link in the search results + + Scenario: I logout and should see project code I am looking for + Given project "Shop" is public + And I logout + When I visit project "Shop" page + And I search for "rspec" on project page + Then I should see code results for project "Shop" diff --git a/features/steps/admin/appearance.rb b/features/steps/admin/appearance.rb new file mode 100644 index 00000000000..0d1be46d11d --- /dev/null +++ b/features/steps/admin/appearance.rb @@ -0,0 +1,72 @@ +class Spinach::Features::AdminAppearance < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + + step 'submit form with new appearance' do + fill_in 'appearance_title', with: 'MyCompany' + fill_in 'appearance_description', with: 'dev server' + click_button 'Save' + end + + step 'I should be redirected to admin appearance page' do + expect(current_path).to eq admin_appearances_path + expect(page).to have_content 'Appearance settings' + end + + step 'I should see newly created appearance' do + expect(page).to have_field('appearance_title', with: 'MyCompany') + expect(page).to have_field('appearance_description', with: 'dev server') + expect(page).to have_content 'Last edit' + end + + step 'I click preview button' do + click_link "Preview" + end + + step 'application has custom appearance' do + create(:appearance) + end + + step 'I should see a customized appearance' do + expect(page).to have_content appearance.title + expect(page).to have_content appearance.description + end + + step 'I attach a logo' do + attach_file(:appearance_logo, Rails.root.join('spec', 'fixtures', 'dk.png')) + click_button 'Save' + end + + step 'I attach header logos' do + attach_file(:appearance_header_logo, Rails.root.join('spec', 'fixtures', 'dk.png')) + click_button 'Save' + end + + step 'I should see a logo' do + expect(page).to have_xpath('//img[@src="/uploads/appearance/logo/1/dk.png"]') + end + + step 'I should see header logos' do + expect(page).to have_xpath('//img[@src="/uploads/appearance/header_logo/1/dk.png"]') + end + + step 'I remove the logo' do + click_link 'Remove logo' + end + + step 'I remove the header logos' do + click_link 'Remove header logo' + end + + step 'I should see logo removed' do + expect(page).not_to have_xpath('//img[@src="/uploads/appearance/logo/1/gitlab_logo.png"]') + end + + step 'I should see header logos removed' do + expect(page).not_to have_xpath('//img[@src="/uploads/appearance/header_logo/1/header_logo_light.png"]') + end + + def appearance + Appearance.last + end +end diff --git a/features/steps/project/project_milestone.rb b/features/steps/project/project_milestone.rb index ec881c0d8fc..2508c09e36d 100644 --- a/features/steps/project/project_milestone.rb +++ b/features/steps/project/project_milestone.rb @@ -41,6 +41,12 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps end end + step 'I should see the "bug" label listed only once' do + page.within('#tab-labels') do + expect(page).to have_content('bug', count: 1) + end + end + step 'I click link "v2.2"' do click_link "v2.2" end diff --git a/features/steps/search.rb b/features/steps/search.rb index 79273cbad9a..48ea3fa3876 100644 --- a/features/steps/search.rb +++ b/features/steps/search.rb @@ -18,6 +18,11 @@ class Spinach::Features::Search < Spinach::FeatureSteps click_button "Search" end + step 'I search for "rspec" on project page' do + fill_in "search", with: "rspec" + click_button "Go" + end + step 'I search for "Wiki content"' do fill_in "dashboard_search", with: "content" click_button "Search" @@ -103,4 +108,8 @@ class Spinach::Features::Search < Spinach::FeatureSteps @wiki = ::ProjectWiki.new(project, current_user) @wiki.create_page("test_wiki", "Some Wiki content", :markdown, "first commit") end + + step 'project "Shop" is public' do + project.update_attributes(visibility_level: Project::PUBLIC) + end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 0f9835e7356..6432a786bfc 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -7,6 +7,10 @@ module SharedPaths visit new_project_path end + step 'I visit login page' do + visit new_user_session_path + end + # ---------------------------------------- # User # ---------------------------------------- @@ -187,6 +191,10 @@ module SharedPaths visit admin_groups_path end + step 'I visit admin appearance page' do + visit admin_appearances_path + end + step 'I visit admin teams page' do visit admin_teams_path end diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 9422d438d21..8e74e177ea0 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -18,10 +18,12 @@ module API # Examples: # GET /projects/:id/repository/commits/:sha/statuses get ':id/repository/commits/:sha/statuses' do - authorize! :read_commit_status, user_project - sha = params[:sha] - ci_commit = user_project.ci_commit(sha) - not_found! 'Commit' unless ci_commit + authorize!(:read_commit_status, user_project) + + not_found!('Commit') unless user_project.commit(params[:sha]) + ci_commit = user_project.ci_commit(params[:sha]) + return [] unless ci_commit + statuses = ci_commit.statuses statuses = statuses.latest unless parse_boolean(params[:all]) statuses = statuses.where(ref: params[:ref]) if params[:ref].present? diff --git a/lib/api/internal.rb b/lib/api/internal.rb index e38736fc28b..2200208b946 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -14,6 +14,14 @@ module API # ref - branch name # forced_push - forced_push # + + helpers do + def wiki? + @wiki ||= params[:project].end_with?('.wiki') && + !Project.find_with_namespace(params[:project]) + end + end + post "/allowed" do status 200 @@ -30,13 +38,12 @@ module API # Strip out the .wiki from the pathname before finding the # project. This applies the correct project permissions to # the wiki repository as well. - wiki = project_path.end_with?('.wiki') - project_path.chomp!('.wiki') if wiki + project_path.chomp!('.wiki') if wiki? project = Project.find_with_namespace(project_path) access = - if wiki + if wiki? Gitlab::GitAccessWiki.new(actor, project) else Gitlab::GitAccess.new(actor, project) diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index fe01dae4850..bcf5297e382 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -50,21 +50,30 @@ module Banzai # See https://github.com/gollum/gollum/wiki # # Rubular: http://rubular.com/r/7dQnE5CUCH - TAGS_PATTERN = %r{\[\[(.+?)\]\]} + TAGS_PATTERN = %r{\[\[(.+?)\]\]}.freeze # Pattern to match allowed image extensions - ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i + ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i.freeze def call search_text_nodes(doc).each do |node| - content = node.content + # A Gollum ToC tag is `[[_TOC_]]`, but due to MarkdownFilter running + # before this one, it will be converted into `[[<em>TOC</em>]]`, so it + # needs special-case handling + if toc_tag?(node) + next unless result[:toc].present? - next unless content.match(TAGS_PATTERN) + process_toc_tag(node) + else + content = node.content - html = process_tag($1) + next unless content =~ TAGS_PATTERN - if html && html != node.content - node.replace(html) + html = process_tag($1) + + if html && html != node.content + node.replace(html) + end end end @@ -73,6 +82,12 @@ module Banzai private + # Replace an entire `[[<em>TOC</em>]]` node with the result generated by + # TableOfContentsFilter + def process_toc_tag(node) + node.parent.parent.replace(result[:toc]) + end + # Process a single tag into its final HTML form. # # tag - The String tag contents (the stuff inside the double brackets). @@ -108,6 +123,12 @@ module Banzai end end + def toc_tag?(node) + node.content == 'TOC' && + node.parent.name == 'em' && + node.parent.parent.text == '[[TOC]]' + end + def image?(path) path =~ ALLOWED_IMAGE_EXTENSIONS end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 04ddfe53ed6..abd79b329ae 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -7,6 +7,8 @@ module Banzai # # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. class SanitizationFilter < HTML::Pipeline::SanitizationFilter + UNSAFE_PROTOCOLS = %w(javascript :javascript data vbscript).freeze + def whitelist whitelist = super @@ -43,8 +45,8 @@ module Banzai # Allow any protocol in `a` elements... whitelist[:protocols].delete('a') - # ...but then remove links with the `javascript` protocol - whitelist[:transformers].push(remove_javascript_links) + # ...but then remove links with unsafe protocols + whitelist[:transformers].push(remove_unsafe_links) # Remove `rel` attribute from `a` elements whitelist[:transformers].push(remove_rel) @@ -55,14 +57,14 @@ module Banzai whitelist end - def remove_javascript_links + def remove_unsafe_links lambda do |env| node = env[:node] return unless node.name == 'a' return unless node.has_attribute?('href') - if node['href'].start_with?('javascript', ':javascript') + if node['href'].start_with?(*UNSAFE_PROTOCOLS) node.remove_attribute('href') end end diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb index 50b5450e70b..bc2bf111971 100644 --- a/lib/banzai/pipeline/wiki_pipeline.rb +++ b/lib/banzai/pipeline/wiki_pipeline.rb @@ -4,7 +4,11 @@ module Banzai module Pipeline class WikiPipeline < FullPipeline def self.filters - super.insert(1, Filter::GollumTagsFilter) + @filters ||= begin + filters = super + toc = filters.index(Filter::TableOfContentsFilter) + filters.insert(toc + 1, Filter::GollumTagsFilter) + end end end end diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index 59926084d07..850b73244c6 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -12,7 +12,7 @@ module Gitlab end def execute - project_identifier = CGI.escape(project.import_source, '/') + project_identifier = CGI.escape(project.import_source) #Issues && Comments issues = client.issues(project_identifier) diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 9e2fb429d57..f221afcf73a 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -1,5 +1,5 @@ namespace :cache do - CLEAR_BATCH_SIZE = 1000 # The more the faster, but having too many can crash Ruby + CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000 REDIS_SCAN_START_STOP = '0' # Magic value, see http://redis.io/commands/scan desc "GitLab | Clear redis cache" diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 104a5f50143..7337ff58be1 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -41,6 +41,7 @@ describe UsersController do end describe 'GET #calendar' do + it 'renders calendar' do sign_in(user) @@ -48,6 +49,23 @@ describe UsersController do expect(response).to render_template('calendar') end + + context 'forked project' do + let!(:project) { create(:project) } + let!(:forked_project) { Projects::ForkService.new(project, user).execute } + + before do + sign_in(user) + project.team << [user, :developer] + EventCreateService.new.push(project, user, []) + EventCreateService.new.push(forked_project, user, []) + end + + it 'includes forked projects' do + get :calendar, username: user.username + expect(assigns(:contributions_calendar).projects.count).to eq(2) + end + end end describe 'GET #calendar_activities' do diff --git a/spec/factories.rb b/spec/factories.rb index 264e3ed2c8d..cd57661c1cd 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -32,6 +32,7 @@ FactoryGirl.define do before(:create) do |user| user.two_factor_enabled = true user.otp_secret = User.generate_otp_secret(32) + user.otp_grace_period_started_at = Time.now user.generate_otp_backup_codes! end end diff --git a/spec/factories/appearances.rb b/spec/factories/appearances.rb new file mode 100644 index 00000000000..cf2a2b76bcb --- /dev/null +++ b/spec/factories/appearances.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :appearance do + title "MepMep" + description "This is my Community Edition instance" + end +end diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb index 38baa819957..11cc290d6d0 100644 --- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb +++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb @@ -86,4 +86,56 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do expect(doc.at_css('a')['href']).to eq 'wiki-slug' end end + + context 'table of contents' do + let(:pipeline) { Banzai::Pipeline[:wiki] } + + it 'replaces the tag with the TableOfContentsFilter result' do + markdown = <<-MD.strip_heredoc + [[_TOC_]] + + ## Header + + Foo + MD + + result = pipeline.call(markdown, project_wiki: project_wiki, project: project) + + aggregate_failures do + expect(result[:output].text).not_to include '[[_TOC_]]' + expect(result[:output].text).not_to include '[[' + expect(result[:output].to_html).to include(result[:toc]) + end + end + + it 'is case-sensitive' do + markdown = <<-MD.strip_heredoc + [[_toc_]] + + # Header 1 + + Foo + MD + + output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project) + + expect(output).to include('[[<em>toc</em>]]') + end + + it 'handles an empty pipeline result' do + # No Markdown headers in this doc, so `result[:toc]` will be empty + markdown = <<-MD.strip_heredoc + [[_TOC_]] + + Foo + MD + + output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project) + + aggregate_failures do + expect(output).not_to include('<ul>') + expect(output).to include('[[<em>TOC</em>]]') + end + end + end end diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index e14a6dbf922..4a7b00c7660 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -156,13 +156,27 @@ describe Banzai::Filter::SanitizationFilter, lib: true do } protocols.each do |name, data| - it "handles #{name}" do + it "disallows #{name}" do doc = filter(data[:input]) expect(doc.to_html).to eq data[:output] end end + it 'disallows data links' do + input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>' + output = filter(input) + + expect(output.to_html).to eq '<a>XSS</a>' + end + + it 'disallows vbscript links' do + input = '<a href="vbscript:alert(document.domain)">XSS</a>' + output = filter(input) + + expect(output.to_html).to eq '<a>XSS</a>' + end + it 'allows non-standard anchor schemes' do exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>} act = filter(exp) diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb new file mode 100644 index 00000000000..c5658bd26e1 --- /dev/null +++ b/spec/models/appearance_spec.rb @@ -0,0 +1,10 @@ +require 'rails_helper' + +RSpec.describe Appearance, type: :model do + subject { create(:appearance) } + + it { is_expected.to be_valid } + + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_presence_of(:description) } +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 88821dd0dad..412101ac9f9 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -176,7 +176,7 @@ describe User, models: true do end describe 'avatar' do - it 'only validates when avatar is present' do + it 'only validates when avatar is present and changed' do user = build(:user, :with_avatar) user.avatar_crop_x = nil @@ -184,6 +184,20 @@ describe User, models: true do user.avatar_crop_size = nil expect(user).not_to be_valid + expect(user.errors.keys). + to match_array %i(avatar_crop_x avatar_crop_y avatar_crop_size) + end + + it 'does not validate when avatar has not changed' do + user = create(:user, :with_avatar) + + expect { user.avatar_crop_x = nil }.not_to change(user, :valid?) + end + + it 'does not validate when avatar is not present' do + user = create(:user) + + expect { user.avatar_crop_y = nil }.not_to change(user, :valid?) end end end @@ -268,6 +282,7 @@ describe User, models: true do expect(user).to be_two_factor_enabled expect(user.encrypted_otp_secret).not_to be_nil expect(user.otp_backup_codes).not_to be_nil + expect(user.otp_grace_period_started_at).not_to be_nil user.disable_two_factor! @@ -276,6 +291,7 @@ describe User, models: true do expect(user.encrypted_otp_secret_iv).to be_nil expect(user.encrypted_otp_secret_salt).to be_nil expect(user.otp_backup_codes).to be_nil + expect(user.otp_grace_period_started_at).to be_nil end end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index 89b554622ef..8017ed97d88 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -2,88 +2,125 @@ require 'spec_helper' describe API::CommitStatus, api: true do include ApiHelpers + let!(:project) { create(:project) } let(:commit) { project.repository.commit } - let!(:ci_commit) { project.ensure_ci_commit(commit.id) } let(:commit_status) { create(:commit_status, commit: ci_commit) } let(:guest) { create_user(ProjectMember::GUEST) } let(:reporter) { create_user(ProjectMember::REPORTER) } let(:developer) { create_user(ProjectMember::DEVELOPER) } + let(:sha) { commit.id } + describe "GET /projects/:id/repository/commits/:sha/statuses" do - it_behaves_like 'a paginated resources' do - let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) } - end + let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" } - context "reporter user" do - let(:statuses_id) { json_response.map { |status| status['id'] } } + context 'ci commit exists' do + let!(:ci_commit) { project.ensure_ci_commit(commit.id) } - before do - @status1 = create(:commit_status, commit: ci_commit, status: 'running') - @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending') - @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running', allow_failure: true) - @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success') - @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success') - @status6 = create(:commit_status, commit: ci_commit, status: 'success') + it_behaves_like 'a paginated resources' do + let(:request) { get api(get_url, reporter) } end - it "should return latest commit statuses" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) - expect(response.status).to eq(200) + context "reporter user" do + let(:statuses_id) { json_response.map { |status| status['id'] } } - expect(json_response).to be_an Array - expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) - json_response.sort_by!{ |status| status['id'] } - expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false]) - end + def create_status(opts = {}) + create(:commit_status, { commit: ci_commit }.merge(opts)) + end - it "should return all commit statuses" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", reporter) - expect(response.status).to eq(200) + let!(:status1) { create_status(status: 'running') } + let!(:status2) { create_status(name: 'coverage', status: 'pending') } + let!(:status3) { create_status(ref: 'develop', status: 'running', allow_failure: true) } + let!(:status4) { create_status(name: 'coverage', status: 'success') } + let!(:status5) { create_status(name: 'coverage', ref: 'develop', status: 'success') } + let!(:status6) { create_status(status: 'success') } - expect(json_response).to be_an Array - expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id) - end + context 'latest commit statuses' do + before { get api(get_url, reporter) } - it "should return latest commit statuses for specific ref" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", reporter) - expect(response.status).to eq(200) + it 'returns latest commit statuses' do + expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(statuses_id).to contain_exactly(@status3.id, @status5.id) + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id) + json_response.sort_by!{ |status| status['id'] } + expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false]) + end + end + + context 'all commit statuses' do + before { get api(get_url, reporter), all: 1 } + + it 'returns all commit statuses' do + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(status1.id, status2.id, + status3.id, status4.id, + status5.id, status6.id) + end + end + + context 'latest commit statuses for specific ref' do + before { get api(get_url, reporter), ref: 'develop' } + + it 'returns latest commit statuses for specific ref' do + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(status3.id, status5.id) + end + end + + context 'latest commit statues for specific name' do + before { get api(get_url, reporter), name: 'coverage' } + + it 'return latest commit statuses for specific name' do + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(status4.id, status5.id) + end + end end + end - it "should return latest commit statuses for specific name" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", reporter) - expect(response.status).to eq(200) + context 'ci commit does not exist' do + before { get api(get_url, reporter) } + it 'returns empty array' do + expect(response.status).to eq 200 expect(json_response).to be_an Array - expect(statuses_id).to contain_exactly(@status3.id, @status4.id) + expect(json_response).to be_empty end end context "guest user" do + before { get api(get_url, guest) } + it "should not return project commits" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", guest) expect(response.status).to eq(403) end end context "unauthorized user" do + before { get api(get_url) } + it "should not return project commits" do - get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses") expect(response.status).to eq(401) end end end describe 'POST /projects/:id/statuses/:sha' do - let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } + let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" } context 'developer user' do - context 'should create commit status' do - it 'with only required parameters' do - post api(post_url, developer), state: 'success' + context 'only required parameters' do + before { post api(post_url, developer), state: 'success' } + + it 'creates commit status' do expect(response.status).to eq(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') @@ -92,9 +129,17 @@ describe API::CommitStatus, api: true do expect(json_response['target_url']).to be_nil expect(json_response['description']).to be_nil end + end - it 'with all optional parameters' do - post api(post_url, developer), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' + context 'with all optional parameters' do + before do + optional_params = { state: 'success', context: 'coverage', + ref: 'develop', target_url: 'url', description: 'test' } + + post api(post_url, developer), optional_params + end + + it 'creates commit status' do expect(response.status).to eq(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') @@ -105,41 +150,52 @@ describe API::CommitStatus, api: true do end end - context 'should not create commit status' do - it 'with invalid state' do - post api(post_url, developer), state: 'invalid' + context 'invalid status' do + before { post api(post_url, developer), state: 'invalid' } + + it 'does not create commit status' do expect(response.status).to eq(400) end + end - it 'without state' do - post api(post_url, developer) + context 'request without state' do + before { post api(post_url, developer) } + + it 'does not create commit status' do expect(response.status).to eq(400) end + end - it 'invalid commit' do - post api("/projects/#{project.id}/statuses/invalid_sha", developer), state: 'running' + context 'invalid commit' do + let(:sha) { 'invalid_sha' } + before { post api(post_url, developer), state: 'running' } + + it 'returns not found error' do expect(response.status).to eq(404) end end end context 'reporter user' do + before { post api(post_url, reporter) } + it 'should not create commit status' do - post api(post_url, reporter) expect(response.status).to eq(403) end end context 'guest user' do + before { post api(post_url, guest) } + it 'should not create commit status' do - post api(post_url, guest) expect(response.status).to eq(403) end end context 'unauthorized user' do + before { post api(post_url) } + it 'should not create commit status' do - post api(post_url) expect(response.status).to eq(401) end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 8d0ae1475c2..22802dd0e05 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -54,6 +54,18 @@ describe API::API, api: true do project.team << [user, :developer] end + context "git push with project.wiki" do + it 'responds with success' do + project_wiki = create(:project, name: 'my.wiki', path: 'my.wiki') + project_wiki.team << [user, :developer] + + push(key, project_wiki) + + expect(response.status).to eq(200) + expect(json_response["status"]).to be_truthy + end + end + context "git pull" do it do pull(key, project) |