Commit b5d1e634 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'pipeline-hooks-without-slack' into wall-clock-time-for-showing-pipeline

* pipeline-hooks-without-slack: (156 commits)
  Fix test failures
  Make pipeline to be in created state for hooks tests
  Make `execute_methods` public
  Added specs for started_at and finished_at
  Use explicit events to transition between states
  Fix tests. We cannot reload unless it's already saved:
  Have trait all_events_enabled so that's easier to reuse, feedback:
  Simplify the name for data builder, feedback:
  Prefer extend self over module_function, feedback:
  Make it more grammatically correct, feedback:
  if -> when; when -> `when`; %w() -> %w[]; and fix some typos:
  Prefer described_class, feedback:
  Make the comment more clear, feedback:
  Update CHANGELOG
  render only commit title
  Fix test failures, that did occur because of missing previously used `reload_status!` call
  Use state machine for pipeline event processing
  Upgrade Rails to 4.2.7.1 for security fixes.
  Update gitlab-shell to v3.3.3
  Verify the pipeline status after executing events on builds
  ...
parents 21fdc1ed a391652f
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
/config/secrets.yml /config/secrets.yml
/config/sidekiq.yml /config/sidekiq.yml
/coverage/* /coverage/*
/coverage-javascript/
/db/*.sqlite3 /db/*.sqlite3
/db/*.sqlite3-journal /db/*.sqlite3-journal
/db/data.yml /db/data.yml
......
image: "ruby:2.1" image: "ruby:2.3"
cache: cache:
key: "ruby21" key: "ruby-23"
paths: paths:
- vendor/apt - vendor/apt
- vendor/ruby - vendor/ruby
...@@ -138,57 +138,57 @@ spinach 7 10: *spinach-knapsack ...@@ -138,57 +138,57 @@ spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.3 # Execute all testing suites against Ruby 2.1
.ruby-23: &ruby-23 .ruby-21: &ruby-21
image: "ruby:2.3" image: "ruby:2.1"
<<: *use-db <<: *use-db
only: only:
- master - master
cache: cache:
key: "ruby-23" key: "ruby21"
paths: paths:
- vendor/apt - vendor/apt
- vendor/ruby - vendor/ruby
.rspec-knapsack-ruby23: &rspec-knapsack-ruby23 .rspec-knapsack-ruby21: &rspec-knapsack-ruby21
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *ruby-23 <<: *ruby-21
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23 .spinach-knapsack-ruby21: &spinach-knapsack-ruby21
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *ruby-23 <<: *ruby-21
rspec 0 20 ruby23: *rspec-knapsack-ruby23 rspec 0 20 ruby21: *rspec-knapsack-ruby21
rspec 1 20 ruby23: *rspec-knapsack-ruby23 rspec 1 20 ruby21: *rspec-knapsack-ruby21
rspec 2 20 ruby23: *rspec-knapsack-ruby23 rspec 2 20 ruby21: *rspec-knapsack-ruby21
rspec 3 20 ruby23: *rspec-knapsack-ruby23 rspec 3 20 ruby21: *rspec-knapsack-ruby21
rspec 4 20 ruby23: *rspec-knapsack-ruby23 rspec 4 20 ruby21: *rspec-knapsack-ruby21
rspec 5 20 ruby23: *rspec-knapsack-ruby23 rspec 5 20 ruby21: *rspec-knapsack-ruby21
rspec 6 20 ruby23: *rspec-knapsack-ruby23 rspec 6 20 ruby21: *rspec-knapsack-ruby21
rspec 7 20 ruby23: *rspec-knapsack-ruby23 rspec 7 20 ruby21: *rspec-knapsack-ruby21
rspec 8 20 ruby23: *rspec-knapsack-ruby23 rspec 8 20 ruby21: *rspec-knapsack-ruby21
rspec 9 20 ruby23: *rspec-knapsack-ruby23 rspec 9 20 ruby21: *rspec-knapsack-ruby21
rspec 10 20 ruby23: *rspec-knapsack-ruby23 rspec 10 20 ruby21: *rspec-knapsack-ruby21
rspec 11 20 ruby23: *rspec-knapsack-ruby23 rspec 11 20 ruby21: *rspec-knapsack-ruby21
rspec 12 20 ruby23: *rspec-knapsack-ruby23 rspec 12 20 ruby21: *rspec-knapsack-ruby21
rspec 13 20 ruby23: *rspec-knapsack-ruby23 rspec 13 20 ruby21: *rspec-knapsack-ruby21
rspec 14 20 ruby23: *rspec-knapsack-ruby23 rspec 14 20 ruby21: *rspec-knapsack-ruby21
rspec 15 20 ruby23: *rspec-knapsack-ruby23 rspec 15 20 ruby21: *rspec-knapsack-ruby21
rspec 16 20 ruby23: *rspec-knapsack-ruby23 rspec 16 20 ruby21: *rspec-knapsack-ruby21
rspec 17 20 ruby23: *rspec-knapsack-ruby23 rspec 17 20 ruby21: *rspec-knapsack-ruby21
rspec 18 20 ruby23: *rspec-knapsack-ruby23 rspec 18 20 ruby21: *rspec-knapsack-ruby21
rspec 19 20 ruby23: *rspec-knapsack-ruby23 rspec 19 20 ruby21: *rspec-knapsack-ruby21
spinach 0 10 ruby23: *spinach-knapsack-ruby23 spinach 0 10 ruby21: *spinach-knapsack-ruby21
spinach 1 10 ruby23: *spinach-knapsack-ruby23 spinach 1 10 ruby21: *spinach-knapsack-ruby21
spinach 2 10 ruby23: *spinach-knapsack-ruby23 spinach 2 10 ruby21: *spinach-knapsack-ruby21
spinach 3 10 ruby23: *spinach-knapsack-ruby23 spinach 3 10 ruby21: *spinach-knapsack-ruby21
spinach 4 10 ruby23: *spinach-knapsack-ruby23 spinach 4 10 ruby21: *spinach-knapsack-ruby21
spinach 5 10 ruby23: *spinach-knapsack-ruby23 spinach 5 10 ruby21: *spinach-knapsack-ruby21
spinach 6 10 ruby23: *spinach-knapsack-ruby23 spinach 6 10 ruby21: *spinach-knapsack-ruby21
spinach 7 10 ruby23: *spinach-knapsack-ruby23 spinach 7 10 ruby21: *spinach-knapsack-ruby21
spinach 8 10 ruby23: *spinach-knapsack-ruby23 spinach 8 10 ruby21: *spinach-knapsack-ruby21
spinach 9 10 ruby23: *spinach-knapsack-ruby23 spinach 9 10 ruby21: *spinach-knapsack-ruby21
# Other generic tests # Other generic tests
...@@ -222,7 +222,22 @@ teaspoon: ...@@ -222,7 +222,22 @@ teaspoon:
stage: test stage: test
<<: *use-db <<: *use-db
script: script:
- curl --silent --location https://deb.nodesource.com/setup_6.x | bash -
- apt-get install --assume-yes nodejs
- npm install --global istanbul
- teaspoon - teaspoon
artifacts:
name: coverage-javascript
expire_in: 31d
paths:
- coverage-javascript/default/
lint-doc:
stage: test
image: "phusion/baseimage:latest"
before_script: []
script:
- scripts/lint-doc.sh
bundler:audit: bundler:audit:
stage: test stage: test
...@@ -269,10 +284,12 @@ pages: ...@@ -269,10 +284,12 @@ pages:
stage: pages stage: pages
dependencies: dependencies:
- coverage - coverage
- teaspoon
script: script:
- mv public/ .public/ - mv public/ .public/
- mkdir public/ - mkdir public/
- mv coverage public/coverage-ruby - mv coverage public/coverage-ruby
- mv coverage-javascript/default/ public/coverage-javascript/
artifacts: artifacts:
paths: paths:
- public - public
......
#
# This list is used by git-shortlog to make contributions from the
# same person appearing to be so.
#
Achilleas Pipinellis <axilleas@axilleas.me> <axilleas@archlinux.gr>
Achilleas Pipinellis <axilleas@axilleas.me> <axilleas@users.noreply.github.com>
Dmitriy Zaporozhets <dzaporozhets@gitlab.com> <dmitriy.zaporozhets@gmail.com>
Dmitriy Zaporozhets <dzaporozhets@gitlab.com> <dzaporozhets@sphereconsultinginc.com>
Douwe Maan <douwe@gitlab.com> <douwe@selenight.nl>
Douwe Maan <douwe@gitlab.com> <me@douwe.me>
Grzegorz Bizon <grzegorz@gitlab.com> <grzegorz.bizon@ntsn.pl>
Grzegorz Bizon <grzegorz@gitlab.com> <grzesiek.bizon@gmail.com>
Jacob Vosmaer <jacob@gitlab.com> <contact@jacobvosmaer.nl>
Jacob Vosmaer <jacob@gitlab.com> Jacob Vosmaer (GitLab) <jacob@gitlab.com>
Jacob Schatz <jschatz@gitlab.com> <jacobschatz@Jacobs-MacBook-Pro.local>
Jacob Schatz <jschatz@gitlab.com> <jacobschatz@Jacobs-MBP.fios-router.home>
Jacob Schatz <jschatz@gitlab.com> <jschatz1@gmail.com>
James Lopez <james@jameslopez.es> <james@gitlab.com>
James Lopez <james@jameslopez.es> <james.lopez@vodafone.com>
Kamil Trzciński <kamil@gitlab.com> <ayufan@ayufan.eu>
Marin Jankovski <maxlazio@gmail.com> <marin@gitlab.com>
Phil Hughes <me@iamphill.com> <theephil@gmail.com>
Rémy Coutable <remy@rymai.me> <remy@gitlab.com>
Robert Schilling <rschilling@student.tugraz.at> <Razer6@users.noreply.github.com>
Robert Schilling <rschilling@student.tugraz.at> <schilling.ro@gmail.com>
Robert Speicher <robert@gitlab.com> <rspeicher@gmail.com>
Stan Hu <stanhu@gmail.com> <stanhu@alum.mit.edu>
Stan Hu <stanhu@gmail.com> <stanhu@packetzoom.com>
Stan Hu <stanhu@gmail.com> <stanhu@users.noreply.github.com>
Stan Hu <stanhu@gmail.com> stanhu <stanhu@gmail.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytse+admin@gitlab.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytse@dosire.com>
Sytse Sijbrandij <sytse@gitlab.com> <sytses@gmail.com>
Sytse Sijbrandij <sytse@gitlab.com> dosire <sytse@gitlab.com>
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.11.0 (unreleased) v 8.11.0 (unreleased)
- Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres) - Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres) - Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres) - Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Rename `markdown_preview` routes to `preview_markdown`. (Christopher Bartz)
- Update to Ruby 2.3.1. !4948
- Improve diff performance by eliminating redundant checks for text blobs - Improve diff performance by eliminating redundant checks for text blobs
- Ensure that branch names containing escapable characters (e.g. %20) aren't unescaped indiscriminately. !5770 (ewiltshi)
- Convert switch icon into icon font (ClemMakesApps) - Convert switch icon into icon font (ClemMakesApps)
- API: Endpoints for enabling and disabling deploy keys - API: Endpoints for enabling and disabling deploy keys
- API: List access requests, request access, approve, and deny access requests to a project or a group. !4833
- Use long options for curl examples in documentation !5703 (winniehell)
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell) - Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell) - Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Ignore URLs starting with // in Markdown links !5677 (winniehell) - Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps) - Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented - The Repository class is now instrumented
- Fix filter label tooltip HTML rendering (ClemMakesApps)
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive - Cache the commit author in RequestStore to avoid extra lookups in PostReceive
- Expand commit message width in repo view (ClemMakesApps) - Expand commit message width in repo view (ClemMakesApps)
- Cache highlighted diff lines for merge requests - Cache highlighted diff lines for merge requests
- Pre-create all builds for a Pipeline when the new Pipeline is created !5295
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI' - Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
- Show wall clock time when showing a pipeline. !5734 - Show wall clock time when showing a pipeline. !5734
- Show member roles to all users on members page
- Fix awardable button mutuality loading spinners (ClemMakesApps)
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable - Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes - Optimize maximum user access level lookup in loading of notes
- Add "No one can push" as an option for protected branches. !5081 - Add "No one can push" as an option for protected branches. !5081
- Improve performance of AutolinkFilter#text_parse by using XPath - Improve performance of AutolinkFilter#text_parse by using XPath
- Add experimental Redis Sentinel support !1877
- Fix branches page dropdown sort initial state (ClemMakesApps)
- Environments have an url to link to - Environments have an url to link to
- Various redundant database indexes have been removed
- Update `timeago` plugin to use multiple string/locale settings - Update `timeago` plugin to use multiple string/locale settings
- Remove unused images (ClemMakesApps) - Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check - Limit git rev-list output count to one in forced push check
...@@ -34,9 +47,12 @@ v 8.11.0 (unreleased) ...@@ -34,9 +47,12 @@ v 8.11.0 (unreleased)
- Remove delay when hitting "Reply..." button on page with a lot of discussions - Remove delay when hitting "Reply..." button on page with a lot of discussions
- Retrieve rendered HTML from cache in one request - Retrieve rendered HTML from cache in one request
- Fix renaming repository when name contains invalid chararacters under project settings - Fix renaming repository when name contains invalid chararacters under project settings
- Upgrade Grape from 0.13.0 to 0.15.0. !4601
- Trigram indexes for the "ci_runners" table have been removed to speed up UPDATE queries
- Fix devise deprecation warnings. - Fix devise deprecation warnings.
- Update version_sorter and use new interface for faster tag sorting - Update version_sorter and use new interface for faster tag sorting
- Optimize checking if a user has read access to a list of issues !5370 - Optimize checking if a user has read access to a list of issues !5370
- Store all DB secrets in secrets.yml, under descriptive names !5274
- Nokogiri's various parsing methods are now instrumented - Nokogiri's various parsing methods are now instrumented
- Add simple identifier to public SSH keys (muteor) - Add simple identifier to public SSH keys (muteor)
- Admin page now references docs instead of a specific file !5600 (AnAverageHuman) - Admin page now references docs instead of a specific file !5600 (AnAverageHuman)
...@@ -48,9 +64,11 @@ v 8.11.0 (unreleased) ...@@ -48,9 +64,11 @@ v 8.11.0 (unreleased)
- Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon) - Document that webhook secret token is sent in X-Gitlab-Token HTTP header !5664 (lycoperdon)
- Gitlab::Highlight is now instrumented - Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333 - All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- Allow users to import cross-repository pull requests from GitHub
- The overhead of instrumented method calls has been reduced - The overhead of instrumented method calls has been reduced
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le) - Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members` - Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Add pipeline events hook
- Bump gitlab_git to speedup DiffCollection iterations - Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich) - Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell) - Make branches sortable without push permission !5462 (winniehell)
...@@ -63,6 +81,7 @@ v 8.11.0 (unreleased) ...@@ -63,6 +81,7 @@ v 8.11.0 (unreleased)
- Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska) - Add GitLab Workhorse version to admin dashboard (Katarzyna Kobierska Ula Budziszewska)
- Allow branch names ending with .json for graph and network page !5579 (winniehell) - Allow branch names ending with .json for graph and network page !5579 (winniehell)
- Add the `sprockets-es6` gem - Add the `sprockets-es6` gem
- Improve OAuth2 client documentation (muteor)
- Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska) - Multiple trigger variables show in separate lines (Katarzyna Kobierska Ula Budziszewska)
- Profile requests when a header is passed - Profile requests when a header is passed
- Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab. - Avoid calculation of line_code and position for _line partial when showing diff notes on discussion tab.
...@@ -77,14 +96,22 @@ v 8.11.0 (unreleased) ...@@ -77,14 +96,22 @@ v 8.11.0 (unreleased)
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y) - Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix RequestProfiler::Middleware error when code is reloaded in development - Fix RequestProfiler::Middleware error when code is reloaded in development
- Catch what warden might throw when profiling requests to re-throw it - Catch what warden might throw when profiling requests to re-throw it
- Avoid commit lookup on diff_helper passing existing local variable to the helper method
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac) - Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker - Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko) - Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
- Adds support for pending invitation project members importing projects - Adds support for pending invitation project members importing projects
- Update devise initializer to turn on changed password notification emails. !5648 (tombell) - Update devise initializer to turn on changed password notification emails. !5648 (tombell)
- Avoid to show the original password field when password is automatically set. !5712 (duduribeiro) - Avoid to show the original password field when password is automatically set. !5712 (duduribeiro)
- Fix importing GitLab projects with an invalid MR source project
v 8.10.5 (unreleased) - Sort folders with submodules in Files view !5521
- Each `File::exists?` replaced to `File::exist?` because of deprecate since ruby version 2.2.0
- Add auto-completition in pipeline (Katarzyna Kobierska Ula Budziszewska)
v 8.10.5
- Add a data migration to fix some missing timestamps in the members table. !5670
- Revert the "Defend against 'Host' header injection" change in the source NGINX templates. !5706
- Cache project count for 5 minutes to reduce DB load. !5746 & !5754
v 8.10.4 v 8.10.4
- Don't close referenced upstream issues from a forked project. - Don't close referenced upstream issues from a forked project.
......
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'rails', '4.2.7' gem 'rails', '4.2.7.1'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
...@@ -69,7 +69,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false ...@@ -69,7 +69,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.13.0' gem 'grape', '~> 0.15.0'
gem 'grape-entity', '~> 0.4.2' gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
...@@ -163,9 +163,6 @@ gem 'redis-rails', '~> 4.0.0' ...@@ -163,9 +163,6 @@ gem 'redis-rails', '~> 4.0.0'
gem 'redis', '~> 3.2' gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0' gem 'connection_pool', '~> 2.0'
# Campfire integration
gem 'tinder', '~> 1.10.0'
# HipChat integration # HipChat integration
gem 'hipchat', '~> 1.5.0' gem 'hipchat', '~> 1.5.0'
......
...@@ -3,34 +3,34 @@ GEM ...@@ -3,34 +3,34 @@ GEM
specs: specs:
RedCloth (4.3.2) RedCloth (4.3.2)
ace-rails-ap (4.0.2) ace-rails-ap (4.0.2)
actionmailer (4.2.7) actionmailer (4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activejob (= 4.2.7) activejob (= 4.2.7.1)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.7) actionpack (4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.7) actionview (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.7) activejob (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.7) activemodel (4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.7) activerecord (4.2.7.1)
activemodel (= 4.2.7) activemodel (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (1.0.0) activerecord-session_store (1.0.0)
actionpack (>= 4.0, < 5.1) actionpack (>= 4.0, < 5.1)
...@@ -38,7 +38,7 @@ GEM ...@@ -38,7 +38,7 @@ GEM
multi_json (~> 1.11, >= 1.11.2) multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3) rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1) railties (>= 4.0, < 5.1)
activesupport (4.2.7) activesupport (4.2.7.1)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -289,7 +289,7 @@ GEM ...@@ -289,7 +289,7 @@ GEM
omniauth (~> 1.0) omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.3) rubyntlm (~> 0.3)
globalid (0.3.6) globalid (0.3.7)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.1) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
...@@ -308,7 +308,7 @@ GEM ...@@ -308,7 +308,7 @@ GEM
json json
multi_json multi_json
request_store (>= 1.0) request_store (>= 1.0)
grape (0.13.0) grape (0.15.0)
activesupport activesupport
builder builder
hashie (>= 2.1.0) hashie (>= 2.1.0)
...@@ -335,7 +335,6 @@ GEM ...@@ -335,7 +335,6 @@ GEM
activesupport (>= 2) activesupport (>= 2)
nokogiri (~> 1.4) nokogiri (~> 1.4)
htmlentities (4.3.4) htmlentities (4.3.4)
http_parser.rb (0.5.3)
httparty (0.13.7) httparty (0.13.7)
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
...@@ -519,16 +518,16 @@ GEM ...@@ -519,16 +518,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.7) rails (4.2.7.1)
actionmailer (= 4.2.7) actionmailer (= 4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
actionview (= 4.2.7) actionview (= 4.2.7.1)
activejob (= 4.2.7) activejob (= 4.2.7.1)
activemodel (= 4.2.7) activemodel (= 4.2.7.1)
activerecord (= 4.2.7) activerecord (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7) railties (= 4.2.7.1)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
...@@ -538,9 +537,9 @@ GEM ...@@ -538,9 +537,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
railties (4.2.7) railties (4.2.7.1)
actionpack (= 4.2.7) actionpack (= 4.2.7.1)
activesupport (= 4.2.7) activesupport (= 4.2.7.1)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.1.0) rainbow (2.1.0)
...@@ -672,7 +671,6 @@ GEM ...@@ -672,7 +671,6 @@ GEM
redis-namespace (>= 1.5.2) redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24) rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0) sidekiq (>= 4.0.0)
simple_oauth (0.1.9)
simplecov (0.12.0) simplecov (0.12.0)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
...@@ -742,21 +740,8 @@ GEM ...@@ -742,21 +740,8 @@ GEM
tilt (2.0.5) tilt (2.0.5)
timecop (0.8.1) timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
tinder (1.10.1)
eventmachine (~> 1.0)
faraday (~> 0.9.0)
faraday_middleware (~> 0.9)
hashie (>= 1.0)
json (~> 1.8.0)
mime-types
multi_json (~> 1.7)
twitter-stream (~> 0.1)
turbolinks (2.5.3) turbolinks (2.5.3)
coffee-rails coffee-rails
twitter-stream (0.1.16)
eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4)
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1) u2f (0.2.1)
...@@ -876,7 +861,7 @@ DEPENDENCIES ...@@ -876,7 +861,7 @@ DEPENDENCIES
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0) gon (~> 6.1.0)
grape (~> 0.13.0) grape (~> 0.15.0)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
hamlit (~> 2.5) hamlit (~> 2.5)
health_check (~> 2.1.0) health_check (~> 2.1.0)
...@@ -929,7 +914,7 @@ DEPENDENCIES ...@@ -929,7 +914,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.7) rails (= 4.2.7.1)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0) rainbow (~> 2.1.0)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
...@@ -981,7 +966,6 @@ DEPENDENCIES ...@@ -981,7 +966,6 @@ DEPENDENCIES
teaspoon-jasmine (~> 2.2.0) teaspoon-jasmine (~> 2.2.0)
test_after_commit (~> 0.4.2) test_after_commit (~> 0.4.2)
thin (~> 1.7.0) thin (~> 1.7.0)
tinder (~> 1.10.0)
turbolinks (~> 2.5.0) turbolinks (~> 2.5.0)
u2f (~> 0.2.1) u2f (~> 0.2.1)
uglifier (~> 2.7.2) uglifier (~> 2.7.2)
......
...@@ -161,23 +161,11 @@ ...@@ -161,23 +161,11 @@
$emojiButton = votesBlock.find("[data-emoji=" + mutualVote + "]").parent(); $emojiButton = votesBlock.find("[data-emoji=" + mutualVote + "]").parent();
isAlreadyVoted = $emojiButton.hasClass('active'); isAlreadyVoted = $emojiButton.hasClass('active');
if (isAlreadyVoted) { if (isAlreadyVoted) {
this.showEmojiLoader($emojiButton); this.addAward(votesBlock, awardUrl, mutualVote, false);
return this.addAward(votesBlock, awardUrl, mutualVote, false, function() {
return $emojiButton.removeClass('is-loading');
});
} }
} }
}; };
AwardsHandler.prototype.showEmojiLoader = function($emojiButton) {
var $loader;
$loader = $emojiButton.find('.fa-spinner');
if (!$loader.length) {
$emojiButton.append('<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>');
}
return $emojiButton.addClass('is-loading');
};
AwardsHandler.prototype.isActive = function($emojiButton) { AwardsHandler.prototype.isActive = function($emojiButton) {
return $emojiButton.hasClass('active'); return $emojiButton.hasClass('active');
}; };
......
...@@ -186,6 +186,12 @@ ...@@ -186,6 +186,12 @@
break; break;
case 'projects': case 'projects':
new NamespaceSelects(); new NamespaceSelects();
break;
case 'labels':
switch (path[2]) {
case 'edit':
new Labels();
}
} }
break; break;
case 'dashboard': case 'dashboard':
...@@ -211,6 +217,7 @@ ...@@ -211,6 +217,7 @@
new ProjectNew(); new ProjectNew();
break; break;
case 'show': case 'show':
new Star();
new ProjectNew(); new ProjectNew();
new ProjectShow(); new ProjectShow();
new NotificationsDropdown(); new NotificationsDropdown();
......
/*= require markdown_preview */ /*= require preview_markdown */
(function() { (function() {
this.DropzoneInput = (function() { this.DropzoneInput = (function() {
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
}; };
MarkdownPreview.prototype.renderMarkdown = function(text, success) { MarkdownPreview.prototype.renderMarkdown = function(text, success) {
if (!window.markdown_preview_path) { if (!window.preview_markdown_path) {
return; return;
} }
if (text === this.ajaxCache.text) { if (text === this.ajaxCache.text) {
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
} }
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
url: window.markdown_preview_path, url: window.preview_markdown_path,
data: { data: {
text: text text: text
}, },
......
...@@ -182,6 +182,17 @@ ...@@ -182,6 +182,17 @@
.btn { .btn {
color: inherit; color: inherit;
} }
a.btn {
padding: 0;
.has-tooltip {
top: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
line-height: 1.1;
}
}
} }
.label-options-toggle { .label-options-toggle {
......
...@@ -7,11 +7,16 @@ module ServiceParams ...@@ -7,11 +7,16 @@ module ServiceParams
:build_key, :server, :teamcity_url, :drone_url, :build_type, :build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels, :colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events, # We're using `issues_events` and `merge_requests_events`
:note_events, :build_events, :wiki_page_events, # in the view so we still need to explicitly state them
:notify_only_broken_builds, :add_pusher, # here. `Service#event_names` would only give
:send_from_committer_email, :disable_diffs, :external_wiki_url, # `issue_events` and `merge_request_events` (singular!)
:notify, :color, # See app/helpers/services_helper.rb for how we
# make those event names plural as special case.
:issues_events, :merge_requests_events,
:notify_only_broken_builds, :notify_only_broken_pipelines,
:add_pusher, :send_from_committer_email, :disable_diffs,
:external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id] :jira_issue_transition_id]
...@@ -19,9 +24,7 @@ module ServiceParams ...@@ -19,9 +24,7 @@ module ServiceParams
FILTER_BLANK_PARAMS = [:password] FILTER_BLANK_PARAMS = [:password]
def service_params def service_params
dynamic_params = [] dynamic_params = @service.event_channel_names + @service.event_names
dynamic_params.concat(@service.event_channel_names)
service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params) service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
if service_params[:service].is_a?(Hash) if service_params[:service].is_a?(Hash)
......
...@@ -8,8 +8,9 @@ class Projects::BadgesController < Projects::ApplicationController ...@@ -8,8 +8,9 @@ class Projects::BadgesController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html { render_404 } format.html { render_404 }
format.svg do format.svg do
send_data(badge.data, type: badge.type, disposition: 'inline') render 'badge', locals: { badge: badge.template }
end end
end end
end end
......
class Projects::BranchesController < Projects::ApplicationController class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
include SortingHelper
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy] before_action :authorize_push_code!, only: [:new, :create, :destroy]
def index def index
@sort = params[:sort].presence || 'name' @sort = params[:sort].presence || sort_value_name
@branches = BranchesFinder.new(@repository, params).execute @branches = BranchesFinder.new(@repository, params).execute
@branches = Kaminari.paginate_array(@branches).page(params[:page]) @branches = Kaminari.paginate_array(@branches).page(params[:page])
......
...@@ -6,7 +6,7 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -6,7 +6,7 @@ class Projects::BuildsController < Projects::ApplicationController
def index def index
@scope = params[:scope] @scope = params[:scope]
@all_builds = project.builds @all_builds = project.builds.relevant
@builds = @all_builds.order('created_at DESC') @builds = @all_builds.order('created_at DESC')
@builds = @builds =
case @scope case @scope
......
...@@ -134,8 +134,8 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -134,8 +134,8 @@ class Projects::CommitController < Projects::ApplicationController
end end
def define_status_vars def define_status_vars
@statuses = CommitStatus.where(pipeline: pipelines) @statuses = CommitStatus.where(pipeline: pipelines).relevant
@builds = Ci::Build.where(pipeline: pipelines) @builds = Ci::Build.where(pipeline: pipelines).relevant
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars(mr_source_branch)
......
# This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpClientController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
attr_reader :user
# Git clients will not know what authenticity token to send along
skip_before_action :verify_authenticity_token
skip_before_action :repository
before_action :authenticate_user
before_action :ensure_project_found!
private
def authenticate_user
if project && project.public? && download_request?
return # Allow access
end
if allow_basic_auth? && basic_auth_provided?
login, password = user_name_and_password(request)
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && download_request?
@ci = true
elsif auth_result.type == :oauth && !download_request?
# Not allowed
else
@user = auth_result.user
end
if ci? || user
return # Allow access
end
elsif allow_kerberos_spnego_auth? && spnego_provided?
@user = find_kerberos_user
if user
send_final_spnego_response
return # Allow access
end
end
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
end
def basic_auth_provided?
has_basic_credentials?(request)
end
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
challenges << spnego_challenge if allow_kerberos_spnego_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
def ensure_project_found!
render_not_found if project.blank?
end
def project
return @project if defined?(@project)
project_id, _ = project_id_with_suffix
if project_id.blank?
@project = nil
else
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
end
end
# This method returns two values so that we can parse
# params[:project_id] (untrusted input!) in exactly one place.
def project_id_with_suffix
id = params[:project_id] || ''
%w[.wiki.git .git].each do |suffix|
if id.end_with?(suffix)
# Be careful to only remove the suffix from the end of 'id'.
# Accidentally removing it from the middle is how security
# vulnerabilities happen!
return [id.slice(0, id.length - suffix.length), suffix]
end
end
# Something is wrong with params[:project_id]; do not pass it on.
[nil, nil]
end
def repository
_, suffix = project_id_with_suffix
if suffix == '.wiki.git'
project.wiki.repository
else
project.repository
end
end
def render_not_found
render plain: 'Not Found', status: :not_found
end
def ci?
@ci.present?
end
end
# This file should be identical in GitLab Community Edition and Enterprise Edition # This file should be identical in GitLab Community Edition and Enterprise Edition
class Projects::GitHttpController < Projects::ApplicationController class Projects::GitHttpController < Projects::GitHttpClientController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
attr_reader :user
# Git clients will not know what authenticity token to send along
skip_before_action :verify_authenticity_token
skip_before_action :repository
before_action :authenticate_user
before_action :ensure_project_found!
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull) # GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push) # GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def info_refs def info_refs
...@@ -46,81 +35,8 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -46,81 +35,8 @@ class Projects::GitHttpController < Projects::ApplicationController
private private
def authenticate_user def download_request?
if project && project.public? && upload_pack? upload_pack?
return # Allow access
end
if allow_basic_auth? && basic_auth_provided?
login, password = user_name_and_password(request)
auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && upload_pack?
@ci = true
elsif auth_result.type == :oauth && !upload_pack?
# Not allowed
else
@user = auth_result.user
end
if ci? || user
return # Allow access
end
elsif allow_kerberos_spnego_auth? && spnego_provided?
@user = find_kerberos_user
if user
send_final_spnego_response
return # Allow access
end
end
send_challenges
render plain: "HTTP Basic: Access denied\n", status: 401
end
def basic_auth_provided?
has_basic_credentials?(request)
end
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
challenges << spnego_challenge if allow_kerberos_spnego_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
def ensure_project_found!
render_not_found if project.blank?
end
def project
return @project if defined?(@project)
project_id, _ = project_id_with_suffix
if project_id.blank?
@project = nil
else
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
end
end
# This method returns two values so that we can parse
# params[:project_id] (untrusted input!) in exactly one place.
def project_id_with_suffix
id = params[:project_id] || ''
%w[.wiki.git .git].each do |suffix|
if id.end_with?(suffix)
# Be careful to only remove the suffix from the end of 'id'.
# Accidentally removing it from the middle is how security
# vulnerabilities happen!
return [id.slice(0, id.length - suffix.length), suffix]
end
end
# Something is wrong with params[:project_id]; do not pass it on.
[nil, nil]
end end
def upload_pack? def upload_pack?
...@@ -143,19 +59,6 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -143,19 +59,6 @@ class Projects::GitHttpController < Projects::ApplicationController
render json: Gitlab::Workhorse.git_http_ok(repository, user) render json: Gitlab::Workhorse.git_http_ok(repository, user)
end end
def repository
_, suffix = project_id_with_suffix
if suffix == '.wiki.git'
project.wiki.repository
else
project.repository
end
end
def render_not_found
render plain: 'Not Found', status: :not_found
end
def render_http_not_allowed def render_http_not_allowed
render plain: access_check.message, status: :forbidden render plain: access_check.message, status: :forbidden
end end
...@@ -169,10 +72,6 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -169,10 +72,6 @@ class Projects::GitHttpController < Projects::ApplicationController
end end
end end
def ci?
@ci.present?
end
def upload_pack_allowed? def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack return false unless Gitlab.config.gitlab_shell.upload_pack
......
...@@ -56,6 +56,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -56,6 +56,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params def hook_params
params.require(:hook).permit( params.require(:hook).permit(
:build_events, :build_events,
:pipeline_events,
:enable_ssl_verification, :enable_ssl_verification,
:issues_events, :issues_events,
:merge_requests_events, :merge_requests_events,
......
class Projects::LfsApiController < Projects::GitHttpClientController
include LfsHelper
before_action :require_lfs_enabled!
before_action :lfs_check_access!, except: [:deprecated]
def batch
unless objects.present?
render_lfs_not_found
return
end
if download_request?
render json: { objects: download_objects! }
elsif upload_request?
render json: { objects: upload_objects! }
else
raise "Never reached"
end
end
def deprecated
render(
json: {
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
documentation_url: "#{Gitlab.config.gitlab.url}/help",
},
status: 501
)
end
private
def objects
@objects ||= (params[:objects] || []).to_a
end
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
end
end
def download_objects!
objects.each do |object|
if existing_oids.include?(object[:oid])
object[:actions] = download_actions(object)
else
object[:error] = {
code: 404,
message: "Object does not exist on the server or you don't have permissions to access it",
}
end
end
objects
end
def upload_objects!
objects.each do |object|
object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
end
objects
end
def download_actions(object)
{
download: {
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}",
header: {
Authorization: request.headers['Authorization']
}.compact
}
}
end
def upload_actions(object)
{
upload: {
href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
header: {
Authorization: request.headers['Authorization']
}.compact
}
}
end
def download_request?
params[:operation] == 'download'
end
def upload_request?
params[:operation] == 'upload'
end
end
class Projects::LfsStorageController < Projects::GitHttpClientController
include LfsHelper
before_action :require_lfs_enabled!
before_action :lfs_check_access!
def download
lfs_object = LfsObject.find_by_oid(oid)
unless lfs_object && lfs_object.file.exists?
render_lfs_not_found
return
end
send_file lfs_object.file.path, content_type: "application/octet-stream"
end
def upload_authorize
render(
json: {
StoreLFSPath: "#{Gitlab.config.lfs.storage_path}/tmp/upload",
LfsOid: oid,
LfsSize: size,
},
content_type: 'application/json; charset=utf-8'
)
end
def upload_finalize
unless tmp_filename
render_lfs_forbidden
return
end
if store_file(oid, size, tmp_filename)
head 200
else
render plain: 'Unprocessable entity', status: 422
end
end
private
def download_request?
action_name == 'download'
end
def upload_request?
%w[upload_authorize upload_finalize].include? action_name
end
def oid
params[:oid].to_s
end
def size
params[:size].to_i
end
def tmp_filename
name = request.headers['X-Gitlab-Lfs-Tmp']
return if name.include?('/')
return unless oid.present? && name.start_with?(oid)
name
end
def store_file(oid, size, tmp_file)
# Define tmp_file_path early because we use it in "ensure"
tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
object = LfsObject.find_or_create_by(oid: oid, size: size)
file_exists = object.file.exists? || move_tmp_file_to_storage(object, tmp_file_path)
file_exists && link_to_project(object)
ensure
FileUtils.rm_f(tmp_file_path)
end
def move_tmp_file_to_storage(object, path)
File.open(path) do |f|
object.file = f
end
object.file.store!
object.save
end
def link_to_project(object)
if object && !object.projects.exists?(storage_project.id)
object.projects << storage_project
object.save
end
end
end
...@@ -160,7 +160,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -160,7 +160,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@diff_notes_disabled = true @diff_notes_disabled = true
@pipeline = @merge_request.pipeline @pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline @statuses = @pipeline.statuses.relevant if @pipeline
@note_counts = Note.where(commit_id: @commits.map(&:id)). @note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count group(:commit_id).count
...@@ -362,7 +362,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -362,7 +362,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits_count = @merge_request.commits.count @commits_count = @merge_request.commits.count
@pipeline = @merge_request.pipeline @pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline @statuses = @pipeline.statuses.relevant if @pipeline
if @merge_request.locked_long_ago? if @merge_request.locked_long_ago?
@merge_request.unlock_mr @merge_request.unlock_mr
......
...@@ -19,7 +19,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -19,7 +19,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def create def create
@pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute @pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute(ignore_skip_ci: true, save_on_errors: false)
unless @pipeline.persisted? unless @pipeline.persisted?
render 'new' render 'new'
return return
......
...@@ -3,7 +3,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -3,7 +3,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def show def show
@ref = params[:ref] || @project.default_branch || 'master' @ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref) @build_badge = Gitlab::Badge::Build.new(@project, @ref).metadata
end end
def update def update
......
...@@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController
) )
end end
def markdown_preview def preview_markdown
text = params[:text] text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user) ext = Gitlab::ReferenceExtractor.new(@project, current_user)
......
...@@ -125,7 +125,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -125,7 +125,7 @@ class ProjectsController < Projects::ApplicationController
def destroy def destroy
return access_denied! unless can?(current_user, :remove_project, @project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).pending_delete! ::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:alert] = "Project '#{@project.name}' will be deleted." flash[:alert] = "Project '#{@project.name}' will be deleted."
redirect_to dashboard_projects_path redirect_to dashboard_projects_path
...@@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController
} }
end end
def markdown_preview def preview_markdown
text = params[:text] text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user) ext = Gitlab::ReferenceExtractor.new(@project, current_user)
......
...@@ -109,11 +109,10 @@ module DiffHelper ...@@ -109,11 +109,10 @@ module DiffHelper
end end
end end
def diff_file_html_data(project, diff_file) def diff_file_html_data(project, diff_file_path, diff_commit_id)
commit = commit_for_diff(diff_file)
{ {
blob_diff_path: namespace_project_blob_diff_path(project.namespace, project, blob_diff_path: namespace_project_blob_diff_path(project.namespace, project,
tree_join(commit.id, diff_file.file_path)), tree_join(diff_commit_id, diff_file_path)),
view: diff_view view: diff_view
} }
end end
......
module LfsHelper
def require_lfs_enabled!
return if Gitlab.config.lfs.enabled
render(
json: {
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
documentation_url: "#{Gitlab.config.gitlab.url}/help",
},
status: 501
)
end
def lfs_check_access!
return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access?
if project.public? || (user && user.can?(:read_project, project))
render_lfs_forbidden
else
render_lfs_not_found
end
end
def lfs_download_access?
project.public? || ci? || (user && user.can?(:download_code, project))
end
def lfs_upload_access?
user && user.can?(:push_code, project)
end
def render_lfs_forbidden
render(
json: {
message: 'Access forbidden. Check your access level.',
documentation_url: "#{Gitlab.config.gitlab.url}/help",
},
content_type: "application/vnd.git-lfs+json",
status: 403
)
end
def render_lfs_not_found
render(
json: {
message: 'Not found.',
documentation_url: "#{Gitlab.config.gitlab.url}/help",
},
content_type: "application/vnd.git-lfs+json",
status: 404
)
end
def storage_project
@storage_project ||= begin
result = project
loop do
break unless result.forked?
result = result.forked_from_project
end
result
end
end
end
...@@ -6,12 +6,6 @@ module MembersHelper ...@@ -6,12 +6,6 @@ module MembersHelper
"#{action}_#{member.type.underscore}".to_sym "#{action}_#{member.type.underscore}".to_sym
end end
def default_show_roles(member)
can?(current_user, action_member_permission(:update, member), member) ||
can?(current_user, action_member_permission(:destroy, member), member) ||
can?(current_user, action_member_permission(:admin, member), member.source)
end
def remove_member_message(member, user: nil) def remove_member_message(member, user: nil)
user = current_user if defined?(current_user) user = current_user if defined?(current_user)
......
...@@ -4,23 +4,11 @@ module TreeHelper ...@@ -4,23 +4,11 @@ module TreeHelper
# #
# contents - A Grit::Tree object for the current tree # contents - A Grit::Tree object for the current tree
def render_tree(tree) def render_tree(tree)
# Render Folders before Files/Submodules # Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules folders, files, submodules = tree.trees, tree.blobs, tree.submodules
tree = "" tree = ""
items = (folders + submodules).sort_by(&:name) + files
# Render folders if we have any tree << render(partial: "projects/tree/tree_row", collection: items) if items.present?
tree << render(partial: 'projects/tree/tree_item', collection: folders,
locals: { type: 'folder' }) if folders.present?
# Render files if we have any
tree << render(partial: 'projects/tree/blob_item', collection: files,
locals: { type: 'file' }) if files.present?
# Render submodules if we have any
tree << render(partial: 'projects/tree/submodule_item',
collection: submodules) if submodules.present?
tree.html_safe tree.html_safe
end end
......
...@@ -16,7 +16,7 @@ module Ci ...@@ -16,7 +16,7 @@ module Ci
scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) } scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual) } scope :manual_actions, ->() { where(when: :manual).relevant }
mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader mount_uploader :artifacts_metadata, ArtifactUploader
...@@ -42,40 +42,35 @@ module Ci ...@@ -42,40 +42,35 @@ module Ci
end end
def retry(build, user = nil) def retry(build, user = nil)
new_build = Ci::Build.new(status: 'pending') new_build = Ci::Build.create(
new_build.ref = build.ref ref: build.ref,
new_build.tag = build.tag tag: build.tag,
new_build.options = build.options options: build.options,
new_build.commands = build.commands commands: build.commands,
new_build.tag_list = build.tag_list tag_list: build.tag_list,
new_build.project = build.project project: build.project,
new_build.pipeline = build.pipeline pipeline: build.pipeline,
new_build.name = build.name name: build.name,
new_build.allow_failure = build.allow_failure allow_failure: build.allow_failure,
new_build.stage = build.stage stage: build.stage,
new_build.stage_idx = build.stage_idx stage_idx: build.stage_idx,
new_build.trigger_request = build.trigger_request trigger_request: build.trigger_request,
new_build.yaml_variables = build.yaml_variables yaml_variables: build.yaml_variables,
new_build.when = build.when when: build.when,
new_build.user = user user: user,
new_build.environment = build.environment environment: build.environment,
new_build.save status_event: 'queue'
)
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build) MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build new_build
end end
end end
state_machine :status, initial: :pending do state_machine :status do
after_transition pending: :running do |build| after_transition pending: :running do |build|
build.execute_hooks build.execute_hooks
end end
# We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed
around_transition any => [:success, :failed, :canceled] do |build, block|
block.call
build.pipeline.create_next_builds(build) if build.pipeline
end
after_transition any => [:success, :failed, :canceled] do |build| after_transition any => [:success, :failed, :canceled] do |build|
build.update_coverage build.update_coverage
build.execute_hooks build.execute_hooks
...@@ -349,7 +344,7 @@ module Ci ...@@ -349,7 +344,7 @@ module Ci
def execute_hooks def execute_hooks
return unless project return unless project
build_data = Gitlab::BuildDataBuilder.build(self) build_data = Gitlab::DataBuilder::Build.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)
project.running_or_pending_build_count(force: true) project.running_or_pending_build_count(force: true)
...@@ -461,7 +456,7 @@ module Ci ...@@ -461,7 +456,7 @@ module Ci
def build_attributes_from_config def build_attributes_from_config
return {} unless pipeline.config_processor return {} unless pipeline.config_processor
pipeline.config_processor.build_attributes(name) pipeline.config_processor.build_attributes(name)
end end
end end
......
...@@ -13,13 +13,57 @@ module Ci ...@@ -13,13 +13,57 @@ module Ci
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
validates_presence_of :sha validates_presence_of :sha
validates_presence_of :ref
validates_presence_of :status validates_presence_of :status
validate :valid_commit_sha validate :valid_commit_sha
# Invalidate object and save if when touched
after_touch :update_state
after_save :keep_around_commits after_save :keep_around_commits
delegate :stages, to: :statuses
state_machine :status, initial: :created do
event :queue do
transition created: :pending
transition any - [:created, :pending] => :running
end
event :run do
transition any => :running
end
event :skip do
transition any => :skipped
end
event :drop do
transition any => :failed
end
event :succeed do
transition any => :success
end
event :cancel do
transition any => :canceled
end
before_transition [:created, :pending] => :running do |pipeline|
pipeline.started_at = Time.now
end
before_transition any => [:success, :failed, :canceled] do |pipeline|
pipeline.finished_at = Time.now
end
before_transition do |pipeline|
pipeline.update_duration
end
after_transition do |pipeline, transition|
pipeline.execute_hooks unless transition.loopback?
end
end
# ref can't be HEAD or SHA, can only be branch/tag name # ref can't be HEAD or SHA, can only be branch/tag name
scope :latest_successful_for, ->(ref = default_branch) do scope :latest_successful_for, ->(ref = default_branch) do
where(ref: ref).success.order(id: :desc).limit(1) where(ref: ref).success.order(id: :desc).limit(1)
...@@ -113,37 +157,6 @@ module Ci ...@@ -113,37 +157,6 @@ module Ci
trigger_requests.any? trigger_requests.any?
end end
def create_builds(user, trigger_request = nil)
##
# We persist pipeline only if there are builds available
#
return unless config_processor
build_builds_for_stages(config_processor.stages, user,
'success', trigger_request) && save
end
def create_next_builds(build)
return unless config_processor
# don't create other builds if this one is retried
latest_builds = builds.latest
return unless latest_builds.exists?(build.id)
# get list of stages after this build
next_stages = config_processor.stages.drop_while { |stage| stage != build.stage }
next_stages.delete(build.stage)
# get status for all prior builds
prior_builds = latest_builds.where.not(stage: next_stages)
prior_status = prior_builds.status
# build builds for next stage that has builds available
# and save pipeline if we have builds
build_builds_for_stages(next_stages, build.user, prior_status,
build.trigger_request) && save
end
def retried def retried
@retried ||= (statuses.order(id: :desc) - statuses.latest) @retried ||= (statuses.order(id: :desc) - statuses.latest)
end end
...@@ -155,6 +168,14 @@ module Ci ...@@ -155,6 +168,14 @@ module Ci
end end
end end
def config_builds_attributes
return [] unless config_processor
config_processor.
builds_for_ref(ref, tag?, trigger_requests.first).
sort_by { |build| build[:stage_idx] }
end
def has_warnings? def has_warnings?
builds.latest.ignored.any? builds.latest.ignored.any?
end end
...@@ -186,10 +207,6 @@ module Ci ...@@ -186,10 +207,6 @@ module Ci
end end
end end
def skip_ci?
git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message
end
def environments def environments
builds.where.not(environment: nil).success.pluck(:environment).uniq builds.where.not(environment: nil).success.pluck(:environment).uniq
end end
...@@ -211,37 +228,52 @@ module Ci ...@@ -211,37 +228,52 @@ module Ci
Note.for_commit_id(sha) Note.for_commit_id(sha)
end end
def process!
Ci::ProcessPipelineService.new(project, user).execute(self)
end
def build_updated
case latest_builds_status
when 'pending'
queue
when 'running'
run
when 'success'
succeed
when 'failed'
drop
when 'canceled'
cancel
when 'skipped'
skip
end
end
def predefined_variables def predefined_variables
[ [
{ key: 'CI_PIPELINE_ID', value: id.to_s, public: true } { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }
] ]
end end
def update_duration
self.duration = statuses.latest.duration
end
def execute_hooks
project.execute_hooks(pipeline_data, :pipeline_hooks)
project.execute_services(pipeline_data, :pipeline_hooks)
end
private private
def build_builds_for_stages(stages, user, status, trigger_request) def pipeline_data
## Gitlab::DataBuilder::Pipeline.build(self)
# Note that `Array#any?` implements a short circuit evaluation, so we end
# build builds only for the first stage that has builds available.
# def latest_builds_status
stages.any? do |stage| return 'failed' unless yaml_errors.blank?
CreateBuildsService.new(self).
execute(stage, user, status, trigger_request). statuses.latest.status || 'skipped'
any?(&:active?)
end
end
def update_state
statuses.reload
self.status = if yaml_errors.blank?
statuses.latest.status || 'skipped'
else
'failed'
end
self.started_at = statuses.started_at
self.finished_at = statuses.finished_at
self.duration = calculate_duration.to_i
save
end end
def keep_around_commits def keep_around_commits
......
...@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds' self.table_name = 'ci_builds'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :user belongs_to :user
delegate :commit, to: :pipeline delegate :commit, to: :pipeline
...@@ -26,28 +26,36 @@ class CommitStatus < ActiveRecord::Base ...@@ -26,28 +26,36 @@ class CommitStatus < ActiveRecord::Base
scope :ordered, -> { order(:name) } scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
state_machine :status, initial: :pending do state_machine :status do
event :queue do event :queue do
transition skipped: :pending transition [:created, :skipped] => :pending
end end
event :run do event :run do
transition pending: :running transition pending: :running
end end
event :skip do
transition [:created, :pending] => :skipped
end
event :drop do event :drop do
transition [:pending, :running] => :failed transition [:created, :pending, :running] => :failed
end end
event :success do event :success do
transition [:pending, :running] => :success transition [:created, :pending, :running] => :success
end end
event :cancel do event :cancel do
transition [:pending, :running] => :canceled transition [:created, :pending, :running] => :canceled
end end
after_transition pending: :running do |commit_status| after_transition created: [:pending, :running] do |commit_status|
commit_status.update_attributes queued_at: Time.now
end
after_transition [:created, :pending] => :running do |commit_status|
commit_status.update_attributes started_at: Time.now commit_status.update_attributes started_at: Time.now
end end
...@@ -55,7 +63,18 @@ class CommitStatus < ActiveRecord::Base ...@@ -55,7 +63,18 @@ class CommitStatus < ActiveRecord::Base
commit_status.update_attributes finished_at: Time.now commit_status.update_attributes finished_at: Time.now
end end
after_transition [:pending, :running] => :success do |commit_status| # We use around_transition to process pipeline on next stages as soon as possible, before the `after_*` is executed
around_transition any => [:success, :failed, :canceled] do |commit_status, block|
block.call
commit_status.pipeline.try(:process!)
end
after_transition do |commit_status, transition|
commit_status.pipeline.try(:build_updated) unless transition.loopback?
end
after_transition [:created, :pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status) MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end end
......
module Statuseable module Statuseable
extend ActiveSupport::Concern extend ActiveSupport::Concern
AVAILABLE_STATUSES = %w(pending running success failed canceled skipped) AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]
STARTED_STATUSES = %w[running success failed skipped]
ACTIVE_STATUSES = %w[pending running]
COMPLETED_STATUSES = %w[success failed canceled]
class_methods do class_methods do
def status_sql def status_sql
builds = all.select('count(*)').to_sql scope = all.relevant
success = all.success.select('count(*)').to_sql builds = scope.select('count(*)').to_sql
ignored = all.ignored.select('count(*)').to_sql if all.respond_to?(:ignored) success = scope.success.select('count(*)').to_sql
ignored = scope.ignored.select('count(*)').to_sql if scope.respond_to?(:ignored)
ignored ||= '0' ignored ||= '0'
pending = all.pending.select('count(*)').to_sql pending = scope.pending.select('count(*)').to_sql
running = all.running.select('count(*)').to_sql running = scope.running.select('count(*)').to_sql
canceled = all.canceled.select('count(*)').to_sql canceled = scope.canceled.select('count(*)').to_sql
skipped = all.skipped.select('count(*)').to_sql skipped = scope.skipped.select('count(*)').to_sql
deduce_status = "(CASE deduce_status = "(CASE
WHEN (#{builds})=0 THEN NULL WHEN (#{builds})=0 THEN NULL
...@@ -43,7 +47,8 @@ module Statuseable ...@@ -43,7 +47,8 @@ module Statuseable
included do included do
validates :status, inclusion: { in: AVAILABLE_STATUSES } validates :status, inclusion: { in: AVAILABLE_STATUSES }
state_machine :status, initial: :pending do state_machine :status, initial: :created do
state :created, value: 'created'
state :pending, value: 'pending' state :pending, value: 'pending'
state :running, value: 'running' state :running, value: 'running'
state :failed, value: 'failed' state :failed, value: 'failed'
...@@ -52,6 +57,8 @@ module Statuseable ...@@ -52,6 +57,8 @@ module Statuseable
state :skipped, value: 'skipped' state :skipped, value: 'skipped'
end end
scope :created, -> { where(status: 'created') }
scope :relevant, -> { where.not(status: 'created') }
scope :running, -> { where(status: 'running') } scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') } scope :pending, -> { where(status: 'pending') }
scope :success, -> { where(status: 'success') } scope :success, -> { where(status: 'success') }
...@@ -63,15 +70,15 @@ module Statuseable ...@@ -63,15 +70,15 @@ module Statuseable
end end
def started? def started?
!pending? && !canceled? && started_at STARTED_STATUSES.include?(status) && started_at
end end
def active? def active?
running? || pending? ACTIVE_STATUSES.include?(status)
end end
def complete? def complete?
canceled? || success? || failed? COMPLETED_STATUSES.include?(status)
end end
private private
......
...@@ -5,5 +5,6 @@ class ProjectHook < WebHook ...@@ -5,5 +5,6 @@ class ProjectHook < WebHook
scope :note_hooks, -> { where(note_events: true) } scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :build_hooks, -> { where(build_events: true) } scope :build_hooks, -> { where(build_events: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end end
...@@ -8,6 +8,7 @@ class WebHook < ActiveRecord::Base ...@@ -8,6 +8,7 @@ class WebHook < ActiveRecord::Base
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false default_value_for :tag_push_events, false
default_value_for :build_events, false default_value_for :build_events, false
default_value_for :pipeline_events, false
default_value_for :enable_ssl_verification, true default_value_for :enable_ssl_verification, true
scope :push_hooks, -> { where(push_events: true) } scope :push_hooks, -> { where(push_events: true) }
......
...@@ -8,6 +8,7 @@ class ProjectMember < Member ...@@ -8,6 +8,7 @@ class ProjectMember < Member
# Make sure project member points only to project as it source # Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE default_value_for :source_type, SOURCE_TYPE
validates_format_of :source_type, with: /\AProject\z/ validates_format_of :source_type, with: /\AProject\z/
validates :access_level, inclusion: { in: Gitlab::Access.values }
default_scope { where(source_type: SOURCE_TYPE) } default_scope { where(source_type: SOURCE_TYPE) }
scope :in_project, ->(project) { where(source_id: project.id) } scope :in_project, ->(project) { where(source_id: project.id) }
......
...@@ -104,6 +104,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -104,6 +104,7 @@ class MergeRequest < ActiveRecord::Base
scope :from_project, ->(project) { where(source_project_id: project.id) } scope :from_project, ->(project) { where(source_project_id: project.id) }
scope :merged, -> { with_state(:merged) } scope :merged, -> { with_state(:merged) }
scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) }
scope :from_source_branches, ->(branches) { where(source_branch: branches) }
scope :join_project, -> { joins(:target_project) } scope :join_project, -> { joins(:target_project) }
scope :references_project, -> { references(:target_project) } scope :references_project, -> { references(:target_project) }
......
...@@ -378,6 +378,12 @@ class Project < ActiveRecord::Base ...@@ -378,6 +378,12 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC') joins(join_body).reorder('join_note_counts.amount DESC')
end end
def cached_count
Rails.cache.fetch('total_project_count', expires_in: 5.minutes) do
Project.count
end
end
end end
def repository_storage_path def repository_storage_path
...@@ -993,6 +999,10 @@ class Project < ActiveRecord::Base ...@@ -993,6 +999,10 @@ class Project < ActiveRecord::Base
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
def add_user(user, access_level, current_user = nil)
team.add_user(user, access_level, current_user)
end
def default_branch def default_branch
@default_branch ||= repository.root_ref if repository.exists? @default_branch ||= repository.root_ref if repository.exists?
end end
...@@ -1155,16 +1165,6 @@ class Project < ActiveRecord::Base ...@@ -1155,16 +1165,6 @@ class Project < ActiveRecord::Base
@wiki ||= ProjectWiki.new(self, self.owner) @wiki ||= ProjectWiki.new(self, self.owner)
end end
def schedule_delete!(user_id, params)
# Queue this task for after the commit, so once we mark pending_delete it will run
run_after_commit do
job_id = ProjectDestroyWorker.perform_async(id, user_id, params)
Rails.logger.info("User #{user_id} scheduled destruction of project #{path_with_namespace} with job ID #{job_id}")
end
update_attribute(:pending_delete, true)
end
def running_or_pending_build_count(force: false) def running_or_pending_build_count(force: false)
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
builds.running_or_pending.count(:all) builds.running_or_pending.count(:all)
......
...@@ -51,8 +51,7 @@ class BuildsEmailService < Service ...@@ -51,8 +51,7 @@ class BuildsEmailService < Service
end end
def test_data(project = nil, user = nil) def test_data(project = nil, user = nil)
build = project.builds.last Gitlab::DataBuilder::Build.build(project.builds.last)
Gitlab::BuildDataBuilder.build(build)
end end
def fields def fields
......
class CampfireService < Service class CampfireService < Service
include HTTParty
prop_accessor :token, :subdomain, :room prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
...@@ -29,18 +31,53 @@ class CampfireService < Service ...@@ -29,18 +31,53 @@ class CampfireService < Service
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
room = gate.find_room_by_name(self.room) self.class.base_uri base_uri
return true unless room
message = build_message(data) message = build_message(data)
speak(self.room, message, auth)
room.speak(message)
end end
private private
def gate def base_uri
@gate ||= Tinder::Campfire.new(subdomain, token: token) @base_uri ||= "https://#{subdomain}.campfirenow.com"
end
def auth
# use a dummy password, as explained in the Campfire API doc:
# https://github.com/basecamp/campfire-api#authentication
@auth ||= {
basic_auth: {
username: token,
password: 'X'
}
}
end
# Post a message into a room, returns the message Hash in case of success.
# Returns nil otherwise.
# https://github.com/basecamp/campfire-api/blob/master/sections/messages.md#create-message
def speak(room_name, message, auth)
room = rooms(auth).find { |r| r["name"] == room_name }
return nil unless room
path = "/room/#{room["id"]}/speak.json"
body = {
body: {
message: {
type: 'TextMessage',
body: message
}
}
}
res = self.class.post(path, auth.merge(body))
res.code == 201 ? res : nil
end
# Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
def rooms(auth)
res = self.class.get("/rooms.json", auth)
res.code == 200 ? res["rooms"] : []
end end
def build_message(push) def build_message(push)
......
...@@ -36,6 +36,7 @@ class Service < ActiveRecord::Base ...@@ -36,6 +36,7 @@ class Service < ActiveRecord::Base
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
scope :note_hooks, -> { where(note_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) } scope :build_hooks, -> { where(build_events: true, active: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active.without_defaults } scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
...@@ -79,13 +80,17 @@ class Service < ActiveRecord::Base ...@@ -79,13 +80,17 @@ class Service < ActiveRecord::Base
end end
def test_data(project, user) def test_data(project, user)
Gitlab::PushDataBuilder.build_sample(project, user) Gitlab::DataBuilder::Push.build_sample(project, user)
end end
def event_channel_names def event_channel_names
[] []
end end
def event_names
supported_events.map { |event| "#{event}_events" }
end
def event_field(event) def event_field(event)
nil nil
end end
......
...@@ -23,13 +23,13 @@ class User < ActiveRecord::Base ...@@ -23,13 +23,13 @@ class User < ActiveRecord::Base
default_value_for :theme_id, gitlab_config.default_theme default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret, attr_encrypted :otp_secret,
key: Gitlab::Application.config.secret_key_base, key: Gitlab::Application.secrets.otp_key_base,
mode: :per_attribute_iv_and_salt, mode: :per_attribute_iv_and_salt,
insecure_mode: true, insecure_mode: true,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
devise :two_factor_authenticatable, devise :two_factor_authenticatable,
otp_secret_encryption_key: Gitlab::Application.config.secret_key_base otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base
devise :two_factor_backupable, otp_number_of_backup_codes: 10 devise :two_factor_backupable, otp_number_of_backup_codes: 10
serialize :otp_backup_codes, JSON serialize :otp_backup_codes, JSON
......
module Ci
class CreateBuildsService
def initialize(pipeline)
@pipeline = pipeline
@config = pipeline.config_processor
end
def execute(stage, user, status, trigger_request = nil)
builds_attrs = @config.builds_for_stage_and_ref(stage, @pipeline.ref, @pipeline.tag, trigger_request)
# check when to create next build
builds_attrs = builds_attrs.select do |build_attrs|
case build_attrs[:when]
when 'on_success'
status == 'success'
when 'on_failure'
status == 'failed'
when 'always', 'manual'
%w(success failed).include?(status)
end
end
# don't create the same build twice
builds_attrs.reject! do |build_attrs|
@pipeline.builds.find_by(ref: @pipeline.ref,
tag: @pipeline.tag,
trigger_request: trigger_request,
name: build_attrs[:name])
end
builds_attrs.map do |build_attrs|
build_attrs.slice!(:name,
:commands,
:tag_list,
:options,
:allow_failure,
:stage,
:stage_idx,
:environment,
:when,
:yaml_variables)
build_attrs.merge!(pipeline: @pipeline,
ref: @pipeline.ref,
tag: @pipeline.tag,
trigger_request: trigger_request,
user: user,
project: @pipeline.project)
# TODO: The proper implementation for this is in
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5295
build_attrs[:status] = 'skipped' if build_attrs[:when] == 'manual'
##
# We do not persist new builds here.
# Those will be persisted when @pipeline is saved.
#
@pipeline.builds.new(build_attrs)
end
end
end
end
module Ci
class CreatePipelineBuildsService < BaseService
attr_reader :pipeline
def execute(pipeline)
@pipeline = pipeline
new_builds.map do |build_attributes|
create_build(build_attributes)
end
end
private
def create_build(build_attributes)
build_attributes = build_attributes.merge(
pipeline: pipeline,
project: pipeline.project,
ref: pipeline.ref,
tag: pipeline.tag,
user: current_user,
trigger_request: trigger_request
)
pipeline.builds.create(build_attributes)
end
def new_builds
@new_builds ||= pipeline.config_builds_attributes.
reject { |build| existing_build_names.include?(build[:name]) }
end
def existing_build_names
@existing_build_names ||= pipeline.builds.pluck(:name)
end
def trigger_request
return @trigger_request if defined?(@trigger_request)
@trigger_request ||= pipeline.trigger_requests.first
end
end
end
module Ci module Ci
class CreatePipelineService < BaseService class CreatePipelineService < BaseService
def execute attr_reader :pipeline
pipeline = project.pipelines.new(params)
pipeline.user = current_user
unless ref_names.include?(params[:ref]) def execute(ignore_skip_ci: false, save_on_errors: true, trigger_request: nil)
pipeline.errors.add(:base, 'Reference not found') @pipeline = Ci::Pipeline.new(
return pipeline project: project,
ref: ref,
sha: sha,
before_sha: before_sha,
tag: tag?,
trigger_requests: Array(trigger_request),
user: current_user
)
unless project.builds_enabled?
return error('Pipeline is disabled')
end end
if commit unless trigger_request || can?(current_user, :create_pipeline, project)
pipeline.sha = commit.id return error('Insufficient permissions to create a new pipeline')
else
pipeline.errors.add(:base, 'Commit not found')
return pipeline
end end
unless can?(current_user, :create_pipeline, project) unless branch? || tag?
pipeline.errors.add(:base, 'Insufficient permissions to create a new pipeline') return error('Reference not found')
return pipeline end
unless commit
return error('Commit not found')
end end
unless pipeline.config_processor unless pipeline.config_processor
pipeline.errors.add(:base, pipeline.yaml_errors || 'Missing .gitlab-ci.yml file') unless pipeline.ci_yaml_file
return pipeline return error('Missing .gitlab-ci.yml file')
end
return error(pipeline.yaml_errors, save: save_on_errors)
end end
pipeline.save! if !ignore_skip_ci && skip_ci?
pipeline.skip if save_on_errors
return pipeline
end
unless pipeline.create_builds(current_user) unless pipeline.config_builds_attributes.present?
pipeline.errors.add(:base, 'No builds for this pipeline.') return error('No builds for this pipeline.')
end end
pipeline.save pipeline.save
pipeline.process!
pipeline pipeline
end end
private private
def ref_names def skip_ci?
@ref_names ||= project.repository.ref_names pipeline.git_commit_message =~ /\[(ci skip|skip ci)\]/i if pipeline.git_commit_message
end end
def commit def commit
@commit ||= project.commit(params[:ref]) @commit ||= project.commit(origin_sha || origin_ref)
end
def sha
commit.try(:id)
end
def before_sha
params[:checkout_sha] || params[:before] || Gitlab::Git::BLANK_SHA
end
def origin_sha
params[:checkout_sha] || params[:after]
end
def origin_ref
params[:ref]
end
def branch?
project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
end
def tag?
project.repository.ref_exists?(Gitlab::Git::TAG_REF_PREFIX + ref)
end
def ref
Gitlab::Git.ref_name(origin_ref)
end
def valid_sha?
origin_sha && origin_sha != Gitlab::Git::BLANK_SHA
end
def error(message, save: false)
pipeline.errors.add(:base, message)
pipeline.drop if save
pipeline
end end
end end
end end
module Ci module Ci
class CreateTriggerRequestService class CreateTriggerRequestService
def execute(project, trigger, ref, variables = nil) def execute(project, trigger, ref, variables = nil)
commit = project.commit(ref) trigger_request = trigger.trigger_requests.create(variables: variables)
return unless commit
# check if ref is tag pipeline = Ci::CreatePipelineService.new(project, nil, ref: ref).
tag = project.repository.find_tag(ref).present? execute(ignore_skip_ci: true, trigger_request: trigger_request)
if pipeline.persisted?
pipeline = project.pipelines.create(sha: commit.sha, ref: ref, tag: tag)
trigger_request = trigger.trigger_requests.create!(
variables: variables,
pipeline: pipeline,
)
if pipeline.create_builds(nil, trigger_request)
trigger_request trigger_request
end end
end end
......
module Ci
class ProcessPipelineService < BaseService
attr_reader :pipeline
def execute(pipeline)
@pipeline = pipeline
# This method will ensure that our pipeline does have all builds for all stages created
if created_builds.empty?
create_builds!
end
new_builds =
stage_indexes_of_created_builds.map do |index|
process_stage(index)
end
# Return a flag if a when builds got enqueued
new_builds.flatten.any?
end
private
def create_builds!
Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
end
def process_stage(index)
current_status = status_for_prior_stages(index)
created_builds_in_stage(index).select do |build|
process_build(build, current_status)
end
end
def process_build(build, current_status)
return false unless Statuseable::COMPLETED_STATUSES.include?(current_status)
if valid_statuses_for_when(build.when).include?(current_status)
build.queue
true
else
build.skip
false
end
end
def valid_statuses_for_when(value)
case value
when 'on_success'
%w[success]
when 'on_failure'
%w[failed]
when 'always'
%w[success failed]
else
[]
end
end
def status_for_prior_stages(index)
pipeline.builds.where('stage_idx < ?', index).latest.status || 'success'
end
def stage_indexes_of_created_builds
created_builds.order(:stage_idx).pluck('distinct stage_idx')
end
def created_builds_in_stage(index)
created_builds.where(stage_idx: index)
end
def created_builds
pipeline.builds.created
end
end
end
class CreateCommitBuildsService
def execute(project, user, params)
return unless project.builds_enabled?
before_sha = params[:checkout_sha] || params[:before]
sha = params[:checkout_sha] || params[:after]
origin_ref = params[:ref]
ref = Gitlab::Git.ref_name(origin_ref)
tag = Gitlab::Git.tag_ref?(origin_ref)
# Skip branch removal
if sha == Gitlab::Git::BLANK_SHA
return false
end
@pipeline = Ci::Pipeline.new(
project: project,
sha: sha,
ref: ref,
before_sha: before_sha,
tag: tag,
user: user)
##
# Skip creating pipeline if no gitlab-ci.yml is found
#
unless @pipeline.ci_yaml_file
return false
end
##
# Skip creating builds for commits that have [ci skip]
# but save pipeline object
#
if @pipeline.skip_ci?
return save_pipeline!
end
##
# Skip creating builds when CI config is invalid
# but save pipeline object
#
unless @pipeline.config_processor
return save_pipeline!
end
##
# Skip creating pipeline object if there are no builds for it.
#
unless @pipeline.create_builds(user)
@pipeline.errors.add(:base, 'No builds created')
return false
end
save_pipeline!
end
private
##
# Create a new pipeline and touch object to calculate status
#
def save_pipeline!
@pipeline.save!
@pipeline.touch
@pipeline
end
end
...@@ -39,7 +39,12 @@ class DeleteBranchService < BaseService ...@@ -39,7 +39,12 @@ class DeleteBranchService < BaseService
end end
def build_push_data(branch) def build_push_data(branch)
Gitlab::PushDataBuilder Gitlab::DataBuilder::Push.build(
.build(project, current_user, branch.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) project,
current_user,
branch.target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}",
[])
end end
end end
...@@ -33,7 +33,12 @@ class DeleteTagService < BaseService ...@@ -33,7 +33,12 @@ class DeleteTagService < BaseService
end end
def build_push_data(tag) def build_push_data(tag)
Gitlab::PushDataBuilder Gitlab::DataBuilder::Push.build(
.build(project, current_user, tag.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) project,
current_user,
tag.target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
[])
end end
end end
...@@ -18,7 +18,7 @@ class DeleteUserService ...@@ -18,7 +18,7 @@ class DeleteUserService
user.personal_projects.each do |project| user.personal_projects.each do |project|
# Skip repository removal because we remove directory with namespace # Skip repository removal because we remove directory with namespace
# that contain all this repositories # that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete! ::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
end end
user.destroy user.destroy
......
...@@ -9,7 +9,7 @@ class DestroyGroupService ...@@ -9,7 +9,7 @@ class DestroyGroupService
group.projects.each do |project| group.projects.each do |project|
# Skip repository removal because we remove directory with namespace # Skip repository removal because we remove directory with namespace
# that contain all this repositories # that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).pending_delete! ::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
end end
group.destroy group.destroy
......
...@@ -69,7 +69,7 @@ class GitPushService < BaseService ...@@ -69,7 +69,7 @@ class GitPushService < BaseService
SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks) SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks)
@project.execute_hooks(build_push_data.dup, :push_hooks) @project.execute_hooks(build_push_data.dup, :push_hooks)
@project.execute_services(build_push_data.dup, :push_hooks) @project.execute_services(build_push_data.dup, :push_hooks)
CreateCommitBuildsService.new.execute(@project, current_user, build_push_data) Ci::CreatePipelineService.new(project, current_user, build_push_data).execute
ProjectCacheWorker.perform_async(@project.id) ProjectCacheWorker.perform_async(@project.id)
end end
...@@ -138,13 +138,23 @@ class GitPushService < BaseService ...@@ -138,13 +138,23 @@ class GitPushService < BaseService
end end
def build_push_data def build_push_data
@push_data ||= Gitlab::PushDataBuilder. @push_data ||= Gitlab::DataBuilder::Push.build(
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], push_commits) @project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
push_commits)
end end
def build_push_data_system_hook def build_push_data_system_hook
@push_data_system ||= Gitlab::PushDataBuilder. @push_data_system ||= Gitlab::DataBuilder::Push.build(
build(@project, current_user, params[:oldrev], params[:newrev], params[:ref], []) @project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
[])
end end
def push_to_existing_branch? def push_to_existing_branch?
......
...@@ -11,7 +11,7 @@ class GitTagPushService < BaseService ...@@ -11,7 +11,7 @@ class GitTagPushService < BaseService
SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks) SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks)
CreateCommitBuildsService.new.execute(project, current_user, @push_data) Ci::CreatePipelineService.new(project, current_user, @push_data).execute
ProjectCacheWorker.perform_async(project.id) ProjectCacheWorker.perform_async(project.id)
true true
...@@ -34,12 +34,24 @@ class GitTagPushService < BaseService ...@@ -34,12 +34,24 @@ class GitTagPushService < BaseService
end end
end end
Gitlab::PushDataBuilder. Gitlab::DataBuilder::Push.build(
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], commits, message) project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
commits,
message)
end end
def build_system_push_data def build_system_push_data
Gitlab::PushDataBuilder. Gitlab::DataBuilder::Push.build(
build(project, current_user, params[:oldrev], params[:newrev], params[:ref], [], '') project,
current_user,
params[:oldrev],
params[:newrev],
params[:ref],
[],
'')
end end
end end
...@@ -2,8 +2,9 @@ module Members ...@@ -2,8 +2,9 @@ module Members
class DestroyService < BaseService class DestroyService < BaseService
attr_accessor :member, :current_user attr_accessor :member, :current_user
def initialize(member, user) def initialize(member, current_user)
@member, @current_user = member, user @member = member
@current_user = current_user
end end
def execute def execute
......
module MergeRequests
class GetUrlsService < BaseService
attr_reader :project
def initialize(project)
@project = project
end
def execute(changes)
branches = get_branches(changes)
merge_requests_map = opened_merge_requests_from_source_branches(branches)
branches.map do |branch|
existing_merge_request = merge_requests_map[branch]
if existing_merge_request
url_for_existing_merge_request(existing_merge_request)
else
url_for_new_merge_request(branch)
end
end
end
private
def opened_merge_requests_from_source_branches(branches)
merge_requests = MergeRequest.from_project(project).opened.from_source_branches(branches)
merge_requests.inject({}) do |hash, mr|
hash[mr.source_branch] = mr
hash
end
end
def get_branches(changes)
changes_list = Gitlab::ChangesList.new(changes)
changes_list.map do |change|
next unless Gitlab::Git.branch_ref?(change[:ref])
Gitlab::Git.branch_name(change[:ref])
end.compact
end
def url_for_new_merge_request(branch_name)
merge_request_params = { source_branch: branch_name }
url = Gitlab::Routing.url_helpers.new_namespace_project_merge_request_url(project.namespace, project, merge_request: merge_request_params)
{ branch_name: branch_name, url: url, new_merge_request: true }
end
def url_for_existing_merge_request(merge_request)
target_project = merge_request.target_project
url = Gitlab::Routing.url_helpers.namespace_project_merge_request_url(target_project.namespace, target_project, merge_request)
{ branch_name: merge_request.source_branch, url: url, new_merge_request: false }
end
end
end
...@@ -16,7 +16,7 @@ module Notes ...@@ -16,7 +16,7 @@ module Notes
end end
def hook_data def hook_data
Gitlab::NoteDataBuilder.build(@note, @note.author) Gitlab::DataBuilder::Note.build(@note, @note.author)
end end
def execute_note_hooks def execute_note_hooks
......
...@@ -6,8 +6,12 @@ module Projects ...@@ -6,8 +6,12 @@ module Projects
DELETED_FLAG = '+deleted' DELETED_FLAG = '+deleted'
def pending_delete! def async_execute
project.schedule_delete!(current_user.id, params) project.transaction do
project.update_attribute(:pending_delete, true)
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
end
end end
def execute def execute
......
class TestHookService class TestHookService
def execute(hook, current_user) def execute(hook, current_user)
data = Gitlab::PushDataBuilder.build_sample(hook.project, current_user) data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
hook.execute(data, 'push_hooks') hook.execute(data, 'push_hooks')
end end
end end
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
%h4 Projects %h4 Projects
.data .data
= link_to admin_namespaces_projects_path do = link_to admin_namespaces_projects_path do
%h1= number_with_delimiter(Project.count) %h1= number_with_delimiter(Project.cached_count)
%hr %hr
= link_to('New Project', new_project_path, class: "btn btn-new") = link_to('New Project', new_project_path, class: "btn btn-new")
.col-sm-4 .col-sm-4
......
...@@ -28,6 +28,3 @@ ...@@ -28,6 +28,3 @@
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save js-save-button' = f.submit 'Save', class: 'btn btn-save js-save-button'
= link_to "Cancel", admin_labels_path, class: 'btn btn-cancel' = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel'
:javascript
new Labels();
...@@ -4,10 +4,6 @@ ...@@ -4,10 +4,6 @@
%i.fa.fa-github %i.fa.fa-github
Import projects from GitHub Import projects from GitHub
%p
%i.fa.fa-warning
To import GitHub pull requests, any pull request source branches that had been deleted are temporarily restored on GitHub. To prevent any connected CI services from being overloaded with dozens of irrelevant branches being created and deleted again, GitHub webhooks are temporarily disabled during the import process, but only if you have admin access to the GitHub repository.
%p.light %p.light
Select projects you want to import. Select projects you want to import.
%hr %hr
......
...@@ -6,13 +6,13 @@ ...@@ -6,13 +6,13 @@
- content_for :scripts_body_top do - content_for :scripts_body_top do
- project = @target_project || @project - project = @target_project || @project
- if @project_wiki && @page - if @project_wiki && @page
- markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, @page.slug) - preview_markdown_path = namespace_project_wiki_preview_markdown_path(project.namespace, project, @page.slug)
- else - else
- markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project) - preview_markdown_path = preview_markdown_namespace_project_path(project.namespace, project)
- if current_user - if current_user
:javascript :javascript
window.project_uploads_path = "#{namespace_project_uploads_path project.namespace,project}"; window.project_uploads_path = "#{namespace_project_uploads_path project.namespace,project}";
window.markdown_preview_path = "#{markdown_preview_path}"; window.preview_markdown_path = "#{preview_markdown_path}";
- content_for :scripts_body do - content_for :scripts_body do
= render "layouts/init_auto_complete" if current_user = render "layouts/init_auto_complete" if current_user
......
...@@ -24,6 +24,3 @@ ...@@ -24,6 +24,3 @@
.project-clone-holder .project-clone-holder
= render "shared/clone_panel" = render "shared/clone_panel"
:javascript
new Star();
<svg xmlns="http://www.w3.org/2000/svg" width="<%= badge.width %>" height="20">
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="a">
<rect width="<%= badge.width %>" height="20" rx="3" fill="#fff"/>
</mask>
<g mask="url(#a)">
<path fill="<%= badge.key_color %>"
d="M0 0 h<%= badge.key_width %> v20 H0 z"/>
<path fill="<%= badge.value_color %>"
d="M<%= badge.key_width %> 0 h<%= badge.value_width %> v20 H<%= badge.key_width %> z"/>
<path fill="url(#b)"
d="M0 0 h<%= badge.width %> v20 H0 z"/>
</g>
<g fill="#fff" text-anchor="middle">
<g font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="<%= badge.key_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
<%= badge.key_text %>
</text>
<text x="<%= badge.key_text_anchor %>" y="14">
<%= badge.key_text %>
</text>
<text x="<%= badge.value_text_anchor %>" y="15" fill="#010101" fill-opacity=".3">
<%= badge.value_text %>
</text>
<text x="<%= badge.value_text_anchor %>" y="14">
<%= badge.value_text %>
</text>
</g>
</g>
</svg>
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
- stages_status = pipeline.statuses.latest.stages_status - stages_status = pipeline.statuses.relevant.latest.stages_status
- stages.each do |stage| - stages.each do |stage|
%td.stage-cell %td.stage-cell
- status = stages_status[stage] - status = stages_status[stage]
......
...@@ -46,5 +46,5 @@ ...@@ -46,5 +46,5 @@
- if pipeline.project.build_coverage_enabled? - if pipeline.project.build_coverage_enabled?
%th Coverage %th Coverage
%th %th
- pipeline.statuses.stages.each do |stage| - pipeline.statuses.relevant.stages.each do |stage|
= render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.where(stage: stage) = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.relevant.where(stage: stage)
.diff-file.file-holder{id: "diff-#{index}", data: diff_file_html_data(project, diff_file)} .diff-file.file-holder{id: "diff-#{index}", data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)}
.file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"} .file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"}
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{index}" = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{index}"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.col-md-8.col-lg-7 .col-md-8.col-lg-7
%strong.light-header= hook.url %strong.light-header= hook.url
%div %div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events wiki_page_events).each do |trigger| - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events pipeline_events wiki_page_events).each do |trigger|
- if hook.send(trigger) - if hook.send(trigger)
%span.label.label-gray.deploy-project-label= trigger.titleize %span.label.label-gray.deploy-project-label= trigger.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5 .col-md-4.col-lg-5.text-right-lg.prepend-top-5
......
...@@ -46,28 +46,18 @@ ...@@ -46,28 +46,18 @@
%div %div
- if github_import_enabled? - if github_import_enabled?
= link_to new_import_github_path, class: 'btn import_github' do = link_to new_import_github_path, class: 'btn import_github' do
= icon 'github', text: 'GitHub' = icon('github', text: 'GitHub')
%div %div
- if bitbucket_import_enabled? - if bitbucket_import_enabled?
- if bitbucket_import_configured? = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}", "data-no-turbolink" => "true" do
= link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do = icon('bitbucket', text: 'Bitbucket')
%i.fa.fa-bitbucket - unless bitbucket_import_configured?
Bitbucket
- else
= link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do
%i.fa.fa-bitbucket
Bitbucket
= render 'bitbucket_import_modal' = render 'bitbucket_import_modal'
%div %div
- if gitlab_import_enabled? - if gitlab_import_enabled?
- if gitlab_import_configured? = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless bitbucket_import_configured?}" do
= link_to status_import_gitlab_path, class: 'btn import_gitlab' do = icon('gitlab', text: 'GitLab.com')
%i.fa.fa-heart - unless gitlab_import_configured?
GitLab.com
- else
= link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do
%i.fa.fa-heart
GitLab.com
= render 'gitlab_import_modal' = render 'gitlab_import_modal'
%div %div
- if gitorious_import_enabled? - if gitorious_import_enabled?
...@@ -77,23 +67,19 @@ ...@@ -77,23 +67,19 @@
%div %div
- if google_code_import_enabled? - if google_code_import_enabled?
= link_to new_import_google_code_path, class: 'btn import_google_code' do = link_to new_import_google_code_path, class: 'btn import_google_code' do
%i.fa.fa-google = icon('google', text: 'Google Code')
Google Code
%div %div
- if fogbugz_import_enabled? - if fogbugz_import_enabled?
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
%i.fa.fa-bug = icon('bug', text: 'Fogbugz')
Fogbugz
%div %div
- if git_import_enabled? - if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do = link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git = icon('git', text: 'Repo by URL')
%span Repo by URL
%div{ class: 'import_gitlab_project' } %div{ class: 'import_gitlab_project' }
- if gitlab_project_import_enabled? - if gitlab_project_import_enabled?
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
%i.fa.fa-gitlab = icon('gitlab', text: 'GitLab export')
%span GitLab export
.js-toggle-content.hide .js-toggle-content.hide
= render "shared/import_form", f: f = render "shared/import_form", f: f
...@@ -159,4 +145,4 @@ ...@@ -159,4 +145,4 @@
$('.import_git').click(function( event ) { $('.import_git').click(function( event ) {
$projectImportUrl = $('#project_import_url') $projectImportUrl = $('#project_import_url')
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled')) $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'))
}); });
\ No newline at end of file
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.form-group .form-group
= f.label :ref, 'Create for', class: 'control-label' = f.label :ref, 'Create for', class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :ref, required: true, tabindex: 2, class: 'form-control' = f.text_field :ref, required: true, tabindex: 2, class: 'form-control js-branch-name ui-autocomplete-input', autocomplete: :false, id: :ref
.help-block Existing branch name, tag .help-block Existing branch name, tag
.form-actions .form-actions
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 = f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3
......
- if tree_row.type == :tree
= render partial: 'projects/tree/tree_item', object: tree_row, as: 'tree_item', locals: { type: 'folder' }
- elsif tree_row.type == :blob
= render partial: 'projects/tree/blob_item', object: tree_row, as: 'blob_item', locals: { type: 'file' }
- elsif tree_row.type == :commit
= render partial: 'projects/tree/submodule_item', object: tree_row, as: 'submodule_item'
- labels.each do |label| - labels.each do |label|
%span.label-row.btn-group{ role: "group", aria: { label: label.name }, style: "color: #{text_color_for_bg(label.color)}" } %span.label-row.btn-group{ role: "group", aria: { label: label.name }, style: "color: #{text_color_for_bg(label.color)}" }
= link_to label.name, label_filter_path(@project, label, type: controller.controller_name), = link_to_label(label, css_class: 'btn btn-transparent')
class: "btn btn-transparent has-tooltip",
style: "background-color: #{label.color};",
title: escape_once(label.description),
data: { container: "body" }
%button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } } %button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } }
= icon("times") = icon("times")
- show_roles = local_assigns.fetch(:show_roles, default_show_roles(member)) - show_roles = local_assigns.fetch(:show_roles, true)
- show_controls = local_assigns.fetch(:show_controls, true) - show_controls = local_assigns.fetch(:show_controls, true)
- user = member.user - user = member.user
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= icon('star') = icon('star')
= project.star_count = project.star_count
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)} %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)}
= visibility_level_icon(project.visibility_level, fw: false) = visibility_level_icon(project.visibility_level, fw: true)
.title .title
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
......
...@@ -65,6 +65,13 @@ ...@@ -65,6 +65,13 @@
%strong Build events %strong Build events
%p.light %p.light
This url will be triggered when the build status changes This url will be triggered when the build status changes
%li
= f.check_box :pipeline_events, class: 'pull-left'
.prepend-left-20
= f.label :pipeline_events, class: 'list-label' do
%strong Pipeline events
%p.light
This url will be triggered when the pipeline status changes
%li %li
= f.check_box :wiki_page_events, class: 'pull-left' = f.check_box :wiki_page_events, class: 'pull-left'
.prepend-left-20 .prepend-left-20
......
...@@ -107,7 +107,8 @@ module Gitlab ...@@ -107,7 +107,8 @@ module Gitlab
end end
end end
redis_config_hash = Gitlab::Redis.redis_store_options # Use Redis caching across all environments
redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE
redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
config.cache_store = :redis_store, redis_config_hash config.cache_store = :redis_store, redis_config_hash
......
# GIT over HTTP
require_dependency Rails.root.join('lib/gitlab/backend/grack_auth')
# GIT over SSH # GIT over SSH
require_dependency Rails.root.join('lib/gitlab/backend/shell') require_dependency Rails.root.join('lib/gitlab/backend/shell')
......
...@@ -12,3 +12,10 @@ Mime::Type.register_alias "text/html", :md ...@@ -12,3 +12,10 @@ Mime::Type.register_alias "text/html", :md
Mime::Type.register "video/mp4", :mp4, [], [:m4v, :mov] Mime::Type.register "video/mp4", :mp4, [], [:m4v, :mov]
Mime::Type.register "video/webm", :webm Mime::Type.register "video/webm", :webm
Mime::Type.register "video/ogg", :ogv Mime::Type.register "video/ogg", :ogv
middlewares = Gitlab::Application.config.middleware
middlewares.swap(ActionDispatch::ParamsParser, ActionDispatch::ParamsParser, {
Mime::Type.lookup('application/vnd.git-lfs+json') => lambda do |body|
ActiveSupport::JSON.decode(body)
end
})
...@@ -2,49 +2,86 @@ ...@@ -2,49 +2,86 @@
require 'securerandom' require 'securerandom'
# Your secret key for verifying the integrity of signed cookies. # Transition material in .secret to the secret_key_base key in config/secrets.yml.
# If you change this key, all old signed cookies will become invalid! # Historically, ENV['SECRET_KEY_BASE'] takes precedence over .secret, so we maintain that
# Make sure the secret is at least 30 characters and all random, # behavior.
# no regular words or you'll be exposed to dictionary attacks. #
# It also used to be the case that the key material in ENV['SECRET_KEY_BASE'] or .secret
def find_secure_token # was used to encrypt OTP (two-factor authentication) data so if present, we copy that key
token_file = Rails.root.join('.secret') # material into config/secrets.yml under otp_key_base.
if ENV.key?('SECRET_KEY_BASE') #
ENV['SECRET_KEY_BASE'] # Finally, if we have successfully migrated all secrets to config/secrets.yml, delete the
elsif File.exist? token_file # .secret file to avoid confusion.
# Use the existing token. #
File.read(token_file).chomp def create_tokens
else secret_file = Rails.root.join('.secret')
# Generate a new token of 64 random hexadecimal characters and store it in token_file. file_secret_key = File.read(secret_file).chomp if File.exist?(secret_file)
token = SecureRandom.hex(64) env_secret_key = ENV['SECRET_KEY_BASE']
File.write(token_file, token)
token # Ensure environment variable always overrides secrets.yml.
Rails.application.secrets.secret_key_base = env_secret_key if env_secret_key.present?
defaults = {
secret_key_base: file_secret_key || generate_new_secure_token,
otp_key_base: env_secret_key || file_secret_key || generate_new_secure_token,
db_key_base: generate_new_secure_token
}
missing_secrets = set_missing_keys(defaults)
write_secrets_yml(missing_secrets) unless missing_secrets.empty?
begin
File.delete(secret_file) if file_secret_key
rescue => e
warn "Error deleting useless .secret file: #{e}"
end end
end end
Rails.application.config.secret_token = find_secure_token
Rails.application.config.secret_key_base = find_secure_token
# CI
def generate_new_secure_token def generate_new_secure_token
SecureRandom.hex(64) SecureRandom.hex(64)
end end
if Rails.application.secrets.db_key_base.blank? def warn_missing_secret(secret)
warn "Missing `db_key_base` for '#{Rails.env}' environment. The secrets will be generated and stored in `config/secrets.yml`" warn "Missing Rails.application.secrets.#{secret} for #{Rails.env} environment. The secret will be generated and stored in config/secrets.yml."
end
all_secrets = YAML.load_file('config/secrets.yml') if File.exist?('config/secrets.yml') def set_missing_keys(defaults)
all_secrets ||= {} defaults.stringify_keys.each_with_object({}) do |(key, default), missing|
if Rails.application.secrets[key].blank?
warn_missing_secret(key)
# generate secrets missing[key] = Rails.application.secrets[key] = default
env_secrets = all_secrets[Rails.env.to_s] || {} end
env_secrets['db_key_base'] ||= generate_new_secure_token end
all_secrets[Rails.env.to_s] = env_secrets end
def write_secrets_yml(missing_secrets)
secrets_yml = Rails.root.join('config/secrets.yml')
rails_env = Rails.env.to_s
secrets = YAML.load_file(secrets_yml) if File.exist?(secrets_yml)
secrets ||= {}
secrets[rails_env] ||= {}
secrets[rails_env].merge!(missing_secrets) do |key, old, new|
# Previously, it was possible this was set to the literal contents of an Erb
# expression that evaluated to an empty value. We don't want to support that
# specifically, just ensure we don't break things further.
#
if old.present?
warn <<EOM
Rails.application.secrets.#{key} was blank, but the literal value in config/secrets.yml was:
#{old}
# save secrets This probably isn't the expected value for this secret. To keep using a literal Erb string in config/secrets.yml, replace `<%` with `<%%`.
File.open('config/secrets.yml', 'w', 0600) do |file| EOM
file.write(YAML.dump(all_secrets))
exit 1 # rubocop:disable Rails/Exit
end
new
end end
Rails.application.secrets.db_key_base = env_secrets['db_key_base'] File.write(secrets_yml, YAML.dump(secrets), mode: 'w', perm: 0600)
end end
create_tokens
...@@ -13,9 +13,9 @@ end ...@@ -13,9 +13,9 @@ end
if Rails.env.test? if Rails.env.test?
Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session"
else else
redis_config = Gitlab::Redis.redis_store_options redis_config = Gitlab::Redis.params
redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: redis_config, servers: redis_config,
......
# Custom Redis configuration
redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.redis = { config.redis = redis_config_hash
url: Gitlab::Redis.url,
namespace: Gitlab::Redis::SIDEKIQ_NAMESPACE
}
config.server_middleware do |chain| config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
...@@ -39,8 +40,5 @@ Sidekiq.configure_server do |config| ...@@ -39,8 +40,5 @@ Sidekiq.configure_server do |config|
end end
Sidekiq.configure_client do |config| Sidekiq.configure_client do |config|
config.redis = { config.redis = redis_config_hash
url: Gitlab::Redis.url,
namespace: Gitlab::Redis::SIDEKIQ_NAMESPACE
}
end end
# If you change this file in a Merge Request, please also create
# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
#
:mailboxes: :mailboxes:
<% <%
require "yaml" require_relative "lib/gitlab/mail_room" unless defined?(Gitlab::MailRoom)
require "json" config = Gitlab::MailRoom.config
require_relative "lib/gitlab/redis" unless defined?(Gitlab::Redis)
rails_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development" if Gitlab::MailRoom.enabled?
%>
config_file = ENV["MAIL_ROOM_GITLAB_CONFIG_FILE"] || "config/gitlab.yml"
if File.exists?(config_file)
all_config = YAML.load_file(config_file)[rails_env]
config = all_config["incoming_email"] || {}
config['enabled'] = false if config['enabled'].nil?
config['port'] = 143 if config['port'].nil?
config['ssl'] = false if config['ssl'].nil?
config['start_tls'] = false if config['start_tls'].nil?
config['mailbox'] = "inbox" if config['mailbox'].nil?
if config['enabled'] && config['address']
redis_url = Gitlab::Redis.new(rails_env).url
%>
- -
:host: <%= config['host'].to_json %> :host: <%= config[:host].to_json %>
:port: <%= config['port'].to_json %> :port: <%= config[:port].to_json %>
:ssl: <%= config['ssl'].to_json %> :ssl: <%= config[:ssl].to_json %>
:start_tls: <%= config['start_tls'].to_json %> :start_tls: <%= config[:start_tls].to_json %>
:email: <%= config['user'].to_json %> :email: <%= config[:user].to_json %>
:password: <%= config['password'].to_json %> :password: <%= config[:password].to_json %>
:idle_timeout: 60
:name: <%= config['mailbox'].to_json %> :name: <%= config[:mailbox].to_json %>
:delete_after_delivery: true :delete_after_delivery: true
:delivery_method: sidekiq :delivery_method: sidekiq
:delivery_options: :delivery_options:
:redis_url: <%= redis_url.to_json %> :redis_url: <%= config[:redis_url].to_json %>
:namespace: resque:gitlab :namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
:queue: incoming_email :queue: incoming_email
:worker: EmailReceiverWorker :worker: EmailReceiverWorker
:arbitration_method: redis :arbitration_method: redis
:arbitration_options: :arbitration_options:
:redis_url: <%= redis_url.to_json %> :redis_url: <%= config[:redis_url].to_json %>
:namespace: mail_room:gitlab :namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %>
<% end %> <% end %>
<% end %>
# If you change this file in a Merge Request, please also create # If you change this file in a Merge Request, please also create
# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests # a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
# #
development: redis://localhost:6379 development:
test: redis://localhost:6379 url: redis://localhost:6379
production: unix:/var/run/redis/redis.sock # sentinels:
# -
# host: localhost
# port: 26380 # point to sentinel, not to redis port
# -
# host: slave2
# port: 26381 # point to sentinel, not to redis port
test:
url: redis://localhost:6379
production:
# Redis (single instance)
url: unix:/var/run/redis/redis.sock
##
# Redis + Sentinel (for HA)
#
# Please read instructions carefully before using it as you may lose data:
# http://redis.io/topics/sentinel
#
# You must specify a list of a few sentinels that will handle client connection
# please read here for more information: https://docs.gitlab.com/ce/administration/high_availability/redis.html
##
# url: redis://master:6379
# sentinels:
# -
# host: slave1
# port: 26379 # point to sentinel, not to redis port
# -
# host: slave2
# port: 26379 # point to sentinel, not to redis port
...@@ -84,9 +84,6 @@ Rails.application.routes.draw do ...@@ -84,9 +84,6 @@ Rails.application.routes.draw do
# Health check # Health check
get 'health_check(/:checks)' => 'health_check#index', as: :health_check get 'health_check(/:checks)' => 'health_check#index', as: :health_check
# Enable Grack support (for LFS only)
mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\/(info\/lfs|gitlab-lfs)/.match(request.path_info) }, via: [:get, :post, :put]
# Help # Help
get 'help' => 'help#index' get 'help' => 'help#index'
get 'help/shortcuts' => 'help#shortcuts' get 'help/shortcuts' => 'help#shortcuts'
...@@ -471,7 +468,7 @@ Rails.application.routes.draw do ...@@ -471,7 +468,7 @@ Rails.application.routes.draw do
post :unarchive post :unarchive
post :housekeeping post :housekeeping
post :toggle_star post :toggle_star
post :markdown_preview post :preview_markdown
post :export post :export
post :remove_export post :remove_export
post :generate_new_export post :generate_new_export
...@@ -482,11 +479,26 @@ Rails.application.routes.draw do ...@@ -482,11 +479,26 @@ Rails.application.routes.draw do
end end
scope module: :projects do scope module: :projects do
# Git HTTP clients ('git clone' etc.)
scope constraints: { id: /.+\.git/, format: nil } do scope constraints: { id: /.+\.git/, format: nil } do
# Git HTTP clients ('git clone' etc.)
get '/info/refs', to: 'git_http#info_refs' get '/info/refs', to: 'git_http#info_refs'
post '/git-upload-pack', to: 'git_http#git_upload_pack' post '/git-upload-pack', to: 'git_http#git_upload_pack'
post '/git-receive-pack', to: 'git_http#git_receive_pack' post '/git-receive-pack', to: 'git_http#git_receive_pack'
# Git LFS API (metadata)
post '/info/lfs/objects/batch', to: 'lfs_api#batch'
post '/info/lfs/objects', to: 'lfs_api#deprecated'
get '/info/lfs/objects/*oid', to: 'lfs_api#deprecated'
# GitLab LFS object storage
scope constraints: { oid: /[a-f0-9]{64}/ } do
get '/gitlab-lfs/objects/*oid', to: 'lfs_storage#download'
scope constraints: { size: /[0-9]+/ } do
put '/gitlab-lfs/objects/*oid/*size/authorize', to: 'lfs_storage#upload_authorize'
put '/gitlab-lfs/objects/*oid/*size', to: 'lfs_storage#upload_finalize'
end
end
end end
# Allow /info/refs, /info/refs?service=git-upload-pack, and # Allow /info/refs, /info/refs?service=git-upload-pack, and
...@@ -660,7 +672,7 @@ Rails.application.routes.draw do ...@@ -660,7 +672,7 @@ Rails.application.routes.draw do
get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
post '/wikis/*id/markdown_preview', to: 'wikis#markdown_preview', constraints: WIKI_SLUG_ID, as: 'wiki_markdown_preview' post '/wikis/*id/preview_markdown', to: 'wikis#preview_markdown', constraints: WIKI_SLUG_ID, as: 'wiki_preview_markdown'
end end
resource :repository, only: [:create] do resource :repository, only: [:create] do
......
class AddQueuedAtToCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_builds, :queued_at, :timestamp
end
end
class AddPipelineEventsToWebHooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:web_hooks, :pipeline_events, :boolean,
default: false, allow_null: false)
end
def down
remove_column(:web_hooks, :pipeline_events)
end
end
class AddPipelineEventsToServices < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:services, :pipeline_events, :boolean,
default: false, allow_null: false)
end
def down
remove_column(:services, :pipeline_events)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveCiRunnerTrigramIndexes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
# Disabled for the "down" method so the indexes can be re-created concurrently.
disable_ddl_transaction!
def up
return unless Gitlab::Database.postgresql?
transaction do
execute 'DROP INDEX IF EXISTS index_ci_runners_on_token_trigram;'
execute 'DROP INDEX IF EXISTS index_ci_runners_on_description_trigram;'
end
end
def down
return unless Gitlab::Database.postgresql?
execute 'CREATE INDEX CONCURRENTLY index_ci_runners_on_token_trigram ON ci_runners USING gin(token gin_trgm_ops);'
execute 'CREATE INDEX CONCURRENTLY index_ci_runners_on_description_trigram ON ci_runners USING gin(description gin_trgm_ops);'
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveRedundantIndexes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
indexes = [
[:ci_taggings, 'ci_taggings_idx'],
[:audit_events, 'index_audit_events_on_author_id'],
[:audit_events, 'index_audit_events_on_type'],
[:ci_builds, 'index_ci_builds_on_erased_by_id'],
[:ci_builds, 'index_ci_builds_on_project_id_and_commit_id'],
[:ci_builds, 'index_ci_builds_on_type'],
[:ci_commits, 'index_ci_commits_on_project_id'],
[:ci_commits, 'index_ci_commits_on_project_id_and_committed_at'],
[:ci_commits, 'index_ci_commits_on_project_id_and_committed_at_and_id'],
[:ci_commits, 'index_ci_commits_on_project_id_and_sha'],
[:ci_commits, 'index_ci_commits_on_sha'],
[:ci_events, 'index_ci_events_on_created_at'],
[:ci_events, 'index_ci_events_on_is_admin'],
[:ci_events, 'index_ci_events_on_project_id'],
[:ci_jobs, 'index_ci_jobs_on_deleted_at'],
[:ci_jobs, 'index_ci_jobs_on_project_id'],
[:ci_projects, 'index_ci_projects_on_gitlab_id'],
[:ci_projects, 'index_ci_projects_on_shared_runners_enabled'],
[:ci_services, 'index_ci_services_on_project_id'],
[:ci_sessions, 'index_ci_sessions_on_session_id'],
[:ci_sessions, 'index_ci_sessions_on_updated_at'],
[:ci_tags, 'index_ci_tags_on_name'],
[:ci_triggers, 'index_ci_triggers_on_deleted_at'],
[:identities, 'index_identities_on_created_at_and_id'],
[:issues, 'index_issues_on_title'],
[:keys, 'index_keys_on_created_at_and_id'],
[:members, 'index_members_on_created_at_and_id'],
[:members, 'index_members_on_type'],
[:milestones, 'index_milestones_on_created_at_and_id'],
[:namespaces, 'index_namespaces_on_visibility_level'],
[:projects, 'index_projects_on_builds_enabled_and_shared_runners_enabled'],
[:services, 'index_services_on_category'],
[:services, 'index_services_on_created_at_and_id'],
[:services, 'index_services_on_default'],
[:snippets, 'index_snippets_on_created_at'],
[:snippets, 'index_snippets_on_created_at_and_id'],
[:todos, 'index_todos_on_state'],
[:web_hooks, 'index_web_hooks_on_created_at_and_id'],
# These indexes _may_ be used but they can be replaced by other existing
# indexes.
# There's already a composite index on (project_id, iid) which means that
# a separate index for _just_ project_id is not needed.
[:issues, 'index_issues_on_project_id'],
# These are all composite indexes for the columns (created_at, id). In all
# these cases there's already a standalone index for "created_at" which
# can be used instead.
#
# Because the "id" column of these composite indexes is never needed (due
# to "id" already being indexed as its a primary key) these composite
# indexes are useless.
[:issues, 'index_issues_on_created_at_and_id'],
[:merge_requests, 'index_merge_requests_on_created_at_and_id'],
[:namespaces, 'index_namespaces_on_created_at_and_id'],
[:notes, 'index_notes_on_created_at_and_id'],
[:projects, 'index_projects_on_created_at_and_id'],
[:users, 'index_users_on_created_at_and_id'],
]
transaction do
indexes.each do |(table, index)|
remove_index(table, name: index) if index_exists_by_name?(table, index)
end
end
add_concurrent_index(:users, :created_at)
add_concurrent_index(:projects, :created_at)
add_concurrent_index(:namespaces, :created_at)
end
def down
# We're only restoring the composite indexes that could be replaced with
# individual ones, just in case somebody would ever want to revert.
transaction do
remove_index(:users, :created_at)
remove_index(:projects, :created_at)
remove_index(:namespaces, :created_at)
end
[:issues, :merge_requests, :namespaces, :notes, :projects, :users].each do |table|
add_concurrent_index(table, [:created_at, :id],
name: "index_#{table}_on_created_at_and_id")
end
end
# Rails' index_exists? doesn't work when you only give it a table and index
# name. As such we have to use some extra code to check if an index exists for
# a given name.
def index_exists_by_name?(table, index)
indexes_for_table[table].include?(index)
end
def indexes_for_table
@indexes_for_table ||= Hash.new do |hash, table_name|
hash[table_name] = indexes(table_name).map(&:name)
end
end
end
This diff is collapsed.
...@@ -121,6 +121,10 @@ Registry is exposed to the outside world is `4567`, here is what you need to set ...@@ -121,6 +121,10 @@ Registry is exposed to the outside world is `4567`, here is what you need to set
in `gitlab.rb` or `gitlab.yml` if you are using Omnibus GitLab or installed in `gitlab.rb` or `gitlab.yml` if you are using Omnibus GitLab or installed
GitLab from source respectively. GitLab from source respectively.
>**Note:**
Be careful to choose a port different than the one that Registry listens to (`5000` by default),
otherwise you will run into conflicts .
--- ---
**Omnibus GitLab installations** **Omnibus GitLab installations**
......
...@@ -16,6 +16,8 @@ following locations: ...@@ -16,6 +16,8 @@ following locations:
- [Commits](commits.md) - [Commits](commits.md)
- [Deploy Keys](deploy_keys.md) - [Deploy Keys](deploy_keys.md)
- [Groups](groups.md) - [Groups](groups.md)
- [Group Access Requests](access_requests.md)
- [Group Members](members.md)
- [Issues](issues.md) - [Issues](issues.md)
- [Keys](keys.md) - [Keys](keys.md)
- [Labels](labels.md) - [Labels](labels.md)
...@@ -25,6 +27,8 @@ following locations: ...@@ -25,6 +27,8 @@ following locations:
- [Namespaces](namespaces.md) - [Namespaces](namespaces.md)
- [Notes](notes.md) (comments) - [Notes](notes.md) (comments)
- [Projects](projects.md) including setting Webhooks - [Projects](projects.md) including setting Webhooks
- [Project Access Requests](access_requests.md)
- [Project Members](members.md)
- [Project Snippets](project_snippets.md) - [Project Snippets](project_snippets.md)
- [Repositories](repositories.md) - [Repositories](repositories.md)
- [Repository Files](repository_files.md) - [Repository Files](repository_files.md)
...@@ -74,7 +78,7 @@ You can use an OAuth 2 token to authenticate with the API by passing it either i ...@@ -74,7 +78,7 @@ You can use an OAuth 2 token to authenticate with the API by passing it either i
Example of using the OAuth2 token in the header: Example of using the OAuth2 token in the header:
```shell ```shell
curl -H "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v3/projects curl --header "Authorization: Bearer OAUTH-TOKEN" https://gitlab.example.com/api/v3/projects
``` ```
Read more about [GitLab as an OAuth2 client](oauth2.md). Read more about [GitLab as an OAuth2 client](oauth2.md).
...@@ -154,7 +158,7 @@ be returned with status code `403`: ...@@ -154,7 +158,7 @@ be returned with status code `403`:
```json ```json
{ {
"message": "403 Forbidden: Must be admin to use sudo" "message": "403 Forbidden - Must be admin to use sudo"
} }
``` ```
...@@ -204,7 +208,7 @@ resources you can pass the following parameters: ...@@ -204,7 +208,7 @@ resources you can pass the following parameters:
In the example below, we list 50 [namespaces](namespaces.md) per page. In the example below, we list 50 [namespaces](namespaces.md) per page.
```bash ```bash
curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/namespaces?per_page=50 curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/namespaces?per_page=50
``` ```
### Pagination Link header ### Pagination Link header
...@@ -218,7 +222,7 @@ and we request the second page (`page=2`) of [comments](notes.md) of the issue ...@@ -218,7 +222,7 @@ and we request the second page (`page=2`) of [comments](notes.md) of the issue
with ID `8` which belongs to the project with ID `8`: with ID `8` which belongs to the project with ID `8`:
```bash ```bash
curl -I -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/issues/8/notes?per_page=3&page=2 curl --head --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/issues/8/notes?per_page=3&page=2
``` ```
The response will then be: The response will then be:
......
# Group and project access requests
>**Note:** This feature was introduced in GitLab 8.11
**Valid access levels**
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
```
10 => Guest access
20 => Reporter access
30 => Developer access
40 => Master access
50 => Owner access # Only valid for groups
```
## List access requests for a group or project
Gets a list of access requests viewable by the authenticated user.
Returns `200` if the request succeeds.
```
GET /groups/:id/access_requests
GET /projects/:id/access_requests
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests
```
Example response:
```json
[
{
"id": 1,
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
"created_at": "2012-10-22T14:13:35Z",
"requested_at": "2012-10-22T14:13:35Z"
},
{
"id": 2,
"username": "john_doe",
"name": "John Doe",
"state": "active",
"created_at": "2012-10-22T14:13:35Z",
"requested_at": "2012-10-22T14:13:35Z"
}
]
```
## Request access to a group or project
Requests access for the authenticated user to a group or project.
Returns `201` if the request succeeds.
```
POST /groups/:id/access_requests
POST /projects/:id/access_requests
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests
```
Example response:
```json
{
"id": 1,
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
"created_at": "2012-10-22T14:13:35Z",
"requested_at": "2012-10-22T14:13:35Z"
}
```
## Approve an access request
Approves an access request for the given user.
Returns `201` if the request succeeds.
```
PUT /groups/:id/access_requests/:user_id/approve
PUT /projects/:id/access_requests/:user_id/approve
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
| `user_id` | integer | yes | The user ID of the access requester |
| `access_level` | integer | no | A valid access level (defaults: `30`, developer access level) |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests/:user_id/approve?access_level=20
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests/:user_id/approve?access_level=20
```
Example response:
```json
{
"id": 1,
"username": "raymond_smith",
"name": "Raymond Smith",
"state": "active",
"created_at": "2012-10-22T14:13:35Z",
"access_level": 20
}
```
## Deny an access request
Denies an access request for the given user.
Returns `200` if the request succeeds.
```
DELETE /groups/:id/access_requests/:user_id
DELETE /projects/:id/access_requests/:user_id
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The group/project ID or path |
| `user_id` | integer | yes | The user ID of the access requester |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/:id/access_requests/:user_id
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/:id/access_requests/:user_id
```
...@@ -25,7 +25,7 @@ Parameters: ...@@ -25,7 +25,7 @@ Parameters:
| `awardable_id` | integer | yes | The ID of an awardable | | `awardable_id` | integer | yes | The ID of an awardable |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji
``` ```
Example Response: Example Response:
...@@ -85,7 +85,7 @@ Parameters: ...@@ -85,7 +85,7 @@ Parameters:
| `award_id` | integer | yes | The ID of the award emoji | | `award_id` | integer | yes | The ID of the award emoji |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/1 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/1
``` ```
Example Response: Example Response:
...@@ -127,7 +127,7 @@ Parameters: ...@@ -127,7 +127,7 @@ Parameters:
| `name` | string | yes | The name of the emoji, without colons | | `name` | string | yes | The name of the emoji, without colons |
```bash ```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji?name=blowfish curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji?name=blowfish
``` ```
Example Response: Example Response:
...@@ -170,7 +170,7 @@ Parameters: ...@@ -170,7 +170,7 @@ Parameters:
| `award_id` | integer | yes | The ID of a award_emoji | | `award_id` | integer | yes | The ID of a award_emoji |
```bash ```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/344 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/344
``` ```
Example Response: Example Response:
...@@ -217,7 +217,7 @@ Parameters: ...@@ -217,7 +217,7 @@ Parameters:
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji
``` ```
Example Response: Example Response:
...@@ -259,7 +259,7 @@ Parameters: ...@@ -259,7 +259,7 @@ Parameters:
| `award_id` | integer | yes | The ID of the award emoji | | `award_id` | integer | yes | The ID of the award emoji |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji/2 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji/2
``` ```
Example Response: Example Response:
...@@ -299,7 +299,7 @@ Parameters: ...@@ -299,7 +299,7 @@ Parameters:
| `name` | string | yes | The name of the emoji, without colons | | `name` | string | yes | The name of the emoji, without colons |
```bash ```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji?name=rocket curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/notes/1/award_emoji?name=rocket
``` ```
Example Response: Example Response:
...@@ -342,7 +342,7 @@ Parameters: ...@@ -342,7 +342,7 @@ Parameters:
| `award_id` | integer | yes | The ID of a award_emoji | | `award_id` | integer | yes | The ID of a award_emoji |
```bash ```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/345 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/345
``` ```
Example Response: Example Response:
......
...@@ -13,7 +13,7 @@ GET /projects/:id/repository/branches ...@@ -13,7 +13,7 @@ GET /projects/:id/repository/branches
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches
``` ```
Example response: Example response:
...@@ -57,7 +57,7 @@ GET /projects/:id/repository/branches/:branch ...@@ -57,7 +57,7 @@ GET /projects/:id/repository/branches/:branch
| `branch` | string | yes | The name of the branch | | `branch` | string | yes | The name of the branch |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master
``` ```
Example response: Example response:
...@@ -95,7 +95,7 @@ PUT /projects/:id/repository/branches/:branch/protect ...@@ -95,7 +95,7 @@ PUT /projects/:id/repository/branches/:branch/protect
``` ```
```bash ```bash
curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/protect?developers_can_push=true&developers_can_merge=true curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/protect?developers_can_push=true&developers_can_merge=true
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -140,7 +140,7 @@ PUT /projects/:id/repository/branches/:branch/unprotect ...@@ -140,7 +140,7 @@ PUT /projects/:id/repository/branches/:branch/unprotect
``` ```
```bash ```bash
curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/unprotect curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/repository/branches/master/unprotect
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -185,7 +185,7 @@ POST /projects/:id/repository/branches ...@@ -185,7 +185,7 @@ POST /projects/:id/repository/branches
| `ref` | string | yes | The branch name or commit SHA to create branch from | | `ref` | string | yes | The branch name or commit SHA to create branch from |
```bash ```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches?branch_name=newbranch&ref=master" curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches?branch_name=newbranch&ref=master"
``` ```
Example response: Example response:
...@@ -230,7 +230,7 @@ It returns `200` if it succeeds, `404` if the branch to be deleted does not exis ...@@ -230,7 +230,7 @@ It returns `200` if it succeeds, `404` if the branch to be deleted does not exis
or `400` for other reasons. In case of an error, an explaining message is provided. or `400` for other reasons. In case of an error, an explaining message is provided.
```bash ```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch" curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch"
``` ```
Example response: Example response:
......
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.
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