Commit a4e522a8 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into minimize-on-diff

parents 9f0a71f4 4a9affa7
image: "ruby:2.2" image: "ruby:2.1"
services: services:
- mysql:latest - mysql:latest
...@@ -6,7 +6,7 @@ services: ...@@ -6,7 +6,7 @@ services:
- redis:latest - redis:latest
cache: cache:
key: "ruby22" key: "ruby21"
paths: paths:
- vendor - vendor
...@@ -24,7 +24,12 @@ before_script: ...@@ -24,7 +24,12 @@ before_script:
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
- RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate
stages:
- test
- notifications
spec:feature: spec:feature:
stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
...@@ -33,6 +38,7 @@ spec:feature: ...@@ -33,6 +38,7 @@ spec:feature:
- mysql - mysql
spec:api: spec:api:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
tags: tags:
...@@ -40,6 +46,7 @@ spec:api: ...@@ -40,6 +46,7 @@ spec:api:
- mysql - mysql
spec:models: spec:models:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
tags: tags:
...@@ -47,6 +54,7 @@ spec:models: ...@@ -47,6 +54,7 @@ spec:models:
- mysql - mysql
spec:lib: spec:lib:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
tags: tags:
...@@ -54,6 +62,7 @@ spec:lib: ...@@ -54,6 +62,7 @@ spec:lib:
- mysql - mysql
spec:services: spec:services:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
tags: tags:
...@@ -61,6 +70,7 @@ spec:services: ...@@ -61,6 +70,7 @@ spec:services:
- mysql - mysql
spec:benchmark: spec:benchmark:
stage: test
script: script:
- RAILS_ENV=test bundle exec rake spec:benchmark - RAILS_ENV=test bundle exec rake spec:benchmark
tags: tags:
...@@ -69,6 +79,7 @@ spec:benchmark: ...@@ -69,6 +79,7 @@ spec:benchmark:
allow_failure: true allow_failure: true
spec:other: spec:other:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
tags: tags:
...@@ -76,6 +87,7 @@ spec:other: ...@@ -76,6 +87,7 @@ spec:other:
- mysql - mysql
spinach:project:half: spinach:project:half:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
tags: tags:
...@@ -83,6 +95,7 @@ spinach:project:half: ...@@ -83,6 +95,7 @@ spinach:project:half:
- mysql - mysql
spinach:project:rest: spinach:project:rest:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags: tags:
...@@ -90,6 +103,7 @@ spinach:project:rest: ...@@ -90,6 +103,7 @@ spinach:project:rest:
- mysql - mysql
spinach:other: spinach:other:
stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
tags: tags:
...@@ -97,6 +111,7 @@ spinach:other: ...@@ -97,6 +111,7 @@ spinach:other:
- mysql - mysql
teaspoon: teaspoon:
stage: test
script: script:
- RAILS_ENV=test bundle exec teaspoon - RAILS_ENV=test bundle exec teaspoon
tags: tags:
...@@ -104,6 +119,7 @@ teaspoon: ...@@ -104,6 +119,7 @@ teaspoon:
- mysql - mysql
rubocop: rubocop:
stage: test
script: script:
- bundle exec rubocop - bundle exec rubocop
tags: tags:
...@@ -111,6 +127,7 @@ rubocop: ...@@ -111,6 +127,7 @@ rubocop:
- mysql - mysql
brakeman: brakeman:
stage: test
script: script:
- bundle exec rake brakeman - bundle exec rake brakeman
tags: tags:
...@@ -118,6 +135,7 @@ brakeman: ...@@ -118,6 +135,7 @@ brakeman:
- mysql - mysql
flog: flog:
stage: test
script: script:
- bundle exec rake flog - bundle exec rake flog
tags: tags:
...@@ -125,6 +143,7 @@ flog: ...@@ -125,6 +143,7 @@ flog:
- mysql - mysql
flay: flay:
stage: test
script: script:
- bundle exec rake flay - bundle exec rake flay
tags: tags:
...@@ -132,6 +151,7 @@ flay: ...@@ -132,6 +151,7 @@ flay:
- mysql - mysql
bundler:audit: bundler:audit:
stage: test
script: script:
- "bundle exec bundle-audit update" - "bundle exec bundle-audit update"
- "bundle exec bundle-audit check" - "bundle exec bundle-audit check"
...@@ -140,87 +160,93 @@ bundler:audit: ...@@ -140,87 +160,93 @@ bundler:audit:
- mysql - mysql
allow_failure: true allow_failure: true
# Ruby 2.1 jobs # Ruby 2.2 jobs
spec:feature:ruby21: spec:feature:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spec:api:ruby21: spec:api:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spec:models:ruby21: spec:models:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spec:lib:ruby21: spec:lib:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spec:services:ruby21: spec:services:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spec:benchmark:ruby21: spec:benchmark:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test bundle exec rake spec:benchmark - RAILS_ENV=test bundle exec rake spec:benchmark
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
...@@ -228,59 +254,74 @@ spec:benchmark:ruby21: ...@@ -228,59 +254,74 @@ spec:benchmark:ruby21:
- mysql - mysql
allow_failure: true allow_failure: true
spec:other:ruby21: spec:other:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spinach:project:half:ruby21: spinach:project:half:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spinach:project:rest:ruby21: spinach:project:rest:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
spinach:other:ruby21: spinach:other:ruby22:
image: ruby:2.1 stage: test
image: ruby:2.2
only: only:
- master - master
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
cache: cache:
key: "ruby21" key: "ruby22"
paths: paths:
- vendor - vendor
tags: tags:
- ruby - ruby
- mysql - mysql
notify:slack:
stage: notifications
script:
- ./scripts/notify_slack.sh "#builds" "Build failed for master/tags!"
when: on_failure
only:
- master@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- tags@gitlab-org/gitlab-ee
\ No newline at end of file
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased) v 8.6.0 (unreleased)
- Improve the formatting for the user page bio (Connor Shea)
v 8.5.1
- Fix group projects styles
- Show Crowd login tab when sign in is disabled and Crowd is enabled (Peter Hudec)
- Fix a set of small UI glitches in project, profile, and wiki pages
- Restrict permissions on public/uploads
- Fix the merge request side-by-side view after loading diff results
- Fix the look of tooltip for the "Revert" button
- Add when the Builds & Runners API changes got introduced
- Fix error 500 on some merged merge requests
- Fix an issue causing the content of the issuable sidebar to disappear
- Fix error 500 when trying to mark an already done todo as "done"
- Fix an issue where MRs weren't sortable
- Issues can now be dragged & dropped into empty milestone lists. This is also
possible with MRs
- Changed padding & background color for highlighted notes
- Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu)
- Update sentry-raven gem to 0.15.6
v 8.5.0
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu) - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- Cache various Repository methods to improve performance (Yorick Peterse) - Cache various Repository methods to improve performance (Yorick Peterse)
- Fix duplicated branch creation/deletion Web hooks/service notifications when using Web UI (Stan Hu) - Fix duplicated branch creation/deletion Web hooks/service notifications when using Web UI (Stan Hu)
...@@ -46,22 +67,38 @@ v 8.5.0 (unreleased) ...@@ -46,22 +67,38 @@ v 8.5.0 (unreleased)
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead - Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead - Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
- Prevent parse error when name of project ends with .atom and prevent path issues - Prevent parse error when name of project ends with .atom and prevent path issues
- Discover branches for commit statuses ref-less when doing merge when succeeded
- Mark inline difference between old and new paths when a file is renamed - Mark inline difference between old and new paths when a file is renamed
- Support Akismet spam checking for creation of issues via API (Stan Hu) - Support Akismet spam checking for creation of issues via API (Stan Hu)
- API: Allow to set or update a merge-request's milestone (Kirill Skachkov) - API: Allow to set or update a merge-request's milestone (Kirill Skachkov)
- Improve UI consistency between projects and groups lists - Improve UI consistency between projects and groups lists
- Add sort dropdown to dashboard projects page - Add sort dropdown to dashboard projects page
- Fixed logo animation on Safari (Roman Rott) - Fixed logo animation on Safari (Roman Rott)
- Fix Merge When Succeeded when multiple stages
- Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg) - Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
- In seach autocomplete show only groups and projects you are member of - In seach autocomplete show only groups and projects you are member of
- Don't process cross-reference notes from forks - Don't process cross-reference notes from forks
- Fix: init.d script not working on OS X - Fix: init.d script not working on OS X
- Faster snippet search - Faster snippet search
- Added API to download build artifacts
- Title for milestones should be unique (Zeger-Jan van de Weg) - Title for milestones should be unique (Zeger-Jan van de Weg)
- Validate correctness of maximum attachment size application setting - Validate correctness of maximum attachment size application setting
- Replaces "Create merge request" link with one to the "Merge Request" when one exists - Replaces "Create merge request" link with one to the "Merge Request" when one exists
- Fix CI builds badge, add a new link to builds badge, deprecate the old one - Fix CI builds badge, add a new link to builds badge, deprecate the old one
- Fix broken link to project in build notification emails - Fix broken link to project in build notification emails
- Ability to see and sort on vote count from Issues and MR lists
- Fix builds scheduler when first build in stage was allowed to fail
- User project limit is reached notice is hidden if the projects limit is zero
- Add API support for managing runners and project's runners
- Allow SAML users to login with no previous account without having to allow
all Omniauth providers to do so.
- Allow existing users to auto link their SAML credentials by logging in via SAML
- Make it possible to erase a build (trace, artifacts) using UI and API
- Ability to revert changes from a Merge Request or Commit
- Emoji comment on diffs are not award emoji
- Add label description (Nuttanart Pornprasitsakul)
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos
v 8.4.4 v 8.4.4
- Update omniauth-saml gem to 1.4.2 - Update omniauth-saml gem to 1.4.2
......
...@@ -34,7 +34,7 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial ...@@ -34,7 +34,7 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial
edition. Throughout this guide you will see references to CE and EE for edition. Throughout this guide you will see references to CE and EE for
abbreviation. abbreviation.
If you have read this guide and want to know how the GitLab [core-team][] If you have read this guide and want to know how the GitLab [core team][core-team]
operates please see [the GitLab contributing process](PROCESS.md). operates please see [the GitLab contributing process](PROCESS.md).
## Contributor license agreement ## Contributor license agreement
...@@ -68,10 +68,10 @@ for audiences of all ages. ...@@ -68,10 +68,10 @@ for audiences of all ages.
## Helping others ## Helping others
Please help other GitLab users when you can. The channels people will reach out Please help other GitLab users when you can. The channels people will reach out
on can be found on the [getting help page][]. on can be found on the [getting help page][getting-help].
Sign up for the mailing list, answer GitLab questions on StackOverflow or Sign up for the mailing list, answer GitLab questions on StackOverflow or
respond in the IRC channel. You can also sign up on [CodeTriage][] to help with respond in the IRC channel. You can also sign up on [CodeTriage][codetriage] to help with
the remaining issues on the GitHub issue tracker. the remaining issues on the GitHub issue tracker.
## I want to contribute! ## I want to contribute!
...@@ -115,7 +115,7 @@ For feature proposals for EE, open an issue on the ...@@ -115,7 +115,7 @@ For feature proposals for EE, open an issue on the
In order to help track the feature proposals, we have created a In order to help track the feature proposals, we have created a
[`feature proposal`][fpl] label. For the time being, users that are not members [`feature proposal`][fpl] label. For the time being, users that are not members
of the project cannot add labels. You can instead ask one of the [core team][] of the project cannot add labels. You can instead ask one of the [core team][core-team]
members to add the label `feature proposal` to the issue. members to add the label `feature proposal` to the issue.
Please keep feature proposals as small and simple as possible, complex ones Please keep feature proposals as small and simple as possible, complex ones
...@@ -299,8 +299,8 @@ to us than having a minimal commit log. The smaller an MR is the more likely it ...@@ -299,8 +299,8 @@ 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. 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 For examples of feedback on merge requests please look at already
[closed merge requests][]. If you would like quick feedback on your merge [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][]. request feel free to mention one of the Merge Marshalls of the [core team][core-team].
Please ensure that your merge request meets the contribution acceptance criteria. Please ensure that your merge request meets the contribution acceptance criteria.
When having your code reviewed and when reviewing merge requests please take the When having your code reviewed and when reviewing merge requests please take the
...@@ -369,7 +369,7 @@ Like all merge requests the target should be master so all bugfixes are in maste ...@@ -369,7 +369,7 @@ Like all merge requests the target should be master so all bugfixes are in maste
## Definition of done ## Definition of done
If you contribute to GitLab please know that changes involve more than just If you contribute to GitLab please know that changes involve more than just
code. We have the following [definition of done][]. Please ensure you support code. We have the following [definition of done][definition-of-done]. Please ensure you support
the feature you contribute through all of these steps. the feature you contribute through all of these steps.
1. Description explaining the relevancy (see following item) 1. Description explaining the relevancy (see following item)
...@@ -448,12 +448,12 @@ when an individual is representing the project or its community. ...@@ -448,12 +448,12 @@ when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior can be Instances of abusive, harassing, or otherwise unacceptable behavior can be
reported by emailing `contact@gitlab.com`. reported by emailing `contact@gitlab.com`.
This Code of Conduct is adapted from the [Contributor Covenant][], version 1.1.0, This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
[core-team]: https://about.gitlab.com/core-team/ [core-team]: https://about.gitlab.com/core-team/
[getting help page]: https://about.gitlab.com/getting-help/ [getting-help]: https://about.gitlab.com/getting-help/
[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455 [medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues [ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
...@@ -467,9 +467,9 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -467,9 +467,9 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[github-mr-tracker]: https://github.com/gitlabhq/gitlabhq/pulls [github-mr-tracker]: https://github.com/gitlabhq/gitlabhq/pulls
[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit [gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits [git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
[closed merge requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed [closed-merge-requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed
[definition of done]: http://guide.agilealliance.org/guide/definition-of-done.html [definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
[Contributor Covenant]: http://contributor-covenant.org [contributor-covenant]: http://contributor-covenant.org
[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout [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 [rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
...@@ -50,7 +50,7 @@ gem "browser", '~> 1.0.0' ...@@ -50,7 +50,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 8.1' gem "gitlab_git", '~> 8.2'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
...@@ -112,7 +112,7 @@ gem 'diffy', '~> 3.0.3' ...@@ -112,7 +112,7 @@ gem 'diffy', '~> 3.0.3'
# Application server # Application server
group :unicorn do group :unicorn do
gem "unicorn", '~> 4.8.2' gem "unicorn", '~> 4.9.0'
gem 'unicorn-worker-killer', '~> 0.4.2' gem 'unicorn-worker-killer', '~> 0.4.2'
end end
...@@ -204,7 +204,7 @@ gem 'jquery-turbolinks', '~> 2.1.0' ...@@ -204,7 +204,7 @@ gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0' gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.2' gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.2.0' gem 'gitlab_emoji', '~> 0.3.0'
gem 'gon', '~> 6.0.1' gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.0.0' gem 'jquery-rails', '~> 4.0.0'
...@@ -218,7 +218,7 @@ gem 'virtus', '~> 1.0.1' ...@@ -218,7 +218,7 @@ gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1' gem 'net-ssh', '~> 3.0.1'
# Sentry integration # Sentry integration
gem 'sentry-raven' gem 'sentry-raven', '~> 0.15'
# Metrics # Metrics
group :metrics do group :metrics do
...@@ -303,6 +303,8 @@ group :production do ...@@ -303,6 +303,8 @@ group :production do
gem "gitlab_meta", '7.0' gem "gitlab_meta", '7.0'
end end
gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 3.8.0' gem 'octokit', '~> 3.8.0'
gem "mail_room", "~> 0.6.1" gem "mail_room", "~> 0.6.1"
......
...@@ -336,11 +336,11 @@ GEM ...@@ -336,11 +336,11 @@ GEM
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6) gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21) rugged (~> 0.21)
gemojione (2.1.1) gemojione (2.2.1)
json json
get_process_mem (0.2.0) get_process_mem (0.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
github-linguist (4.7.3) github-linguist (4.7.5)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
...@@ -355,13 +355,13 @@ GEM ...@@ -355,13 +355,13 @@ GEM
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_emoji (0.2.0) gitlab_emoji (0.3.1)
gemojione (~> 2.1) gemojione (~> 2.2, >= 2.2.1)
gitlab_git (8.1.0) gitlab_git (8.2.0)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
rugged (~> 0.23.3) rugged (~> 0.24.0b13)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.2.1) gitlab_omniauth-ldap (1.2.1)
net-ldap (~> 0.9) net-ldap (~> 0.9)
...@@ -479,6 +479,7 @@ GEM ...@@ -479,6 +479,7 @@ GEM
net-ldap (0.12.1) net-ldap (0.12.1)
net-ssh (3.0.1) net-ssh (3.0.1)
netrc (0.11.0) netrc (0.11.0)
newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2) nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2) mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7) nprogress-rails (0.1.6.7)
...@@ -700,7 +701,7 @@ GEM ...@@ -700,7 +701,7 @@ GEM
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rufus-scheduler (3.1.10) rufus-scheduler (3.1.10)
rugged (0.23.3) rugged (0.24.0b13)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -722,7 +723,7 @@ GEM ...@@ -722,7 +723,7 @@ GEM
activesupport (>= 3.1, < 4.3) activesupport (>= 3.1, < 4.3)
select2-rails (3.5.9.3) select2-rails (3.5.9.3)
thor (~> 0.14) thor (~> 0.14)
sentry-raven (0.15.4) sentry-raven (0.15.6)
faraday (>= 0.7.6) faraday (>= 0.7.6)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.6.0) sexp_processor (4.6.0)
...@@ -833,7 +834,7 @@ GEM ...@@ -833,7 +834,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.1) unf_ext (0.0.7.1)
unicorn (4.8.3) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
raindrops (~> 0.7) raindrops (~> 0.7)
...@@ -931,8 +932,8 @@ DEPENDENCIES ...@@ -931,8 +932,8 @@ DEPENDENCIES
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.3.1) github-markup (~> 1.3.1)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_emoji (~> 0.2.0) gitlab_emoji (~> 0.3.0)
gitlab_git (~> 8.1) gitlab_git (~> 8.2)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0) gollum-lib (~> 4.1.0)
...@@ -960,6 +961,7 @@ DEPENDENCIES ...@@ -960,6 +961,7 @@ DEPENDENCIES
nested_form (~> 0.3.2) nested_form (~> 0.3.2)
net-ssh (~> 3.0.1) net-ssh (~> 3.0.1)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
newrelic_rpm (~> 3.14)
nprogress-rails (~> 0.1.6.7) nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.8.0) octokit (~> 3.8.0)
...@@ -1007,7 +1009,7 @@ DEPENDENCIES ...@@ -1007,7 +1009,7 @@ DEPENDENCIES
sdoc (~> 0.3.20) sdoc (~> 0.3.20)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven sentry-raven (~> 0.15)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack sham_rack
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
...@@ -1034,7 +1036,7 @@ DEPENDENCIES ...@@ -1034,7 +1036,7 @@ DEPENDENCIES
uglifier (~> 2.7.2) uglifier (~> 2.7.2)
underscore-rails (~> 1.8.0) underscore-rails (~> 1.8.0)
unf (~> 0.1.4) unf (~> 0.1.4)
unicorn (~> 4.8.2) unicorn (~> 4.9.0)
unicorn-worker-killer (~> 0.4.2) unicorn-worker-killer (~> 0.4.2)
version_sorter (~> 2.0.0) version_sorter (~> 2.0.0)
virtus (~> 1.0.1) virtus (~> 1.0.1)
......
...@@ -67,7 +67,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the ...@@ -67,7 +67,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software: GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL - Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.1 or 2.2 - Ruby (MRI) 2.1
- Git 1.7.10+ - Git 1.7.10+
- Redis 2.8+ - Redis 2.8+
- MySQL or PostgreSQL - MySQL or PostgreSQL
......
8.5.0-pre 8.6.0-pre
app/assets/images/emoji.png

813 KB | W: | H:

app/assets/images/emoji.png

257 KB | W: | H:

app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
  • 2-up
  • Swipe
  • Onion skin
class @Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true
$(".event-filter a").bind "click", (event) => $(".event-filter-link").on "click", (event) =>
event.preventDefault() event.preventDefault()
@toggleFilter($(event.currentTarget)) @toggleFilter($(event.currentTarget))
@reloadActivities() @reloadActivities()
...@@ -12,18 +12,10 @@ class @Activities ...@@ -12,18 +12,10 @@ class @Activities
toggleFilter: (sender) -> toggleFilter: (sender) ->
sender.closest('li').toggleClass "active" $('.event-filter .active').removeClass "active"
event_filters = $.cookie("event_filter") event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0] filter = sender.attr("id").split("_")[0]
if event_filters $.cookie "event_filter", (if event_filters isnt filter then filter else ""), { path: '/' }
event_filters = event_filters.split(",")
else
event_filters = new Array()
index = event_filters.indexOf(filter) if event_filters isnt filter
if index is -1 sender.closest('li').toggleClass "active"
event_filters.push filter
else
event_filters.splice index, 1
$.cookie "event_filter", event_filters.join(","), { path: '/' }
class @AwardsHandler class @AwardsHandler
constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) -> constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
$(".add-award").click (event)-> $(".add-award").click (event) =>
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
$(".emoji-menu").show()
$("#emoji_search").focus() @showEmojiMenu()
$("html").on 'click', (event) -> $("html").on 'click', (event) ->
if !$(event.target).closest(".emoji-menu").length if !$(event.target).closest(".emoji-menu").length
...@@ -14,6 +14,16 @@ class @AwardsHandler ...@@ -14,6 +14,16 @@ class @AwardsHandler
@renderFrequentlyUsedBlock() @renderFrequentlyUsedBlock()
@setupSearch() @setupSearch()
showEmojiMenu: ->
if $(".emoji-menu").length
$(".emoji-menu").show()
$("#emoji_search").focus()
else
$.get "/emojis", (response) ->
$(".add-award").after response
$(".emoji-menu").show()
$("#emoji_search").focus()
addAward: (emoji) -> addAward: (emoji) ->
emoji = @normilizeEmojiName(emoji) emoji = @normilizeEmojiName(emoji)
@postEmoji emoji, => @postEmoji emoji, =>
......
...@@ -76,6 +76,8 @@ class Dispatcher ...@@ -76,6 +76,8 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:show' when 'projects:show'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new TreeView() if $('#tree-slider').length
when 'groups:show' when 'groups:show'
new Activities() new Activities()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -88,10 +90,11 @@ class Dispatcher ...@@ -88,10 +90,11 @@ class Dispatcher
when 'groups:new', 'groups:edit', 'admin:groups:edit', 'admin:groups:new' when 'groups:new', 'groups:edit', 'admin:groups:edit', 'admin:groups:new'
new GroupAvatar() new GroupAvatar()
when 'projects:tree:show' when 'projects:tree:show'
shortcut_handler = new ShortcutsNavigation()
new TreeView() new TreeView()
when 'projects:find_file:show' when 'projects:find_file:show'
shortcut_handler = true shortcut_handler = true
when 'projects:blob:show' when 'projects:blob:show', 'projects:blame:show'
new LineHighlighter() new LineHighlighter()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit' when 'projects:labels:new', 'projects:labels:edit'
......
...@@ -15,3 +15,5 @@ class @IssuableContext ...@@ -15,3 +15,5 @@ class @IssuableContext
block.find('.selectbox').show() block.find('.selectbox').show()
block.find('.value').hide() block.find('.value').hide()
block.find('.js-select2').select2("open") block.find('.js-select2').select2("open")
$(".right-sidebar").niceScroll()
...@@ -146,6 +146,7 @@ class @MergeRequestTabs ...@@ -146,6 +146,7 @@ class @MergeRequestTabs
success: (data) => success: (data) =>
document.querySelector("div#diffs").innerHTML = data.html document.querySelector("div#diffs").innerHTML = data.html
$('div#diffs .js-syntax-highlight').syntaxHighlight() $('div#diffs .js-syntax-highlight').syntaxHighlight()
@expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true @diffsLoaded = true
@scrollToElement("#diffs") @scrollToElement("#diffs")
...@@ -177,3 +178,10 @@ class @MergeRequestTabs ...@@ -177,3 +178,10 @@ class @MergeRequestTabs
options = $.extend({}, defaults, options) options = $.extend({}, defaults, options)
$.ajax(options) $.ajax(options)
# Returns diff view type
diffViewType: ->
$('.inline-parallel-buttons a.active').data('view-type')
expandViewContainer: ->
$('.container-fluid').removeClass('container-limited')
...@@ -62,14 +62,24 @@ class @Milestone ...@@ -62,14 +62,24 @@ class @Milestone
dataType: "json" dataType: "json"
constructor: -> constructor: ->
oldMouseStart = $.ui.sortable.prototype._mouseStart
$.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) ->
this._trigger "beforeStart", event, this._uiHash()
oldMouseStart.apply this, [event, overrideHandle, noActivation]
@bindIssuesSorting() @bindIssuesSorting()
@bindMergeRequestSorting() @bindMergeRequestSorting()
@bindTabsSwitching
bindIssuesSorting: -> bindIssuesSorting: ->
$("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable( $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable(
connectWith: ".issues-sortable-list", connectWith: ".issues-sortable-list",
dropOnEmpty: true, dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)", items: "li:not(.ui-sort-disabled)",
beforeStart: (event, ui) ->
$(".issues-sortable-list").css "min-height", ui.item.outerHeight()
stop: (event, ui) ->
$(".issues-sortable-list").css "min-height", "0px"
update: (event, ui) -> update: (event, ui) ->
data = $(this).sortable("serialize") data = $(this).sortable("serialize")
Milestone.sortIssues(data) Milestone.sortIssues(data)
...@@ -95,10 +105,22 @@ class @Milestone ...@@ -95,10 +105,22 @@ class @Milestone
).disableSelection() ).disableSelection()
bindMergeRequestSorting: -> bindMergeRequestSorting: ->
$('a[data-toggle="tab"]').on 'show.bs.tab', (e) ->
currentTabClass = $(e.target).data('show')
previousTabClass = $(e.relatedTarget).data('show')
$(previousTabClass).hide()
$(currentTabClass).removeClass('hidden')
$(currentTabClass).show()
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable( $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable(
connectWith: ".merge_requests-sortable-list", connectWith: ".merge_requests-sortable-list",
dropOnEmpty: true, dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)", items: "li:not(.ui-sort-disabled)",
beforeStart: (event, ui) ->
$(".merge_requests-sortable-list").css "min-height", ui.item.outerHeight()
stop: (event, ui) ->
$(".merge_requests-sortable-list").css "min-height", "0px"
update: (event, ui) -> update: (event, ui) ->
data = $(this).sortable("serialize") data = $(this).sortable("serialize")
Milestone.sortMergeRequests(data) Milestone.sortMergeRequests(data)
......
...@@ -24,6 +24,10 @@ class @ShortcutsIssuable extends ShortcutsNavigation ...@@ -24,6 +24,10 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@nextIssue() @nextIssue()
return false return false
) )
Mousetrap.bind('e', =>
@editIssue()
return false
)
if isMergeRequest if isMergeRequest
...@@ -63,3 +67,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation ...@@ -63,3 +67,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation
# Focus the input field # Focus the input field
replyField.focus() replyField.focus()
editIssue: ->
$editBtn = $('.issuable-edit')
Turbolinks.visit($editBtn.attr('href'))
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
class @Wikis class @Wikis
constructor: -> constructor: ->
$('.build-new-wiki').bind 'click', (e) => $('.new-wiki-page').on 'submit', (e) =>
$('[data-error~=slug]').addClass('hidden') $('[data-error~=slug]').addClass('hidden')
field = $('#new_wiki_path') field = $('#new_wiki_path')
slug = @slugify(field.val()) slug = @slugify(field.val())
...@@ -10,6 +10,7 @@ class @Wikis ...@@ -10,6 +10,7 @@ class @Wikis
if (slug.length > 0) if (slug.length > 0)
path = field.attr('data-wikis-path') path = field.attr('data-wikis-path')
location.href = path + '/' + slug location.href = path + '/' + slug
e.preventDefault()
dasherize: (value) -> dasherize: (value) ->
value.replace(/[_\s]+/g, '-') value.replace(/[_\s]+/g, '-')
......
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
} }
.oneline { .oneline {
line-height: 42px; line-height: 35px;
} }
> p:last-child { > p:last-child {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
&:focus, &:focus,
&:active { &:active {
outline: none; outline: none;
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); @include box-shadow($gl-btn-active-background);
} }
} }
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
} }
&:active { &:active {
@include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); @include box-shadow ($gl-btn-active-background);
background-color: $dark; background-color: $dark;
border-color: $border-dark; border-color: $border-dark;
...@@ -68,6 +68,12 @@ ...@@ -68,6 +68,12 @@
@include btn-default; @include btn-default;
@include btn-white; @include btn-white;
color: $gl-text-color;
&:focus:active {
outline: 0;
}
&.btn-small, &.btn-small,
&.btn-sm { &.btn-sm {
padding: 4px 10px; padding: 4px 10px;
...@@ -130,6 +136,11 @@ ...@@ -130,6 +136,11 @@
&.disabled { &.disabled {
pointer-events: auto !important; pointer-events: auto !important;
} }
.caret {
margin-left: 5px;
color: $gray-darkest;
}
} }
.btn-block { .btn-block {
...@@ -179,7 +190,7 @@ ...@@ -179,7 +190,7 @@
} }
.active { .active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); @include box-shadow($gl-btn-active-background);
border: 1px solid #c6cacf !important; border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important; background-color: #e4e7ed !important;
......
...@@ -56,6 +56,10 @@ hr { ...@@ -56,6 +56,10 @@ hr {
margin: $gl-padding 0; margin: $gl-padding 0;
} }
.dropdown-menu {
margin: 6px 0 0;
}
.dropdown-menu > li > a { .dropdown-menu > li > a {
text-shadow: none; text-shadow: none;
} }
......
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
} }
&:hover { &:hover {
background: $hover; background: $row-hover;
} }
} }
} }
......
...@@ -21,10 +21,3 @@ ...@@ -21,10 +21,3 @@
} }
} }
} }
.issues-filters,
.issues_bulk_update {
.select2-container .select2-choice {
color: #444 !important;
}
}
...@@ -117,20 +117,4 @@ body { ...@@ -117,20 +117,4 @@ body {
&.ui_violet { &.ui_violet {
@include gitlab-theme(#9988CC, $theme-violet, #443366, #332255); @include gitlab-theme(#9988CC, $theme-violet, #443366, #332255);
} }
}
::-webkit-scrollbar{
width: 3px;
}
::-webkit-scrollbar-thumb{
background-color:$theme-charcoal; border-radius: 0;
}
::-webkit-scrollbar-thumb:hover{
background-color:$theme-charcoal;
}
::-webkit-scrollbar-track{
background-color:#FFF;
} }
\ No newline at end of file
...@@ -77,6 +77,7 @@ header { ...@@ -77,6 +77,7 @@ header {
line-height: $header-height; line-height: $header-height;
font-weight: normal; font-weight: normal;
color: #4c4e54; color: #4c4e54;
overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
vertical-align: top; vertical-align: top;
white-space: nowrap; white-space: nowrap;
......
...@@ -44,8 +44,10 @@ ...@@ -44,8 +44,10 @@
white-space: nowrap; white-space: nowrap;
i { i {
float: left;
margin-top: 3px;
margin-right: 5px;
visibility: hidden; visibility: hidden;
@extend .pull-left;
} }
&:hover i { &:hover i {
......
...@@ -6,31 +6,28 @@ ...@@ -6,31 +6,28 @@
.status-box { .status-box {
@include border-radius(3px); @include border-radius(3px);
display: block; display: block;
float: left; float: left;
padding: 0 $gl-btn-padding; padding: 0 $gl-btn-padding;
font-weight: normal; margin-top: 5px;
margin-right: 10px; margin-right: 10px;
color: #FFF;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 25px;
&.status-box-closed { &.status-box-closed {
background-color: $gl-danger; background-color: $gl-danger;
color: #FFF;
} }
&.status-box-merged { &.status-box-merged {
background-color: $gl-primary; background-color: $gl-primary;
color: #FFF;
} }
&.status-box-open { &.status-box-open {
background-color: $green-light; background-color: $green-light;
color: #FFF;
} }
&.status-box-expired { &.status-box-expired {
background: #cea61b; background: #cea61b;
color: #FFF;
} }
} }
...@@ -48,8 +48,8 @@ ...@@ -48,8 +48,8 @@
.ui-state-hover, .ui-state-hover,
.ui-state-focus { .ui-state-focus {
border: 1px solid $hover; border: 1px solid $row-hover;
background: $hover; background: $row-hover;
color: #333; color: #333;
} }
} }
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
&.smoke { background-color: $background-color; } &.smoke { background-color: $background-color; }
&:hover { &:hover {
background: $hover; background: $row-hover;
} }
&:last-child { &:last-child {
......
...@@ -77,12 +77,21 @@ ...@@ -77,12 +77,21 @@
margin-bottom: 0px; margin-bottom: 0px;
> .dropdown { > .dropdown {
margin-right: 10px; margin-right: $gl-padding-top;
display: inline-block; display: inline-block;
} }
> .btn { > .btn {
margin-right: $gl-padding-top;
display: inline-block; display: inline-block;
&:last-child {
margin-right: 0;
}
}
> .btn-grouped {
float: none;
} }
> form { > form {
...@@ -94,7 +103,7 @@ ...@@ -94,7 +103,7 @@
display: inline-block; display: inline-block;
position: relative; position: relative;
top: 1px; top: 1px;
margin-right: 10px; margin-right: $gl-padding-top;
/* Medium devices (desktops, 992px and up) */ /* Medium devices (desktops, 992px and up) */
@media (min-width: $screen-md-min) { width: 200px; } @media (min-width: $screen-md-min) { width: 200px; }
......
/** Select2 selectbox style override **/ /** Select2 selectbox style override **/
.select2-container {
width: 100% !important;
}
.select2-container, .select2-container.select2-drop-above { .select2-container, .select2-container.select2-drop-above {
.select2-choice { .select2-choice {
background: #FFF; background: #fff;
border-color: #DDD; border-color: $input-border;
height: 36px; border-color: $border-white-light;
padding: 6px $gl-padding; height: 35px;
padding: $gl-vert-padding $gl-btn-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
@include border-radius(2px); @include border-radius($border-radius-default);
.select2-arrow { .select2-arrow {
background: #FFF; background-image: none;
border-left: none; background-color: transparent;
padding-top: 5px; border: none;
padding-top: 6px;
padding-right: 10px;
b {
@extend .caret;
color: $gray-darkest;
}
} }
.select2-chosen { .select2-chosen {
color: $gl-text-color; margin-right: 15px;
} }
&.select2-default { &:hover {
.select2-chosen { background-color: $gray-dark;
color: #999; border-color: $border-white-normal;
} color: $gl-text-color;
} }
} }
} }
.select2-container .select2-choice, .select2-container.select2-drop-above .select2-choice{
color: #7f8fa4;
border: 1px solid #e7e9ed;
}
.select2-drop { .select2-drop {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px); @include border-radius ($border-radius-default);
border: none;
padding: 16px;
border: none !important;
} }
.select2-results .select2-result-label { .select2-results .select2-result-label {
padding: 9px; padding: 10px 15px;
} }
.select2-drop{ .select2-drop{
...@@ -56,15 +60,30 @@ ...@@ -56,15 +60,30 @@
.select2-results li.select2-result-with-children > .select2-result-label { .select2-results li.select2-result-with-children > .select2-result-label {
font-weight: 600; font-weight: 600;
color: #313236; color: $gl-text-color;
}
.select2-container-active {
.select2-choice, .select2-choices {
@include box-shadow(none);
}
}
.select2-dropdown-open {
.select2-choice {
border-color: $border-white-normal;
outline: 0;
background-image: none;
background-color: $white-dark;
@include box-shadow($gl-btn-active-gradient);
}
} }
.select2-container-multi { .select2-container-multi {
.select2-choices { .select2-choices {
@include border-radius(2px); @include border-radius($border-radius-default);
border-color: $input-border; border-color: $input-border;
background: white; background: none;
padding-left: $gl-padding / 2;
.select2-search-field input { .select2-search-field input {
padding: $gl-padding / 2; padding: $gl-padding / 2;
...@@ -76,14 +95,16 @@ ...@@ -76,14 +95,16 @@
.select2-search-choice { .select2-search-choice {
margin: 8px 0 0 8px; margin: 8px 0 0 8px;
background: white;
box-shadow: none; box-shadow: none;
border-color: $input-border; border-color: $input-border;
color: $gl-text-color; color: $gl-text-color;
line-height: 15px; line-height: 15px;
background-color: $background-color;
background-image: none;
.select2-search-choice-close { .select2-search-choice-close {
top: 5px; top: 4px;
left: 3px;
} }
&.select2-search-choice-focus { &.select2-search-choice-focus {
...@@ -91,22 +112,25 @@ ...@@ -91,22 +112,25 @@
} }
} }
} }
&.select2-container-active .select2-choices,
&.select2-dropdown-open .select2-choices {
border-color: $border-white-normal;
@include box-shadow($gl-btn-active-gradient);
}
}
.select2-container-multi .select2-choices .select2-search-choice {
} }
.select2-drop-active { .select2-drop-active {
border: 1px solid #BBB !important; margin-top: 6px;
margin-top: 4px; font-size: 14px;
font-size: 13px;
&.select2-drop-above { &.select2-drop-above {
margin-bottom: 8px; margin-bottom: 8px;
} }
.select2-search input {
background: #fafafa;
border-color: #DDD;
}
.select2-results { .select2-results {
max-height: 350px; max-height: 350px;
.select2-highlighted { .select2-highlighted {
...@@ -115,8 +139,34 @@ ...@@ -115,8 +139,34 @@
} }
} }
.select2-container { .select2-search {
width: 100% !important; padding: 15px 15px 5px;
.select2-drop-auto-width & {
padding: 15px 15px 5px;
}
}
.select2-search input {
padding: 2px 25px 2px 5px;
background: #fff image-url('select2.png');
background-repeat: no-repeat;
background-position: right 0px bottom 6px;
border: 1px solid $input-border;
@include border-radius($border-radius-default);
@include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s);
&:focus {
border-color: $input-border-focus;
}
}
.select2-search input.select2-active {
background-color: #fff;
background-image: image-url('select2-spinner.gif') !important;
background-repeat: no-repeat;
background-position: right 5px center !important;
background-size: 16px 16px !important;
} }
/** Branch/tag selector **/ /** Branch/tag selector **/
...@@ -124,10 +174,19 @@ ...@@ -124,10 +174,19 @@
width: 160px !important; width: 160px !important;
} }
.ajax-users-dropdown, .ajax-project-users-dropdown { .select2-results .select2-no-results,
.select2-search { .select2-results .select2-searching,
padding-top: 2px; .select2-results .select2-ajax-error,
} .select2-results .select2-selection-limit {
background: $gray-light;
display: list-item;
padding: 10px 15px;
}
.select2-results {
margin: 0;
padding: 10px 0;
} }
.ajax-users-select { .ajax-users-select {
......
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding 0; padding: $gl-padding $gl-btn-padding;
border-color: $table-border-color; border-color: $table-border-color;
color: $gl-gray; color: $gl-gray;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;
&:target { &:target {
background: $hover; background: $row-hover;
} }
&:last-child { &:last-child {
......
...@@ -70,7 +70,7 @@ $pagination-bg: #fff; ...@@ -70,7 +70,7 @@ $pagination-bg: #fff;
$pagination-border: $border-color; $pagination-border: $border-color;
$pagination-hover-color: $gl-gray; $pagination-hover-color: $gl-gray;
$pagination-hover-bg: $hover; $pagination-hover-bg: $row-hover;
$pagination-hover-border: $border-color; $pagination-hover-border: $border-color;
$pagination-active-color: $blue-dark; $pagination-active-color: $blue-dark;
......
$hover: #faf9f9; $row-hover: #f4f8fe;
$gl-text-color: #54565B; $gl-text-color: #54565B;
$gl-text-green: #4A2; $gl-text-green: #4A2;
$gl-text-red: #D12F19; $gl-text-red: #D12F19;
...@@ -31,6 +31,7 @@ $gl-padding-top:10px; ...@@ -31,6 +31,7 @@ $gl-padding-top:10px;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
$secondary-text: #7f8fa4; $secondary-text: #7f8fa4;
$error-exclamation-point: #E62958; $error-exclamation-point: #E62958;
$border-radius-default: 3px;
/* /*
* Color schema * Color schema
...@@ -100,6 +101,8 @@ $gl-success: $green-normal; ...@@ -100,6 +101,8 @@ $gl-success: $green-normal;
$gl-info: $blue-normal; $gl-info: $blue-normal;
$gl-warning: $orange-normal; $gl-warning: $orange-normal;
$gl-danger: $red-normal; $gl-danger: $red-normal;
$gl-btn-active-background: rgba(0, 0, 0, 0.12);
$gl-btn-active-gradient: inset 0 0 4px $gl-btn-active-background;
/* /*
* Commit Diff Colors * Commit Diff Colors
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -64,7 +64,6 @@ ...@@ -64,7 +64,6 @@
// This prevents the mess when resizing the sidebar // This prevents the mess when resizing the sidebar
// of elements repositioning themselves.. // of elements repositioning themselves..
width: $gutter_inner_width; width: $gutter_inner_width;
overflow-x: hidden;
// -- // --
&:first-child { &:first-child {
...@@ -90,7 +89,6 @@ ...@@ -90,7 +89,6 @@
.gutter-toggle { .gutter-toggle {
margin-left: 20px; margin-left: 20px;
border-left: 1px solid $border-gray-light;
padding-left: 10px; padding-left: 10px;
&:hover { &:hover {
...@@ -157,11 +155,10 @@ ...@@ -157,11 +155,10 @@
.right-sidebar { .right-sidebar {
position: fixed; position: fixed;
top: 58px; top: 58px;
bottom: 0;
right: 0; right: 0;
height: 100%; transition: width .3s;
transition-duration: .3s;
background: $gray-light; background: $gray-light;
overflow: scroll;
padding: 10px 20px; padding: 10px 20px;
&.right-sidebar-expanded { &.right-sidebar-expanded {
...@@ -170,6 +167,14 @@ ...@@ -170,6 +167,14 @@
hr { hr {
display: none; display: none;
} }
.sidebar-collapsed-icon {
display: none;
}
.gutter-toggle {
border-left: 1px solid $border-gray-light;
}
} }
.subscribe-button { .subscribe-button {
...@@ -181,7 +186,6 @@ ...@@ -181,7 +186,6 @@
&.right-sidebar-collapsed { &.right-sidebar-collapsed {
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
padding-top: 0; padding-top: 0;
overflow-x: hidden;
hr { hr {
margin: 0; margin: 0;
...@@ -192,21 +196,13 @@ ...@@ -192,21 +196,13 @@
} }
.block { .block {
border-bottom: none; width: $sidebar_collapsed_width - 1px;
margin-left: -19px;
padding: 15px 0 0 0; padding: 15px 0 0 0;
border-bottom: none;
overflow: hidden;
} }
}
.btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
}
}
&.right-sidebar-collapsed {
.issuable-count, .issuable-count,
.issuable-nav, .issuable-nav,
.assignee > *, .assignee > *,
...@@ -219,15 +215,13 @@ ...@@ -219,15 +215,13 @@
} }
.gutter-toggle { .gutter-toggle {
margin-left: -$gutter_inner_width + 4; margin-left: -36px;
} }
.sidebar-collapsed-icon { .sidebar-collapsed-icon {
display: block; display: block;
float: left; width: 100%;
width: 62px;
text-align: center; text-align: center;
margin-left: -19px;
padding-bottom: 10px; padding-bottom: 10px;
color: #999999; color: #999999;
...@@ -247,14 +241,15 @@ ...@@ -247,14 +241,15 @@
color: #999999; color: #999999;
} }
} }
} }
} }
&.right-sidebar-expanded { .btn {
.sidebar-collapsed-icon { background: $gray-normal;
display: none; border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
} }
} }
} }
...@@ -263,4 +258,4 @@ ...@@ -263,4 +258,4 @@
small { small {
color: $gray-darkest; color: $gray-darkest;
} }
} }
\ No newline at end of file
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
} }
} }
.manage-labels-list { .label-row {
.label { .label {
padding: 9px; padding: 9px;
font-size: 14px; font-size: 14px;
......
...@@ -236,4 +236,4 @@ ...@@ -236,4 +236,4 @@
} }
} }
} }
} }
\ No newline at end of file
...@@ -11,3 +11,60 @@ li.milestone { ...@@ -11,3 +11,60 @@ li.milestone {
height: 6px; height: 6px;
} }
} }
.milestone-content {
.issues-count {
margin-right: 17px;
float: right;
width: 105px;
}
.issue-row {
.color-label {
border-radius: 2px;
padding: 3px !important;
}
// Issue title
span a {
color: rgba(0,0,0,0.64);
}
}
}
.milestone-summary {
margin-bottom: 25px;
.milestone-stat {
margin-right: 10px;
}
.time-elapsed {
color: $orange-light;
}
}
.issues-sortable-list {
.issue-detail {
display: block;
.issue-number{
color: rgba(0,0,0,0.44);
margin-right: 5px;
}
.color-label {
padding: 6px 10px;
margin-right: 7px;
margin-top: 10px;
}
.avatar {
float: none;
}
}
}
.milestone-detail {
border-bottom: 1px solid $border-color;
padding: 20px 0;
}
...@@ -51,9 +51,17 @@ ...@@ -51,9 +51,17 @@
.profile-link-holder { .profile-link-holder {
display: inline; display: inline;
a {
color: $blue-dark;
text-decoration: none;
}
}
// Middle dot divider between each element in a list of items.
.middle-dot-divider {
&:after { &:after {
content: "\00B7"; content: "\00B7"; // Middle Dot
padding: 0px 6px; padding: 0 6px;
font-weight: bold; font-weight: bold;
} }
...@@ -63,9 +71,10 @@ ...@@ -63,9 +71,10 @@
padding: 0; padding: 0;
} }
} }
}
a { .profile-user-bio {
color: $blue-dark; // Limits the width of the user bio for readability.
text-decoration: none; max-width: 750px;
} margin: auto;
} }
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
.cover-controls { .cover-controls {
.project-settings-dropdown { .project-settings-dropdown {
margin-left: 10px; margin-left: 10px;
display: inline-block;
} }
} }
...@@ -73,24 +74,19 @@ ...@@ -73,24 +74,19 @@
font-weight: normal; font-weight: normal;
} }
.visibility-icon {
display: inline-block;
margin-left: 5px;
font-size: 18px;
color: $gray;
}
p { p {
padding: 0 $gl-padding; padding: 0 $gl-padding;
color: #5c5d5e; color: #5c5d5e;
} }
} }
.visibility-level-label {
@extend .btn;
@extend .btn-gray;
color: $gray;
cursor: default;
i {
color: inherit;
}
}
.project-repo-buttons { .project-repo-buttons {
margin-top: 20px; margin-top: 20px;
margin-bottom: 0px; margin-bottom: 0px;
...@@ -191,10 +187,10 @@ ...@@ -191,10 +187,10 @@
.dropdown-menu { .dropdown-menu {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px); @include border-radius ($border-radius-default);
border: none; border: none;
padding: 16px 0; padding: 10px 0;
font-size: 14px; font-size: 14px;
font-weight: 100; font-weight: 100;
......
/**
* Dashboard Todos
*
*/
.navbar-nav {
li {
.badge.todos-pending-count {
background-color: #7f8fa4;
margin-top: -5px;
}
}
}
.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;
}
}
a {
color: #4c4e54;
}
.avatar {
margin-left: -($gl-avatar-size + $gl-padding-top);
}
.todo-title {
@include str-truncated(calc(100% - 174px));
font-weight: 600;
.author_name {
color: #333;
}
}
.todo-body {
margin-right: 174px;
.todo-note {
word-wrap: break-word;
.md {
color: #7f8fa4;
font-size: $gl-font-size;
p {
color: #5c5d5e;
}
}
pre {
border: none;
background: #f9f9f9;
border-radius: 0;
color: #777;
margin: 0 20px;
overflow: hidden;
}
.note-image-attach {
margin-top: 4px;
margin-left: 0px;
max-width: 200px;
float: none;
}
p:last-child {
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) {
.todo-item {
padding-left: $gl-padding;
.todo-title {
white-space: normal;
overflow: visible;
max-width: 100%;
}
.avatar {
display: none;
}
.todo-body {
margin: 0;
border-left: 2px solid #DDD;
padding-left: 10px;
}
}
}
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
&:hover { &:hover {
td { td {
background: $hover; background: $row-hover;
} }
cursor: pointer; cursor: pointer;
} }
......
...@@ -4,8 +4,3 @@ ...@@ -4,8 +4,3 @@
margin-right: auto; margin-right: auto;
padding-right: 7px; padding-right: 7px;
} }
.wiki-last-edit-by {
font-size: 80%;
font-weight: normal;
}
...@@ -53,6 +53,6 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -53,6 +53,6 @@ class Admin::LabelsController < Admin::ApplicationController
end end
def label_params def label_params
params[:label].permit(:title, :color) params[:label].permit(:title, :description, :color)
end end
end end
...@@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base ...@@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can?, :current_application_settings helper_method :abilities, :can?, :current_application_settings
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
helper_method :repository helper_method :repository, :can_collaborate_with_project?
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception) log_exception(exception)
...@@ -410,6 +410,13 @@ class ApplicationController < ActionController::Base ...@@ -410,6 +410,13 @@ class ApplicationController < ActionController::Base
current_user.nil? && root_path == request.path current_user.nil? && root_path == request.path
end end
def can_collaborate_with_project?(project = nil)
project ||= @project
can?(current_user, :push_code, project) ||
(current_user && current_user.already_forked?(project))
end
private private
def set_default_sort def set_default_sort
......
...@@ -13,17 +13,11 @@ module CreatesCommit ...@@ -13,17 +13,11 @@ module CreatesCommit
result = service.new(@tree_edit_project, current_user, commit_params).execute result = service.new(@tree_edit_project, current_user, commit_params).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = success_notice || "Your changes have been successfully committed." update_flash_notice(success_notice)
if create_merge_request?
success_path = new_merge_request_path
target = different_project? ? "project" : "branch"
flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
end
respond_to do |format| respond_to do |format|
format.html { redirect_to success_path } format.html { redirect_to final_success_path(success_path) }
format.json { render json: { message: "success", filePath: success_path } } format.json { render json: { message: "success", filePath: final_success_path(success_path) } }
end end
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
...@@ -41,14 +35,32 @@ module CreatesCommit ...@@ -41,14 +35,32 @@ module CreatesCommit
end end
def authorize_edit_tree! def authorize_edit_tree!
return if can?(current_user, :push_code, project) return if can_collaborate_with_project?
return if current_user && current_user.already_forked?(project)
access_denied! access_denied!
end end
private private
def update_flash_notice(success_notice)
flash[:notice] = success_notice || "Your changes have been successfully committed."
if create_merge_request?
if merge_request_exists?
flash[:notice] = nil
else
target = different_project? ? "project" : "branch"
flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
end
end
end
def final_success_path(success_path)
return success_path unless create_merge_request?
merge_request_exists? ? existing_merge_request_path : new_merge_request_path
end
def new_merge_request_path def new_merge_request_path
new_namespace_project_merge_request_path( new_namespace_project_merge_request_path(
@mr_source_project.namespace, @mr_source_project.namespace,
...@@ -62,6 +74,19 @@ module CreatesCommit ...@@ -62,6 +74,19 @@ module CreatesCommit
) )
end end
def existing_merge_request_path
namespace_project_merge_request_path(@mr_target_project.namespace, @mr_target_project, @merge_request)
end
def merge_request_exists?
return @merge_request if defined?(@merge_request)
@merge_request = @mr_target_project.merge_requests.opened.find_by(
source_branch: @mr_source_branch,
target_branch: @mr_target_branch
)
end
def different_project? def different_project?
@mr_source_project != @mr_target_project @mr_source_project != @mr_target_project
end end
...@@ -75,7 +100,7 @@ module CreatesCommit ...@@ -75,7 +100,7 @@ module CreatesCommit
end end
def set_commit_variables def set_commit_variables
@mr_source_branch = @target_branch @mr_source_branch ||= @target_branch
if can?(current_user, :push_code, @project) if can?(current_user, :push_code, @project)
# Edit file in this project # Edit file in this project
...@@ -89,7 +114,7 @@ module CreatesCommit ...@@ -89,7 +114,7 @@ module CreatesCommit
else else
# Merge request to this project # Merge request to this project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch = @ref @mr_target_branch ||= @ref
end end
else else
# Edit file in fork # Edit file in fork
...@@ -97,7 +122,7 @@ module CreatesCommit ...@@ -97,7 +122,7 @@ module CreatesCommit
# Merge request from fork to this project # Merge request from fork to this project
@mr_source_project = @tree_edit_project @mr_source_project = @tree_edit_project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch = @ref @mr_target_branch ||= @ref
end end
end end
end end
...@@ -6,6 +6,8 @@ module IssuesAction ...@@ -6,6 +6,8 @@ module IssuesAction
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE) @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
@label = @issuable_finder.labels.first
respond_to do |format| respond_to do |format|
format.html format.html
format.atom { render layout: false } format.atom { render layout: false }
......
...@@ -5,5 +5,7 @@ module MergeRequestsAction ...@@ -5,5 +5,7 @@ module MergeRequestsAction
@merge_requests = get_merge_requests_collection @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE) @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
@label = @issuable_finder.labels.first
end end
end end
class Dashboard::TodosController < Dashboard::ApplicationController
before_action :find_todos, only: [:index, :destroy_all]
def index
@todos = @todos.page(params[:page]).per(PER_PAGE)
end
def destroy
todo.done!
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
format.js { render nothing: true }
end
end
def destroy_all
@todos.each(&:done!)
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { render nothing: true }
end
end
private
def todo
@todo ||= current_user.todos.find(params[:id])
end
def find_todos
@todos = TodosFinder.new(current_user, params).execute
end
end
class EmojisController < ApplicationController
layout false
def index
end
end
...@@ -42,6 +42,26 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -42,6 +42,26 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end end
end end
def saml
if current_user
log_audit_event(current_user, with: :saml)
# Update SAML identity if data has changed.
identity = current_user.identities.find_by(extern_uid: oauth['uid'], provider: :saml)
if identity.nil?
current_user.identities.create(extern_uid: oauth['uid'], provider: :saml)
redirect_to profile_account_path, notice: 'Authentication method updated'
else
redirect_to after_sign_in_path_for(current_user)
end
else
saml_user = Gitlab::Saml::User.new(oauth)
saml_user.save
@user = saml_user.gl_user
continue_login_process
end
end
def omniauth_error def omniauth_error
@provider = params[:provider] @provider = params[:provider]
@error = params[:error] @error = params[:error]
...@@ -65,25 +85,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -65,25 +85,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
log_audit_event(current_user, with: oauth['provider']) log_audit_event(current_user, with: oauth['provider'])
redirect_to profile_account_path, notice: 'Authentication method updated' redirect_to profile_account_path, notice: 'Authentication method updated'
else else
@user = Gitlab::OAuth::User.new(oauth) oauth_user = Gitlab::OAuth::User.new(oauth)
@user.save oauth_user.save
@user = oauth_user.gl_user
# Only allow properly saved users to login. continue_login_process
if @user.persisted? && @user.valid?
log_audit_event(@user.gl_user, with: oauth['provider'])
sign_in_and_redirect(@user.gl_user)
else
error_message =
if @user.gl_user.errors.any?
@user.gl_user.errors.map do |attribute, message|
"#{attribute} #{message}"
end.join(", ")
else
''
end
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end
end end
rescue Gitlab::OAuth::SignupDisabledError rescue Gitlab::OAuth::SignupDisabledError
label = Gitlab::OAuth::Provider.label_for(oauth['provider']) label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
...@@ -104,6 +110,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -104,6 +110,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
session[:service_tickets][provider] = ticket session[:service_tickets][provider] = ticket
end end
def continue_login_process
# Only allow properly saved users to login.
if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider'])
sign_in_and_redirect(@user)
else
error_message = @user.errors.full_messages.to_sentence
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end
end
def oauth def oauth
@oauth ||= request.env['omniauth.auth'] @oauth ||= request.env['omniauth.auth']
end end
......
...@@ -87,7 +87,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -87,7 +87,7 @@ class Projects::BlobController < Projects::ApplicationController
private private
def blob def blob
@blob ||= @repository.blob_at(@commit.id, @path) @blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path))
if @blob if @blob
@blob @blob
......
...@@ -56,6 +56,12 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -56,6 +56,12 @@ class Projects::BuildsController < Projects::ApplicationController
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end end
def erase
@build.erase(erased_by: current_user)
redirect_to namespace_project_build_path(project.namespace, project, @build),
notice: "Build has been sucessfully erased!"
end
private private
def build def build
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# #
# Not to be confused with CommitsController, plural. # Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
include CreatesCommit
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds] before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
...@@ -9,6 +11,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -9,6 +11,7 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_read_commit_status!, only: [:builds] before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :define_show_vars, only: [:show, :builds] before_action :define_show_vars, only: [:show, :builds]
before_action :authorize_edit_tree!, only: [:revert]
def show def show
apply_diff_view_cookie! apply_diff_view_cookie!
...@@ -55,8 +58,37 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -55,8 +58,37 @@ class Projects::CommitController < Projects::ApplicationController
render layout: false render layout: false
end end
def revert
assign_revert_commit_vars
return render_404 if @target_branch.blank?
create_commit(Commits::RevertService, success_notice: "The #{revert_type_title} has been successfully reverted.",
success_path: successful_revert_path, failure_path: failed_revert_path)
end
private private
def revert_type_title
@commit.merged_merge_request ? 'merge request' : 'commit'
end
def successful_revert_path
return referenced_merge_request_url if @commit.merged_merge_request
namespace_project_commits_url(@project.namespace, @project, @target_branch)
end
def failed_revert_path
return referenced_merge_request_url if @commit.merged_merge_request
namespace_project_commit_url(@project.namespace, @project, params[:id])
end
def referenced_merge_request_url
namespace_project_merge_request_url(@project.namespace, @project, @commit.merged_merge_request)
end
def commit def commit
@commit ||= @project.commit(params[:id]) @commit ||= @project.commit(params[:id])
end end
...@@ -79,4 +111,16 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -79,4 +111,16 @@ class Projects::CommitController < Projects::ApplicationController
@statuses = ci_commit.statuses if ci_commit @statuses = ci_commit.statuses if ci_commit
end end
def assign_revert_commit_vars
@commit = project.commit(params[:id])
@target_branch = params[:target_branch]
@mr_source_branch = @commit.revert_branch_name
@mr_target_branch = @target_branch
@commit_params = {
commit: @commit,
revert_type_title: revert_type_title,
create_merge_request: params[:create_merge_request].present? || different_project?
}
end
end end
...@@ -32,7 +32,7 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -32,7 +32,7 @@ class Projects::ForksController < Projects::ApplicationController
if continue_params if continue_params
redirect_to continue_params[:to], notice: continue_params[:notice] redirect_to continue_params[:to], notice: continue_params[:notice]
else else
redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked." redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project '#{@forked_project.name}' was successfully forked."
end end
end end
else else
......
...@@ -3,6 +3,7 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -3,6 +3,7 @@ class Projects::ImportsController < Projects::ApplicationController
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :require_no_repo, only: [:new, :create] before_action :require_no_repo, only: [:new, :create]
before_action :redirect_if_progress, only: [:new, :create] before_action :redirect_if_progress, only: [:new, :create]
before_action :redirect_if_no_import, only: :show
def new def new
end end
...@@ -63,14 +64,19 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -63,14 +64,19 @@ class Projects::ImportsController < Projects::ApplicationController
def require_no_repo def require_no_repo
if @project.repository_exists? if @project.repository_exists?
redirect_to(namespace_project_path(@project.namespace, @project)) redirect_to namespace_project_path(@project.namespace, @project)
end end
end end
def redirect_if_progress def redirect_if_progress
if @project.import_in_progress? if @project.import_in_progress?
redirect_to namespace_project_import_path(@project.namespace, @project) && redirect_to namespace_project_import_path(@project.namespace, @project)
return end
end
def redirect_if_no_import
if @project.repository_exists? && @project.no_import?
redirect_to namespace_project_path(@project.namespace, @project)
end end
end end
end end
...@@ -32,6 +32,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -32,6 +32,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
@issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.page(params[:page]).per(PER_PAGE)
@label = @project.labels.find_by(title: params[:label_name])
respond_to do |format| respond_to do |format|
format.html format.html
......
...@@ -69,7 +69,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -69,7 +69,7 @@ class Projects::LabelsController < Projects::ApplicationController
end end
def label_params def label_params
params.require(:label).permit(:title, :color) params.require(:label).permit(:title, :description, :color)
end end
def label def label
......
...@@ -34,6 +34,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -34,6 +34,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.preload(:target_project) @merge_requests = @merge_requests.preload(:target_project)
@label = @project.labels.find_by(title: params[:label_name])
respond_to do |format| respond_to do |format|
format.html format.html
format.json do format.json do
...@@ -179,6 +181,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -179,6 +181,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return return
end end
TodoService.new.merge_merge_request(merge_request, current_user)
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active? if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
......
...@@ -35,6 +35,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -35,6 +35,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@issues = @milestone.issues @issues = @milestone.issues
@users = @milestone.participants.uniq @users = @milestone.participants.uniq
@merge_requests = @milestone.merge_requests @merge_requests = @milestone.merge_requests
@labels = @milestone.labels
end end
def create def create
......
...@@ -64,9 +64,9 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -64,9 +64,9 @@ class Projects::RefsController < Projects::ApplicationController
} }
end end
if @logs.present? offset = (@offset + @limit)
@log_url = namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/')) if contents.size > offset
@more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit)) @more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: offset)
end end
respond_to do |format| respond_to do |format|
......
...@@ -11,7 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -11,7 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController
end end
def archive def archive
render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute RepositoryArchiveCacheWorker.perform_async
headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
head :ok
rescue => ex rescue => ex
logger.error("#{self.class.name}: #{ex}") logger.error("#{self.class.name}: #{ex}")
return git_not_found! return git_not_found!
......
...@@ -236,7 +236,7 @@ class ProjectsController < ApplicationController ...@@ -236,7 +236,7 @@ class ProjectsController < ApplicationController
Emoji.emojis.map do |name, emoji| Emoji.emojis.map do |name, emoji|
{ {
name: name, name: name,
path: view_context.image_url("emoji/#{emoji["unicode"]}.png") path: view_context.image_url("#{emoji["unicode"]}.png")
} }
end end
end end
......
...@@ -119,6 +119,20 @@ class IssuableFinder ...@@ -119,6 +119,20 @@ class IssuableFinder
labels? && params[:label_name] == Label::None.title labels? && params[:label_name] == Label::None.title
end end
def labels
return @labels if defined?(@labels)
if labels? && !filter_by_no_label?
@labels = Label.where(title: label_names)
if projects
@labels = @labels.where(project: projects)
end
else
@labels = Label.none
end
end
def assignee? def assignee?
params[:assignee_id].present? params[:assignee_id].present?
end end
...@@ -253,8 +267,6 @@ class IssuableFinder ...@@ -253,8 +267,6 @@ class IssuableFinder
joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id"). joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id").
where(label_links: { id: nil }) where(label_links: { id: nil })
else else
label_names = params[:label_name].split(",")
items = items.joins(:labels).where(labels: { title: label_names }) items = items.joins(:labels).where(labels: { title: label_names })
if projects if projects
...@@ -266,6 +278,10 @@ class IssuableFinder ...@@ -266,6 +278,10 @@ class IssuableFinder
items items
end end
def label_names
params[:label_name].split(',')
end
def current_user_related? def current_user_related?
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
......
# TodosFinder
#
# Used to filter Todos by set of params
#
# Arguments:
# current_user - which user use
# params:
# action_id: integer
# author_id: integer
# project_id; integer
# state: 'pending' or 'done'
# type: 'Issue' or 'MergeRequest'
#
class TodosFinder
NONE = '0'
attr_accessor :current_user, :params
def initialize(current_user, params)
@current_user = current_user
@params = params
end
def execute
items = current_user.todos
items = by_action_id(items)
items = by_author(items)
items = by_project(items)
items = by_state(items)
items = by_type(items)
items
end
private
def action_id?
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED].include?(action_id.to_i)
end
def action_id
params[:action_id]
end
def author?
params[:author_id].present?
end
def author
return @author if defined?(@author)
@author =
if author? && params[:author_id] != NONE
User.find(params[:author_id])
else
nil
end
end
def project?
params[:project_id].present?
end
def project
return @project if defined?(@project)
if project?
@project = Project.find(params[:project_id])
unless Ability.abilities.allowed?(current_user, :read_project, @project)
@project = nil
end
else
@project = nil
end
@project
end
def type?
type.present? && ['Issue', 'MergeRequest'].include?(type)
end
def type
params[:type]
end
def by_action_id(items)
if action_id?
items = items.where(action: action_id)
end
items
end
def by_author(items)
if author?
items = items.where(author_id: author.try(:id))
end
items
end
def by_project(items)
if project?
items = items.where(project: project)
end
items
end
def by_state(items)
case params[:state]
when 'done'
items.done
else
items.pending
end
end
def by_type(items)
if type?
items = items.where(target_type: type)
end
items
end
end
...@@ -118,12 +118,6 @@ module ApplicationHelper ...@@ -118,12 +118,6 @@ module ApplicationHelper
grouped_options_for_select(options, @ref || @project.default_branch) grouped_options_for_select(options, @ref || @project.default_branch)
end end
def emoji_autocomplete_source
# should be an array of strings
# so to_s can be called, because it is sufficient and to_json is too slow
Emoji.names.to_s
end
# Define whenever show last push event # Define whenever show last push event
# with suggestion to create MR # with suggestion to create MR
def show_last_push_widget?(event) def show_last_push_widget?(event)
......
...@@ -127,10 +127,6 @@ module BlobHelper ...@@ -127,10 +127,6 @@ module BlobHelper
end end
end end
def blob_svg?(blob)
blob.language && blob.language.name == 'SVG'
end
# SVGs can contain malicious JavaScript; only include whitelisted # SVGs can contain malicious JavaScript; only include whitelisted
# elements and attributes. Note that this whitelist is by no means complete # elements and attributes. Note that this whitelist is by no means complete
# and may omit some elements. # and may omit some elements.
......
...@@ -123,6 +123,37 @@ module CommitsHelper ...@@ -123,6 +123,37 @@ module CommitsHelper
) )
end end
def revert_commit_link(commit, continue_to_path, btn_class: nil)
return unless current_user
tooltip = "Revert this #{revert_commit_type(commit)} in a new merge request"
if can_collaborate_with_project?
content_tag :span, 'data-toggle' => 'modal', 'data-target' => '#modal-revert-commit' do
link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class}"
end
elsif can?(current_user, :fork_project, @project)
continue_params = {
to: continue_to_path,
notice: edit_in_new_fork_notice + ' Try to revert this commit again.',
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_forks_path(@project.namespace, @project,
namespace_key: current_user.namespace.id,
continue: continue_params)
link_to 'Revert', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip
end
end
def revert_commit_type(commit)
if commit.merged_merge_request
'merge request'
else
'commit'
end
end
protected protected
# Private: Returns a link to a person. If the person has a matching user and # Private: Returns a link to a person. If the person has a matching user and
......
...@@ -69,7 +69,7 @@ module DiffHelper ...@@ -69,7 +69,7 @@ module DiffHelper
end end
def line_comments def line_comments
@line_comments ||= @line_notes.select(&:active?).group_by(&:line_code) @line_comments ||= @line_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code)
end end
def organize_comments(type_left, type_right, line_code_left, line_code_right) def organize_comments(type_left, type_right, line_code_left, line_code_right)
...@@ -137,7 +137,7 @@ module DiffHelper ...@@ -137,7 +137,7 @@ module DiffHelper
# Always use HTML to handle case where JSON diff rendered this button # Always use HTML to handle case where JSON diff rendered this button
params_copy.delete(:format) params_copy.delete(:format)
link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn') do link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn'), data: { view_type: name } do
title title
end end
end end
......
...@@ -37,6 +37,7 @@ module NavHelper ...@@ -37,6 +37,7 @@ module NavHelper
if current_path?('merge_requests#show') || if current_path?('merge_requests#show') ||
current_path?('merge_requests#diffs') || current_path?('merge_requests#diffs') ||
current_path?('merge_requests#commits') || current_path?('merge_requests#commits') ||
current_path?('merge_requests#builds') ||
current_path?('issues#show') current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true' if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed" "page-gutter right-sidebar-collapsed"
......
...@@ -11,6 +11,8 @@ module SortingHelper ...@@ -11,6 +11,8 @@ module SortingHelper
sort_value_largest_repo => sort_title_largest_repo, sort_value_largest_repo => sort_title_largest_repo,
sort_value_recently_signin => sort_title_recently_signin, sort_value_recently_signin => sort_title_recently_signin,
sort_value_oldest_signin => sort_title_oldest_signin, sort_value_oldest_signin => sort_title_oldest_signin,
sort_value_downvotes => sort_title_downvotes,
sort_value_upvotes => sort_title_upvotes
} }
end end
...@@ -54,6 +56,14 @@ module SortingHelper ...@@ -54,6 +56,14 @@ module SortingHelper
'Oldest sign in' 'Oldest sign in'
end end
def sort_title_downvotes
'Least popular'
end
def sort_title_upvotes
'Most popular'
end
def sort_value_oldest_updated def sort_value_oldest_updated
'updated_asc' 'updated_asc'
end end
...@@ -93,4 +103,12 @@ module SortingHelper ...@@ -93,4 +103,12 @@ module SortingHelper
def sort_value_oldest_signin def sort_value_oldest_signin
'oldest_sign_in' 'oldest_sign_in'
end end
def sort_value_downvotes
'downvotes_desc'
end
def sort_value_upvotes
'upvotes_desc'
end
end end
module TodosHelper
def todos_pending_count
current_user.todos.pending.count
end
def todos_done_count
current_user.todos.done.count
end
def todo_action_name(todo)
case todo.action
when Todo::ASSIGNED then 'assigned you'
when Todo::MENTIONED then 'mentioned you on'
end
end
def todo_target_link(todo)
target = todo.target_type.titleize.downcase
link_to "#{target} #{todo.target.to_reference}", todo_target_path(todo)
end
def todo_target_path(todo)
anchor = dom_id(todo.note) if todo.note.present?
polymorphic_path([todo.project.namespace.becomes(Namespace),
todo.project, todo.target], anchor: anchor)
end
def todos_filter_params
{
state: params[:state],
project_id: params[:project_id],
author_id: params[:author_id],
type: params[:type],
action_id: params[:action_id],
}
end
def todos_filter_path(options = {})
without = options.delete(:without)
options = todos_filter_params.merge(options)
if without.present?
without.each do |key|
options.delete(key)
end
end
path = request.path
path << "?#{options.to_param}"
path
end
def todo_actions_options
actions = [
OpenStruct.new(id: '', title: 'Any Action'),
OpenStruct.new(id: Todo::ASSIGNED, title: 'Assigned'),
OpenStruct.new(id: Todo::MENTIONED, title: 'Mentioned')
]
options_from_collection_for_select(actions, 'id', 'title', params[:action_id])
end
def todo_projects_options
projects = current_user.authorized_projects.sorted_by_activity.non_archived
projects = projects.includes(:namespace)
projects = projects.map do |project|
OpenStruct.new(id: project.id, title: project.name_with_namespace)
end
projects.unshift(OpenStruct.new(id: '', title: 'Any Project'))
options_from_collection_for_select(projects, 'id', 'title', params[:project_id])
end
def todo_types_options
types = [
OpenStruct.new(title: 'Any Type', name: ''),
OpenStruct.new(title: 'Issue', name: 'Issue'),
OpenStruct.new(title: 'Merge Request', name: 'MergeRequest')
]
options_from_collection_for_select(types, 'name', 'title', params[:type])
end
end
...@@ -56,8 +56,7 @@ module TreeHelper ...@@ -56,8 +56,7 @@ module TreeHelper
return false unless on_top_of_branch?(project, ref) return false unless on_top_of_branch?(project, ref)
can?(current_user, :push_code, project) || can_collaborate_with_project?(project)
(current_user && current_user.already_forked?(project))
end end
def tree_edit_branch(project = @project, ref = @ref) def tree_edit_branch(project = @project, ref = @ref)
......
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects
class Blob < SimpleDelegator
# Wrap a Gitlab::Git::Blob object, or return nil when given nil
#
# This method prevents the decorated object from evaluating to "truthy" when
# given a nil value. For example:
#
# blob = Blob.new(nil)
# puts "truthy" if blob # => "truthy"
#
# blob = Blob.decorate(nil)
# puts "truthy" if blob # No output
def self.decorate(blob)
return if blob.nil?
new(blob)
end
def svg?
text? && language && language.name == 'SVG'
end
def to_partial_path
if lfs_pointer?
'download'
elsif image? || svg?
'image'
elsif text?
'text'
else
'download'
end
end
end
...@@ -31,15 +31,19 @@ ...@@ -31,15 +31,19 @@
# artifacts_file :text # artifacts_file :text
# gl_project_id :integer # gl_project_id :integer
# artifacts_metadata :text # artifacts_metadata :text
# erased_by_id :integer
# erased_at :datetime
# #
module Ci module Ci
class Build < CommitStatus class Build < CommitStatus
include Gitlab::Application.routes.url_helpers include Gitlab::Application.routes.url_helpers
LAZY_ATTRIBUTES = ['trace'] LAZY_ATTRIBUTES = ['trace']
belongs_to :runner, class_name: 'Ci::Runner' belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :erased_by, class_name: 'User'
serialize :options serialize :options
...@@ -103,23 +107,22 @@ module Ci ...@@ -103,23 +107,22 @@ module Ci
end end
state_machine :status, initial: :pending do state_machine :status, initial: :pending do
after_transition pending: :running do |build, transition| after_transition pending: :running do |build|
build.execute_hooks build.execute_hooks
end end
after_transition any => [:success, :failed, :canceled] do |build, transition| # We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed
return unless build.project around_transition any => [:success, :failed, :canceled] do |build, block|
block.call
build.commit.create_next_builds(build) if build.commit
end
after_transition any => [:success, :failed, :canceled] do |build|
build.update_coverage build.update_coverage
build.commit.create_next_builds(build)
build.execute_hooks build.execute_hooks
end end
end end
def ignored?
failed? && allow_failure?
end
def retryable? def retryable?
project.builds_enabled? && commands.present? project.builds_enabled? && commands.present?
end end
...@@ -179,6 +182,7 @@ module Ci ...@@ -179,6 +182,7 @@ module Ci
end end
def update_coverage def update_coverage
return unless project
coverage_regex = project.build_coverage_regex coverage_regex = project.build_coverage_regex
return unless coverage_regex return unless coverage_regex
coverage = extract_coverage(trace, coverage_regex) coverage = extract_coverage(trace, coverage_regex)
...@@ -203,6 +207,10 @@ module Ci ...@@ -203,6 +207,10 @@ module Ci
end end
end end
def has_trace?
raw_trace.present?
end
def raw_trace def raw_trace
if File.file?(path_to_trace) if File.file?(path_to_trace)
File.read(path_to_trace) File.read(path_to_trace)
...@@ -330,6 +338,7 @@ module Ci ...@@ -330,6 +338,7 @@ module Ci
end end
def execute_hooks def execute_hooks
return unless project
build_data = Gitlab::BuildDataBuilder.build(self) build_data = Gitlab::BuildDataBuilder.build(self)
project.execute_hooks(build_data.dup, :build_hooks) project.execute_hooks(build_data.dup, :build_hooks)
project.execute_services(build_data.dup, :build_hooks) project.execute_services(build_data.dup, :build_hooks)
...@@ -359,6 +368,33 @@ module Ci ...@@ -359,6 +368,33 @@ module Ci
Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path, **options).to_entry Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path, **options).to_entry
end end
def erase(opts = {})
return false unless erasable?
remove_artifacts_file!
remove_artifacts_metadata!
erase_trace!
update_erased!(opts[:erased_by])
end
def erasable?
complete? && (artifacts? || has_trace?)
end
def erased?
!self.erased_at.nil?
end
private
def erase_trace!
self.trace = nil
end
def update_erased!(user = nil)
self.update(erased_by: user, erased_at: Time.now)
end
private private
def yaml_variables def yaml_variables
......
...@@ -22,6 +22,7 @@ module Ci ...@@ -22,6 +22,7 @@ module Ci
extend Ci::Model extend Ci::Model
LAST_CONTACT_TIME = 5.minutes.ago LAST_CONTACT_TIME = 5.minutes.ago
AVAILABLE_SCOPES = ['specific', 'shared', 'active', 'paused', 'online']
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
...@@ -38,6 +39,11 @@ module Ci ...@@ -38,6 +39,11 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) } scope :ordered, ->() { order(id: :desc) }
scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
end
acts_as_taggable acts_as_taggable
def self.search(query) def self.search(query)
......
...@@ -215,6 +215,44 @@ class Commit ...@@ -215,6 +215,44 @@ class Commit
ci_commit.try(:status) || :not_found ci_commit.try(:status) || :not_found
end end
def revert_branch_name
"revert-#{short_id}"
end
def revert_description
if merged_merge_request
"This reverts merge request #{merged_merge_request.to_reference}"
else
"This reverts commit #{sha}"
end
end
def revert_message
%Q{Revert "#{title}"\n\n#{revert_description}}
end
def reverts_commit?(commit)
description? && description.include?(commit.revert_description)
end
def merge_commit?
parents.size > 1
end
def merged_merge_request
return @merged_merge_request if defined?(@merged_merge_request)
@merged_merge_request = project.merge_requests.find_by(merge_commit_sha: id) if merge_commit?
end
def has_been_reverted?(current_user = nil, noteable = self)
Gitlab::ReferenceExtractor.lazily do
noteable.notes.system.flat_map do |note|
note.all_references(current_user).commits
end
end.any? { |commit_ref| commit_ref.reverts_commit?(self) }
end
private private
def repo_changes def repo_changes
......
...@@ -75,16 +75,16 @@ class CommitStatus < ActiveRecord::Base ...@@ -75,16 +75,16 @@ class CommitStatus < ActiveRecord::Base
transition [:pending, :running] => :canceled transition [:pending, :running] => :canceled
end end
after_transition pending: :running do |build, transition| after_transition pending: :running do |commit_status|
build.update_attributes started_at: Time.now commit_status.update_attributes started_at: Time.now
end end
after_transition any => [:success, :failed, :canceled] do |build, transition| after_transition any => [:success, :failed, :canceled] do |commit_status|
build.update_attributes finished_at: Time.now commit_status.update_attributes finished_at: Time.now
end end
after_transition [:pending, :running] => :success do |build, transition| after_transition [:pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.project, nil).trigger(build) MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status)
end end
state :pending, value: 'pending' state :pending, value: 'pending'
...@@ -113,6 +113,10 @@ class CommitStatus < ActiveRecord::Base ...@@ -113,6 +113,10 @@ class CommitStatus < ActiveRecord::Base
canceled? || success? || failed? canceled? || success? || failed?
end end
def ignored?
failed? && allow_failure?
end
def duration def duration
if started_at && finished_at if started_at && finished_at
finished_at - started_at finished_at - started_at
......
...@@ -69,10 +69,35 @@ module Issuable ...@@ -69,10 +69,35 @@ module Issuable
case method.to_s case method.to_s
when 'milestone_due_asc' then order_milestone_due_asc when 'milestone_due_asc' then order_milestone_due_asc
when 'milestone_due_desc' then order_milestone_due_desc when 'milestone_due_desc' then order_milestone_due_desc
when 'downvotes_desc' then order_downvotes_desc
when 'upvotes_desc' then order_upvotes_desc
else else
order_by(method) order_by(method)
end end
end end
def order_downvotes_desc
order_votes_desc('thumbsdown')
end
def order_upvotes_desc
order_votes_desc('thumbsup')
end
def order_votes_desc(award_emoji_name)
issuable_table = self.arel_table
note_table = Note.arel_table
join_clause = issuable_table.join(note_table, Arel::Nodes::OuterJoin).on(
note_table[:noteable_id].eq(issuable_table[:id]).and(
note_table[:noteable_type].eq(self.name).and(
note_table[:is_award].eq(true).and(note_table[:note].eq(award_emoji_name))
)
)
).join_sources
joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
end
end end
def today? def today?
......
...@@ -2,13 +2,14 @@ ...@@ -2,13 +2,14 @@
# #
# Table name: labels # Table name: labels
# #
# id :integer not null, primary key # id :integer not null, primary key
# title :string(255) # title :string(255)
# color :string(255) # color :string(255)
# project_id :integer # project_id :integer
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# template :boolean default(FALSE) # template :boolean default(FALSE)
# description :string(255)
# #
class Label < ActiveRecord::Base class Label < ActiveRecord::Base
...@@ -85,6 +86,10 @@ class Label < ActiveRecord::Base ...@@ -85,6 +86,10 @@ class Label < ActiveRecord::Base
issues.opened.count issues.opened.count
end end
def closed_issues_count
issues.closed.count
end
def template? def template?
template template
end end
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
# merge_params :text # merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null # merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer # merge_user_id :integer
# merge_commit_sha :string
# #
require Rails.root.join("app/models/commit") require Rails.root.join("app/models/commit")
...@@ -137,7 +138,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -137,7 +138,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) } scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) } scope :of_projects, ->(ids) { where(target_project_id: ids) }
scope :opened, -> { with_state(:opened) } scope :opened, -> { with_states(:opened, :reopened) }
scope :merged, -> { with_state(:merged) } scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) } scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) }
...@@ -532,4 +533,12 @@ class MergeRequest < ActiveRecord::Base ...@@ -532,4 +533,12 @@ class MergeRequest < ActiveRecord::Base
[diff_base_commit, last_commit] [diff_base_commit, last_commit]
end end
def merge_commit
@merge_commit ||= project.commit(merge_commit_sha) if merge_commit_sha
end
def can_be_reverted?(current_user = nil)
merge_commit && !merge_commit.has_been_reverted?(current_user, self)
end
end end
...@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base ...@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :issues has_many :issues
has_many :labels, through: :issues
has_many :merge_requests has_many :merge_requests
has_many :participants, through: :issues, source: :assignee has_many :participants, through: :issues, source: :assignee
...@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base ...@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base
0 0
end end
# Returns the elapsed time (in percent) since the Milestone creation date until today.
# If the Milestone doesn't have a due_date then returns 0 since we can't calculate the elapsed time.
# If the Milestone is overdue then it returns 100%.
def percent_time_used
return 0 unless due_date
return 100 if expired?
duration = ((created_at - due_date.to_datetime) / 1.day)
days_elapsed = ((created_at - Time.now) / 1.day)
((days_elapsed.to_f / duration) * 100).floor
end
def expires_at def expires_at
if due_date if due_date
if due_date.past? if due_date.past?
......
...@@ -37,6 +37,8 @@ class Note < ActiveRecord::Base ...@@ -37,6 +37,8 @@ class Note < ActiveRecord::Base
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User" belongs_to :updated_by, class_name: "User"
has_many :todos, dependent: :destroy
delegate :name, to: :project, prefix: true delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
...@@ -375,6 +377,7 @@ class Note < ActiveRecord::Base ...@@ -375,6 +377,7 @@ class Note < ActiveRecord::Base
# #
def set_award! def set_award!
return unless awards_supported? && contains_emoji_only? return unless awards_supported? && contains_emoji_only?
self.is_award = true self.is_award = true
self.note = award_emoji_name self.note = award_emoji_name
end end
...@@ -382,7 +385,7 @@ class Note < ActiveRecord::Base ...@@ -382,7 +385,7 @@ class Note < ActiveRecord::Base
private private
def awards_supported? def awards_supported?
noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest) (noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest)) && !for_diff_line?
end end
def contains_emoji_only? def contains_emoji_only?
......
...@@ -382,6 +382,10 @@ class Project < ActiveRecord::Base ...@@ -382,6 +382,10 @@ class Project < ActiveRecord::Base
external_import? || forked? external_import? || forked?
end end
def no_import?
import_status == 'none'
end
def external_import? def external_import?
import_url.present? import_url.present?
end end
......
...@@ -136,7 +136,7 @@ class ProjectTeam ...@@ -136,7 +136,7 @@ class ProjectTeam
end end
def human_max_access(user_id) def human_max_access(user_id)
Gitlab::Access.options.key max_member_access(user_id) Gitlab::Access.options_with_owner.key(max_member_access(user_id))
end end
# This method assumes project and group members are eager loaded for optimal # This method assumes project and group members are eager loaded for optimal
......
...@@ -23,13 +23,11 @@ class Repository ...@@ -23,13 +23,11 @@ class Repository
def raw_repository def raw_repository
return nil unless path_with_namespace return nil unless path_with_namespace
@raw_repository ||= begin @raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
repo = Gitlab::Git::Repository.new(path_to_repo) end
repo.autocrlf = :input
repo def update_autocrlf_option
rescue Gitlab::Git::Repository::NoRepository raw_repository.autocrlf = :input if raw_repository.autocrlf != :input
nil
end
end end
# Return absolute path to repository # Return absolute path to repository
...@@ -40,7 +38,12 @@ class Repository ...@@ -40,7 +38,12 @@ class Repository
end end
def exists? def exists?
raw_repository return false unless raw_repository
raw_repository.rugged
true
rescue Gitlab::Git::Repository::NoRepository
false
end end
def empty? def empty?
...@@ -67,7 +70,7 @@ class Repository ...@@ -67,7 +70,7 @@ class Repository
end end
def commit(id = 'HEAD') def commit(id = 'HEAD')
return nil unless raw_repository return nil unless exists?
commit = Gitlab::Git::Commit.find(raw_repository, id) commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit, @project) if commit commit = Commit.new(commit, @project) if commit
commit commit
...@@ -236,6 +239,10 @@ class Repository ...@@ -236,6 +239,10 @@ class Repository
end end
expire_branch_cache(branch_name) expire_branch_cache(branch_name)
# This ensures this particular cache is flushed after the first commit to a
# new repository.
expire_emptiness_caches if empty?
end end
# Expires _all_ caches, including those that would normally only be expired # Expires _all_ caches, including those that would normally only be expired
...@@ -616,6 +623,34 @@ class Repository ...@@ -616,6 +623,34 @@ class Repository
end end
end end
def revert(user, commit, base_branch, target_branch = nil)
source_sha = find_branch(base_branch).target
target_branch ||= base_branch
args = [commit.id, source_sha]
args << { mainline: 1 } if commit.merge_commit?
revert_index = rugged.revert_commit(*args)
return false if revert_index.conflicts?
tree_id = revert_index.write_tree(rugged)
return false unless diff_exists?(source_sha, tree_id)
commit_with_hooks(user, target_branch) do |ref|
committer = user_to_committer(user)
source_sha = Rugged::Commit.create(rugged,
message: commit.revert_message,
author: committer,
committer: committer,
tree: tree_id,
parents: [rugged.lookup(source_sha)],
update_ref: ref)
end
end
def diff_exists?(sha1, sha2)
rugged.diff(sha1, sha2).size > 0
end
def merged_to_root_ref?(branch_name) def merged_to_root_ref?(branch_name)
branch_commit = commit(branch_name) branch_commit = commit(branch_name)
root_ref_commit = commit(root_ref) root_ref_commit = commit(root_ref)
...@@ -693,12 +728,15 @@ class Repository ...@@ -693,12 +728,15 @@ class Repository
end end
def commit_with_hooks(current_user, branch) def commit_with_hooks(current_user, branch)
update_autocrlf_option
oldrev = Gitlab::Git::BLANK_SHA oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
target_branch = find_branch(branch)
was_empty = empty? was_empty = empty?
unless was_empty if !was_empty && target_branch
oldrev = find_branch(branch).target oldrev = target_branch.target
end end
with_tmp_ref(oldrev) do |tmp_ref| with_tmp_ref(oldrev) do |tmp_ref|
...@@ -710,7 +748,7 @@ class Repository ...@@ -710,7 +748,7 @@ class Repository
end end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if was_empty if was_empty || !target_branch
# Create branch # Create branch
rugged.references.create(ref, newrev) rugged.references.create(ref, newrev)
else else
...@@ -725,6 +763,8 @@ class Repository ...@@ -725,6 +763,8 @@ class Repository
end end
end end
end end
newrev
end end
end end
......
# == Schema Information
#
# Table name: todos
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# target_id :integer not null
# target_type :string not null
# author_id :integer
# note_id :integer
# action :integer not null
# state :string not null
# created_at :datetime
# updated_at :datetime
#
class Todo < ActiveRecord::Base
ASSIGNED = 1
MENTIONED = 2
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
belongs_to :target, polymorphic: true, touch: true
belongs_to :user
delegate :name, :email, to: :author, prefix: true, allow_nil: true
validates :action, :project, :target, :user, presence: true
default_scope { reorder(id: :desc) }
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
state_machine :state, initial: :pending do
event :done do
transition [:pending, :done] => :done
end
state :pending
state :done
end
def body
if note.present?
note.note
else
target.title
end
end
end
...@@ -140,7 +140,7 @@ class User < ActiveRecord::Base ...@@ -140,7 +140,7 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build' has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy
# #
# Validations # Validations
......
class ArchiveRepositoryService
attr_reader :project, :ref, :format
def initialize(project, ref, format)
format ||= 'tar.gz'
@project, @ref, @format = project, ref, format.downcase
end
def execute(options = {})
RepositoryArchiveCacheWorker.perform_async
metadata = project.repository.archive_metadata(ref, storage_path, format)
raise "Repository or ref not found" if metadata.empty?
metadata
end
private
def storage_path
Gitlab.config.gitlab.repository_downloads_path
end
end
...@@ -23,6 +23,10 @@ class BaseService ...@@ -23,6 +23,10 @@ class BaseService
EventCreateService.new EventCreateService.new
end end
def todo_service
TodoService.new
end
def log_info(message) def log_info(message)
Gitlab::AppLogger.info message Gitlab::AppLogger.info message
end end
......
...@@ -34,6 +34,7 @@ module Ci ...@@ -34,6 +34,7 @@ module Ci
build = commit.builds.create!(build_attrs) build = commit.builds.create!(build_attrs)
build.execute_hooks build.execute_hooks
build
end end
end end
end end
......
module Commits
class RevertService < ::BaseService
class ValidationError < StandardError; end
class ReversionError < StandardError; end
def execute
@source_project = params[:source_project] || @project
@target_branch = params[:target_branch]
@commit = params[:commit]
@create_merge_request = params[:create_merge_request].present?
validate and commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ReversionError => ex
error(ex.message)
end
def commit
revert_into = @create_merge_request ? @commit.revert_branch_name : @target_branch
if @create_merge_request
# Temporary branch exists and contains the revert commit
return success if repository.find_branch(revert_into)
create_target_branch
end
unless repository.revert(current_user, @commit, revert_into)
error_msg = "Sorry, we cannot revert this #{params[:revert_type_title]} automatically.
It may have already been reverted, or a more recent commit may have updated some of its content."
raise ReversionError, error_msg
end
success
end
private
def create_target_branch
result = CreateBranchService.new(@project, current_user)
.execute(@commit.revert_branch_name, @target_branch, source_project: @source_project)
if result[:status] == :error
raise ReversionError, "There was an error creating the source branch: #{result[:message]}"
end
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(@target_branch)
unless allowed
raise_error('You are not allowed to push into this branch')
end
true
end
end
end
class GitPushService class GitPushService < BaseService
attr_accessor :project, :user, :push_data, :push_commits attr_accessor :push_data, :push_commits
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Gitlab::Access include Gitlab::Access
# This method will be called after each git update # This method will be called after each git update
# and only if the provided user and project is present in GitLab. # and only if the provided user and project are present in GitLab.
# #
# All callbacks for post receive action should be placed here. # All callbacks for post receive action should be placed here.
# #
...@@ -15,67 +15,67 @@ class GitPushService ...@@ -15,67 +15,67 @@ class GitPushService
# 4. Executes the project's web hooks # 4. Executes the project's web hooks
# 5. Executes the project's services # 5. Executes the project's services
# #
def execute(project, user, oldrev, newrev, ref) def execute
@project, @user = project, user @project.repository.expire_cache(branch_name)
branch_name = Gitlab::Git.ref_name(ref)
project.repository.expire_cache(branch_name)
if push_remove_branch?(ref, newrev)
project.repository.expire_has_visible_content_cache
if push_remove_branch?
@project.repository.expire_has_visible_content_cache
@push_commits = [] @push_commits = []
elsif push_to_new_branch?(ref, oldrev) elsif push_to_new_branch?
project.repository.expire_has_visible_content_cache @project.repository.expire_has_visible_content_cache
# Re-find the pushed commits. # Re-find the pushed commits.
if is_default_branch?(ref) if is_default_branch?
# Initial push to the default branch. Take the full history of that branch as "newly pushed". # Initial push to the default branch. Take the full history of that branch as "newly pushed".
@push_commits = project.repository.commits(newrev) process_default_branch
# Ensure HEAD points to the default branch in case it is not master
project.change_head(branch_name)
# Set protection on the default branch if configured
if (current_application_settings.default_branch_protection != PROTECTION_NONE)
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
project.protected_branches.create({ name: project.default_branch, developers_can_push: developers_can_push })
end
else else
# Use the pushed commits that aren't reachable by the default branch # Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually pushed, but # as a heuristic. This may include more commits than are actually pushed, but
# that shouldn't matter because we check for existing cross-references later. # that shouldn't matter because we check for existing cross-references later.
@push_commits = project.repository.commits_between(project.default_branch, newrev) @push_commits = @project.repository.commits_between(@project.default_branch, params[:newrev])
# don't process commits for the initial push to the default branch # don't process commits for the initial push to the default branch
process_commit_messages(ref) process_commit_messages
end end
elsif push_to_existing_branch?(ref, oldrev) elsif push_to_existing_branch?
# Collect data for this git push # Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev) @push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages(ref) process_commit_messages
end end
# Update merge requests that may be affected by this push. A new branch # Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change. # could cause the last commit of a merge request to change.
project.update_merge_requests(oldrev, newrev, ref, @user) update_merge_requests
end
@push_data = build_push_data(oldrev, newrev, ref) protected
EventCreateService.new.push(project, user, @push_data) def update_merge_requests
project.execute_hooks(@push_data.dup, :push_hooks) @project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user)
project.execute_services(@push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(project, @user, @push_data) EventCreateService.new.push(@project, current_user, build_push_data)
ProjectCacheWorker.perform_async(project.id) @project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(@project, current_user, build_push_data)
ProjectCacheWorker.perform_async(@project.id)
end end
protected def process_default_branch
@push_commits = project.repository.commits(params[:newrev])
# Ensure HEAD points to the default branch in case it is not master
project.change_head(branch_name)
# Set protection on the default branch if configured
if (current_application_settings.default_branch_protection != PROTECTION_NONE)
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
end
end
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages(ref) def process_commit_messages
is_default_branch = is_default_branch?(ref) is_default_branch = is_default_branch?
authors = Hash.new do |hash, commit| authors = Hash.new do |hash, commit|
email = commit.author_email email = commit.author_email
...@@ -94,7 +94,7 @@ class GitPushService ...@@ -94,7 +94,7 @@ class GitPushService
# Close issues if these commits were pushed to the project's default branch and the commit message matches the # Close issues if these commits were pushed to the project's default branch and the commit message matches the
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch. # a different branch.
closed_issues = commit.closes_issues(user) closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue| closed_issues.each do |issue|
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
end end
...@@ -104,34 +104,38 @@ class GitPushService ...@@ -104,34 +104,38 @@ class GitPushService
end end
end end
def build_push_data(oldrev, newrev, ref) def build_push_data
Gitlab::PushDataBuilder. @push_data ||= Gitlab::PushDataBuilder.
build(project, user, oldrev, newrev, ref, push_commits) build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits)
end end
def push_to_existing_branch?(ref, oldrev) def push_to_existing_branch?
# Return if this is not a push to a branch (e.g. new commits) # Return if this is not a push to a branch (e.g. new commits)
Gitlab::Git.branch_ref?(ref) && !Gitlab::Git.blank_ref?(oldrev) Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
end end
def push_to_new_branch?(ref, oldrev) def push_to_new_branch?
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(oldrev) Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:oldrev])
end end
def push_remove_branch?(ref, newrev) def push_remove_branch?
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(newrev) Gitlab::Git.branch_ref?(params[:ref]) && Gitlab::Git.blank_ref?(params[:newrev])
end end
def push_to_branch?(ref) def push_to_branch?
Gitlab::Git.branch_ref?(ref) Gitlab::Git.branch_ref?(params[:ref])
end end
def is_default_branch?(ref) def is_default_branch?
Gitlab::Git.branch_ref?(ref) && Gitlab::Git.branch_ref?(params[:ref]) &&
(Gitlab::Git.ref_name(ref) == project.default_branch || project.default_branch.nil?) (Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?)
end end
def commit_user(commit) def commit_user(commit)
commit.author || user commit.author || current_user
end
def branch_name
@branch_name ||= Gitlab::Git.ref_name(params[:ref])
end end
end end
...@@ -54,7 +54,7 @@ class IssuableBaseService < BaseService ...@@ -54,7 +54,7 @@ class IssuableBaseService < BaseService
if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) if params.present? && issuable.update_attributes(params.merge(updated_by: current_user))
issuable.reset_events_cache issuable.reset_events_cache
handle_common_system_notes(issuable, old_labels: old_labels) handle_common_system_notes(issuable, old_labels: old_labels)
handle_changes(issuable) handle_changes(issuable, old_labels: old_labels)
issuable.create_new_cross_references!(current_user) issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update') execute_hooks(issuable, 'update')
end end
...@@ -71,6 +71,19 @@ class IssuableBaseService < BaseService ...@@ -71,6 +71,19 @@ class IssuableBaseService < BaseService
end end
end end
def has_changes?(issuable, options = {})
valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]
attrs_changed = valid_attrs.any? do |attr|
issuable.previous_changes.include?(attr.to_s)
end
old_labels = options[:old_labels]
labels_changed = old_labels && issuable.labels != old_labels
attrs_changed || labels_changed
end
def handle_common_system_notes(issuable, options = {}) def handle_common_system_notes(issuable, options = {})
if issuable.previous_changes.include?('title') if issuable.previous_changes.include?('title')
create_title_change_note(issuable, issuable.previous_changes['title'].first) create_title_change_note(issuable, issuable.previous_changes['title'].first)
......
...@@ -3,6 +3,7 @@ module Issues ...@@ -3,6 +3,7 @@ module Issues
def execute(issue, commit = nil) def execute(issue, commit = nil)
if project.jira_tracker? && project.jira_service.active if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue) project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user)
return issue return issue
end end
...@@ -10,6 +11,7 @@ module Issues ...@@ -10,6 +11,7 @@ module Issues
event_service.close_issue(issue, current_user) event_service.close_issue(issue, current_user)
create_note(issue, commit) create_note(issue, commit)
notification_service.close_issue(issue, current_user) notification_service.close_issue(issue, current_user)
todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close') execute_hooks(issue, 'close')
end end
......
...@@ -9,6 +9,7 @@ module Issues ...@@ -9,6 +9,7 @@ module Issues
if issue.save if issue.save
issue.update_attributes(label_ids: label_params) issue.update_attributes(label_ids: label_params)
notification_service.new_issue(issue, current_user) notification_service.new_issue(issue, current_user)
todo_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user) event_service.open_issue(issue, current_user)
issue.create_cross_references!(current_user) issue.create_cross_references!(current_user)
execute_hooks(issue, 'open') execute_hooks(issue, 'open')
......
...@@ -4,7 +4,16 @@ module Issues ...@@ -4,7 +4,16 @@ module Issues
update(issue) update(issue)
end end
def handle_changes(issue) def handle_changes(issue, options = {})
if has_changes?(issue, options)
todo_service.mark_pending_todos_as_done(issue, current_user)
end
if issue.previous_changes.include?('title') ||
issue.previous_changes.include?('description')
todo_service.update_issue(issue, current_user)
end
if issue.previous_changes.include?('milestone_id') if issue.previous_changes.include?('milestone_id')
create_milestone_note(issue) create_milestone_note(issue)
end end
...@@ -12,6 +21,7 @@ module Issues ...@@ -12,6 +21,7 @@ module Issues
if issue.previous_changes.include?('assignee_id') if issue.previous_changes.include?('assignee_id')
create_assignee_note(issue) create_assignee_note(issue)
notification_service.reassigned_issue(issue, current_user) notification_service.reassigned_issue(issue, current_user)
todo_service.reassigned_issue(issue, current_user)
end end
end end
......
...@@ -56,7 +56,7 @@ module MergeRequests ...@@ -56,7 +56,7 @@ module MergeRequests
if commits && commits.count == 1 if commits && commits.count == 1
commit = commits.first commit = commits.first
merge_request.title = commit.title merge_request.title = commit.title
merge_request.description = commit.description.try(:strip) merge_request.description ||= commit.description.try(:strip)
else else
merge_request.title = merge_request.source_branch.titleize.humanize merge_request.title = merge_request.source_branch.titleize.humanize
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment