Commit c09d233611e00328f0e8d493a106737f0638d9a2
Exists in
master
and in
4 other branches
Merge branch 'master' into fix_project_access_notification
Showing
74 changed files
with
1150 additions
and
838 deletions
Show diff stats
CHANGELOG
| 1 | +v 2.9.1 | |
| 2 | + - Fixed resque custom config init | |
| 3 | + | |
| 1 | 4 | v 2.9.0 |
| 2 | 5 | - fixed inline notes bugs |
| 3 | 6 | - refactored rspecs |
| ... | ... | @@ -9,8 +12,10 @@ v 2.9.0 |
| 9 | 12 | - scss refactoring. gitlab_bootstrap/ dir |
| 10 | 13 | - fix git push http body bigger than 112k problem |
| 11 | 14 | - list of labels page under issues tab |
| 12 | - - API for milestones | |
| 15 | + - API for milestones, keys | |
| 13 | 16 | - restyled buttons |
| 17 | + - OAuth | |
| 18 | + - Comment order changed | |
| 14 | 19 | |
| 15 | 20 | v 2.8.1 |
| 16 | 21 | - ability to disable gravatars | ... | ... |
Gemfile
Gemfile.lock
| ... | ... | @@ -199,6 +199,7 @@ GEM |
| 199 | 199 | httpauth (0.1) |
| 200 | 200 | i18n (0.6.1) |
| 201 | 201 | journey (1.0.4) |
| 202 | + jquery-atwho-rails (0.1.6) | |
| 202 | 203 | jquery-rails (2.0.2) |
| 203 | 204 | railties (>= 3.2.0, < 5.0) |
| 204 | 205 | thor (~> 0.14) |
| ... | ... | @@ -441,6 +442,7 @@ DEPENDENCIES |
| 441 | 442 | haml-rails |
| 442 | 443 | headless |
| 443 | 444 | httparty |
| 445 | + jquery-atwho-rails (= 0.1.6) | |
| 444 | 446 | jquery-rails (= 2.0.2) |
| 445 | 447 | jquery-ui-rails (= 0.5.0) |
| 446 | 448 | kaminari | ... | ... |
Guardfile
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do |
| 5 | 5 | watch(%r{^spec/.+_spec\.rb$}) |
| 6 | 6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } |
| 7 | + watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } | |
| 7 | 8 | watch('spec/spec_helper.rb') { "spec" } |
| 8 | 9 | |
| 9 | 10 | # Rails example | ... | ... |
VERSION
app/assets/javascripts/application.js
app/assets/stylesheets/application.css
app/assets/stylesheets/common.scss
| ... | ... | @@ -185,36 +185,6 @@ span.update-author { |
| 185 | 185 | } |
| 186 | 186 | } |
| 187 | 187 | |
| 188 | -.event_label { | |
| 189 | - @extend .label; | |
| 190 | - background-color: #999; | |
| 191 | - | |
| 192 | - &.pushed { | |
| 193 | - background-color: #4A97BD; | |
| 194 | - } | |
| 195 | - | |
| 196 | - &.opened { | |
| 197 | - background-color: #469847; | |
| 198 | - } | |
| 199 | - | |
| 200 | - &.closed { | |
| 201 | - background-color: #B94A48; | |
| 202 | - } | |
| 203 | - | |
| 204 | - &.merged { | |
| 205 | - background-color: #2A2; | |
| 206 | - } | |
| 207 | - | |
| 208 | - &.joined { | |
| 209 | - background-color: #1ca9dd; | |
| 210 | - } | |
| 211 | - | |
| 212 | - &.left { | |
| 213 | - background-color: #888; | |
| 214 | - float:none; | |
| 215 | - } | |
| 216 | -} | |
| 217 | - | |
| 218 | 188 | form { |
| 219 | 189 | @extend .form-horizontal; |
| 220 | 190 | |
| ... | ... | @@ -355,41 +325,6 @@ p.time { |
| 355 | 325 | border:2px solid #ddd; |
| 356 | 326 | } |
| 357 | 327 | |
| 358 | -.event_feed { | |
| 359 | - min-height:40px; | |
| 360 | - border-bottom:1px solid #ddd; | |
| 361 | - .avatar { | |
| 362 | - width:32px; | |
| 363 | - } | |
| 364 | - .event_icon { | |
| 365 | - float:right; | |
| 366 | - margin-right:2px; | |
| 367 | - img { | |
| 368 | - width:20px; | |
| 369 | - } | |
| 370 | - } | |
| 371 | - ul { | |
| 372 | - margin-left:50px; | |
| 373 | - margin-bottom:5px; | |
| 374 | - .avatar { | |
| 375 | - width:24px; | |
| 376 | - } | |
| 377 | - } | |
| 378 | - | |
| 379 | - padding: 15px 5px; | |
| 380 | - &:last-child { border:none } | |
| 381 | - .wll:hover { background:none } | |
| 382 | - | |
| 383 | - .event_commits { | |
| 384 | - margin-top: 5px; | |
| 385 | - | |
| 386 | - li.commit { | |
| 387 | - background: transparent; | |
| 388 | - padding:5px; | |
| 389 | - border:none; | |
| 390 | - } | |
| 391 | - } | |
| 392 | -} | |
| 393 | 328 | |
| 394 | 329 | .ico { |
| 395 | 330 | background: url("images.png") no-repeat -85px -77px; |
| ... | ... | @@ -639,22 +574,6 @@ li.note { |
| 639 | 574 | background:#fff; |
| 640 | 575 | } |
| 641 | 576 | |
| 642 | -/** | |
| 643 | - * Push event widget | |
| 644 | - * | |
| 645 | - */ | |
| 646 | -.event_lp { | |
| 647 | - @extend .ui-box; | |
| 648 | - color:#777; | |
| 649 | - margin-bottom:20px; | |
| 650 | - padding:8px; | |
| 651 | - @include border-radius(4px); | |
| 652 | - min-height:22px; | |
| 653 | - | |
| 654 | - .avatar { | |
| 655 | - width:24px; | |
| 656 | - } | |
| 657 | -} | |
| 658 | 577 | |
| 659 | 578 | .supp_diff_link, |
| 660 | 579 | .mr_show_all_commits { | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/lists.scss
app/assets/stylesheets/main.scss
| ... | ... | @@ -143,6 +143,7 @@ $hover: #fdf5d9; |
| 143 | 143 | @import "sections/projects.scss"; |
| 144 | 144 | @import "sections/merge_requests.scss"; |
| 145 | 145 | @import "sections/graph.scss"; |
| 146 | +@import "sections/events.scss"; | |
| 146 | 147 | |
| 147 | 148 | /** |
| 148 | 149 | * This scss file redefine chozen selectbox styles for | ... | ... |
app/assets/stylesheets/sections/commits.scss
| 1 | 1 | .commit-box { |
| 2 | 2 | @extend .main_box; |
| 3 | 3 | |
| 4 | - .commit-head { | |
| 4 | + .commit-head { | |
| 5 | 5 | @extend .top_box_content; |
| 6 | 6 | |
| 7 | 7 | .commit-title { |
| ... | ... | @@ -29,11 +29,11 @@ |
| 29 | 29 | |
| 30 | 30 | .sha-block { |
| 31 | 31 | text-align:right; |
| 32 | - &:first-child { | |
| 32 | + &:first-child { | |
| 33 | 33 | padding-bottom:6px; |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | - a { | |
| 36 | + a { | |
| 37 | 37 | border-bottom: 1px solid #aaa; |
| 38 | 38 | margin-left: 9px; |
| 39 | 39 | } |
| ... | ... | @@ -54,7 +54,7 @@ |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | 56 | /** |
| 57 | - * | |
| 57 | + * | |
| 58 | 58 | * COMMIT SHOw |
| 59 | 59 | * |
| 60 | 60 | */ |
| ... | ... | @@ -71,7 +71,7 @@ |
| 71 | 71 | background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); |
| 72 | 72 | background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); |
| 73 | 73 | |
| 74 | - span { | |
| 74 | + span { | |
| 75 | 75 | font-size:14px; |
| 76 | 76 | } |
| 77 | 77 | } |
| ... | ... | @@ -111,8 +111,8 @@ |
| 111 | 111 | } |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | - &.img_compared { | |
| 115 | - img { | |
| 114 | + &.img_compared { | |
| 115 | + img { | |
| 116 | 116 | max-width:300px; |
| 117 | 117 | } |
| 118 | 118 | } |
| ... | ... | @@ -120,12 +120,12 @@ |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | .diff_file_content{ |
| 123 | - table { | |
| 123 | + table { | |
| 124 | 124 | border:none; |
| 125 | 125 | margin:0px; |
| 126 | 126 | padding:0px; |
| 127 | 127 | tr { |
| 128 | - td { | |
| 128 | + td { | |
| 129 | 129 | font-size:12px; |
| 130 | 130 | } |
| 131 | 131 | } |
| ... | ... | @@ -145,29 +145,29 @@ |
| 145 | 145 | moz-user-select: none; |
| 146 | 146 | -khtml-user-select: none; |
| 147 | 147 | user-select: none; |
| 148 | - a { | |
| 148 | + a { | |
| 149 | 149 | float:left; |
| 150 | 150 | width:35px; |
| 151 | 151 | font-weight:normal; |
| 152 | 152 | color:#666; |
| 153 | - &:hover { | |
| 153 | + &:hover { | |
| 154 | 154 | text-decoration:underline; |
| 155 | 155 | } |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | - .line_content { | |
| 159 | - white-space:pre; | |
| 158 | + .line_content { | |
| 159 | + white-space:pre; | |
| 160 | 160 | height:14px; |
| 161 | 161 | margin:0px; |
| 162 | 162 | padding:0px; |
| 163 | 163 | border:none; |
| 164 | - &.new { | |
| 164 | + &.new { | |
| 165 | 165 | background: #CFD; |
| 166 | 166 | } |
| 167 | - &.old { | |
| 167 | + &.old { | |
| 168 | 168 | background: #FDD; |
| 169 | 169 | } |
| 170 | - &.matched { | |
| 170 | + &.matched { | |
| 171 | 171 | color:#ccc; |
| 172 | 172 | background:#fafafa; |
| 173 | 173 | } |
| ... | ... | @@ -182,32 +182,32 @@ |
| 182 | 182 | |
| 183 | 183 | |
| 184 | 184 | /** COMMIT ROW **/ |
| 185 | -.commit { | |
| 185 | +.commit { | |
| 186 | 186 | @extend .wll; |
| 187 | 187 | |
| 188 | - .browse_code_link_holder { | |
| 188 | + .browse_code_link_holder { | |
| 189 | 189 | @extend .span2; |
| 190 | 190 | float:right; |
| 191 | 191 | } |
| 192 | 192 | |
| 193 | - .committed_ago { | |
| 193 | + .committed_ago { | |
| 194 | 194 | float:right; |
| 195 | 195 | @extend .cgray; |
| 196 | 196 | } |
| 197 | 197 | |
| 198 | - code { | |
| 198 | + code { | |
| 199 | 199 | background:#FCEEC1; |
| 200 | 200 | color:$style_color; |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | - .commit_short_id { | |
| 203 | + .commit_short_id { | |
| 204 | 204 | float:left; |
| 205 | 205 | @extend .lined; |
| 206 | 206 | min-width:65px; |
| 207 | 207 | font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; |
| 208 | 208 | } |
| 209 | - | |
| 210 | - .commit-author-name { | |
| 209 | + | |
| 210 | + .commit-author-name { | |
| 211 | 211 | color: #777; |
| 212 | 212 | } |
| 213 | 213 | } | ... | ... |
| ... | ... | @@ -0,0 +1,118 @@ |
| 1 | +/** | |
| 2 | + * Events labels | |
| 3 | + * | |
| 4 | + */ | |
| 5 | +.event_label { | |
| 6 | + &.pushed { | |
| 7 | + padding:0 2px; | |
| 8 | + @extend .alert; | |
| 9 | + @extend .alert-info; | |
| 10 | + } | |
| 11 | + | |
| 12 | + &.opened { | |
| 13 | + padding:0 2px; | |
| 14 | + @extend .alert; | |
| 15 | + @extend .alert-success; | |
| 16 | + } | |
| 17 | + | |
| 18 | + &.closed { | |
| 19 | + padding:0 2px; | |
| 20 | + @extend .alert; | |
| 21 | + @extend .alert-error; | |
| 22 | + } | |
| 23 | + | |
| 24 | + &.merged { | |
| 25 | + padding:0 2px; | |
| 26 | + @extend .alert; | |
| 27 | + @extend .alert-success; | |
| 28 | + } | |
| 29 | + | |
| 30 | + &.left, | |
| 31 | + &.joined { | |
| 32 | + padding:0 2px; | |
| 33 | + @extend .alert; | |
| 34 | + } | |
| 35 | +} | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * Dashboard events feed | |
| 39 | + * | |
| 40 | + */ | |
| 41 | +.event-item { | |
| 42 | + min-height:40px; | |
| 43 | + border-bottom:1px solid #eee; | |
| 44 | + .event-title { | |
| 45 | + color:#333; | |
| 46 | + font-weight: bold; | |
| 47 | + .author_name { | |
| 48 | + color:#333; | |
| 49 | + } | |
| 50 | + } | |
| 51 | + .event-body { | |
| 52 | + p { | |
| 53 | + color:#555; | |
| 54 | + } | |
| 55 | + .event-info { | |
| 56 | + color:#666; | |
| 57 | + } | |
| 58 | + } | |
| 59 | + .avatar { | |
| 60 | + width:32px; | |
| 61 | + } | |
| 62 | + .event_icon { | |
| 63 | + float: right; | |
| 64 | + border: 1px solid #EEE; | |
| 65 | + padding: 5px; | |
| 66 | + @include border-radius(5px); | |
| 67 | + background: #F9F9F9; | |
| 68 | + img { | |
| 69 | + width:20px; | |
| 70 | + } | |
| 71 | + } | |
| 72 | + ul { | |
| 73 | + margin-left:50px; | |
| 74 | + margin-bottom:5px; | |
| 75 | + .avatar { | |
| 76 | + width:18px; | |
| 77 | + margin-top:3px; | |
| 78 | + } | |
| 79 | + } | |
| 80 | + | |
| 81 | + padding: 15px 5px; | |
| 82 | + &:last-child { border:none } | |
| 83 | + .wll:hover { background:none } | |
| 84 | + | |
| 85 | + .event_commits { | |
| 86 | + margin-top: 5px; | |
| 87 | + | |
| 88 | + li { | |
| 89 | + &.commit { | |
| 90 | + background: transparent; | |
| 91 | + padding:3px; | |
| 92 | + border:none; | |
| 93 | + font-size:12px; | |
| 94 | + } | |
| 95 | + &.commits-stat { | |
| 96 | + display: block; | |
| 97 | + margin-top: 5px; | |
| 98 | + } | |
| 99 | + } | |
| 100 | + } | |
| 101 | +} | |
| 102 | + | |
| 103 | +/** | |
| 104 | + * Push event widget | |
| 105 | + * | |
| 106 | + */ | |
| 107 | +.event_lp { | |
| 108 | + @extend .ui-box; | |
| 109 | + color:#777; | |
| 110 | + margin-bottom:20px; | |
| 111 | + padding:8px; | |
| 112 | + @include border-radius(4px); | |
| 113 | + min-height:22px; | |
| 114 | + | |
| 115 | + .avatar { | |
| 116 | + width:24px; | |
| 117 | + } | |
| 118 | +} | ... | ... |
app/assets/stylesheets/sections/notes.scss
| ... | ... | @@ -43,7 +43,9 @@ |
| 43 | 43 | padding: 8px 0; |
| 44 | 44 | overflow: hidden; |
| 45 | 45 | display: block; |
| 46 | + position:relative; | |
| 46 | 47 | img {float: left; margin-right: 10px;} |
| 48 | + img.emoji {float:none;margin:0;} | |
| 47 | 49 | .note-author cite{font-style: italic;} |
| 48 | 50 | p { color:$style_color; } |
| 49 | 51 | .note-author { color: $style_color;} |
| ... | ... | @@ -55,7 +57,9 @@ |
| 55 | 57 | |
| 56 | 58 | .delete-note { |
| 57 | 59 | display:none; |
| 58 | - float:right; | |
| 60 | + position:absolute; | |
| 61 | + right:0; | |
| 62 | + top:0; | |
| 59 | 63 | } |
| 60 | 64 | |
| 61 | 65 | &:hover { | ... | ... |
app/controllers/admin/users_controller.rb
| ... | ... | @@ -30,7 +30,7 @@ class Admin::UsersController < AdminController |
| 30 | 30 | |
| 31 | 31 | |
| 32 | 32 | def new |
| 33 | - @admin_user = User.new(projects_limit: Gitlab.config.default_projects_limit) | |
| 33 | + @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin) | |
| 34 | 34 | end |
| 35 | 35 | |
| 36 | 36 | def edit |
| ... | ... | @@ -60,7 +60,7 @@ class Admin::UsersController < AdminController |
| 60 | 60 | def create |
| 61 | 61 | admin = params[:user].delete("admin") |
| 62 | 62 | |
| 63 | - @admin_user = User.new(params[:user]) | |
| 63 | + @admin_user = User.new(params[:user], as: :admin) | |
| 64 | 64 | @admin_user.admin = (admin && admin.to_i > 0) |
| 65 | 65 | |
| 66 | 66 | respond_to do |format| |
| ... | ... | @@ -86,7 +86,7 @@ class Admin::UsersController < AdminController |
| 86 | 86 | @admin_user.admin = (admin && admin.to_i > 0) |
| 87 | 87 | |
| 88 | 88 | respond_to do |format| |
| 89 | - if @admin_user.update_attributes(params[:user]) | |
| 89 | + if @admin_user.update_attributes(params[:user], as: :admin) | |
| 90 | 90 | format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } |
| 91 | 91 | format.json { head :ok } |
| 92 | 92 | else | ... | ... |
app/controllers/commits_controller.rb
app/controllers/hooks_controller.rb
app/controllers/issues_controller.rb
app/controllers/labels_controller.rb
app/controllers/merge_requests_controller.rb
app/controllers/milestones_controller.rb
app/controllers/snippets_controller.rb
app/helpers/tree_helper.rb
| ... | ... | @@ -32,7 +32,11 @@ module TreeHelper |
| 32 | 32 | # |
| 33 | 33 | # Returns boolean |
| 34 | 34 | def markup?(filename) |
| 35 | - filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole | |
| 36 | - .mediawiki .rst .asciidoc .pod)) | |
| 35 | + filename.end_with?(*%w(.textile .rdoc .org .creole | |
| 36 | + .mediawiki .rst .asciidoc .pod)) | |
| 37 | + end | |
| 38 | + | |
| 39 | + def gitlab_markdown?(filename) | |
| 40 | + filename.end_with?(*%w(.mdown .md .markdown)) | |
| 37 | 41 | end |
| 38 | 42 | end | ... | ... |
app/models/commit.rb
| ... | ... | @@ -82,20 +82,24 @@ class Commit |
| 82 | 82 | end |
| 83 | 83 | |
| 84 | 84 | def compare(project, from, to) |
| 85 | - first = project.commit(to.try(:strip)) | |
| 86 | - last = project.commit(from.try(:strip)) | |
| 87 | - | |
| 88 | 85 | result = { |
| 89 | 86 | commits: [], |
| 90 | 87 | diffs: [], |
| 91 | - commit: nil | |
| 88 | + commit: nil, | |
| 89 | + same: false | |
| 92 | 90 | } |
| 93 | 91 | |
| 92 | + return result unless from && to | |
| 93 | + | |
| 94 | + first = project.commit(to.try(:strip)) | |
| 95 | + last = project.commit(from.try(:strip)) | |
| 96 | + | |
| 94 | 97 | if first && last |
| 95 | 98 | commits = [first, last].sort_by(&:created_at) |
| 96 | 99 | younger = commits.first |
| 97 | 100 | older = commits.last |
| 98 | 101 | |
| 102 | + result[:same] = (younger.id == older.id) | |
| 99 | 103 | result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} |
| 100 | 104 | result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] |
| 101 | 105 | result[:commit] = Commit.new(older) | ... | ... |
app/models/event.rb
| ... | ... | @@ -132,6 +132,7 @@ class Event < ActiveRecord::Base |
| 132 | 132 | end |
| 133 | 133 | end |
| 134 | 134 | |
| 135 | + | |
| 135 | 136 | delegate :name, :email, to: :author, prefix: true, allow_nil: true |
| 136 | 137 | delegate :title, to: :issue, prefix: true, allow_nil: true |
| 137 | 138 | delegate :title, to: :merge_request, prefix: true, allow_nil: true | ... | ... |
app/models/user.rb
| ... | ... | @@ -6,8 +6,9 @@ class User < ActiveRecord::Base |
| 6 | 6 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable |
| 7 | 7 | |
| 8 | 8 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, |
| 9 | - :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, | |
| 10 | - :theme_id, :force_random_password, :extern_uid, :provider | |
| 9 | + :name, :skype, :linkedin, :twitter, :dark_scheme, | |
| 10 | + :theme_id, :force_random_password, :extern_uid, :provider, :as => [:default, :admin] | |
| 11 | + attr_accessible :projects_limit, :as => :admin | |
| 11 | 12 | |
| 12 | 13 | attr_accessor :force_random_password |
| 13 | 14 | ... | ... |
app/roles/repository.rb
| ... | ... | @@ -79,6 +79,14 @@ module Repository |
| 79 | 79 | @heads ||= repo.heads |
| 80 | 80 | end |
| 81 | 81 | |
| 82 | + def branches_names | |
| 83 | + heads.map(&:name) | |
| 84 | + end | |
| 85 | + | |
| 86 | + def ref_names | |
| 87 | + [branches_names + tags].flatten | |
| 88 | + end | |
| 89 | + | |
| 82 | 90 | def tree(fcommit, path = nil) |
| 83 | 91 | fcommit = commit if fcommit == :head |
| 84 | 92 | tree = fcommit.tree | ... | ... |
app/views/commits/compare.html.haml
| 1 | 1 | = render "head" |
| 2 | 2 | |
| 3 | -%h3 | |
| 3 | +%h3.page_title | |
| 4 | 4 | Compare View |
| 5 | 5 | %hr |
| 6 | 6 | |
| 7 | 7 | %div |
| 8 | - %p | |
| 8 | + %p.slead | |
| 9 | 9 | Fill input field with commit id like |
| 10 | - %code '4eedf23' | |
| 10 | + %code.label_branch 4eedf23 | |
| 11 | 11 | or branch/tag name like |
| 12 | - %code master | |
| 13 | - & press compare button for commits list, code diff. | |
| 12 | + %code.label_branch master | |
| 13 | + and press compare button for commits list, code diff. | |
| 14 | 14 | |
| 15 | 15 | %br |
| 16 | 16 | |
| ... | ... | @@ -19,22 +19,24 @@ |
| 19 | 19 | = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" |
| 20 | 20 | = "..." |
| 21 | 21 | = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" |
| 22 | + - if @refs_are_same | |
| 23 | + .alert | |
| 24 | + %span Refs are the same | |
| 22 | 25 | .actions |
| 23 | - = submit_tag "Compare", class: "btn primary" | |
| 26 | + = submit_tag "Compare", class: "btn primary wide commits-compare-btn" | |
| 24 | 27 | |
| 25 | - | |
| 26 | -- unless @commits.empty? | |
| 28 | +- if @commits.present? | |
| 27 | 29 | %div.ui-box |
| 28 | 30 | %h5.small Commits (#{@commits.count}) |
| 29 | 31 | %ul.unstyled= render @commits |
| 30 | 32 | |
| 31 | -- unless @diffs.empty? | |
| 32 | - %h4 Diff | |
| 33 | - = render "commits/diffs", diffs: @diffs | |
| 33 | + - unless @diffs.empty? | |
| 34 | + %h4 Diff | |
| 35 | + = render "commits/diffs", diffs: @diffs | |
| 34 | 36 | |
| 35 | 37 | :javascript |
| 36 | 38 | $(function() { |
| 37 | - var availableTags = #{@project.heads.map(&:name).to_json}; | |
| 39 | + var availableTags = #{@project.ref_names.to_json}; | |
| 38 | 40 | |
| 39 | 41 | $("#from").autocomplete({ |
| 40 | 42 | source: availableTags, |
| ... | ... | @@ -45,5 +47,7 @@ |
| 45 | 47 | source: availableTags, |
| 46 | 48 | minLength: 1 |
| 47 | 49 | }); |
| 50 | + | |
| 51 | + disableButtonIfEmptyField('#to', '.commits-compare-btn'); | |
| 48 | 52 | }); |
| 49 | 53 | ... | ... |
app/views/events/_commit.html.haml
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | %li.commit |
| 3 | 3 | %p |
| 4 | 4 | = link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" |
| 5 | - %strong.cdark= commit.author_name | |
| 5 | + %span= commit.author_name | |
| 6 | 6 | – |
| 7 | 7 | = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 |
| 8 | 8 | = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" | ... | ... |
app/views/events/_event.html.haml
| 1 | 1 | - if event.allowed? |
| 2 | - - if event.issue? | |
| 3 | - .event_feed | |
| 2 | + %div.event-item | |
| 3 | + - if event.issue? | |
| 4 | 4 | = render "events/event_issue", event: event |
| 5 | 5 | |
| 6 | - - elsif event.merge_request? | |
| 7 | - .event_feed | |
| 6 | + - elsif event.merge_request? | |
| 8 | 7 | = render "events/event_merge_request", event: event |
| 9 | 8 | |
| 10 | - - elsif event.push? | |
| 11 | - .event_feed | |
| 9 | + - elsif event.push? | |
| 12 | 10 | = render "events/event_push", event: event |
| 13 | 11 | |
| 14 | - - elsif event.membership_changed? | |
| 15 | - .event_feed | |
| 12 | + - elsif event.membership_changed? | |
| 16 | 13 | = render "events/event_membership_changed", event: event |
| 17 | 14 | |
| 15 | + %span.cgray.right | |
| 16 | + = time_ago_in_words(event.created_at) | |
| 17 | + ago. | |
| 18 | + .clearfix | ... | ... |
app/views/events/_event_issue.html.haml
| 1 | 1 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 2 | -%strong #{event.author_name} | |
| 3 | -%span.event_label{class: event.action_name}= event.action_name | |
| 4 | -issue | |
| 5 | -= link_to project_issue_path(event.project, event.issue) do | |
| 6 | - %strong= truncate event.issue_title | |
| 7 | -at | |
| 8 | -%strong= link_to event.project.name, event.project | |
| 9 | -%span.cgray | |
| 10 | - = time_ago_in_words(event.created_at) | |
| 11 | - ago. | |
| 2 | +.event-title | |
| 3 | + %strong.author_name #{event.author_name} | |
| 4 | + %span.event_label{class: event.action_name} #{event.action_name} issue | |
| 5 | + = link_to project_issue_path(event.project, event.issue) do | |
| 6 | + %strong= truncate event.issue_title | |
| 7 | + at | |
| 8 | + %strong= link_to event.project.name, event.project | ... | ... |
app/views/events/_event_last_push.html.haml
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | .event_lp |
| 3 | 3 | %div |
| 4 | 4 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 5 | - %span Your pushed to | |
| 5 | + %span You pushed to | |
| 6 | 6 | = event.ref_type |
| 7 | 7 | = link_to project_commits_path(event.project, ref: event.ref_name) do |
| 8 | 8 | %strong= truncate(event.ref_name, length: 28) | ... | ... |
app/views/events/_event_membership_changed.html.haml
| 1 | 1 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 2 | -%strong #{event.author_name} | |
| 3 | -%span.event_label{class: event.action_name}= event.action_name | |
| 4 | -project | |
| 5 | -%strong= link_to event.project_name, event.project | |
| 6 | -%span.cgray | |
| 7 | - = time_ago_in_words(event.created_at) | |
| 8 | - ago. | |
| 2 | +.event-title | |
| 3 | + %strong.author_name #{event.author_name} | |
| 4 | + %span.event_label{class: event.action_name} #{event.action_name} project | |
| 5 | + %strong= link_to event.project_name, event.project | |
| 6 | + %span.cgray | |
| 7 | + = time_ago_in_words(event.created_at) | |
| 8 | + ago. | |
| 9 | 9 | ... | ... |
app/views/events/_event_merge_request.html.haml
| 1 | 1 | - if event.action_name == "merged" |
| 2 | 2 | .event_icon= image_tag "event_mr_merged.png" |
| 3 | 3 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 4 | -%strong #{event.author_name} | |
| 5 | -%span.event_label{class: event.action_name}= event.action_name | |
| 6 | -merge request | |
| 7 | -= link_to project_merge_request_path(event.project, event.merge_request) do | |
| 8 | - %strong= truncate event.merge_request_title | |
| 9 | -at | |
| 10 | -%strong= link_to event.project.name, event.project | |
| 11 | -%span.cgray | |
| 12 | - = time_ago_in_words(event.created_at) | |
| 13 | - ago. | |
| 14 | -%br | |
| 15 | -%span= event.merge_request.source_branch | |
| 16 | -→ | |
| 17 | -%span= event.merge_request.target_branch | |
| 4 | +.event-title | |
| 5 | + %strong.author_name #{event.author_name} | |
| 6 | + %span.event_label{class: event.action_name} #{event.action_name} merge request | |
| 7 | + = link_to project_merge_request_path(event.project, event.merge_request) do | |
| 8 | + %strong= truncate event.merge_request_title | |
| 9 | + at | |
| 10 | + %strong= link_to event.project.name, event.project | |
| 11 | +.event-body | |
| 12 | + .event-info | |
| 13 | + %span= event.merge_request.source_branch | |
| 14 | + → | |
| 15 | + %span= event.merge_request.target_branch | |
| 18 | 16 | ... | ... |
app/views/events/_event_push.html.haml
| 1 | 1 | %div |
| 2 | 2 | .event_icon= image_tag "event_push.png" |
| 3 | 3 | = image_tag gravatar_icon(event.author_email), class: "avatar" |
| 4 | - %strong #{event.author_name} | |
| 5 | - %span.event_label.pushed= event.push_action_name | |
| 6 | - = event.ref_type | |
| 7 | - = link_to project_commits_path(event.project, ref: event.ref_name) do | |
| 8 | - %strong= event.ref_name | |
| 9 | - at | |
| 10 | - %strong= link_to event.project.name, event.project | |
| 11 | - %span.cgray | |
| 12 | - = time_ago_in_words(event.created_at) | |
| 13 | - ago. | |
| 4 | + | |
| 5 | + .event-title | |
| 6 | + %strong.author_name #{event.author_name} | |
| 7 | + %span.event_label.pushed #{event.push_action_name} #{event.ref_type} | |
| 8 | + = link_to project_commits_path(event.project, ref: event.ref_name) do | |
| 9 | + %strong= event.ref_name | |
| 10 | + at | |
| 11 | + %strong= link_to event.project.name, event.project | |
| 14 | 12 | |
| 15 | 13 | - if event.push_with_commits? |
| 16 | - - if event.commits_count > 1 | |
| 17 | - = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do | |
| 18 | - %strong #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} | |
| 19 | 14 | - project = event.project |
| 20 | - %ul.unstyled.event_commits | |
| 21 | - - if event.commits_count > 3 | |
| 22 | - - event.commits[0...2].each do |commit| | |
| 23 | - = render "events/commit", commit: commit, project: project | |
| 24 | - %li | |
| 25 | - %br | |
| 26 | - \... and #{event.commits_count - 2} more commits | |
| 27 | - - else | |
| 28 | - - event.commits.each do |commit| | |
| 15 | + .event-body | |
| 16 | + %ul.unstyled.event_commits | |
| 17 | + - few_commits = event.commits[0...2] | |
| 18 | + - few_commits.each do |commit| | |
| 29 | 19 | = render "events/commit", commit: commit, project: project |
| 30 | 20 | |
| 21 | + %li.commits-stat | |
| 22 | + - if event.commits_count > 2 | |
| 23 | + %span ... and #{event.commits_count - 2} more commits. | |
| 24 | + = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do | |
| 25 | + %strong Compare → #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} | |
| 26 | + .clearfix | ... | ... |
app/views/help/api.html.haml
| 1 | -%h3 API | |
| 1 | +%h3.page_title API | |
| 2 | 2 | .back_link |
| 3 | 3 | = link_to help_path do |
| 4 | 4 | ← to index |
| 5 | -%hr | |
| 5 | +%br | |
| 6 | 6 | |
| 7 | -%ol | |
| 7 | +%ul.nav.nav-tabs.log-tabs | |
| 8 | + %li.active | |
| 9 | + = link_to "README", "#README", 'data-toggle' => 'tab' | |
| 8 | 10 | %li |
| 9 | - %a{href: "#README"} README | |
| 11 | + = link_to "Projects", "#projects", 'data-toggle' => 'tab' | |
| 10 | 12 | %li |
| 11 | - %a{href: "#projects"} Projects | |
| 13 | + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' | |
| 12 | 14 | %li |
| 13 | - %a{href: "#snippets"} Snippets | |
| 15 | + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' | |
| 14 | 16 | %li |
| 15 | - %a{href: "#users"} Users | |
| 17 | + = link_to "Users", "#users", 'data-toggle' => 'tab' | |
| 16 | 18 | %li |
| 17 | - %a{href: "#issues"} Issues | |
| 19 | + = link_to "Session", "#session", 'data-toggle' => 'tab' | |
| 18 | 20 | %li |
| 19 | - %a{href: "#milestones"} Milestones | |
| 20 | - | |
| 21 | -.file_holder#README | |
| 22 | - .file_title | |
| 23 | - %i.icon-file | |
| 24 | - README | |
| 25 | - .file_content.wiki | |
| 26 | - = preserve do | |
| 27 | - = markdown File.read(Rails.root.join("doc", "api", "README.md")) | |
| 28 | - | |
| 29 | -%br | |
| 30 | - | |
| 31 | -.file_holder#projects | |
| 32 | - .file_title | |
| 33 | - %i.icon-file | |
| 34 | - Projects | |
| 35 | - .file_content.wiki | |
| 36 | - = preserve do | |
| 37 | - = markdown File.read(Rails.root.join("doc", "api", "projects.md")) | |
| 21 | + = link_to "Issues", "#issues", 'data-toggle' => 'tab' | |
| 22 | + %li | |
| 23 | + = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' | |
| 38 | 24 | |
| 39 | -%br | |
| 25 | +.tab-content | |
| 26 | + .tab-pane.active#README | |
| 27 | + .file_holder | |
| 28 | + .file_title | |
| 29 | + %i.icon-file | |
| 30 | + README | |
| 31 | + .file_content.wiki | |
| 32 | + = preserve do | |
| 33 | + = markdown File.read(Rails.root.join("doc", "api", "README.md")) | |
| 40 | 34 | |
| 41 | -.file_holder#snippets | |
| 42 | - .file_title | |
| 43 | - %i.icon-file | |
| 44 | - Projects Snippets | |
| 45 | - .file_content.wiki | |
| 46 | - = preserve do | |
| 47 | - = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) | |
| 35 | + .tab-pane#projects | |
| 36 | + .file_holder | |
| 37 | + .file_title | |
| 38 | + %i.icon-file | |
| 39 | + Projects | |
| 40 | + .file_content.wiki | |
| 41 | + = preserve do | |
| 42 | + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) | |
| 48 | 43 | |
| 49 | -%br | |
| 44 | + .tab-pane#snippets | |
| 45 | + .file_holder | |
| 46 | + .file_title | |
| 47 | + %i.icon-file | |
| 48 | + Projects Snippets | |
| 49 | + .file_content.wiki | |
| 50 | + = preserve do | |
| 51 | + = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) | |
| 50 | 52 | |
| 51 | -.file_holder#users | |
| 52 | - .file_title | |
| 53 | - %i.icon-file | |
| 54 | - Users | |
| 55 | - .file_content.wiki | |
| 56 | - = preserve do | |
| 57 | - = markdown File.read(Rails.root.join("doc", "api", "users.md")) | |
| 53 | + .tab-pane#repositories | |
| 54 | + .file_holder | |
| 55 | + .file_title | |
| 56 | + %i.icon-file | |
| 57 | + Projects | |
| 58 | + .file_content.wiki | |
| 59 | + = preserve do | |
| 60 | + = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) | |
| 58 | 61 | |
| 59 | -%br | |
| 62 | + .tab-pane#users | |
| 63 | + .file_holder | |
| 64 | + .file_title | |
| 65 | + %i.icon-file | |
| 66 | + Users | |
| 67 | + .file_content.wiki | |
| 68 | + = preserve do | |
| 69 | + = markdown File.read(Rails.root.join("doc", "api", "users.md")) | |
| 60 | 70 | |
| 61 | -.file_holder#issues | |
| 62 | - .file_title | |
| 63 | - %i.icon-file | |
| 64 | - Issues | |
| 65 | - .file_content.wiki | |
| 66 | - = preserve do | |
| 67 | - = markdown File.read(Rails.root.join("doc", "api", "issues.md")) | |
| 71 | + .tab-pane#session | |
| 72 | + .file_holder | |
| 73 | + .file_title | |
| 74 | + %i.icon-file | |
| 75 | + Session | |
| 76 | + .file_content.wiki | |
| 77 | + = preserve do | |
| 78 | + = markdown File.read(Rails.root.join("doc", "api", "session.md")) | |
| 68 | 79 | |
| 69 | -%br | |
| 80 | + .tab-pane#issues | |
| 81 | + .file_holder | |
| 82 | + .file_title | |
| 83 | + %i.icon-file | |
| 84 | + Issues | |
| 85 | + .file_content.wiki | |
| 86 | + = preserve do | |
| 87 | + = markdown File.read(Rails.root.join("doc", "api", "issues.md")) | |
| 70 | 88 | |
| 71 | -.file_holder#milestones | |
| 72 | - .file_title | |
| 73 | - %i.icon-file | |
| 74 | - Milestones | |
| 75 | - .file_content.wiki | |
| 76 | - = preserve do | |
| 77 | - = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) | |
| 89 | + .tab-pane#milestones | |
| 90 | + .file_holder | |
| 91 | + .file_title | |
| 92 | + %i.icon-file | |
| 93 | + Milestones | |
| 94 | + .file_content.wiki | |
| 95 | + = preserve do | |
| 96 | + = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) | ... | ... |
app/views/notes/_common_form.html.haml
| ... | ... | @@ -37,3 +37,14 @@ |
| 37 | 37 | = f.file_field :attachment, class: "input-file" |
| 38 | 38 | %span.hint Any file less than 10 MB |
| 39 | 39 | |
| 40 | +:javascript | |
| 41 | + $(function(){ | |
| 42 | + var names = #{@project.users.pluck(:name)}, emoji = ['+1', '-1']; | |
| 43 | + var emoji = $.map(emoji, function(value, i) {return {key:value + ':', name:value}}); | |
| 44 | + $('#note_note'). | |
| 45 | + atWho('@', { data: names }). | |
| 46 | + atWho(':', { | |
| 47 | + data: emoji, | |
| 48 | + tpl: "<li data-value='${key}'>${name} #{escape_javascript image_tag('emoji/${name}.png', :size => '20x20')}</li>" | |
| 49 | + }); | |
| 50 | + }); | ... | ... |
app/views/refs/_tree.html.haml
| ... | ... | @@ -43,7 +43,11 @@ |
| 43 | 43 | %i.icon-file |
| 44 | 44 | = content.name |
| 45 | 45 | .file_content.wiki |
| 46 | - = raw GitHub::Markup.render(content.name, content.data) | |
| 46 | + - if gitlab_markdown?(content.name) | |
| 47 | + = preserve do | |
| 48 | + = markdown(content.data) | |
| 49 | + - else | |
| 50 | + = raw GitHub::Markup.render(content.name, content.data) | |
| 47 | 51 | |
| 48 | 52 | :javascript |
| 49 | 53 | $(function(){ | ... | ... |
app/views/refs/_tree_file.html.haml
| ... | ... | @@ -9,7 +9,11 @@ |
| 9 | 9 | = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" |
| 10 | 10 | = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" |
| 11 | 11 | - if file.text? |
| 12 | - - if markup?(name) | |
| 12 | + - if gitlab_markdown?(name) | |
| 13 | + .file_content.wiki | |
| 14 | + = preserve do | |
| 15 | + = markdown(file.data) | |
| 16 | + - elsif markup?(name) | |
| 13 | 17 | .file_content.wiki |
| 14 | 18 | = raw GitHub::Markup.render(name, file.data) |
| 15 | 19 | - else | ... | ... |
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +# Custom Redis configuration | |
| 2 | +rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' | |
| 3 | +rails_env = ENV['RAILS_ENV'] || 'development' | |
| 4 | +config_file = File.join(rails_root, 'config', 'resque.yml') | |
| 5 | + | |
| 6 | +if File.exists?(config_file) | |
| 7 | + resque_config = YAML.load_file(config_file) | |
| 8 | + Resque.redis = resque_config[rails_env] | |
| 9 | +end | |
| 10 | + | |
| 11 | +# Queues | |
| 12 | +Resque.watch_queue(PostReceive.instance_variable_get("@queue")) | |
| 13 | + | |
| 14 | +# Authentication | |
| 15 | +require 'resque/server' | |
| 16 | +class Authentication | |
| 17 | + def initialize(app) | |
| 18 | + @app = app | |
| 19 | + end | |
| 20 | + | |
| 21 | + def call(env) | |
| 22 | + account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) | |
| 23 | + raise "Access denied" if !account.admin? | |
| 24 | + @app.call(env) | |
| 25 | + end | |
| 26 | +end | |
| 27 | + | |
| 28 | +Resque::Server.use Authentication | |
| 29 | + | |
| 30 | +# Mailer | |
| 31 | +Resque::Mailer.excluded_environments = [] | ... | ... |
config/initializers/4_resque_queues.rb
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -Resque.watch_queue(PostReceive.instance_variable_get("@queue")) |
config/initializers/resque.rb
| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | -rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' | |
| 2 | -rails_env = ENV['RAILS_ENV'] || 'development' | |
| 3 | -config_file = File.join(rails_root, 'config', 'resque.yml') | |
| 4 | - | |
| 5 | -if File.exists?(config_file) | |
| 6 | - resque_config = YAML.load_file(config_file) | |
| 7 | - Resque.redis = resque_config[rails_env] | |
| 8 | -end |
config/initializers/resque_authentication.rb
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -require 'resque/server' | |
| 2 | -class Authentication | |
| 3 | - def initialize(app) | |
| 4 | - @app = app | |
| 5 | - end | |
| 6 | - | |
| 7 | - def call(env) | |
| 8 | - account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) | |
| 9 | - raise "Access denied" if !account.admin? | |
| 10 | - @app.call(env) | |
| 11 | - end | |
| 12 | -end | |
| 13 | - | |
| 14 | -Resque::Server.use Authentication | |
| 15 | 0 | \ No newline at end of file |
config/initializers/resque_mailer.rb
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -Resque::Mailer.excluded_environments = [] |
config/unicorn.rb.example
doc/api/README.md
| ... | ... | @@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: |
| 30 | 30 | ## Contents |
| 31 | 31 | |
| 32 | 32 | + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) |
| 33 | ++ [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) | |
| 33 | 34 | + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) |
| 34 | 35 | + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) |
| 36 | ++ [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) | |
| 35 | 37 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) |
| 36 | 38 | + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) |
| 37 | -+ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md) | ... | ... |
doc/api/keys.md
| ... | ... | @@ -1,79 +0,0 @@ |
| 1 | -## List keys | |
| 2 | - | |
| 3 | -Get a list of currently authenticated user's keys. | |
| 4 | - | |
| 5 | -``` | |
| 6 | -GET /keys | |
| 7 | -``` | |
| 8 | - | |
| 9 | -```json | |
| 10 | -[ | |
| 11 | - { | |
| 12 | - "id": 1, | |
| 13 | - "title" : "Public key" | |
| 14 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 15 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 16 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | |
| 17 | - }, | |
| 18 | - { | |
| 19 | - "id": 3, | |
| 20 | - "title" : "Another Public key" | |
| 21 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 22 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 23 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 24 | - } | |
| 25 | -] | |
| 26 | -``` | |
| 27 | - | |
| 28 | -## Single key | |
| 29 | - | |
| 30 | -Get a single key. | |
| 31 | - | |
| 32 | -``` | |
| 33 | -GET /keys/:id | |
| 34 | -``` | |
| 35 | - | |
| 36 | -Parameters: | |
| 37 | - | |
| 38 | -+ `id` (required) - The ID of a key | |
| 39 | - | |
| 40 | -```json | |
| 41 | -{ | |
| 42 | - "id": 1, | |
| 43 | - "title" : "Public key" | |
| 44 | - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 45 | - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 46 | - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 47 | -} | |
| 48 | -``` | |
| 49 | -## Add key | |
| 50 | - | |
| 51 | -Create new key owned by currently authenticated user | |
| 52 | - | |
| 53 | -``` | |
| 54 | -POST /keys | |
| 55 | -``` | |
| 56 | - | |
| 57 | -Parameters: | |
| 58 | - | |
| 59 | -+ `title` (required) - new SSH Key's title | |
| 60 | -+ `key` (required) - new SSH key | |
| 61 | - | |
| 62 | -Will return created key with status `201 Created` on success, or `404 Not | |
| 63 | -found` on fail. | |
| 64 | - | |
| 65 | -## Delete key | |
| 66 | - | |
| 67 | -Delete key owned by currently authenticated user | |
| 68 | - | |
| 69 | -``` | |
| 70 | -DELETE /keys/:id | |
| 71 | -``` | |
| 72 | - | |
| 73 | -Parameters: | |
| 74 | - | |
| 75 | -+ `id` (required) - key ID | |
| 76 | - | |
| 77 | -Will return `200 OK` on success, or `404 Not Found` on fail. | |
| 78 | - | |
| 79 | - |
doc/api/projects.md
| ... | ... | @@ -102,7 +102,7 @@ Parameters: |
| 102 | 102 | + `name` (required) - new project name |
| 103 | 103 | + `code` (optional) - new project code, uses project name if not set |
| 104 | 104 | + `path` (optional) - new project path, uses project name if not set |
| 105 | -+ `description (optional) - short project description | |
| 105 | ++ `description` (optional) - short project description | |
| 106 | 106 | + `default_branch` (optional) - 'master' by default |
| 107 | 107 | + `issues_enabled` (optional) - enabled by default |
| 108 | 108 | + `wall_enabled` (optional) - enabled by default |
| ... | ... | @@ -112,66 +112,89 @@ Parameters: |
| 112 | 112 | Will return created project with status `201 Created` on success, or `404 Not |
| 113 | 113 | found` on fail. |
| 114 | 114 | |
| 115 | -## Get project users | |
| 115 | +## List project team members | |
| 116 | 116 | |
| 117 | -Get users and access roles for existing project | |
| 117 | +Get a list of project team members. | |
| 118 | 118 | |
| 119 | 119 | ``` |
| 120 | -GET /projects/:id/users | |
| 120 | +GET /projects/:id/members | |
| 121 | 121 | ``` |
| 122 | 122 | |
| 123 | 123 | Parameters: |
| 124 | 124 | |
| 125 | 125 | + `id` (required) - The ID or code name of a project |
| 126 | 126 | |
| 127 | -Will return users and their access roles with status `200 OK` on success, or `404 Not found` on fail. | |
| 127 | +## Get project team member | |
| 128 | 128 | |
| 129 | -## Add project users | |
| 129 | +Get a project team member. | |
| 130 | 130 | |
| 131 | -Add users to exiting project | |
| 131 | +``` | |
| 132 | +GET /projects/:id/members/:user_id | |
| 133 | +``` | |
| 134 | + | |
| 135 | +Parameters: | |
| 136 | + | |
| 137 | ++ `id` (required) - The ID or code name of a project | |
| 138 | ++ `user_id` (required) - The ID of a user | |
| 132 | 139 | |
| 140 | +```json | |
| 141 | +{ | |
| 142 | + | |
| 143 | + "id": 1, | |
| 144 | + "email": "john@example.com", | |
| 145 | + "name": "John Smith", | |
| 146 | + "blocked": false, | |
| 147 | + "created_at": "2012-05-23T08:00:58Z", | |
| 148 | + "access_level": 40 | |
| 149 | +} | |
| 133 | 150 | ``` |
| 134 | -POST /projects/:id/users | |
| 151 | + | |
| 152 | +## Add project team member | |
| 153 | + | |
| 154 | +Add a user to a project team. | |
| 155 | + | |
| 156 | +``` | |
| 157 | +POST /projects/:id/members | |
| 135 | 158 | ``` |
| 136 | 159 | |
| 137 | 160 | Parameters: |
| 138 | 161 | |
| 139 | 162 | + `id` (required) - The ID or code name of a project |
| 140 | -+ `user_ids` (required) - The ID list of users to add | |
| 141 | -+ `project_access` (required) - Project access level | |
| 163 | ++ `user_id` (required) - The ID of a user to add | |
| 164 | ++ `access_level` (required) - Project access level | |
| 142 | 165 | |
| 143 | 166 | Will return status `201 Created` on success, or `404 Not found` on fail. |
| 144 | 167 | |
| 145 | -## Update project users access level | |
| 168 | +## Edit project team member | |
| 146 | 169 | |
| 147 | -Update existing users to specified access level | |
| 170 | +Update project team member to specified access level. | |
| 148 | 171 | |
| 149 | 172 | ``` |
| 150 | -PUT /projects/:id/users | |
| 173 | +PUT /projects/:id/members/:user_id | |
| 151 | 174 | ``` |
| 152 | 175 | |
| 153 | 176 | Parameters: |
| 154 | 177 | |
| 155 | 178 | + `id` (required) - The ID or code name of a project |
| 156 | -+ `user_ids` (required) - The ID list of users to add | |
| 157 | -+ `project_access` (required) - Project access level | |
| 179 | ++ `user_id` (required) - The ID of a team member | |
| 180 | ++ `access_level` (required) - Project access level | |
| 158 | 181 | |
| 159 | 182 | Will return status `200 OK` on success, or `404 Not found` on fail. |
| 160 | 183 | |
| 161 | -## Delete project users | |
| 184 | +## Remove project team member | |
| 162 | 185 | |
| 163 | -Delete users from exiting project | |
| 186 | +Removes user from project team. | |
| 164 | 187 | |
| 165 | 188 | ``` |
| 166 | -DELETE /projects/:id/users | |
| 189 | +DELETE /projects/:id/members/:user_id | |
| 167 | 190 | ``` |
| 168 | 191 | |
| 169 | 192 | Parameters: |
| 170 | 193 | |
| 171 | 194 | + `id` (required) - The ID or code name of a project |
| 172 | -+ `user_ids` (required) - The ID list of users to add | |
| 195 | ++ `user_id` (required) - The ID of a team member | |
| 173 | 196 | |
| 174 | -Will return status `200 OK` on success, or `404 Not found` on fail. | |
| 197 | +Status code `200` will be returned on success. | |
| 175 | 198 | |
| 176 | 199 | ## Get project hooks |
| 177 | 200 | |
| ... | ... | @@ -216,135 +239,3 @@ Parameters: |
| 216 | 239 | + `hook_id` (required) - The ID of hook to delete |
| 217 | 240 | |
| 218 | 241 | Will return status `200 OK` on success, or `404 Not found` on fail. |
| 219 | - | |
| 220 | -## Project repository branches | |
| 221 | - | |
| 222 | -Get a list of repository branches from a project, sorted by name alphabetically. | |
| 223 | - | |
| 224 | -``` | |
| 225 | -GET /projects/:id/repository/branches | |
| 226 | -``` | |
| 227 | - | |
| 228 | -Parameters: | |
| 229 | - | |
| 230 | -+ `id` (required) - The ID or code name of a project | |
| 231 | - | |
| 232 | -```json | |
| 233 | -[ | |
| 234 | - { | |
| 235 | - "name": "master", | |
| 236 | - "commit": { | |
| 237 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | |
| 238 | - "parents": [ | |
| 239 | - { | |
| 240 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | |
| 241 | - } | |
| 242 | - ], | |
| 243 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | |
| 244 | - "message": "add projects API", | |
| 245 | - "author": { | |
| 246 | - "name": "John Smith", | |
| 247 | - "email": "john@example.com" | |
| 248 | - }, | |
| 249 | - "committer": { | |
| 250 | - "name": "John Smith", | |
| 251 | - "email": "john@example.com" | |
| 252 | - }, | |
| 253 | - "authored_date": "2012-06-27T05:51:39-07:00", | |
| 254 | - "committed_date": "2012-06-28T03:44:20-07:00" | |
| 255 | - } | |
| 256 | - } | |
| 257 | -] | |
| 258 | -``` | |
| 259 | - | |
| 260 | -Get a single project repository branch. | |
| 261 | - | |
| 262 | -``` | |
| 263 | -GET /projects/:id/repository/branches/:branch | |
| 264 | -``` | |
| 265 | - | |
| 266 | -Parameters: | |
| 267 | - | |
| 268 | -+ `id` (required) - The ID or code name of a project | |
| 269 | -+ `branch` (required) - The name of the branch | |
| 270 | - | |
| 271 | -```json | |
| 272 | -{ | |
| 273 | - "name": "master", | |
| 274 | - "commit": { | |
| 275 | - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | |
| 276 | - "parents": [ | |
| 277 | - { | |
| 278 | - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | |
| 279 | - } | |
| 280 | - ], | |
| 281 | - "tree": "46e82de44b1061621357f24c05515327f2795a95", | |
| 282 | - "message": "add projects API", | |
| 283 | - "author": { | |
| 284 | - "name": "John Smith", | |
| 285 | - "email": "john@example.com" | |
| 286 | - }, | |
| 287 | - "committer": { | |
| 288 | - "name": "John Smith", | |
| 289 | - "email": "john@example.com" | |
| 290 | - }, | |
| 291 | - "authored_date": "2012-06-27T05:51:39-07:00", | |
| 292 | - "committed_date": "2012-06-28T03:44:20-07:00" | |
| 293 | - } | |
| 294 | -} | |
| 295 | -``` | |
| 296 | - | |
| 297 | -## Project repository tags | |
| 298 | - | |
| 299 | -Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | |
| 300 | - | |
| 301 | -``` | |
| 302 | -GET /projects/:id/repository/tags | |
| 303 | -``` | |
| 304 | - | |
| 305 | -Parameters: | |
| 306 | - | |
| 307 | -+ `id` (required) - The ID or code name of a project | |
| 308 | - | |
| 309 | -```json | |
| 310 | -[ | |
| 311 | - { | |
| 312 | - "name": "v1.0.0", | |
| 313 | - "commit": { | |
| 314 | - "id": "2695effb5807a22ff3d138d593fd856244e155e7", | |
| 315 | - "parents": [ | |
| 316 | - | |
| 317 | - ], | |
| 318 | - "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", | |
| 319 | - "message": "Initial commit", | |
| 320 | - "author": { | |
| 321 | - "name": "John Smith", | |
| 322 | - "email": "john@example.com" | |
| 323 | - }, | |
| 324 | - "committer": { | |
| 325 | - "name": "Jack Smith", | |
| 326 | - "email": "jack@example.com" | |
| 327 | - }, | |
| 328 | - "authored_date": "2012-05-28T04:42:42-07:00", | |
| 329 | - "committed_date": "2012-05-28T04:42:42-07:00" | |
| 330 | - } | |
| 331 | - } | |
| 332 | -] | |
| 333 | -``` | |
| 334 | - | |
| 335 | -## Raw blob content | |
| 336 | - | |
| 337 | -Get the raw file contents for a file. | |
| 338 | - | |
| 339 | -``` | |
| 340 | -GET /projects/:id/repository/commits/:sha/blob | |
| 341 | -``` | |
| 342 | - | |
| 343 | -Parameters: | |
| 344 | - | |
| 345 | -+ `id` (required) - The ID or code name of a project | |
| 346 | -+ `sha` (required) - The commit or branch name | |
| 347 | -+ `filepath` (required) - The path the file | |
| 348 | - | |
| 349 | -Will return the raw file contents. | |
| 350 | - | ... | ... |
| ... | ... | @@ -0,0 +1,166 @@ |
| 1 | +## Project repository branches | |
| 2 | + | |
| 3 | +Get a list of repository branches from a project, sorted by name alphabetically. | |
| 4 | + | |
| 5 | +``` | |
| 6 | +GET /projects/:id/repository/branches | |
| 7 | +``` | |
| 8 | + | |
| 9 | +Parameters: | |
| 10 | + | |
| 11 | ++ `id` (required) - The ID or code name of a project | |
| 12 | + | |
| 13 | +```json | |
| 14 | +[ | |
| 15 | + { | |
| 16 | + "name": "master", | |
| 17 | + "commit": { | |
| 18 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | |
| 19 | + "parents": [ | |
| 20 | + { | |
| 21 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | |
| 22 | + } | |
| 23 | + ], | |
| 24 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | |
| 25 | + "message": "add projects API", | |
| 26 | + "author": { | |
| 27 | + "name": "John Smith", | |
| 28 | + "email": "john@example.com" | |
| 29 | + }, | |
| 30 | + "committer": { | |
| 31 | + "name": "John Smith", | |
| 32 | + "email": "john@example.com" | |
| 33 | + }, | |
| 34 | + "authored_date": "2012-06-27T05:51:39-07:00", | |
| 35 | + "committed_date": "2012-06-28T03:44:20-07:00" | |
| 36 | + } | |
| 37 | + } | |
| 38 | +] | |
| 39 | +``` | |
| 40 | + | |
| 41 | +## Project repository branch | |
| 42 | + | |
| 43 | +Get a single project repository branch. | |
| 44 | + | |
| 45 | +``` | |
| 46 | +GET /projects/:id/repository/branches/:branch | |
| 47 | +``` | |
| 48 | + | |
| 49 | +Parameters: | |
| 50 | + | |
| 51 | ++ `id` (required) - The ID or code name of a project | |
| 52 | ++ `branch` (required) - The name of the branch | |
| 53 | + | |
| 54 | +```json | |
| 55 | +{ | |
| 56 | + "name": "master", | |
| 57 | + "commit": { | |
| 58 | + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", | |
| 59 | + "parents": [ | |
| 60 | + { | |
| 61 | + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" | |
| 62 | + } | |
| 63 | + ], | |
| 64 | + "tree": "46e82de44b1061621357f24c05515327f2795a95", | |
| 65 | + "message": "add projects API", | |
| 66 | + "author": { | |
| 67 | + "name": "John Smith", | |
| 68 | + "email": "john@example.com" | |
| 69 | + }, | |
| 70 | + "committer": { | |
| 71 | + "name": "John Smith", | |
| 72 | + "email": "john@example.com" | |
| 73 | + }, | |
| 74 | + "authored_date": "2012-06-27T05:51:39-07:00", | |
| 75 | + "committed_date": "2012-06-28T03:44:20-07:00" | |
| 76 | + } | |
| 77 | +} | |
| 78 | +``` | |
| 79 | + | |
| 80 | +## Project repository tags | |
| 81 | + | |
| 82 | +Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | |
| 83 | + | |
| 84 | +``` | |
| 85 | +GET /projects/:id/repository/tags | |
| 86 | +``` | |
| 87 | + | |
| 88 | +Parameters: | |
| 89 | + | |
| 90 | ++ `id` (required) - The ID or code name of a project | |
| 91 | + | |
| 92 | +```json | |
| 93 | +[ | |
| 94 | + { | |
| 95 | + "name": "v1.0.0", | |
| 96 | + "commit": { | |
| 97 | + "id": "2695effb5807a22ff3d138d593fd856244e155e7", | |
| 98 | + "parents": [ | |
| 99 | + | |
| 100 | + ], | |
| 101 | + "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", | |
| 102 | + "message": "Initial commit", | |
| 103 | + "author": { | |
| 104 | + "name": "John Smith", | |
| 105 | + "email": "john@example.com" | |
| 106 | + }, | |
| 107 | + "committer": { | |
| 108 | + "name": "Jack Smith", | |
| 109 | + "email": "jack@example.com" | |
| 110 | + }, | |
| 111 | + "authored_date": "2012-05-28T04:42:42-07:00", | |
| 112 | + "committed_date": "2012-05-28T04:42:42-07:00" | |
| 113 | + } | |
| 114 | + } | |
| 115 | +] | |
| 116 | +``` | |
| 117 | + | |
| 118 | +## Project repository commits | |
| 119 | + | |
| 120 | +Get a list of repository commits in a project. | |
| 121 | + | |
| 122 | +``` | |
| 123 | +GET /projects/:id/repository/commits | |
| 124 | +``` | |
| 125 | + | |
| 126 | +Parameters: | |
| 127 | + | |
| 128 | ++ `id` (required) - The ID or code name of a project | |
| 129 | ++ `ref_name` (optional) - The name of a repository branch or tag | |
| 130 | + | |
| 131 | +```json | |
| 132 | +[ | |
| 133 | + { | |
| 134 | + "id": "ed899a2f4b50b4370feeea94676502b42383c746", | |
| 135 | + "short_id": "ed899a2f4b5", | |
| 136 | + "title": "Replace sanitize with escape once", | |
| 137 | + "author_name": "Dmitriy Zaporozhets", | |
| 138 | + "author_email": "dzaporozhets@sphereconsultinginc.com", | |
| 139 | + "created_at": "2012-09-20T11:50:22+03:00" | |
| 140 | + }, | |
| 141 | + { | |
| 142 | + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", | |
| 143 | + "short_id": "6104942438c", | |
| 144 | + "title": "Sanitize for network graph", | |
| 145 | + "author_name": "randx", | |
| 146 | + "author_email": "dmitriy.zaporozhets@gmail.com", | |
| 147 | + "created_at": "2012-09-20T09:06:12+03:00" | |
| 148 | + } | |
| 149 | +] | |
| 150 | +``` | |
| 151 | + | |
| 152 | +## Raw blob content | |
| 153 | + | |
| 154 | +Get the raw file contents for a file. | |
| 155 | + | |
| 156 | +``` | |
| 157 | +GET /projects/:id/repository/commits/:sha/blob | |
| 158 | +``` | |
| 159 | + | |
| 160 | +Parameters: | |
| 161 | + | |
| 162 | ++ `id` (required) - The ID or code name of a project | |
| 163 | ++ `sha` (required) - The commit or branch name | |
| 164 | ++ `filepath` (required) - The path the file | |
| 165 | + | |
| 166 | +Will return the raw file contents. | ... | ... |
| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | +Login to get private token | |
| 2 | + | |
| 3 | +``` | |
| 4 | +POST /session | |
| 5 | +``` | |
| 6 | + | |
| 7 | +Parameters: | |
| 8 | + | |
| 9 | ++ `email` (required) - The email of user | |
| 10 | ++ `password` (required) - Valid password | |
| 11 | + | |
| 12 | + | |
| 13 | +```json | |
| 14 | +{ | |
| 15 | + "id": 1, | |
| 16 | + "email": "john@example.com", | |
| 17 | + "name": "John Smith", | |
| 18 | + "private_token": "dd34asd13as", | |
| 19 | + "created_at": "2012-05-23T08:00:58Z", | |
| 20 | + "blocked": true | |
| 21 | +} | |
| 22 | +``` | ... | ... |
doc/api/users.md
| ... | ... | @@ -88,3 +88,81 @@ GET /user |
| 88 | 88 | "theme_id": 1 |
| 89 | 89 | } |
| 90 | 90 | ``` |
| 91 | + | |
| 92 | +## List SSH keys | |
| 93 | + | |
| 94 | +Get a list of currently authenticated user's SSH keys. | |
| 95 | + | |
| 96 | +``` | |
| 97 | +GET /user/keys | |
| 98 | +``` | |
| 99 | + | |
| 100 | +```json | |
| 101 | +[ | |
| 102 | + { | |
| 103 | + "id": 1, | |
| 104 | + "title" : "Public key" | |
| 105 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 106 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 107 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | |
| 108 | + }, | |
| 109 | + { | |
| 110 | + "id": 3, | |
| 111 | + "title" : "Another Public key" | |
| 112 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 113 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 114 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 115 | + } | |
| 116 | +] | |
| 117 | +``` | |
| 118 | + | |
| 119 | +## Single SSH key | |
| 120 | + | |
| 121 | +Get a single key. | |
| 122 | + | |
| 123 | +``` | |
| 124 | +GET /user/keys/:id | |
| 125 | +``` | |
| 126 | + | |
| 127 | +Parameters: | |
| 128 | + | |
| 129 | ++ `id` (required) - The ID of an SSH key | |
| 130 | + | |
| 131 | +```json | |
| 132 | +{ | |
| 133 | + "id": 1, | |
| 134 | + "title" : "Public key" | |
| 135 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | |
| 136 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | |
| 137 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | |
| 138 | +} | |
| 139 | +``` | |
| 140 | +## Add SSH key | |
| 141 | + | |
| 142 | +Create new key owned by currently authenticated user | |
| 143 | + | |
| 144 | +``` | |
| 145 | +POST /user/keys | |
| 146 | +``` | |
| 147 | + | |
| 148 | +Parameters: | |
| 149 | + | |
| 150 | ++ `title` (required) - new SSH Key's title | |
| 151 | ++ `key` (required) - new SSH key | |
| 152 | + | |
| 153 | +Will return created key with status `201 Created` on success, or `404 Not | |
| 154 | +found` on fail. | |
| 155 | + | |
| 156 | +## Delete SSH key | |
| 157 | + | |
| 158 | +Delete key owned by currently authenticated user | |
| 159 | + | |
| 160 | +``` | |
| 161 | +DELETE /user/keys/:id | |
| 162 | +``` | |
| 163 | + | |
| 164 | +Parameters: | |
| 165 | + | |
| 166 | ++ `id` (required) - SSH key ID | |
| 167 | + | |
| 168 | +Will return `200 OK` on success, or `404 Not Found` on fail. | ... | ... |
features/steps/dashboard/dashboard.rb
| ... | ... | @@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | 18 | Then 'I should see last push widget' do |
| 19 | - page.should have_content "Your pushed to branch new_design" | |
| 19 | + page.should have_content "You pushed to branch new_design" | |
| 20 | 20 | page.should have_link "Create Merge Request" |
| 21 | 21 | end |
| 22 | 22 | ... | ... |
lib/api.rb
lib/api/entities.rb
| ... | ... | @@ -9,6 +9,10 @@ module Gitlab |
| 9 | 9 | expose :id, :email, :name, :blocked, :created_at |
| 10 | 10 | end |
| 11 | 11 | |
| 12 | + class UserLogin < UserBasic | |
| 13 | + expose :private_token | |
| 14 | + end | |
| 15 | + | |
| 12 | 16 | class Hook < Grape::Entity |
| 13 | 17 | expose :id, :url |
| 14 | 18 | end |
| ... | ... | @@ -20,15 +24,20 @@ module Gitlab |
| 20 | 24 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at |
| 21 | 25 | end |
| 22 | 26 | |
| 23 | - class UsersProject < Grape::Entity | |
| 24 | - expose :user, using: Entities::UserBasic | |
| 25 | - expose :project_access | |
| 27 | + class ProjectMember < UserBasic | |
| 28 | + expose :project_access, :as => :access_level do |user, options| | |
| 29 | + options[:project].users_projects.find_by_user_id(user.id).project_access | |
| 30 | + end | |
| 26 | 31 | end |
| 27 | 32 | |
| 28 | 33 | class RepoObject < Grape::Entity |
| 29 | 34 | expose :name, :commit |
| 30 | 35 | end |
| 31 | 36 | |
| 37 | + class RepoCommit < Grape::Entity | |
| 38 | + expose :id, :short_id, :title, :author_name, :author_email, :created_at | |
| 39 | + end | |
| 40 | + | |
| 32 | 41 | class ProjectSnippet < Grape::Entity |
| 33 | 42 | expose :id, :title, :file_name |
| 34 | 43 | expose :author, using: Entities::UserBasic |
| ... | ... | @@ -36,7 +45,9 @@ module Gitlab |
| 36 | 45 | end |
| 37 | 46 | |
| 38 | 47 | class Milestone < Grape::Entity |
| 39 | - expose :id, :title, :description, :due_date, :closed, :updated_at, :created_at | |
| 48 | + expose :id | |
| 49 | + expose (:project_id) {|milestone| milestone.project.id} | |
| 50 | + expose :title, :description, :due_date, :closed, :updated_at, :created_at | |
| 40 | 51 | end |
| 41 | 52 | |
| 42 | 53 | class Issue < Grape::Entity |
| ... | ... | @@ -49,10 +60,8 @@ module Gitlab |
| 49 | 60 | expose :closed, :updated_at, :created_at |
| 50 | 61 | end |
| 51 | 62 | |
| 52 | - class Key < Grape::Entity | |
| 53 | - expose :id, | |
| 54 | - :title, | |
| 55 | - :key | |
| 63 | + class SSHKey < Grape::Entity | |
| 64 | + expose :id, :title, :key | |
| 56 | 65 | end |
| 57 | 66 | end |
| 58 | 67 | end | ... | ... |
lib/api/helpers.rb
lib/api/keys.rb
| ... | ... | @@ -1,50 +0,0 @@ |
| 1 | -module Gitlab | |
| 2 | - # Keys API | |
| 3 | - class Keys < Grape::API | |
| 4 | - before { authenticate! } | |
| 5 | - resource :keys do | |
| 6 | - # Get currently authenticated user's keys | |
| 7 | - # | |
| 8 | - # Example Request: | |
| 9 | - # GET /keys | |
| 10 | - get do | |
| 11 | - present current_user.keys, with: Entities::Key | |
| 12 | - end | |
| 13 | - # Get single key owned by currently authenticated user | |
| 14 | - # | |
| 15 | - # Example Request: | |
| 16 | - # GET /keys/:id | |
| 17 | - get "/:id" do | |
| 18 | - key = current_user.keys.find params[:id] | |
| 19 | - present key, with: Entities::Key | |
| 20 | - end | |
| 21 | - # Add new ssh key to currently authenticated user | |
| 22 | - # | |
| 23 | - # Parameters: | |
| 24 | - # key (required) - New SSH Key | |
| 25 | - # title (required) - New SSH Key's title | |
| 26 | - # Example Request: | |
| 27 | - # POST /keys | |
| 28 | - post do | |
| 29 | - attrs = attributes_for_keys [:title, :key] | |
| 30 | - key = current_user.keys.new attrs | |
| 31 | - if key.save | |
| 32 | - present key, with: Entities::Key | |
| 33 | - else | |
| 34 | - not_found! | |
| 35 | - end | |
| 36 | - end | |
| 37 | - # Delete existed ssh key of currently authenticated user | |
| 38 | - # | |
| 39 | - # Parameters: | |
| 40 | - # id (required) - SSH Key ID | |
| 41 | - # Example Request: | |
| 42 | - # DELETE /keys/:id | |
| 43 | - delete "/:id" do | |
| 44 | - key = current_user.keys.find params[:id] | |
| 45 | - key.delete | |
| 46 | - end | |
| 47 | - end | |
| 48 | - end | |
| 49 | -end | |
| 50 | - |
lib/api/milestones.rb
| ... | ... | @@ -11,6 +11,8 @@ module Gitlab |
| 11 | 11 | # Example Request: |
| 12 | 12 | # GET /projects/:id/milestones |
| 13 | 13 | get ":id/milestones" do |
| 14 | + authorize! :read_milestone, user_project | |
| 15 | + | |
| 14 | 16 | present paginate(user_project.milestones), with: Entities::Milestone |
| 15 | 17 | end |
| 16 | 18 | |
| ... | ... | @@ -22,6 +24,8 @@ module Gitlab |
| 22 | 24 | # Example Request: |
| 23 | 25 | # GET /projects/:id/milestones/:milestone_id |
| 24 | 26 | get ":id/milestones/:milestone_id" do |
| 27 | + authorize! :read_milestone, user_project | |
| 28 | + | |
| 25 | 29 | @milestone = user_project.milestones.find(params[:milestone_id]) |
| 26 | 30 | present @milestone, with: Entities::Milestone |
| 27 | 31 | end |
| ... | ... | @@ -36,6 +40,8 @@ module Gitlab |
| 36 | 40 | # Example Request: |
| 37 | 41 | # POST /projects/:id/milestones |
| 38 | 42 | post ":id/milestones" do |
| 43 | + authorize! :admin_milestone, user_project | |
| 44 | + | |
| 39 | 45 | attrs = attributes_for_keys [:title, :description, :due_date] |
| 40 | 46 | @milestone = user_project.milestones.new attrs |
| 41 | 47 | if @milestone.save | ... | ... |
lib/api/projects.rb
| ... | ... | @@ -40,14 +40,14 @@ module Gitlab |
| 40 | 40 | post do |
| 41 | 41 | params[:code] ||= params[:name] |
| 42 | 42 | params[:path] ||= params[:name] |
| 43 | - attrs = attributes_for_keys [:code, | |
| 44 | - :path, | |
| 45 | - :name, | |
| 46 | - :description, | |
| 47 | - :default_branch, | |
| 48 | - :issues_enabled, | |
| 49 | - :wall_enabled, | |
| 50 | - :merge_requests_enabled, | |
| 43 | + attrs = attributes_for_keys [:code, | |
| 44 | + :path, | |
| 45 | + :name, | |
| 46 | + :description, | |
| 47 | + :default_branch, | |
| 48 | + :issues_enabled, | |
| 49 | + :wall_enabled, | |
| 50 | + :merge_requests_enabled, | |
| 51 | 51 | :wiki_enabled] |
| 52 | 52 | @project = Project.create_by_user(attrs, current_user) |
| 53 | 53 | if @project.saved? |
| ... | ... | @@ -57,56 +57,83 @@ module Gitlab |
| 57 | 57 | end |
| 58 | 58 | end |
| 59 | 59 | |
| 60 | - # Get project users | |
| 60 | + # Get a project team members | |
| 61 | 61 | # |
| 62 | 62 | # Parameters: |
| 63 | 63 | # id (required) - The ID or code name of a project |
| 64 | 64 | # Example Request: |
| 65 | - # GET /projects/:id/users | |
| 66 | - get ":id/users" do | |
| 67 | - @users_projects = paginate user_project.users_projects | |
| 68 | - present @users_projects, with: Entities::UsersProject | |
| 65 | + # GET /projects/:id/members | |
| 66 | + get ":id/members" do | |
| 67 | + @members = paginate user_project.users | |
| 68 | + present @members, with: Entities::ProjectMember, project: user_project | |
| 69 | 69 | end |
| 70 | 70 | |
| 71 | - # Add users to project with specified access level | |
| 71 | + # Get a project team members | |
| 72 | 72 | # |
| 73 | 73 | # Parameters: |
| 74 | 74 | # id (required) - The ID or code name of a project |
| 75 | - # user_ids (required) - The ID list of users to add | |
| 76 | - # project_access (required) - Project access level | |
| 75 | + # user_id (required) - The ID of a user | |
| 77 | 76 | # Example Request: |
| 78 | - # POST /projects/:id/users | |
| 79 | - post ":id/users" do | |
| 77 | + # GET /projects/:id/members/:user_id | |
| 78 | + get ":id/members/:user_id" do | |
| 79 | + @member = user_project.users.find params[:user_id] | |
| 80 | + present @member, with: Entities::ProjectMember, project: user_project | |
| 81 | + end | |
| 82 | + | |
| 83 | + # Add a new project team member | |
| 84 | + # | |
| 85 | + # Parameters: | |
| 86 | + # id (required) - The ID or code name of a project | |
| 87 | + # user_id (required) - The ID of a user | |
| 88 | + # access_level (required) - Project access level | |
| 89 | + # Example Request: | |
| 90 | + # POST /projects/:id/members | |
| 91 | + post ":id/members" do | |
| 80 | 92 | authorize! :admin_project, user_project |
| 81 | - user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) | |
| 82 | - nil | |
| 93 | + users_project = user_project.users_projects.new( | |
| 94 | + user_id: params[:user_id], | |
| 95 | + project_access: params[:access_level] | |
| 96 | + ) | |
| 97 | + | |
| 98 | + if users_project.save | |
| 99 | + @member = users_project.user | |
| 100 | + present @member, with: Entities::ProjectMember, project: user_project | |
| 101 | + else | |
| 102 | + not_found! | |
| 103 | + end | |
| 83 | 104 | end |
| 84 | 105 | |
| 85 | - # Update users to specified access level | |
| 106 | + # Update project team member | |
| 86 | 107 | # |
| 87 | 108 | # Parameters: |
| 88 | 109 | # id (required) - The ID or code name of a project |
| 89 | - # user_ids (required) - The ID list of users to add | |
| 90 | - # project_access (required) - New project access level to | |
| 110 | + # user_id (required) - The ID of a team member | |
| 111 | + # access_level (required) - Project access level | |
| 91 | 112 | # Example Request: |
| 92 | - # PUT /projects/:id/add_users | |
| 93 | - put ":id/users" do | |
| 113 | + # PUT /projects/:id/members/:user_id | |
| 114 | + put ":id/members/:user_id" do | |
| 94 | 115 | authorize! :admin_project, user_project |
| 95 | - user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) | |
| 96 | - nil | |
| 116 | + users_project = user_project.users_projects.find_by_user_id params[:user_id] | |
| 117 | + | |
| 118 | + if users_project.update_attributes(project_access: params[:access_level]) | |
| 119 | + @member = users_project.user | |
| 120 | + present @member, with: Entities::ProjectMember, project: user_project | |
| 121 | + else | |
| 122 | + not_found! | |
| 123 | + end | |
| 97 | 124 | end |
| 98 | 125 | |
| 99 | - # Delete project users | |
| 126 | + # Remove a team member from project | |
| 100 | 127 | # |
| 101 | 128 | # Parameters: |
| 102 | 129 | # id (required) - The ID or code name of a project |
| 103 | - # user_ids (required) - The ID list of users to delete | |
| 130 | + # user_id (required) - The ID of a team member | |
| 104 | 131 | # Example Request: |
| 105 | - # DELETE /projects/:id/users | |
| 106 | - delete ":id/users" do | |
| 132 | + # DELETE /projects/:id/members/:user_id | |
| 133 | + delete ":id/members/:user_id" do | |
| 107 | 134 | authorize! :admin_project, user_project |
| 108 | - user_project.delete_users_ids_from_team(params[:user_ids].values) | |
| 109 | - nil | |
| 135 | + users_project = user_project.users_projects.find_by_user_id params[:user_id] | |
| 136 | + users_project.destroy | |
| 110 | 137 | end |
| 111 | 138 | |
| 112 | 139 | # Get project hooks |
| ... | ... | @@ -184,6 +211,24 @@ module Gitlab |
| 184 | 211 | present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject |
| 185 | 212 | end |
| 186 | 213 | |
| 214 | + # Get a project repository commits | |
| 215 | + # | |
| 216 | + # Parameters: | |
| 217 | + # id (required) - The ID or code name of a project | |
| 218 | + # ref_name (optional) - The name of a repository branch or tag | |
| 219 | + # Example Request: | |
| 220 | + # GET /projects/:id/repository/commits | |
| 221 | + get ":id/repository/commits" do | |
| 222 | + authorize! :download_code, user_project | |
| 223 | + | |
| 224 | + page = params[:page] || 0 | |
| 225 | + per_page = params[:per_page] || 20 | |
| 226 | + ref = params[:ref_name] || user_project.try(:default_branch) || 'master' | |
| 227 | + | |
| 228 | + commits = user_project.commits(ref, nil, per_page, page * per_page) | |
| 229 | + present CommitDecorator.decorate(commits), with: Entities::RepoCommit | |
| 230 | + end | |
| 231 | + | |
| 187 | 232 | # Get a project snippet |
| 188 | 233 | # |
| 189 | 234 | # Parameters: |
| ... | ... | @@ -207,6 +252,8 @@ module Gitlab |
| 207 | 252 | # Example Request: |
| 208 | 253 | # POST /projects/:id/snippets |
| 209 | 254 | post ":id/snippets" do |
| 255 | + authorize! :write_snippet, user_project | |
| 256 | + | |
| 210 | 257 | attrs = attributes_for_keys [:title, :file_name] |
| 211 | 258 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? |
| 212 | 259 | attrs[:content] = params[:code] if params[:code].present? |
| ... | ... | @@ -282,6 +329,8 @@ module Gitlab |
| 282 | 329 | # Example Request: |
| 283 | 330 | # GET /projects/:id/repository/commits/:sha/blob |
| 284 | 331 | get ":id/repository/commits/:sha/blob" do |
| 332 | + authorize! :download_code, user_project | |
| 333 | + | |
| 285 | 334 | ref = params[:sha] |
| 286 | 335 | |
| 287 | 336 | commit = user_project.commit ref | ... | ... |
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +module Gitlab | |
| 2 | + # Users API | |
| 3 | + class Session < Grape::API | |
| 4 | + # Login to get token | |
| 5 | + # | |
| 6 | + # Example Request: | |
| 7 | + # POST /session | |
| 8 | + post "/session" do | |
| 9 | + resource = User.find_for_database_authentication(email: params[:email]) | |
| 10 | + | |
| 11 | + return unauthorized! unless resource | |
| 12 | + | |
| 13 | + if resource.valid_password?(params[:password]) | |
| 14 | + present resource, with: Entities::UserLogin | |
| 15 | + else | |
| 16 | + unauthorized! | |
| 17 | + end | |
| 18 | + end | |
| 19 | + end | |
| 20 | +end | ... | ... |
lib/api/users.rb
| ... | ... | @@ -25,12 +25,59 @@ module Gitlab |
| 25 | 25 | end |
| 26 | 26 | end |
| 27 | 27 | |
| 28 | - # Get currently authenticated user | |
| 29 | - # | |
| 30 | - # Example Request: | |
| 31 | - # GET /user | |
| 32 | - get "/user" do | |
| 33 | - present @current_user, with: Entities::User | |
| 28 | + resource :user do | |
| 29 | + # Get currently authenticated user | |
| 30 | + # | |
| 31 | + # Example Request: | |
| 32 | + # GET /user | |
| 33 | + get do | |
| 34 | + present @current_user, with: Entities::User | |
| 35 | + end | |
| 36 | + | |
| 37 | + # Get currently authenticated user's keys | |
| 38 | + # | |
| 39 | + # Example Request: | |
| 40 | + # GET /user/keys | |
| 41 | + get "keys" do | |
| 42 | + present current_user.keys, with: Entities::SSHKey | |
| 43 | + end | |
| 44 | + | |
| 45 | + # Get single key owned by currently authenticated user | |
| 46 | + # | |
| 47 | + # Example Request: | |
| 48 | + # GET /user/keys/:id | |
| 49 | + get "keys/:id" do | |
| 50 | + key = current_user.keys.find params[:id] | |
| 51 | + present key, with: Entities::SSHKey | |
| 52 | + end | |
| 53 | + | |
| 54 | + # Add new ssh key to currently authenticated user | |
| 55 | + # | |
| 56 | + # Parameters: | |
| 57 | + # key (required) - New SSH Key | |
| 58 | + # title (required) - New SSH Key's title | |
| 59 | + # Example Request: | |
| 60 | + # POST /user/keys | |
| 61 | + post "keys" do | |
| 62 | + attrs = attributes_for_keys [:title, :key] | |
| 63 | + key = current_user.keys.new attrs | |
| 64 | + if key.save | |
| 65 | + present key, with: Entities::SSHKey | |
| 66 | + else | |
| 67 | + not_found! | |
| 68 | + end | |
| 69 | + end | |
| 70 | + | |
| 71 | + # Delete existed ssh key of currently authenticated user | |
| 72 | + # | |
| 73 | + # Parameters: | |
| 74 | + # id (required) - SSH Key ID | |
| 75 | + # Example Request: | |
| 76 | + # DELETE /user/keys/:id | |
| 77 | + delete "keys/:id" do | |
| 78 | + key = current_user.keys.find params[:id] | |
| 79 | + key.delete | |
| 80 | + end | |
| 34 | 81 | end |
| 35 | 82 | end |
| 36 | 83 | end | ... | ... |
lib/gitlab/graph_commit.rb
| ... | ... | @@ -5,7 +5,7 @@ module Gitlab |
| 5 | 5 | attr_accessor :time, :space |
| 6 | 6 | attr_accessor :refs |
| 7 | 7 | |
| 8 | - include ActionView::Helpers::SanitizeHelper | |
| 8 | + include ActionView::Helpers::TagHelper | |
| 9 | 9 | |
| 10 | 10 | def self.to_graph(project) |
| 11 | 11 | @repo = project.repo |
| ... | ... | @@ -166,7 +166,7 @@ module Gitlab |
| 166 | 166 | h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? |
| 167 | 167 | h[:id] = sha |
| 168 | 168 | h[:date] = date |
| 169 | - h[:message] = sanitize(Gitlab::Encode.utf8(message)) | |
| 169 | + h[:message] = escape_once(Gitlab::Encode.utf8(message)) | |
| 170 | 170 | h[:login] = author.email |
| 171 | 171 | h |
| 172 | 172 | end | ... | ... |
lib/tasks/bulk_add_permission.rake
| 1 | -desc "Add all users to all projects, system administratos are added as masters" | |
| 1 | +desc "Add all users to all projects (admin users are added as masters)" | |
| 2 | 2 | task :add_users_to_project_teams => :environment do |t, args| |
| 3 | - users = User.find_all_by_admin(false, :select => 'id').map(&:id) | |
| 4 | - admins = User.find_all_by_admin(true, :select => 'id').map(&:id) | |
| 3 | + user_ids = User.where(:admin => false).pluck(:id) | |
| 4 | + admin_ids = User.where(:admin => true).pluck(:id) | |
| 5 | 5 | |
| 6 | - users.each do |user| | |
| 7 | - puts "#{user}" | |
| 8 | - end | |
| 9 | - | |
| 10 | - Project.all.each do |project| | |
| 11 | - puts "Importing #{users.length} users into #{project.path}" | |
| 12 | - UsersProject.bulk_import(project, users, UsersProject::DEVELOPER) | |
| 13 | - puts "Importing #{admins.length} admins into #{project.path}" | |
| 14 | - UsersProject.bulk_import(project, admins, UsersProject::MASTER) | |
| 6 | + Project.find_each do |project| | |
| 7 | + puts "Importing #{user_ids.size} users into #{project.code}" | |
| 8 | + UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER) | |
| 9 | + puts "Importing #{admin_ids.size} admins into #{project.code}" | |
| 10 | + UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER) | |
| 15 | 11 | end |
| 16 | 12 | end |
| 17 | 13 | |
| 18 | 14 | desc "Add user to as a developer to all projects" |
| 19 | 15 | task :add_user_to_project_teams, [:email] => :environment do |t, args| |
| 20 | - user_email = args.email | |
| 21 | - user = User.find_by_email(user_email) | |
| 22 | - | |
| 23 | - project_ids = Project.all.map(&:id) | |
| 16 | + user = User.find_by_email args.email | |
| 17 | + project_ids = Project.pluck(:id) | |
| 24 | 18 | |
| 25 | - UsersProject.user_bulk_import(user,project_ids,UsersProject::DEVELOPER) | |
| 19 | + UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER) | |
| 26 | 20 | end | ... | ... |
lib/tasks/bulk_import.rake
| 1 | - | |
| 2 | 1 | desc "Imports existing Git repos from a directory into new projects in git_base_path" |
| 3 | 2 | task :import_projects, [:directory,:email] => :environment do |t, args| |
| 4 | - user_email = args.email | |
| 5 | - import_directory = args.directory | |
| 3 | + user_email, import_directory = args.email, args.directory | |
| 6 | 4 | repos_to_import = Dir.glob("#{import_directory}/*") |
| 7 | 5 | git_base_path = Gitlab.config.git_base_path |
| 8 | - puts "Found #{repos_to_import.length} repos to import" | |
| 6 | + imported_count, skipped_count, failed_count = 0 | |
| 7 | + | |
| 8 | + puts "Found #{repos_to_import.size} repos to import" | |
| 9 | 9 | |
| 10 | - imported_count = 0 | |
| 11 | - skipped_count = 0 | |
| 12 | - failed_count = 0 | |
| 13 | 10 | repos_to_import.each do |repo_path| |
| 14 | 11 | repo_name = File.basename repo_path |
| 12 | + clone_path = "#{git_base_path}#{repo_name}.git" | |
| 15 | 13 | |
| 16 | 14 | puts " Processing #{repo_name}" |
| 17 | - clone_path = "#{git_base_path}#{repo_name}.git" | |
| 18 | 15 | |
| 19 | 16 | if Dir.exists? clone_path |
| 20 | 17 | if Project.find_by_code(repo_name) |
| ... | ... | @@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| |
| 38 | 35 | else |
| 39 | 36 | failed_count += 1 |
| 40 | 37 | end |
| 41 | - | |
| 42 | 38 | end |
| 43 | 39 | |
| 44 | 40 | puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})." |
| ... | ... | @@ -49,63 +45,39 @@ def clone_bare_repo_as_git(existing_path, new_path) |
| 49 | 45 | git_user = Gitlab.config.ssh_user |
| 50 | 46 | begin |
| 51 | 47 | sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" |
| 52 | - true | |
| 53 | - rescue Exception=> msg | |
| 54 | - puts " ERROR: Faild to clone #{existing_path} to #{new_path}" | |
| 55 | - puts " Make sure #{git_user} can reach #{existing_path}" | |
| 56 | - puts " Exception-MSG: #{msg}" | |
| 57 | - false | |
| 48 | + rescue Exception => msg | |
| 49 | + puts " ERROR: Failed to clone #{existing_path} to #{new_path}" | |
| 50 | + puts " Make sure #{git_user} can reach #{existing_path}" | |
| 51 | + puts " Exception-MSG: #{msg}" | |
| 58 | 52 | end |
| 59 | 53 | end |
| 60 | 54 | |
| 61 | -# Creats a project in Gitlag given a @project_name@ to use (for name, web url, and code | |
| 62 | -# url) and a @user_email@ that will be assigned as the owner of the project. | |
| 55 | +# Creates a project in GitLab given a `project_name` to use | |
| 56 | +# (for name, web url, and code url) and a `user_email` that will be | |
| 57 | +# assigned as the owner of the project. | |
| 63 | 58 | def create_repo_project(project_name, user_email) |
| 64 | - user = User.find_by_email(user_email) | |
| 65 | - if user | |
| 59 | + if user = User.find_by_email(user_email) | |
| 66 | 60 | # Using find_by_code since that's the most important identifer to be unique |
| 67 | 61 | if Project.find_by_code(project_name) |
| 68 | 62 | puts " INFO: Project #{project_name} already exists in Gitlab, skipping." |
| 69 | - false | |
| 70 | 63 | else |
| 71 | - project = nil | |
| 72 | - if Project.find_by_code(project_name) | |
| 73 | - puts " ERROR: Project already exists #{project_name}" | |
| 74 | - return false | |
| 75 | - project = Project.find_by_code(project_name) | |
| 76 | - else | |
| 77 | - project = Project.create( | |
| 78 | - name: project_name, | |
| 79 | - code: project_name, | |
| 80 | - path: project_name, | |
| 81 | - owner: user, | |
| 82 | - description: "Automatically created from Rake on #{Time.now.to_s}" | |
| 83 | - ) | |
| 84 | - end | |
| 85 | - | |
| 86 | - unless project.valid? | |
| 87 | - puts " ERROR: Failed to create project #{project} because #{project.errors.first}" | |
| 88 | - return false | |
| 89 | - end | |
| 90 | - | |
| 91 | - # Add user as admin for project | |
| 92 | - project.users_projects.create!( | |
| 93 | - :project_access => UsersProject::MASTER, | |
| 94 | - :user => user | |
| 64 | + project = Project.create( | |
| 65 | + name: project_name, | |
| 66 | + code: project_name, | |
| 67 | + path: project_name, | |
| 68 | + owner: user, | |
| 69 | + description: "Automatically created from 'import_projects' rake task on #{Time.now}" | |
| 95 | 70 | ) |
| 96 | 71 | |
| 97 | - # Per projects_controller.rb#37 | |
| 98 | - project.update_repository | |
| 99 | - | |
| 100 | 72 | if project.valid? |
| 101 | - true | |
| 73 | + # Add user as admin for project | |
| 74 | + project.users_projects.create!(:project_access => UsersProject::MASTER, :user => user) | |
| 75 | + project.update_repository | |
| 102 | 76 | else |
| 103 | 77 | puts " ERROR: Failed to create project #{project} because #{project.errors.first}" |
| 104 | - false | |
| 105 | 78 | end |
| 106 | 79 | end |
| 107 | 80 | else |
| 108 | - puts " ERROR: #{user_email} not found, skipping" | |
| 109 | - false | |
| 81 | + puts " ERROR: user with #{user_email} not found, skipping" | |
| 110 | 82 | end |
| 111 | 83 | end | ... | ... |
lib/tasks/gitlab/backup.rake
| ... | ... | @@ -2,22 +2,20 @@ require 'active_record/fixtures' |
| 2 | 2 | |
| 3 | 3 | namespace :gitlab do |
| 4 | 4 | namespace :app do |
| 5 | - | |
| 6 | - # Create backup of gitlab system | |
| 7 | - desc "GITLAB | Create a backup of the gitlab system" | |
| 5 | + # Create backup of GitLab system | |
| 6 | + desc "GITLAB | Create a backup of the GitLab system" | |
| 8 | 7 | task :backup_create => :environment do |
| 9 | - | |
| 10 | 8 | Rake::Task["gitlab:app:db_dump"].invoke |
| 11 | 9 | Rake::Task["gitlab:app:repo_dump"].invoke |
| 12 | 10 | |
| 13 | 11 | Dir.chdir(Gitlab.config.backup_path) |
| 14 | 12 | |
| 15 | 13 | # saving additional informations |
| 16 | - s = Hash.new | |
| 17 | - s["db_version"] = "#{ActiveRecord::Migrator.current_version}" | |
| 18 | - s["backup_created_at"] = "#{Time.now}" | |
| 19 | - s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"") | |
| 20 | - s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"") | |
| 14 | + s = {} | |
| 15 | + s[:db_version] = "#{ActiveRecord::Migrator.current_version}" | |
| 16 | + s[:backup_created_at] = "#{Time.now}" | |
| 17 | + s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"") | |
| 18 | + s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"") | |
| 21 | 19 | |
| 22 | 20 | File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| |
| 23 | 21 | file << s.to_yaml.gsub(/^---\n/,'') |
| ... | ... | @@ -32,7 +30,7 @@ namespace :gitlab do |
| 32 | 30 | end |
| 33 | 31 | |
| 34 | 32 | # cleanup: remove tmp files |
| 35 | - print "Deletion of tmp directories..." | |
| 33 | + print "Deleting tmp directories..." | |
| 36 | 34 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
| 37 | 35 | puts "[DONE]".green |
| 38 | 36 | else |
| ... | ... | @@ -52,26 +50,23 @@ namespace :gitlab do |
| 52 | 50 | else |
| 53 | 51 | puts "[SKIPPING]".yellow |
| 54 | 52 | end |
| 55 | - | |
| 56 | 53 | end |
| 57 | 54 | |
| 58 | - | |
| 59 | - # Restore backup of gitlab system | |
| 55 | + # Restore backup of GitLab system | |
| 60 | 56 | desc "GITLAB | Restore a previously created backup" |
| 61 | 57 | task :backup_restore => :environment do |
| 62 | - | |
| 63 | 58 | Dir.chdir(Gitlab.config.backup_path) |
| 64 | 59 | |
| 65 | 60 | # check for existing backups in the backup dir |
| 66 | 61 | file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } |
| 67 | - puts "no backup found" if file_list.count == 0 | |
| 62 | + puts "no backups found" if file_list.count == 0 | |
| 68 | 63 | if file_list.count > 1 && ENV["BACKUP"].nil? |
| 69 | 64 | puts "Found more than one backup, please specify which one you want to restore:" |
| 70 | 65 | puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" |
| 71 | 66 | exit 1; |
| 72 | 67 | end |
| 73 | 68 | |
| 74 | - tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") | |
| 69 | + tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") | |
| 75 | 70 | |
| 76 | 71 | unless File.exists?(tar_file) |
| 77 | 72 | puts "The specified backup doesn't exist!" |
| ... | ... | @@ -102,16 +97,14 @@ namespace :gitlab do |
| 102 | 97 | Rake::Task["gitlab:app:repo_restore"].invoke |
| 103 | 98 | |
| 104 | 99 | # cleanup: remove tmp files |
| 105 | - print "Deletion of tmp directories..." | |
| 100 | + print "Deleting tmp directories..." | |
| 106 | 101 | if Kernel.system("rm -rf repositories/ db/ backup_information.yml") |
| 107 | 102 | puts "[DONE]".green |
| 108 | 103 | else |
| 109 | 104 | puts "[FAILED]".red |
| 110 | 105 | end |
| 111 | - | |
| 112 | 106 | end |
| 113 | 107 | |
| 114 | - | |
| 115 | 108 | ################################################################################ |
| 116 | 109 | ################################# invoked tasks ################################ |
| 117 | 110 | |
| ... | ... | @@ -121,7 +114,7 @@ namespace :gitlab do |
| 121 | 114 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
| 122 | 115 | FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) |
| 123 | 116 | puts "Dumping repositories:" |
| 124 | - project = Project.all.map { |n| [n.path,n.path_to_repo] } | |
| 117 | + project = Project.all.map { |n| [n.path, n.path_to_repo] } | |
| 125 | 118 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
| 126 | 119 | project.each do |project| |
| 127 | 120 | print "- Dumping repository #{project.first}... " |
| ... | ... | @@ -136,11 +129,11 @@ namespace :gitlab do |
| 136 | 129 | task :repo_restore => :environment do |
| 137 | 130 | backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") |
| 138 | 131 | puts "Restoring repositories:" |
| 139 | - project = Project.all.map { |n| [n.path,n.path_to_repo] } | |
| 132 | + project = Project.all.map { |n| [n.path, n.path_to_repo] } | |
| 140 | 133 | project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] |
| 141 | 134 | project.each do |project| |
| 142 | 135 | print "- Restoring repository #{project.first}... " |
| 143 | - FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff | |
| 136 | + FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff | |
| 144 | 137 | if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1") |
| 145 | 138 | permission_commands = [ |
| 146 | 139 | "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", |
| ... | ... | @@ -157,8 +150,9 @@ namespace :gitlab do |
| 157 | 150 | ###################################### DB ###################################### |
| 158 | 151 | |
| 159 | 152 | task :db_dump => :environment do |
| 160 | - backup_path_db = File.join(Gitlab.config.backup_path, "db") | |
| 161 | - FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db) | |
| 153 | + backup_path_db = File.join(Gitlab.config.backup_path, "db") | |
| 154 | + FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db) | |
| 155 | + | |
| 162 | 156 | puts "Dumping database tables:" |
| 163 | 157 | ActiveRecord::Base.connection.tables.each do |tbl| |
| 164 | 158 | print "- Dumping table #{tbl}... " |
| ... | ... | @@ -176,9 +170,11 @@ namespace :gitlab do |
| 176 | 170 | end |
| 177 | 171 | |
| 178 | 172 | task :db_restore=> :environment do |
| 179 | - backup_path_db = File.join(Gitlab.config.backup_path, "db") | |
| 173 | + backup_path_db = File.join(Gitlab.config.backup_path, "db") | |
| 174 | + | |
| 180 | 175 | puts "Restoring database tables:" |
| 181 | 176 | Rake::Task["db:reset"].invoke |
| 177 | + | |
| 182 | 178 | Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| |
| 183 | 179 | fixture_file = File.basename(dir, ".*" ) |
| 184 | 180 | print "- Loading fixture #{fixture_file}..." | ... | ... |
lib/tasks/gitlab/enable_automerge.rake
lib/tasks/gitlab/gitolite_rebuild.rake
| 1 | 1 | namespace :gitlab do |
| 2 | 2 | namespace :gitolite do |
| 3 | 3 | desc "GITLAB | Rebuild each project at gitolite config" |
| 4 | - task :update_repos => :environment do | |
| 4 | + task :update_repos => :environment do | |
| 5 | 5 | puts "Starting Projects" |
| 6 | 6 | Project.find_each(:batch_size => 100) do |project| |
| 7 | - puts | |
| 8 | - puts "=== #{project.name}" | |
| 7 | + puts "\n=== #{project.name}" | |
| 9 | 8 | project.update_repository |
| 10 | 9 | puts |
| 11 | 10 | end | ... | ... |
lib/tasks/gitlab/setup.rake
lib/tasks/gitlab/status.rake
| 1 | 1 | namespace :gitlab do |
| 2 | 2 | namespace :app do |
| 3 | - desc "GITLAB | Check gitlab installation status" | |
| 3 | + desc "GITLAB | Check GitLab installation status" | |
| 4 | 4 | task :status => :environment do |
| 5 | - puts "Starting diagnostic".yellow | |
| 5 | + puts "Starting diagnostics".yellow | |
| 6 | 6 | git_base_path = Gitlab.config.git_base_path |
| 7 | 7 | |
| 8 | 8 | print "config/database.yml............" |
| 9 | - if File.exists?(File.join Rails.root, "config", "database.yml") | |
| 9 | + if File.exists?(Rails.root.join "config", "database.yml") | |
| 10 | 10 | puts "exists".green |
| 11 | - else | |
| 11 | + else | |
| 12 | 12 | puts "missing".red |
| 13 | 13 | return |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | 16 | print "config/gitlab.yml............" |
| 17 | - if File.exists?(File.join Rails.root, "config", "gitlab.yml") | |
| 18 | - puts "exists".green | |
| 17 | + if File.exists?(Rails.root.join "config", "gitlab.yml") | |
| 18 | + puts "exists".green | |
| 19 | 19 | else |
| 20 | 20 | puts "missing".red |
| 21 | 21 | return |
| 22 | 22 | end |
| 23 | 23 | |
| 24 | 24 | print "#{git_base_path}............" |
| 25 | - if File.exists?(git_base_path) | |
| 26 | - puts "exists".green | |
| 27 | - else | |
| 25 | + if File.exists?(git_base_path) | |
| 26 | + puts "exists".green | |
| 27 | + else | |
| 28 | 28 | puts "missing".red |
| 29 | 29 | return |
| 30 | 30 | end |
| 31 | 31 | |
| 32 | 32 | print "#{git_base_path} is writable?............" |
| 33 | 33 | if File.stat(git_base_path).writable? |
| 34 | - puts "YES".green | |
| 34 | + puts "YES".green | |
| 35 | 35 | else |
| 36 | 36 | puts "NO".red |
| 37 | 37 | return |
| ... | ... | @@ -41,16 +41,16 @@ namespace :gitlab do |
| 41 | 41 | `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` |
| 42 | 42 | FileUtils.rm_rf("/tmp/gitolite_gitlab_test") |
| 43 | 43 | print "Can clone gitolite-admin?............" |
| 44 | - puts "YES".green | |
| 45 | - rescue | |
| 44 | + puts "YES".green | |
| 45 | + rescue | |
| 46 | 46 | print "Can clone gitolite-admin?............" |
| 47 | 47 | puts "NO".red |
| 48 | 48 | return |
| 49 | 49 | end |
| 50 | 50 | |
| 51 | 51 | print "UMASK for .gitolite.rc is 0007? ............" |
| 52 | - unless open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).empty? | |
| 53 | - puts "YES".green | |
| 52 | + if open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).any? | |
| 53 | + puts "YES".green | |
| 54 | 54 | else |
| 55 | 55 | puts "NO".red |
| 56 | 56 | return |
| ... | ... | @@ -69,16 +69,15 @@ namespace :gitlab do |
| 69 | 69 | end |
| 70 | 70 | end |
| 71 | 71 | |
| 72 | - | |
| 73 | - if Project.count > 0 | |
| 72 | + if Project.count > 0 | |
| 74 | 73 | puts "Validating projects repositories:".yellow |
| 75 | 74 | Project.find_each(:batch_size => 100) do |project| |
| 76 | 75 | print "#{project.name}....." |
| 77 | - hook_file = File.join(project.path_to_repo, 'hooks','post-receive') | |
| 76 | + hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive') | |
| 78 | 77 | |
| 79 | 78 | unless File.exists?(hook_file) |
| 80 | - puts "post-receive file missing".red | |
| 81 | - next | |
| 79 | + puts "post-receive file missing".red | |
| 80 | + return | |
| 82 | 81 | end |
| 83 | 82 | |
| 84 | 83 | puts "post-receive file ok".green | ... | ... |
lib/tasks/gitlab/write_hook.rake
| ... | ... | @@ -4,7 +4,6 @@ namespace :gitlab do |
| 4 | 4 | task :write_hooks => :environment do |
| 5 | 5 | gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") |
| 6 | 6 | gitlab_hooks_path = Rails.root.join("lib", "hooks") |
| 7 | - | |
| 8 | 7 | gitlab_hook_files = ['post-receive'] |
| 9 | 8 | |
| 10 | 9 | gitlab_hook_files.each do |file_name| |
| ... | ... | @@ -20,4 +19,3 @@ namespace :gitlab do |
| 20 | 19 | end |
| 21 | 20 | end |
| 22 | 21 | end |
| 23 | - | ... | ... |
spec/helpers/tree_helper_spec.rb
| ... | ... | @@ -2,7 +2,7 @@ require 'spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe TreeHelper do |
| 4 | 4 | describe '#markup?' do |
| 5 | - %w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type| | |
| 5 | + %w(textile rdoc org creole mediawiki rst asciidoc pod).each do |type| | |
| 6 | 6 | it "returns true for #{type} files" do |
| 7 | 7 | markup?("README.#{type}").should be_true |
| 8 | 8 | end | ... | ... |
spec/models/user_spec.rb
| ... | ... | @@ -73,4 +73,30 @@ describe User do |
| 73 | 73 | user.authentication_token.should_not be_blank |
| 74 | 74 | end |
| 75 | 75 | end |
| 76 | + | |
| 77 | + describe "attributes can be changed by a regular user" do | |
| 78 | + before do | |
| 79 | + @user = Factory :user | |
| 80 | + @user.update_attributes(skype: "testskype", linkedin: "testlinkedin") | |
| 81 | + end | |
| 82 | + it { @user.skype.should == 'testskype' } | |
| 83 | + it { @user.linkedin.should == 'testlinkedin' } | |
| 84 | + end | |
| 85 | + | |
| 86 | + describe "attributes that shouldn't be changed by a regular user" do | |
| 87 | + before do | |
| 88 | + @user = Factory :user | |
| 89 | + @user.update_attributes(projects_limit: 50) | |
| 90 | + end | |
| 91 | + it { @user.projects_limit.should_not == 50 } | |
| 92 | + end | |
| 93 | + | |
| 94 | + describe "attributes can be changed by an admin user" do | |
| 95 | + before do | |
| 96 | + @admin_user = Factory :admin | |
| 97 | + @admin_user.update_attributes({ skype: "testskype", projects_limit: 50 }, as: :admin) | |
| 98 | + end | |
| 99 | + it { @admin_user.skype.should == 'testskype' } | |
| 100 | + it { @admin_user.projects_limit.should == 50 } | |
| 101 | + end | |
| 76 | 102 | end | ... | ... |
spec/requests/api/projects_spec.rb
| ... | ... | @@ -111,42 +111,52 @@ describe Gitlab::API do |
| 111 | 111 | end |
| 112 | 112 | end |
| 113 | 113 | |
| 114 | - describe "GET /projects/:id/users" do | |
| 115 | - it "should return project users" do | |
| 116 | - get api("/projects/#{project.code}/users", user) | |
| 117 | - | |
| 114 | + describe "GET /projects/:id/members" do | |
| 115 | + it "should return project team members" do | |
| 116 | + get api("/projects/#{project.code}/members", user) | |
| 118 | 117 | response.status.should == 200 |
| 119 | - | |
| 120 | 118 | json_response.should be_an Array |
| 121 | 119 | json_response.count.should == 2 |
| 122 | - json_response.first['user']['id'].should == user.id | |
| 120 | + json_response.first['email'].should == user.email | |
| 123 | 121 | end |
| 124 | 122 | end |
| 125 | 123 | |
| 126 | - describe "POST /projects/:id/users" do | |
| 127 | - it "should add users to project" do | |
| 128 | - expect { | |
| 129 | - post api("/projects/#{project.code}/users", user), | |
| 130 | - user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER | |
| 131 | - }.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1) | |
| 124 | + describe "GET /projects/:id/members/:user_id" do | |
| 125 | + it "should return project team member" do | |
| 126 | + get api("/projects/#{project.code}/members/#{user.id}", user) | |
| 127 | + response.status.should == 200 | |
| 128 | + json_response['email'].should == user.email | |
| 129 | + json_response['access_level'].should == UsersProject::MASTER | |
| 132 | 130 | end |
| 133 | 131 | end |
| 134 | 132 | |
| 135 | - describe "PUT /projects/:id/users" do | |
| 136 | - it "should update users to new access role" do | |
| 133 | + describe "POST /projects/:id/members" do | |
| 134 | + it "should add user to project team" do | |
| 137 | 135 | expect { |
| 138 | - put api("/projects/#{project.code}/users", user), | |
| 139 | - user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER | |
| 140 | - }.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1) | |
| 136 | + post api("/projects/#{project.code}/members", user), user_id: user2.id, | |
| 137 | + access_level: UsersProject::DEVELOPER | |
| 138 | + }.to change { UsersProject.count }.by(1) | |
| 139 | + | |
| 140 | + response.status.should == 201 | |
| 141 | + json_response['email'].should == user2.email | |
| 142 | + json_response['access_level'].should == UsersProject::DEVELOPER | |
| 143 | + end | |
| 144 | + end | |
| 145 | + | |
| 146 | + describe "PUT /projects/:id/members/:user_id" do | |
| 147 | + it "should update project team member" do | |
| 148 | + put api("/projects/#{project.code}/members/#{user3.id}", user), access_level: UsersProject::MASTER | |
| 149 | + response.status.should == 200 | |
| 150 | + json_response['email'].should == user3.email | |
| 151 | + json_response['access_level'].should == UsersProject::MASTER | |
| 141 | 152 | end |
| 142 | 153 | end |
| 143 | 154 | |
| 144 | - describe "DELETE /projects/:id/users" do | |
| 145 | - it "should delete users from project" do | |
| 155 | + describe "DELETE /projects/:id/members/:user_id" do | |
| 156 | + it "should remove user from project team" do | |
| 146 | 157 | expect { |
| 147 | - delete api("/projects/#{project.code}/users", user), | |
| 148 | - user_ids: {"0" => user3.id} | |
| 149 | - }.to change {project.users_projects.count}.by(-1) | |
| 158 | + delete api("/projects/#{project.code}/members/#{user3.id}", user) | |
| 159 | + }.to change { UsersProject.count }.by(-1) | |
| 150 | 160 | end |
| 151 | 161 | end |
| 152 | 162 | |
| ... | ... | @@ -189,6 +199,27 @@ describe Gitlab::API do |
| 189 | 199 | end |
| 190 | 200 | end |
| 191 | 201 | |
| 202 | + describe "GET /projects/:id/repository/commits" do | |
| 203 | + context "authorized user" do | |
| 204 | + before { project.add_access(user2, :read) } | |
| 205 | + | |
| 206 | + it "should return project commits" do | |
| 207 | + get api("/projects/#{project.code}/repository/commits", user) | |
| 208 | + response.status.should == 200 | |
| 209 | + | |
| 210 | + json_response.should be_an Array | |
| 211 | + json_response.first['id'].should == project.commit.id | |
| 212 | + end | |
| 213 | + end | |
| 214 | + | |
| 215 | + context "unauthorized user" do | |
| 216 | + it "should not return project commits" do | |
| 217 | + get api("/projects/#{project.code}/repository/commits") | |
| 218 | + response.status.should == 401 | |
| 219 | + end | |
| 220 | + end | |
| 221 | + end | |
| 222 | + | |
| 192 | 223 | describe "GET /projects/:id/snippets/:snippet_id" do |
| 193 | 224 | it "should return a project snippet" do |
| 194 | 225 | get api("/projects/#{project.code}/snippets/#{snippet.id}", user) | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | +require 'spec_helper' | |
| 2 | + | |
| 3 | +describe Gitlab::API do | |
| 4 | + include ApiHelpers | |
| 5 | + | |
| 6 | + let(:user) { Factory :user } | |
| 7 | + | |
| 8 | + describe "POST /session" do | |
| 9 | + context "when valid password" do | |
| 10 | + it "should return private token" do | |
| 11 | + post api("/session"), email: user.email, password: '123456' | |
| 12 | + response.status.should == 201 | |
| 13 | + | |
| 14 | + json_response['email'].should == user.email | |
| 15 | + json_response['private_token'].should == user.private_token | |
| 16 | + end | |
| 17 | + end | |
| 18 | + | |
| 19 | + context "when invalid password" do | |
| 20 | + it "should return authentication error" do | |
| 21 | + post api("/session"), email: user.email, password: '123' | |
| 22 | + response.status.should == 401 | |
| 23 | + | |
| 24 | + json_response['email'].should be_nil | |
| 25 | + json_response['private_token'].should be_nil | |
| 26 | + end | |
| 27 | + end | |
| 28 | + | |
| 29 | + context "when empty password" do | |
| 30 | + it "should return authentication error" do | |
| 31 | + post api("/session"), email: user.email | |
| 32 | + response.status.should == 401 | |
| 33 | + | |
| 34 | + json_response['email'].should be_nil | |
| 35 | + json_response['private_token'].should be_nil | |
| 36 | + end | |
| 37 | + end | |
| 38 | + end | |
| 39 | +end | ... | ... |
spec/requests/api/ssh_keys_spec.rb
| ... | ... | @@ -1,73 +0,0 @@ |
| 1 | -require 'spec_helper' | |
| 2 | - | |
| 3 | -describe Gitlab::Keys do | |
| 4 | - include ApiHelpers | |
| 5 | - let(:user) { | |
| 6 | - user = Factory.create :user | |
| 7 | - user.reset_authentication_token! | |
| 8 | - user | |
| 9 | - } | |
| 10 | - let(:key) { Factory.create :key, { user: user}} | |
| 11 | - | |
| 12 | - describe "GET /keys" do | |
| 13 | - context "when unauthenticated" do | |
| 14 | - it "should return authentication error" do | |
| 15 | - get api("/keys") | |
| 16 | - response.status.should == 401 | |
| 17 | - end | |
| 18 | - end | |
| 19 | - context "when authenticated" do | |
| 20 | - it "should return array of ssh keys" do | |
| 21 | - user.keys << key | |
| 22 | - user.save | |
| 23 | - get api("/keys", user) | |
| 24 | - response.status.should == 200 | |
| 25 | - json_response.should be_an Array | |
| 26 | - json_response.first["title"].should == key.title | |
| 27 | - end | |
| 28 | - end | |
| 29 | - end | |
| 30 | - | |
| 31 | - describe "GET /keys/:id" do | |
| 32 | - it "should returm single key" do | |
| 33 | - user.keys << key | |
| 34 | - user.save | |
| 35 | - get api("/keys/#{key.id}", user) | |
| 36 | - response.status.should == 200 | |
| 37 | - json_response["title"].should == key.title | |
| 38 | - end | |
| 39 | - it "should return 404 Not Found within invalid ID" do | |
| 40 | - get api("/keys/42", user) | |
| 41 | - response.status.should == 404 | |
| 42 | - end | |
| 43 | - end | |
| 44 | - | |
| 45 | - describe "POST /keys" do | |
| 46 | - it "should not create invalid ssh key" do | |
| 47 | - post api("/keys", user), { title: "invalid key" } | |
| 48 | - response.status.should == 404 | |
| 49 | - end | |
| 50 | - it "should create ssh key" do | |
| 51 | - key_attrs = Factory.attributes :key | |
| 52 | - expect { | |
| 53 | - post api("/keys", user), key_attrs | |
| 54 | - }.to change{ user.keys.count }.by(1) | |
| 55 | - end | |
| 56 | - end | |
| 57 | - | |
| 58 | - describe "DELETE /keys/:id" do | |
| 59 | - it "should delete existed key" do | |
| 60 | - user.keys << key | |
| 61 | - user.save | |
| 62 | - expect { | |
| 63 | - delete api("/keys/#{key.id}", user) | |
| 64 | - }.to change{user.keys.count}.by(-1) | |
| 65 | - end | |
| 66 | - it "should return 404 Not Found within invalid ID" do | |
| 67 | - delete api("/keys/42", user) | |
| 68 | - response.status.should == 404 | |
| 69 | - end | |
| 70 | - end | |
| 71 | - | |
| 72 | -end | |
| 73 | - |
spec/requests/api/users_spec.rb
| ... | ... | @@ -3,7 +3,8 @@ require 'spec_helper' |
| 3 | 3 | describe Gitlab::API do |
| 4 | 4 | include ApiHelpers |
| 5 | 5 | |
| 6 | - let(:user) { Factory :user } | |
| 6 | + let(:user) { Factory :user } | |
| 7 | + let(:key) { Factory :key, user: user } | |
| 7 | 8 | |
| 8 | 9 | describe "GET /users" do |
| 9 | 10 | context "when unauthenticated" do |
| ... | ... | @@ -38,4 +39,68 @@ describe Gitlab::API do |
| 38 | 39 | json_response['email'].should == user.email |
| 39 | 40 | end |
| 40 | 41 | end |
| 42 | + | |
| 43 | + describe "GET /user/keys" do | |
| 44 | + context "when unauthenticated" do | |
| 45 | + it "should return authentication error" do | |
| 46 | + get api("/user/keys") | |
| 47 | + response.status.should == 401 | |
| 48 | + end | |
| 49 | + end | |
| 50 | + | |
| 51 | + context "when authenticated" do | |
| 52 | + it "should return array of ssh keys" do | |
| 53 | + user.keys << key | |
| 54 | + user.save | |
| 55 | + get api("/user/keys", user) | |
| 56 | + response.status.should == 200 | |
| 57 | + json_response.should be_an Array | |
| 58 | + json_response.first["title"].should == key.title | |
| 59 | + end | |
| 60 | + end | |
| 61 | + end | |
| 62 | + | |
| 63 | + describe "GET /user/keys/:id" do | |
| 64 | + it "should returm single key" do | |
| 65 | + user.keys << key | |
| 66 | + user.save | |
| 67 | + get api("/user/keys/#{key.id}", user) | |
| 68 | + response.status.should == 200 | |
| 69 | + json_response["title"].should == key.title | |
| 70 | + end | |
| 71 | + | |
| 72 | + it "should return 404 Not Found within invalid ID" do | |
| 73 | + get api("/user/keys/42", user) | |
| 74 | + response.status.should == 404 | |
| 75 | + end | |
| 76 | + end | |
| 77 | + | |
| 78 | + describe "POST /user/keys" do | |
| 79 | + it "should not create invalid ssh key" do | |
| 80 | + post api("/user/keys", user), { title: "invalid key" } | |
| 81 | + response.status.should == 404 | |
| 82 | + end | |
| 83 | + | |
| 84 | + it "should create ssh key" do | |
| 85 | + key_attrs = Factory.attributes :key | |
| 86 | + expect { | |
| 87 | + post api("/user/keys", user), key_attrs | |
| 88 | + }.to change{ user.keys.count }.by(1) | |
| 89 | + end | |
| 90 | + end | |
| 91 | + | |
| 92 | + describe "DELETE /user/keys/:id" do | |
| 93 | + it "should delete existed key" do | |
| 94 | + user.keys << key | |
| 95 | + user.save | |
| 96 | + expect { | |
| 97 | + delete api("/user/keys/#{key.id}", user) | |
| 98 | + }.to change{user.keys.count}.by(-1) | |
| 99 | + end | |
| 100 | + | |
| 101 | + it "should return 404 Not Found within invalid ID" do | |
| 102 | + delete api("/user/keys/42", user) | |
| 103 | + response.status.should == 404 | |
| 104 | + end | |
| 105 | + end | |
| 41 | 106 | end | ... | ... |