diff --git a/CHANGELOG b/CHANGELOG index 7322efbf..3fec2d86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +v 2.9.1 + - Fixed resque custom config init + v 2.9.0 - fixed inline notes bugs - refactored rspecs @@ -9,8 +12,10 @@ v 2.9.0 - scss refactoring. gitlab_bootstrap/ dir - fix git push http body bigger than 112k problem - list of labels page under issues tab - - API for milestones + - API for milestones, keys - restyled buttons + - OAuth + - Comment order changed v 2.8.1 - ability to disable gravatars diff --git a/Gemfile b/Gemfile index b17d9cf3..71eafaba 100644 --- a/Gemfile +++ b/Gemfile @@ -96,6 +96,7 @@ group :assets do gem "therubyracer" gem 'chosen-rails' + gem 'jquery-atwho-rails', '0.1.6' gem "jquery-rails", "2.0.2" gem "jquery-ui-rails", "0.5.0" gem "modernizr", "2.5.3" diff --git a/Gemfile.lock b/Gemfile.lock index 100e88d0..f9a0128f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -199,6 +199,7 @@ GEM httpauth (0.1) i18n (0.6.1) journey (1.0.4) + jquery-atwho-rails (0.1.6) jquery-rails (2.0.2) railties (>= 3.2.0, < 5.0) thor (~> 0.14) @@ -441,6 +442,7 @@ DEPENDENCIES haml-rails headless httparty + jquery-atwho-rails (= 0.1.6) jquery-rails (= 2.0.2) jquery-ui-rails (= 0.5.0) kaminari diff --git a/Guardfile b/Guardfile index 50a10af9..e682f0b6 100644 --- a/Guardfile +++ b/Guardfile @@ -4,6 +4,7 @@ guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } # Rails example diff --git a/VERSION b/VERSION index a564e653..dedcc7d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.9.0pre +2.9.1 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f9fdb0f7..02ef6cab 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -11,6 +11,7 @@ //= require jquery.endless-scroll //= require jquery.highlight //= require jquery.waitforimages +//= require jquery.atwho //= require bootstrap //= require modernizr //= require chosen-jquery diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 92d542a9..424ba71f 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -4,6 +4,7 @@ * the top of the compiled file, but it's generally better to create a new file per style scope. *= require jquery.ui.all *= require jquery.ui.aristo + *= require jquery.atwho *= require chosen *= require_self *= require main diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index c5b37916..f13a1d6d 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -185,36 +185,6 @@ span.update-author { } } -.event_label { - @extend .label; - background-color: #999; - - &.pushed { - background-color: #4A97BD; - } - - &.opened { - background-color: #469847; - } - - &.closed { - background-color: #B94A48; - } - - &.merged { - background-color: #2A2; - } - - &.joined { - background-color: #1ca9dd; - } - - &.left { - background-color: #888; - float:none; - } -} - form { @extend .form-horizontal; @@ -355,41 +325,6 @@ p.time { border:2px solid #ddd; } -.event_feed { - min-height:40px; - border-bottom:1px solid #ddd; - .avatar { - width:32px; - } - .event_icon { - float:right; - margin-right:2px; - img { - width:20px; - } - } - ul { - margin-left:50px; - margin-bottom:5px; - .avatar { - width:24px; - } - } - - padding: 15px 5px; - &:last-child { border:none } - .wll:hover { background:none } - - .event_commits { - margin-top: 5px; - - li.commit { - background: transparent; - padding:5px; - border:none; - } - } -} .ico { background: url("images.png") no-repeat -85px -77px; @@ -639,22 +574,6 @@ li.note { background:#fff; } -/** - * Push event widget - * - */ -.event_lp { - @extend .ui-box; - color:#777; - margin-bottom:20px; - padding:8px; - @include border-radius(4px); - min-height:22px; - - .avatar { - width:24px; - } -} .supp_diff_link, .mr_show_all_commits { diff --git a/app/assets/stylesheets/gitlab_bootstrap/lists.scss b/app/assets/stylesheets/gitlab_bootstrap/lists.scss index 402ba04b..5585c35c 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/lists.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/lists.scss @@ -1,6 +1,6 @@ /** LISTS **/ -ul { +ul { /** * List li block element #1 * @@ -18,7 +18,7 @@ ul { .author { color: #999; } p { - padding-top:5px; + padding-top:5px; margin:0; color:#222; img { diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 75001d3a..14b289c0 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -143,6 +143,7 @@ $hover: #fdf5d9; @import "sections/projects.scss"; @import "sections/merge_requests.scss"; @import "sections/graph.scss"; +@import "sections/events.scss"; /** * This scss file redefine chozen selectbox styles for diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index 75e38aee..33b05135 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -1,7 +1,7 @@ .commit-box { @extend .main_box; - .commit-head { + .commit-head { @extend .top_box_content; .commit-title { @@ -29,11 +29,11 @@ .sha-block { text-align:right; - &:first-child { + &:first-child { padding-bottom:6px; } - a { + a { border-bottom: 1px solid #aaa; margin-left: 9px; } @@ -54,7 +54,7 @@ } /** - * + * * COMMIT SHOw * */ @@ -71,7 +71,7 @@ background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); - span { + span { font-size:14px; } } @@ -111,8 +111,8 @@ } } - &.img_compared { - img { + &.img_compared { + img { max-width:300px; } } @@ -120,12 +120,12 @@ } .diff_file_content{ - table { + table { border:none; margin:0px; padding:0px; tr { - td { + td { font-size:12px; } } @@ -145,29 +145,29 @@ moz-user-select: none; -khtml-user-select: none; user-select: none; - a { + a { float:left; width:35px; font-weight:normal; color:#666; - &:hover { + &:hover { text-decoration:underline; } } } - .line_content { - white-space:pre; + .line_content { + white-space:pre; height:14px; margin:0px; padding:0px; border:none; - &.new { + &.new { background: #CFD; } - &.old { + &.old { background: #FDD; } - &.matched { + &.matched { color:#ccc; background:#fafafa; } @@ -182,32 +182,32 @@ /** COMMIT ROW **/ -.commit { +.commit { @extend .wll; - .browse_code_link_holder { + .browse_code_link_holder { @extend .span2; float:right; } - .committed_ago { + .committed_ago { float:right; @extend .cgray; } - code { + code { background:#FCEEC1; color:$style_color; } - .commit_short_id { + .commit_short_id { float:left; @extend .lined; min-width:65px; font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; } - - .commit-author-name { + + .commit-author-name { color: #777; } } diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss new file mode 100644 index 00000000..db034e25 --- /dev/null +++ b/app/assets/stylesheets/sections/events.scss @@ -0,0 +1,118 @@ +/** + * Events labels + * + */ +.event_label { + &.pushed { + padding:0 2px; + @extend .alert; + @extend .alert-info; + } + + &.opened { + padding:0 2px; + @extend .alert; + @extend .alert-success; + } + + &.closed { + padding:0 2px; + @extend .alert; + @extend .alert-error; + } + + &.merged { + padding:0 2px; + @extend .alert; + @extend .alert-success; + } + + &.left, + &.joined { + padding:0 2px; + @extend .alert; + } +} + +/** + * Dashboard events feed + * + */ +.event-item { + min-height:40px; + border-bottom:1px solid #eee; + .event-title { + color:#333; + font-weight: bold; + .author_name { + color:#333; + } + } + .event-body { + p { + color:#555; + } + .event-info { + color:#666; + } + } + .avatar { + width:32px; + } + .event_icon { + float: right; + border: 1px solid #EEE; + padding: 5px; + @include border-radius(5px); + background: #F9F9F9; + img { + width:20px; + } + } + ul { + margin-left:50px; + margin-bottom:5px; + .avatar { + width:18px; + margin-top:3px; + } + } + + padding: 15px 5px; + &:last-child { border:none } + .wll:hover { background:none } + + .event_commits { + margin-top: 5px; + + li { + &.commit { + background: transparent; + padding:3px; + border:none; + font-size:12px; + } + &.commits-stat { + display: block; + margin-top: 5px; + } + } + } +} + +/** + * Push event widget + * + */ +.event_lp { + @extend .ui-box; + color:#777; + margin-bottom:20px; + padding:8px; + @include border-radius(4px); + min-height:22px; + + .avatar { + width:24px; + } +} diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index 267a9b43..d24d070d 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -43,7 +43,9 @@ padding: 8px 0; overflow: hidden; display: block; + position:relative; img {float: left; margin-right: 10px;} + img.emoji {float:none;margin:0;} .note-author cite{font-style: italic;} p { color:$style_color; } .note-author { color: $style_color;} @@ -55,7 +57,9 @@ .delete-note { display:none; - float:right; + position:absolute; + right:0; + top:0; } &:hover { diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index e2d61864..c9586ad5 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -30,7 +30,7 @@ class Admin::UsersController < AdminController def new - @admin_user = User.new(projects_limit: Gitlab.config.default_projects_limit) + @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin) end def edit @@ -60,7 +60,7 @@ class Admin::UsersController < AdminController def create admin = params[:user].delete("admin") - @admin_user = User.new(params[:user]) + @admin_user = User.new(params[:user], as: :admin) @admin_user.admin = (admin && admin.to_i > 0) respond_to do |format| @@ -86,7 +86,7 @@ class Admin::UsersController < AdminController @admin_user.admin = (admin && admin.to_i > 0) respond_to do |format| - if @admin_user.update_attributes(params[:user]) + if @admin_user.update_attributes(params[:user], as: :admin) format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } format.json { head :ok } else diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index 6d3f1aea..1e7aec00 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -52,6 +52,7 @@ class CommitsController < ApplicationController @commits = result[:commits] @commit = result[:commit] @diffs = result[:diffs] + @refs_are_same = result[:same] @line_notes = [] @commits = CommitDecorator.decorate(@commits) diff --git a/app/controllers/hooks_controller.rb b/app/controllers/hooks_controller.rb index c81e6b05..4359e996 100644 --- a/app/controllers/hooks_controller.rb +++ b/app/controllers/hooks_controller.rb @@ -1,5 +1,4 @@ class HooksController < ApplicationController - before_filter :authenticate_user! before_filter :project layout "project" diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 1d78a6d9..ceeee009 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,5 +1,4 @@ class IssuesController < ApplicationController - before_filter :authenticate_user! before_filter :project before_filter :module_enabled before_filter :issue, only: [:edit, :update, :destroy, :show] diff --git a/app/controllers/labels_controller.rb b/app/controllers/labels_controller.rb index e703f822..189d8d98 100644 --- a/app/controllers/labels_controller.rb +++ b/app/controllers/labels_controller.rb @@ -1,5 +1,4 @@ class LabelsController < ApplicationController - before_filter :authenticate_user! before_filter :project before_filter :module_enabled diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 187bb407..1d0da43f 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -1,5 +1,4 @@ class MergeRequestsController < ApplicationController - before_filter :authenticate_user! before_filter :project before_filter :module_enabled before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] diff --git a/app/controllers/milestones_controller.rb b/app/controllers/milestones_controller.rb index 10f089f1..e8dbc8e4 100644 --- a/app/controllers/milestones_controller.rb +++ b/app/controllers/milestones_controller.rb @@ -1,5 +1,4 @@ class MilestonesController < ApplicationController - before_filter :authenticate_user! before_filter :project before_filter :module_enabled before_filter :milestone, only: [:edit, :update, :destroy, :show] diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index f852e425..b00c9283 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -1,5 +1,4 @@ class SnippetsController < ApplicationController - before_filter :authenticate_user! before_filter :project before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] layout "project" diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index a5d5c742..2b7265ca 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -32,7 +32,11 @@ module TreeHelper # # Returns boolean def markup?(filename) - filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole - .mediawiki .rst .asciidoc .pod)) + filename.end_with?(*%w(.textile .rdoc .org .creole + .mediawiki .rst .asciidoc .pod)) + end + + def gitlab_markdown?(filename) + filename.end_with?(*%w(.mdown .md .markdown)) end end diff --git a/app/models/commit.rb b/app/models/commit.rb index 15afedcb..73583e9e 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -82,20 +82,24 @@ class Commit end def compare(project, from, to) - first = project.commit(to.try(:strip)) - last = project.commit(from.try(:strip)) - result = { commits: [], diffs: [], - commit: nil + commit: nil, + same: false } + return result unless from && to + + first = project.commit(to.try(:strip)) + last = project.commit(from.try(:strip)) + if first && last commits = [first, last].sort_by(&:created_at) younger = commits.first older = commits.last + result[:same] = (younger.id == older.id) result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] result[:commit] = Commit.new(older) diff --git a/app/models/event.rb b/app/models/event.rb index b11b21bd..dc76b6fd 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -132,6 +132,7 @@ class Event < ActiveRecord::Base end end + delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true delegate :title, to: :merge_request, prefix: true, allow_nil: true diff --git a/app/models/user.rb b/app/models/user.rb index 47876722..a8626cc1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,8 +6,9 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :trackable, :validatable, :omniauthable attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, - :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, - :theme_id, :force_random_password, :extern_uid, :provider + :name, :skype, :linkedin, :twitter, :dark_scheme, + :theme_id, :force_random_password, :extern_uid, :provider, :as => [:default, :admin] + attr_accessible :projects_limit, :as => :admin attr_accessor :force_random_password diff --git a/app/roles/repository.rb b/app/roles/repository.rb index a77de4ad..01156ac1 100644 --- a/app/roles/repository.rb +++ b/app/roles/repository.rb @@ -79,6 +79,14 @@ module Repository @heads ||= repo.heads end + def branches_names + heads.map(&:name) + end + + def ref_names + [branches_names + tags].flatten + end + def tree(fcommit, path = nil) fcommit = commit if fcommit == :head tree = fcommit.tree diff --git a/app/views/commits/compare.html.haml b/app/views/commits/compare.html.haml index 7dab1f5c..db15ba53 100644 --- a/app/views/commits/compare.html.haml +++ b/app/views/commits/compare.html.haml @@ -1,16 +1,16 @@ = render "head" -%h3 +%h3.page_title Compare View %hr %div - %p + %p.slead Fill input field with commit id like - %code '4eedf23' + %code.label_branch 4eedf23 or branch/tag name like - %code master - & press compare button for commits list, code diff. + %code.label_branch master + and press compare button for commits list, code diff. %br @@ -19,22 +19,24 @@ = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" = "..." = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" + - if @refs_are_same + .alert + %span Refs are the same .actions - = submit_tag "Compare", class: "btn primary" + = submit_tag "Compare", class: "btn primary wide commits-compare-btn" - -- unless @commits.empty? +- if @commits.present? %div.ui-box %h5.small Commits (#{@commits.count}) %ul.unstyled= render @commits -- unless @diffs.empty? - %h4 Diff - = render "commits/diffs", diffs: @diffs + - unless @diffs.empty? + %h4 Diff + = render "commits/diffs", diffs: @diffs :javascript $(function() { - var availableTags = #{@project.heads.map(&:name).to_json}; + var availableTags = #{@project.ref_names.to_json}; $("#from").autocomplete({ source: availableTags, @@ -45,5 +47,7 @@ source: availableTags, minLength: 1 }); + + disableButtonIfEmptyField('#to', '.commits-compare-btn'); }); diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index cb25d831..ed4f33c0 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -2,7 +2,7 @@ %li.commit %p = link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" - %strong.cdark= commit.author_name + %span= commit.author_name – = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 7bae8db1..716a214b 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,17 +1,18 @@ - if event.allowed? - - if event.issue? - .event_feed + %div.event-item + - if event.issue? = render "events/event_issue", event: event - - elsif event.merge_request? - .event_feed + - elsif event.merge_request? = render "events/event_merge_request", event: event - - elsif event.push? - .event_feed + - elsif event.push? = render "events/event_push", event: event - - elsif event.membership_changed? - .event_feed + - elsif event.membership_changed? = render "events/event_membership_changed", event: event + %span.cgray.right + = time_ago_in_words(event.created_at) + ago. + .clearfix diff --git a/app/views/events/_event_issue.html.haml b/app/views/events/_event_issue.html.haml index 4d357b7f..60e82c4d 100644 --- a/app/views/events/_event_issue.html.haml +++ b/app/views/events/_event_issue.html.haml @@ -1,11 +1,8 @@ = image_tag gravatar_icon(event.author_email), class: "avatar" -%strong #{event.author_name} -%span.event_label{class: event.action_name}= event.action_name -issue -= link_to project_issue_path(event.project, event.issue) do - %strong= truncate event.issue_title -at -%strong= link_to event.project.name, event.project -%span.cgray - = time_ago_in_words(event.created_at) - ago. +.event-title + %strong.author_name #{event.author_name} + %span.event_label{class: event.action_name} #{event.action_name} issue + = link_to project_issue_path(event.project, event.issue) do + %strong= truncate event.issue_title + at + %strong= link_to event.project.name, event.project diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml index aa1d28f2..81b9994c 100644 --- a/app/views/events/_event_last_push.html.haml +++ b/app/views/events/_event_last_push.html.haml @@ -2,7 +2,7 @@ .event_lp %div = image_tag gravatar_icon(event.author_email), class: "avatar" - %span Your pushed to + %span You pushed to = event.ref_type = link_to project_commits_path(event.project, ref: event.ref_name) do %strong= truncate(event.ref_name, length: 28) diff --git a/app/views/events/_event_membership_changed.html.haml b/app/views/events/_event_membership_changed.html.haml index 464f24b3..e89065b9 100644 --- a/app/views/events/_event_membership_changed.html.haml +++ b/app/views/events/_event_membership_changed.html.haml @@ -1,9 +1,9 @@ = image_tag gravatar_icon(event.author_email), class: "avatar" -%strong #{event.author_name} -%span.event_label{class: event.action_name}= event.action_name -project -%strong= link_to event.project_name, event.project -%span.cgray - = time_ago_in_words(event.created_at) - ago. +.event-title + %strong.author_name #{event.author_name} + %span.event_label{class: event.action_name} #{event.action_name} project + %strong= link_to event.project_name, event.project + %span.cgray + = time_ago_in_words(event.created_at) + ago. diff --git a/app/views/events/_event_merge_request.html.haml b/app/views/events/_event_merge_request.html.haml index ceb39371..655337f3 100644 --- a/app/views/events/_event_merge_request.html.haml +++ b/app/views/events/_event_merge_request.html.haml @@ -1,18 +1,16 @@ - if event.action_name == "merged" .event_icon= image_tag "event_mr_merged.png" = image_tag gravatar_icon(event.author_email), class: "avatar" -%strong #{event.author_name} -%span.event_label{class: event.action_name}= event.action_name -merge request -= link_to project_merge_request_path(event.project, event.merge_request) do - %strong= truncate event.merge_request_title -at -%strong= link_to event.project.name, event.project -%span.cgray - = time_ago_in_words(event.created_at) - ago. -%br -%span= event.merge_request.source_branch -→ -%span= event.merge_request.target_branch +.event-title + %strong.author_name #{event.author_name} + %span.event_label{class: event.action_name} #{event.action_name} merge request + = link_to project_merge_request_path(event.project, event.merge_request) do + %strong= truncate event.merge_request_title + at + %strong= link_to event.project.name, event.project +.event-body + .event-info + %span= event.merge_request.source_branch + → + %span= event.merge_request.target_branch diff --git a/app/views/events/_event_push.html.haml b/app/views/events/_event_push.html.haml index 0adcaf9d..c0be9cf5 100644 --- a/app/views/events/_event_push.html.haml +++ b/app/views/events/_event_push.html.haml @@ -1,30 +1,26 @@ %div .event_icon= image_tag "event_push.png" = image_tag gravatar_icon(event.author_email), class: "avatar" - %strong #{event.author_name} - %span.event_label.pushed= event.push_action_name - = event.ref_type - = link_to project_commits_path(event.project, ref: event.ref_name) do - %strong= event.ref_name - at - %strong= link_to event.project.name, event.project - %span.cgray - = time_ago_in_words(event.created_at) - ago. + + .event-title + %strong.author_name #{event.author_name} + %span.event_label.pushed #{event.push_action_name} #{event.ref_type} + = link_to project_commits_path(event.project, ref: event.ref_name) do + %strong= event.ref_name + at + %strong= link_to event.project.name, event.project - if event.push_with_commits? - - if event.commits_count > 1 - = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do - %strong #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} - project = event.project - %ul.unstyled.event_commits - - if event.commits_count > 3 - - event.commits[0...2].each do |commit| - = render "events/commit", commit: commit, project: project - %li - %br - \... and #{event.commits_count - 2} more commits - - else - - event.commits.each do |commit| + .event-body + %ul.unstyled.event_commits + - few_commits = event.commits[0...2] + - few_commits.each do |commit| = render "events/commit", commit: commit, project: project + %li.commits-stat + - if event.commits_count > 2 + %span ... and #{event.commits_count - 2} more commits. + = link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do + %strong Compare → #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]} + .clearfix diff --git a/app/views/help/api.html.haml b/app/views/help/api.html.haml index 35176f8e..00085166 100644 --- a/app/views/help/api.html.haml +++ b/app/views/help/api.html.haml @@ -1,77 +1,96 @@ -%h3 API +%h3.page_title API .back_link = link_to help_path do ← to index -%hr - -%ol - %li - %a{href: "#README"} README - %li - %a{href: "#projects"} Projects - %li - %a{href: "#snippets"} Snippets - %li - %a{href: "#users"} Users - %li - %a{href: "#issues"} Issues - %li - %a{href: "#milestones"} Milestones - -.file_holder#README - .file_title - %i.icon-file - README - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "README.md")) - %br -.file_holder#projects - .file_title - %i.icon-file - Projects - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "projects.md")) +%ul.nav.nav-tabs.log-tabs + %li.active + = link_to "README", "#README", 'data-toggle' => 'tab' + %li + = link_to "Projects", "#projects", 'data-toggle' => 'tab' + %li + = link_to "Snippets", "#snippets", 'data-toggle' => 'tab' + %li + = link_to "Repositories", "#repositories", 'data-toggle' => 'tab' + %li + = link_to "Users", "#users", 'data-toggle' => 'tab' + %li + = link_to "Session", "#session", 'data-toggle' => 'tab' + %li + = link_to "Issues", "#issues", 'data-toggle' => 'tab' + %li + = link_to "Milestones", "#milestones", 'data-toggle' => 'tab' -%br +.tab-content + .tab-pane.active#README + .file_holder + .file_title + %i.icon-file + README + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "README.md")) -.file_holder#snippets - .file_title - %i.icon-file - Projects Snippets - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) + .tab-pane#projects + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "projects.md")) -%br + .tab-pane#snippets + .file_holder + .file_title + %i.icon-file + Projects Snippets + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "snippets.md")) -.file_holder#users - .file_title - %i.icon-file - Users - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "users.md")) + .tab-pane#repositories + .file_holder + .file_title + %i.icon-file + Projects + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "repositories.md")) -%br + .tab-pane#users + .file_holder + .file_title + %i.icon-file + Users + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "users.md")) -.file_holder#issues - .file_title - %i.icon-file - Issues - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "issues.md")) + .tab-pane#session + .file_holder + .file_title + %i.icon-file + Session + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "session.md")) -%br + .tab-pane#issues + .file_holder + .file_title + %i.icon-file + Issues + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "issues.md")) -.file_holder#milestones - .file_title - %i.icon-file - Milestones - .file_content.wiki - = preserve do - = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) + .tab-pane#milestones + .file_holder + .file_title + %i.icon-file + Milestones + .file_content.wiki + = preserve do + = markdown File.read(Rails.root.join("doc", "api", "milestones.md")) diff --git a/app/views/notes/_common_form.html.haml b/app/views/notes/_common_form.html.haml index fc6e3c7e..626a448c 100644 --- a/app/views/notes/_common_form.html.haml +++ b/app/views/notes/_common_form.html.haml @@ -37,3 +37,14 @@ = f.file_field :attachment, class: "input-file" %span.hint Any file less than 10 MB +:javascript + $(function(){ + var names = #{@project.users.pluck(:name)}, emoji = ['+1', '-1']; + var emoji = $.map(emoji, function(value, i) {return {key:value + ':', name:value}}); + $('#note_note'). + atWho('@', { data: names }). + atWho(':', { + data: emoji, + tpl: "
  • ${name} #{escape_javascript image_tag('emoji/${name}.png', :size => '20x20')}
  • " + }); + }); diff --git a/app/views/refs/_tree.html.haml b/app/views/refs/_tree.html.haml index ec6dba4e..55078718 100644 --- a/app/views/refs/_tree.html.haml +++ b/app/views/refs/_tree.html.haml @@ -43,7 +43,11 @@ %i.icon-file = content.name .file_content.wiki - = raw GitHub::Markup.render(content.name, content.data) + - if gitlab_markdown?(content.name) + = preserve do + = markdown(content.data) + - else + = raw GitHub::Markup.render(content.name, content.data) :javascript $(function(){ diff --git a/app/views/refs/_tree_file.html.haml b/app/views/refs/_tree_file.html.haml index f6566ccf..76173e24 100644 --- a/app/views/refs/_tree_file.html.haml +++ b/app/views/refs/_tree_file.html.haml @@ -9,7 +9,11 @@ = link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small" = link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small" - if file.text? - - if markup?(name) + - if gitlab_markdown?(name) + .file_content.wiki + = preserve do + = markdown(file.data) + - elsif markup?(name) .file_content.wiki = raw GitHub::Markup.render(name, file.data) - else diff --git a/config/initializers/4_resque.rb b/config/initializers/4_resque.rb new file mode 100644 index 00000000..2a5721ec --- /dev/null +++ b/config/initializers/4_resque.rb @@ -0,0 +1,31 @@ +# Custom Redis configuration +rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' +rails_env = ENV['RAILS_ENV'] || 'development' +config_file = File.join(rails_root, 'config', 'resque.yml') + +if File.exists?(config_file) + resque_config = YAML.load_file(config_file) + Resque.redis = resque_config[rails_env] +end + +# Queues +Resque.watch_queue(PostReceive.instance_variable_get("@queue")) + +# Authentication +require 'resque/server' +class Authentication + def initialize(app) + @app = app + end + + def call(env) + account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) + raise "Access denied" if !account.admin? + @app.call(env) + end +end + +Resque::Server.use Authentication + +# Mailer +Resque::Mailer.excluded_environments = [] diff --git a/config/initializers/4_resque_queues.rb b/config/initializers/4_resque_queues.rb deleted file mode 100644 index 5b0087ab..00000000 --- a/config/initializers/4_resque_queues.rb +++ /dev/null @@ -1 +0,0 @@ -Resque.watch_queue(PostReceive.instance_variable_get("@queue")) diff --git a/config/initializers/resque.rb b/config/initializers/resque.rb deleted file mode 100644 index b333ceee..00000000 --- a/config/initializers/resque.rb +++ /dev/null @@ -1,8 +0,0 @@ -rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..' -rails_env = ENV['RAILS_ENV'] || 'development' -config_file = File.join(rails_root, 'config', 'resque.yml') - -if File.exists?(config_file) - resque_config = YAML.load_file(config_file) - Resque.redis = resque_config[rails_env] -end diff --git a/config/initializers/resque_authentication.rb b/config/initializers/resque_authentication.rb deleted file mode 100644 index a439d322..00000000 --- a/config/initializers/resque_authentication.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'resque/server' -class Authentication - def initialize(app) - @app = app - end - - def call(env) - account = env['warden'].authenticate!(:database_authenticatable, :rememberable, scope: :user) - raise "Access denied" if !account.admin? - @app.call(env) - end -end - -Resque::Server.use Authentication \ No newline at end of file diff --git a/config/initializers/resque_mailer.rb b/config/initializers/resque_mailer.rb deleted file mode 100644 index cec9dec9..00000000 --- a/config/initializers/resque_mailer.rb +++ /dev/null @@ -1 +0,0 @@ -Resque::Mailer.excluded_environments = [] diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index 23746d25..425dbf33 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -6,7 +6,7 @@ working_directory app_dir # worker spawn times preload_app true -# nuke workers after 60 seconds (the default) +# nuke workers after 30 seconds (60 is the default) timeout 30 # listen on a Unix domain socket and/or a TCP port, diff --git a/doc/api/README.md b/doc/api/README.md index 9741072c..36a36f8f 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -30,8 +30,9 @@ When listing resources you can pass the following parameters: ## Contents + [Users](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/users.md) ++ [Session](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/session.md) + [Projects](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/projects.md) + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) ++ [Repositories](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/repositories.md) + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) -+ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md) diff --git a/doc/api/keys.md b/doc/api/keys.md deleted file mode 100644 index d22b22e2..00000000 --- a/doc/api/keys.md +++ /dev/null @@ -1,79 +0,0 @@ -## List keys - -Get a list of currently authenticated user's keys. - -``` -GET /keys -``` - -```json -[ - { - "id": 1, - "title" : "Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", - }, - { - "id": 3, - "title" : "Another Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" - } -] -``` - -## Single key - -Get a single key. - -``` -GET /keys/:id -``` - -Parameters: - -+ `id` (required) - The ID of a key - -```json -{ - "id": 1, - "title" : "Public key" - "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 - 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 - soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" -} -``` -## Add key - -Create new key owned by currently authenticated user - -``` -POST /keys -``` - -Parameters: - -+ `title` (required) - new SSH Key's title -+ `key` (required) - new SSH key - -Will return created key with status `201 Created` on success, or `404 Not -found` on fail. - -## Delete key - -Delete key owned by currently authenticated user - -``` -DELETE /keys/:id -``` - -Parameters: - -+ `id` (required) - key ID - -Will return `200 OK` on success, or `404 Not Found` on fail. - - diff --git a/doc/api/projects.md b/doc/api/projects.md index 73d6adc9..d06a41c2 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -102,7 +102,7 @@ Parameters: + `name` (required) - new project name + `code` (optional) - new project code, uses project name if not set + `path` (optional) - new project path, uses project name if not set -+ `description (optional) - short project description ++ `description` (optional) - short project description + `default_branch` (optional) - 'master' by default + `issues_enabled` (optional) - enabled by default + `wall_enabled` (optional) - enabled by default @@ -112,66 +112,89 @@ Parameters: Will return created project with status `201 Created` on success, or `404 Not found` on fail. -## Get project users +## List project team members -Get users and access roles for existing project +Get a list of project team members. ``` -GET /projects/:id/users +GET /projects/:id/members ``` Parameters: + `id` (required) - The ID or code name of a project -Will return users and their access roles with status `200 OK` on success, or `404 Not found` on fail. +## Get project team member -## Add project users - -Add users to exiting project +Get a project team member. ``` -POST /projects/:id/users +GET /projects/:id/members/:user_id ``` Parameters: + `id` (required) - The ID or code name of a project -+ `user_ids` (required) - The ID list of users to add -+ `project_access` (required) - Project access level ++ `user_id` (required) - The ID of a user + +```json +{ + + "id": 1, + "email": "john@example.com", + "name": "John Smith", + "blocked": false, + "created_at": "2012-05-23T08:00:58Z", + "access_level": 40 +} +``` + +## Add project team member + +Add a user to a project team. + +``` +POST /projects/:id/members +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `user_id` (required) - The ID of a user to add ++ `access_level` (required) - Project access level Will return status `201 Created` on success, or `404 Not found` on fail. -## Update project users access level +## Edit project team member -Update existing users to specified access level +Update project team member to specified access level. ``` -PUT /projects/:id/users +PUT /projects/:id/members/:user_id ``` Parameters: + `id` (required) - The ID or code name of a project -+ `user_ids` (required) - The ID list of users to add -+ `project_access` (required) - Project access level ++ `user_id` (required) - The ID of a team member ++ `access_level` (required) - Project access level Will return status `200 OK` on success, or `404 Not found` on fail. -## Delete project users +## Remove project team member -Delete users from exiting project +Removes user from project team. ``` -DELETE /projects/:id/users +DELETE /projects/:id/members/:user_id ``` Parameters: + `id` (required) - The ID or code name of a project -+ `user_ids` (required) - The ID list of users to add ++ `user_id` (required) - The ID of a team member -Will return status `200 OK` on success, or `404 Not found` on fail. +Status code `200` will be returned on success. ## Get project hooks @@ -216,135 +239,3 @@ Parameters: + `hook_id` (required) - The ID of hook to delete Will return status `200 OK` on success, or `404 Not found` on fail. - -## Project repository branches - -Get a list of repository branches from a project, sorted by name alphabetically. - -``` -GET /projects/:id/repository/branches -``` - -Parameters: - -+ `id` (required) - The ID or code name of a project - -```json -[ - { - "name": "master", - "commit": { - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", - "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" - } - } -] -``` - -Get a single project repository branch. - -``` -GET /projects/:id/repository/branches/:branch -``` - -Parameters: - -+ `id` (required) - The ID or code name of a project -+ `branch` (required) - The name of the branch - -```json -{ - "name": "master", - "commit": { - "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", - "parents": [ - { - "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" - } - ], - "tree": "46e82de44b1061621357f24c05515327f2795a95", - "message": "add projects API", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "John Smith", - "email": "john@example.com" - }, - "authored_date": "2012-06-27T05:51:39-07:00", - "committed_date": "2012-06-28T03:44:20-07:00" - } -} -``` - -## Project repository tags - -Get a list of repository tags from a project, sorted by name in reverse alphabetical order. - -``` -GET /projects/:id/repository/tags -``` - -Parameters: - -+ `id` (required) - The ID or code name of a project - -```json -[ - { - "name": "v1.0.0", - "commit": { - "id": "2695effb5807a22ff3d138d593fd856244e155e7", - "parents": [ - - ], - "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", - "message": "Initial commit", - "author": { - "name": "John Smith", - "email": "john@example.com" - }, - "committer": { - "name": "Jack Smith", - "email": "jack@example.com" - }, - "authored_date": "2012-05-28T04:42:42-07:00", - "committed_date": "2012-05-28T04:42:42-07:00" - } - } -] -``` - -## Raw blob content - -Get the raw file contents for a file. - -``` -GET /projects/:id/repository/commits/:sha/blob -``` - -Parameters: - -+ `id` (required) - The ID or code name of a project -+ `sha` (required) - The commit or branch name -+ `filepath` (required) - The path the file - -Will return the raw file contents. - diff --git a/doc/api/repositories.md b/doc/api/repositories.md new file mode 100644 index 00000000..487ad9b2 --- /dev/null +++ b/doc/api/repositories.md @@ -0,0 +1,166 @@ +## Project repository branches + +Get a list of repository branches from a project, sorted by name alphabetically. + +``` +GET /projects/:id/repository/branches +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project + +```json +[ + { + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + } + } +] +``` + +## Project repository branch + +Get a single project repository branch. + +``` +GET /projects/:id/repository/branches/:branch +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `branch` (required) - The name of the branch + +```json +{ + "name": "master", + "commit": { + "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "parents": [ + { + "id": "4ad91d3c1144c406e50c7b33bae684bd6837faf8" + } + ], + "tree": "46e82de44b1061621357f24c05515327f2795a95", + "message": "add projects API", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "John Smith", + "email": "john@example.com" + }, + "authored_date": "2012-06-27T05:51:39-07:00", + "committed_date": "2012-06-28T03:44:20-07:00" + } +} +``` + +## Project repository tags + +Get a list of repository tags from a project, sorted by name in reverse alphabetical order. + +``` +GET /projects/:id/repository/tags +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project + +```json +[ + { + "name": "v1.0.0", + "commit": { + "id": "2695effb5807a22ff3d138d593fd856244e155e7", + "parents": [ + + ], + "tree": "38017f2f189336fe4497e9d230c5bb1bf873f08d", + "message": "Initial commit", + "author": { + "name": "John Smith", + "email": "john@example.com" + }, + "committer": { + "name": "Jack Smith", + "email": "jack@example.com" + }, + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00" + } + } +] +``` + +## Project repository commits + +Get a list of repository commits in a project. + +``` +GET /projects/:id/repository/commits +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `ref_name` (optional) - The name of a repository branch or tag + +```json +[ + { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Replace sanitize with escape once", + "author_name": "Dmitriy Zaporozhets", + "author_email": "dzaporozhets@sphereconsultinginc.com", + "created_at": "2012-09-20T11:50:22+03:00" + }, + { + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "short_id": "6104942438c", + "title": "Sanitize for network graph", + "author_name": "randx", + "author_email": "dmitriy.zaporozhets@gmail.com", + "created_at": "2012-09-20T09:06:12+03:00" + } +] +``` + +## Raw blob content + +Get the raw file contents for a file. + +``` +GET /projects/:id/repository/commits/:sha/blob +``` + +Parameters: + ++ `id` (required) - The ID or code name of a project ++ `sha` (required) - The commit or branch name ++ `filepath` (required) - The path the file + +Will return the raw file contents. diff --git a/doc/api/session.md b/doc/api/session.md new file mode 100644 index 00000000..9fdbeb43 --- /dev/null +++ b/doc/api/session.md @@ -0,0 +1,22 @@ +Login to get private token + +``` +POST /session +``` + +Parameters: + ++ `email` (required) - The email of user ++ `password` (required) - Valid password + + +```json +{ + "id": 1, + "email": "john@example.com", + "name": "John Smith", + "private_token": "dd34asd13as", + "created_at": "2012-05-23T08:00:58Z", + "blocked": true +} +``` diff --git a/doc/api/users.md b/doc/api/users.md index b9b04dc5..4f806b14 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -88,3 +88,81 @@ GET /user "theme_id": 1 } ``` + +## List SSH keys + +Get a list of currently authenticated user's SSH keys. + +``` +GET /user/keys +``` + +```json +[ + { + "id": 1, + "title" : "Public key" + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + }, + { + "id": 3, + "title" : "Another Public key" + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" + } +] +``` + +## Single SSH key + +Get a single key. + +``` +GET /user/keys/:id +``` + +Parameters: + ++ `id` (required) - The ID of an SSH key + +```json +{ + "id": 1, + "title" : "Public key" + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" +} +``` +## Add SSH key + +Create new key owned by currently authenticated user + +``` +POST /user/keys +``` + +Parameters: + ++ `title` (required) - new SSH Key's title ++ `key` (required) - new SSH key + +Will return created key with status `201 Created` on success, or `404 Not +found` on fail. + +## Delete SSH key + +Delete key owned by currently authenticated user + +``` +DELETE /user/keys/:id +``` + +Parameters: + ++ `id` (required) - SSH key ID + +Will return `200 OK` on success, or `404 Not Found` on fail. diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 6c603bbe..154b97e3 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -16,7 +16,7 @@ class Dashboard < Spinach::FeatureSteps end Then 'I should see last push widget' do - page.should have_content "Your pushed to branch new_design" + page.should have_content "You pushed to branch new_design" page.should have_link "Create Merge Request" end diff --git a/lib/api.rb b/lib/api.rb index 37e03849..2890a8cc 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -17,6 +17,6 @@ module Gitlab mount Projects mount Issues mount Milestones - mount Keys + mount Session end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 13a48e12..ee693de6 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -9,6 +9,10 @@ module Gitlab expose :id, :email, :name, :blocked, :created_at end + class UserLogin < UserBasic + expose :private_token + end + class Hook < Grape::Entity expose :id, :url end @@ -20,15 +24,20 @@ module Gitlab expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at end - class UsersProject < Grape::Entity - expose :user, using: Entities::UserBasic - expose :project_access + class ProjectMember < UserBasic + expose :project_access, :as => :access_level do |user, options| + options[:project].users_projects.find_by_user_id(user.id).project_access + end end class RepoObject < Grape::Entity expose :name, :commit end + class RepoCommit < Grape::Entity + expose :id, :short_id, :title, :author_name, :author_email, :created_at + end + class ProjectSnippet < Grape::Entity expose :id, :title, :file_name expose :author, using: Entities::UserBasic @@ -36,7 +45,9 @@ module Gitlab end class Milestone < Grape::Entity - expose :id, :title, :description, :due_date, :closed, :updated_at, :created_at + expose :id + expose (:project_id) {|milestone| milestone.project.id} + expose :title, :description, :due_date, :closed, :updated_at, :created_at end class Issue < Grape::Entity @@ -49,10 +60,8 @@ module Gitlab expose :closed, :updated_at, :created_at end - class Key < Grape::Entity - expose :id, - :title, - :key + class SSHKey < Grape::Entity + expose :id, :title, :key end end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 9a08b995..14390545 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -61,7 +61,7 @@ module Gitlab error!({'message' => message}, status) end - private + private def abilities @abilities ||= begin diff --git a/lib/api/keys.rb b/lib/api/keys.rb deleted file mode 100644 index 4c302727..00000000 --- a/lib/api/keys.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Gitlab - # Keys API - class Keys < Grape::API - before { authenticate! } - resource :keys do - # Get currently authenticated user's keys - # - # Example Request: - # GET /keys - get do - present current_user.keys, with: Entities::Key - end - # Get single key owned by currently authenticated user - # - # Example Request: - # GET /keys/:id - get "/:id" do - key = current_user.keys.find params[:id] - present key, with: Entities::Key - end - # Add new ssh key to currently authenticated user - # - # Parameters: - # key (required) - New SSH Key - # title (required) - New SSH Key's title - # Example Request: - # POST /keys - post do - attrs = attributes_for_keys [:title, :key] - key = current_user.keys.new attrs - if key.save - present key, with: Entities::Key - else - not_found! - end - end - # Delete existed ssh key of currently authenticated user - # - # Parameters: - # id (required) - SSH Key ID - # Example Request: - # DELETE /keys/:id - delete "/:id" do - key = current_user.keys.find params[:id] - key.delete - end - end - end -end - diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index daaff940..f55dfd04 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -11,6 +11,8 @@ module Gitlab # Example Request: # GET /projects/:id/milestones get ":id/milestones" do + authorize! :read_milestone, user_project + present paginate(user_project.milestones), with: Entities::Milestone end @@ -22,6 +24,8 @@ module Gitlab # Example Request: # GET /projects/:id/milestones/:milestone_id get ":id/milestones/:milestone_id" do + authorize! :read_milestone, user_project + @milestone = user_project.milestones.find(params[:milestone_id]) present @milestone, with: Entities::Milestone end @@ -36,6 +40,8 @@ module Gitlab # Example Request: # POST /projects/:id/milestones post ":id/milestones" do + authorize! :admin_milestone, user_project + attrs = attributes_for_keys [:title, :description, :due_date] @milestone = user_project.milestones.new attrs if @milestone.save diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 1d9004f8..c3dc3da6 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -40,14 +40,14 @@ module Gitlab post do params[:code] ||= params[:name] params[:path] ||= params[:name] - attrs = attributes_for_keys [:code, - :path, - :name, - :description, - :default_branch, - :issues_enabled, - :wall_enabled, - :merge_requests_enabled, + attrs = attributes_for_keys [:code, + :path, + :name, + :description, + :default_branch, + :issues_enabled, + :wall_enabled, + :merge_requests_enabled, :wiki_enabled] @project = Project.create_by_user(attrs, current_user) if @project.saved? @@ -57,56 +57,83 @@ module Gitlab end end - # Get project users + # Get a project team members # # Parameters: # id (required) - The ID or code name of a project # Example Request: - # GET /projects/:id/users - get ":id/users" do - @users_projects = paginate user_project.users_projects - present @users_projects, with: Entities::UsersProject + # GET /projects/:id/members + get ":id/members" do + @members = paginate user_project.users + present @members, with: Entities::ProjectMember, project: user_project end - # Add users to project with specified access level + # Get a project team members # # Parameters: # id (required) - The ID or code name of a project - # user_ids (required) - The ID list of users to add - # project_access (required) - Project access level + # user_id (required) - The ID of a user # Example Request: - # POST /projects/:id/users - post ":id/users" do - authorize! :admin_project, user_project - user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access]) - nil + # GET /projects/:id/members/:user_id + get ":id/members/:user_id" do + @member = user_project.users.find params[:user_id] + present @member, with: Entities::ProjectMember, project: user_project end - # Update users to specified access level + # Add a new project team member # # Parameters: # id (required) - The ID or code name of a project - # user_ids (required) - The ID list of users to add - # project_access (required) - New project access level to + # user_id (required) - The ID of a user + # access_level (required) - Project access level # Example Request: - # PUT /projects/:id/add_users - put ":id/users" do + # POST /projects/:id/members + post ":id/members" do authorize! :admin_project, user_project - user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access]) - nil + users_project = user_project.users_projects.new( + user_id: params[:user_id], + project_access: params[:access_level] + ) + + if users_project.save + @member = users_project.user + present @member, with: Entities::ProjectMember, project: user_project + else + not_found! + end end - # Delete project users + # Update project team member # # Parameters: # id (required) - The ID or code name of a project - # user_ids (required) - The ID list of users to delete + # user_id (required) - The ID of a team member + # access_level (required) - Project access level # Example Request: - # DELETE /projects/:id/users - delete ":id/users" do + # PUT /projects/:id/members/:user_id + put ":id/members/:user_id" do authorize! :admin_project, user_project - user_project.delete_users_ids_from_team(params[:user_ids].values) - nil + users_project = user_project.users_projects.find_by_user_id params[:user_id] + + if users_project.update_attributes(project_access: params[:access_level]) + @member = users_project.user + present @member, with: Entities::ProjectMember, project: user_project + else + not_found! + end + end + + # Remove a team member from project + # + # Parameters: + # id (required) - The ID or code name of a project + # user_id (required) - The ID of a team member + # Example Request: + # DELETE /projects/:id/members/:user_id + delete ":id/members/:user_id" do + authorize! :admin_project, user_project + users_project = user_project.users_projects.find_by_user_id params[:user_id] + users_project.destroy end # Get project hooks @@ -184,6 +211,24 @@ module Gitlab present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject end + # Get a project repository commits + # + # Parameters: + # id (required) - The ID or code name of a project + # ref_name (optional) - The name of a repository branch or tag + # Example Request: + # GET /projects/:id/repository/commits + get ":id/repository/commits" do + authorize! :download_code, user_project + + page = params[:page] || 0 + per_page = params[:per_page] || 20 + ref = params[:ref_name] || user_project.try(:default_branch) || 'master' + + commits = user_project.commits(ref, nil, per_page, page * per_page) + present CommitDecorator.decorate(commits), with: Entities::RepoCommit + end + # Get a project snippet # # Parameters: @@ -207,6 +252,8 @@ module Gitlab # Example Request: # POST /projects/:id/snippets post ":id/snippets" do + authorize! :write_snippet, user_project + attrs = attributes_for_keys [:title, :file_name] attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? attrs[:content] = params[:code] if params[:code].present? @@ -282,6 +329,8 @@ module Gitlab # Example Request: # GET /projects/:id/repository/commits/:sha/blob get ":id/repository/commits/:sha/blob" do + authorize! :download_code, user_project + ref = params[:sha] commit = user_project.commit ref diff --git a/lib/api/session.rb b/lib/api/session.rb new file mode 100644 index 00000000..b4050160 --- /dev/null +++ b/lib/api/session.rb @@ -0,0 +1,20 @@ +module Gitlab + # Users API + class Session < Grape::API + # Login to get token + # + # Example Request: + # POST /session + post "/session" do + resource = User.find_for_database_authentication(email: params[:email]) + + return unauthorized! unless resource + + if resource.valid_password?(params[:password]) + present resource, with: Entities::UserLogin + else + unauthorized! + end + end + end +end diff --git a/lib/api/users.rb b/lib/api/users.rb index 98ced6f8..0ca8fb2a 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -25,12 +25,59 @@ module Gitlab end end - # Get currently authenticated user - # - # Example Request: - # GET /user - get "/user" do - present @current_user, with: Entities::User + resource :user do + # Get currently authenticated user + # + # Example Request: + # GET /user + get do + present @current_user, with: Entities::User + end + + # Get currently authenticated user's keys + # + # Example Request: + # GET /user/keys + get "keys" do + present current_user.keys, with: Entities::SSHKey + end + + # Get single key owned by currently authenticated user + # + # Example Request: + # GET /user/keys/:id + get "keys/:id" do + key = current_user.keys.find params[:id] + present key, with: Entities::SSHKey + end + + # Add new ssh key to currently authenticated user + # + # Parameters: + # key (required) - New SSH Key + # title (required) - New SSH Key's title + # Example Request: + # POST /user/keys + post "keys" do + attrs = attributes_for_keys [:title, :key] + key = current_user.keys.new attrs + if key.save + present key, with: Entities::SSHKey + else + not_found! + end + end + + # Delete existed ssh key of currently authenticated user + # + # Parameters: + # id (required) - SSH Key ID + # Example Request: + # DELETE /user/keys/:id + delete "keys/:id" do + key = current_user.keys.find params[:id] + key.delete + end end end end diff --git a/lib/gitlab/graph_commit.rb b/lib/gitlab/graph_commit.rb index e14d670e..d3668a99 100644 --- a/lib/gitlab/graph_commit.rb +++ b/lib/gitlab/graph_commit.rb @@ -5,7 +5,7 @@ module Gitlab attr_accessor :time, :space attr_accessor :refs - include ActionView::Helpers::SanitizeHelper + include ActionView::Helpers::TagHelper def self.to_graph(project) @repo = project.repo @@ -166,7 +166,7 @@ module Gitlab h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? h[:id] = sha h[:date] = date - h[:message] = sanitize(Gitlab::Encode.utf8(message)) + h[:message] = escape_once(Gitlab::Encode.utf8(message)) h[:login] = author.email h end diff --git a/lib/tasks/bulk_add_permission.rake b/lib/tasks/bulk_add_permission.rake index 55797825..bf08ace8 100644 --- a/lib/tasks/bulk_add_permission.rake +++ b/lib/tasks/bulk_add_permission.rake @@ -1,26 +1,20 @@ -desc "Add all users to all projects, system administratos are added as masters" +desc "Add all users to all projects (admin users are added as masters)" task :add_users_to_project_teams => :environment do |t, args| - users = User.find_all_by_admin(false, :select => 'id').map(&:id) - admins = User.find_all_by_admin(true, :select => 'id').map(&:id) + user_ids = User.where(:admin => false).pluck(:id) + admin_ids = User.where(:admin => true).pluck(:id) - users.each do |user| - puts "#{user}" - end - - Project.all.each do |project| - puts "Importing #{users.length} users into #{project.path}" - UsersProject.bulk_import(project, users, UsersProject::DEVELOPER) - puts "Importing #{admins.length} admins into #{project.path}" - UsersProject.bulk_import(project, admins, UsersProject::MASTER) + Project.find_each do |project| + puts "Importing #{user_ids.size} users into #{project.code}" + UsersProject.bulk_import(project, user_ids, UsersProject::DEVELOPER) + puts "Importing #{admin_ids.size} admins into #{project.code}" + UsersProject.bulk_import(project, admin_ids, UsersProject::MASTER) end end desc "Add user to as a developer to all projects" task :add_user_to_project_teams, [:email] => :environment do |t, args| - user_email = args.email - user = User.find_by_email(user_email) + user = User.find_by_email args.email + project_ids = Project.pluck(:id) - project_ids = Project.all.map(&:id) - - UsersProject.user_bulk_import(user,project_ids,UsersProject::DEVELOPER) + UsersProject.user_bulk_import(user, project_ids, UsersProject::DEVELOPER) end diff --git a/lib/tasks/bulk_import.rake b/lib/tasks/bulk_import.rake index edb4a599..914f920a 100644 --- a/lib/tasks/bulk_import.rake +++ b/lib/tasks/bulk_import.rake @@ -1,20 +1,17 @@ - desc "Imports existing Git repos from a directory into new projects in git_base_path" task :import_projects, [:directory,:email] => :environment do |t, args| - user_email = args.email - import_directory = args.directory + user_email, import_directory = args.email, args.directory repos_to_import = Dir.glob("#{import_directory}/*") git_base_path = Gitlab.config.git_base_path - puts "Found #{repos_to_import.length} repos to import" + imported_count, skipped_count, failed_count = 0 + + puts "Found #{repos_to_import.size} repos to import" - imported_count = 0 - skipped_count = 0 - failed_count = 0 repos_to_import.each do |repo_path| repo_name = File.basename repo_path + clone_path = "#{git_base_path}#{repo_name}.git" puts " Processing #{repo_name}" - clone_path = "#{git_base_path}#{repo_name}.git" if Dir.exists? clone_path if Project.find_by_code(repo_name) @@ -38,7 +35,6 @@ task :import_projects, [:directory,:email] => :environment do |t, args| else failed_count += 1 end - end 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) git_user = Gitlab.config.ssh_user begin sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}" - true - rescue Exception=> msg - puts " ERROR: Faild to clone #{existing_path} to #{new_path}" - puts " Make sure #{git_user} can reach #{existing_path}" - puts " Exception-MSG: #{msg}" - false + rescue Exception => msg + puts " ERROR: Failed to clone #{existing_path} to #{new_path}" + puts " Make sure #{git_user} can reach #{existing_path}" + puts " Exception-MSG: #{msg}" end end -# Creats a project in Gitlag given a @project_name@ to use (for name, web url, and code -# url) and a @user_email@ that will be assigned as the owner of the project. +# Creates a project in GitLab given a `project_name` to use +# (for name, web url, and code url) and a `user_email` that will be +# assigned as the owner of the project. def create_repo_project(project_name, user_email) - user = User.find_by_email(user_email) - if user + if user = User.find_by_email(user_email) # Using find_by_code since that's the most important identifer to be unique if Project.find_by_code(project_name) puts " INFO: Project #{project_name} already exists in Gitlab, skipping." - false else - project = nil - if Project.find_by_code(project_name) - puts " ERROR: Project already exists #{project_name}" - return false - project = Project.find_by_code(project_name) - else - project = Project.create( - name: project_name, - code: project_name, - path: project_name, - owner: user, - description: "Automatically created from Rake on #{Time.now.to_s}" - ) - end - - unless project.valid? - puts " ERROR: Failed to create project #{project} because #{project.errors.first}" - return false - end - - # Add user as admin for project - project.users_projects.create!( - :project_access => UsersProject::MASTER, - :user => user + project = Project.create( + name: project_name, + code: project_name, + path: project_name, + owner: user, + description: "Automatically created from 'import_projects' rake task on #{Time.now}" ) - # Per projects_controller.rb#37 - project.update_repository - if project.valid? - true + # Add user as admin for project + project.users_projects.create!(:project_access => UsersProject::MASTER, :user => user) + project.update_repository else puts " ERROR: Failed to create project #{project} because #{project.errors.first}" - false end end else - puts " ERROR: #{user_email} not found, skipping" - false + puts " ERROR: user with #{user_email} not found, skipping" end end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 04d240f6..06d7cb65 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -2,22 +2,20 @@ require 'active_record/fixtures' namespace :gitlab do namespace :app do - - # Create backup of gitlab system - desc "GITLAB | Create a backup of the gitlab system" + # Create backup of GitLab system + desc "GITLAB | Create a backup of the GitLab system" task :backup_create => :environment do - Rake::Task["gitlab:app:db_dump"].invoke Rake::Task["gitlab:app:repo_dump"].invoke Dir.chdir(Gitlab.config.backup_path) # saving additional informations - s = Hash.new - s["db_version"] = "#{ActiveRecord::Migrator.current_version}" - s["backup_created_at"] = "#{Time.now}" - s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"") - s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"") + s = {} + s[:db_version] = "#{ActiveRecord::Migrator.current_version}" + s[:backup_created_at] = "#{Time.now}" + s[:gitlab_version] = %x{git rev-parse HEAD}.gsub(/\n/,"") + s[:tar_version] = %x{tar --version | head -1}.gsub(/\n/,"") File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file| file << s.to_yaml.gsub(/^---\n/,'') @@ -32,7 +30,7 @@ namespace :gitlab do end # cleanup: remove tmp files - print "Deletion of tmp directories..." + print "Deleting tmp directories..." if Kernel.system("rm -rf repositories/ db/ backup_information.yml") puts "[DONE]".green else @@ -52,26 +50,23 @@ namespace :gitlab do else puts "[SKIPPING]".yellow end - end - - # Restore backup of gitlab system + # Restore backup of GitLab system desc "GITLAB | Restore a previously created backup" task :backup_restore => :environment do - Dir.chdir(Gitlab.config.backup_path) # check for existing backups in the backup dir file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i } - puts "no backup found" if file_list.count == 0 + puts "no backups found" if file_list.count == 0 if file_list.count > 1 && ENV["BACKUP"].nil? puts "Found more than one backup, please specify which one you want to restore:" puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup" exit 1; end - tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") + tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar") unless File.exists?(tar_file) puts "The specified backup doesn't exist!" @@ -102,16 +97,14 @@ namespace :gitlab do Rake::Task["gitlab:app:repo_restore"].invoke # cleanup: remove tmp files - print "Deletion of tmp directories..." + print "Deleting tmp directories..." if Kernel.system("rm -rf repositories/ db/ backup_information.yml") puts "[DONE]".green else puts "[FAILED]".red end - end - ################################################################################ ################################# invoked tasks ################################ @@ -121,7 +114,7 @@ namespace :gitlab do backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo) puts "Dumping repositories:" - project = Project.all.map { |n| [n.path,n.path_to_repo] } + project = Project.all.map { |n| [n.path, n.path_to_repo] } project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] project.each do |project| print "- Dumping repository #{project.first}... " @@ -136,11 +129,11 @@ namespace :gitlab do task :repo_restore => :environment do backup_path_repo = File.join(Gitlab.config.backup_path, "repositories") puts "Restoring repositories:" - project = Project.all.map { |n| [n.path,n.path_to_repo] } + project = Project.all.map { |n| [n.path, n.path_to_repo] } project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")] project.each do |project| print "- Restoring repository #{project.first}... " - FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff + FileUtils.rm_rf(project.second) if File.dirname(project.second) # delete old stuff 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") permission_commands = [ "sudo chmod -R g+rwX #{Gitlab.config.git_base_path}", @@ -157,8 +150,9 @@ namespace :gitlab do ###################################### DB ###################################### task :db_dump => :environment do - backup_path_db = File.join(Gitlab.config.backup_path, "db") - FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db) + backup_path_db = File.join(Gitlab.config.backup_path, "db") + FileUtils.mkdir_p(backup_path_db) unless Dir.exists?(backup_path_db) + puts "Dumping database tables:" ActiveRecord::Base.connection.tables.each do |tbl| print "- Dumping table #{tbl}... " @@ -176,9 +170,11 @@ namespace :gitlab do end task :db_restore=> :environment do - backup_path_db = File.join(Gitlab.config.backup_path, "db") + backup_path_db = File.join(Gitlab.config.backup_path, "db") + puts "Restoring database tables:" Rake::Task["db:reset"].invoke + Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir| fixture_file = File.basename(dir, ".*" ) print "- Loading fixture #{fixture_file}..." diff --git a/lib/tasks/gitlab/enable_automerge.rake b/lib/tasks/gitlab/enable_automerge.rake index 0a1a0fa7..13b4bab6 100644 --- a/lib/tasks/gitlab/enable_automerge.rake +++ b/lib/tasks/gitlab/enable_automerge.rake @@ -1,7 +1,7 @@ namespace :gitlab do namespace :app do desc "GITLAB | Enable auto merge" - task :enable_automerge => :environment do + task :enable_automerge => :environment do Gitlab::Gitolite.new.enable_automerge Project.find_each do |project| diff --git a/lib/tasks/gitlab/gitolite_rebuild.rake b/lib/tasks/gitlab/gitolite_rebuild.rake index 534aa315..fce10eb5 100644 --- a/lib/tasks/gitlab/gitolite_rebuild.rake +++ b/lib/tasks/gitlab/gitolite_rebuild.rake @@ -1,11 +1,10 @@ namespace :gitlab do namespace :gitolite do desc "GITLAB | Rebuild each project at gitolite config" - task :update_repos => :environment do + task :update_repos => :environment do puts "Starting Projects" Project.find_each(:batch_size => 100) do |project| - puts - puts "=== #{project.name}" + puts "\n=== #{project.name}" project.update_repository puts end diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 49c86461..08f35c7e 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -4,8 +4,7 @@ namespace :gitlab do task :setup => [ 'db:setup', 'db:seed_fu', - 'gitlab:app:enable_automerge' + 'gitlab:app:enable_automerge' ] end end - diff --git a/lib/tasks/gitlab/status.rake b/lib/tasks/gitlab/status.rake index e5b5e122..302f417c 100644 --- a/lib/tasks/gitlab/status.rake +++ b/lib/tasks/gitlab/status.rake @@ -1,37 +1,37 @@ namespace :gitlab do namespace :app do - desc "GITLAB | Check gitlab installation status" + desc "GITLAB | Check GitLab installation status" task :status => :environment do - puts "Starting diagnostic".yellow + puts "Starting diagnostics".yellow git_base_path = Gitlab.config.git_base_path print "config/database.yml............" - if File.exists?(File.join Rails.root, "config", "database.yml") + if File.exists?(Rails.root.join "config", "database.yml") puts "exists".green - else + else puts "missing".red return end print "config/gitlab.yml............" - if File.exists?(File.join Rails.root, "config", "gitlab.yml") - puts "exists".green + if File.exists?(Rails.root.join "config", "gitlab.yml") + puts "exists".green else puts "missing".red return end print "#{git_base_path}............" - if File.exists?(git_base_path) - puts "exists".green - else + if File.exists?(git_base_path) + puts "exists".green + else puts "missing".red return end print "#{git_base_path} is writable?............" if File.stat(git_base_path).writable? - puts "YES".green + puts "YES".green else puts "NO".red return @@ -41,16 +41,16 @@ namespace :gitlab do `git clone #{Gitlab.config.gitolite_admin_uri} /tmp/gitolite_gitlab_test` FileUtils.rm_rf("/tmp/gitolite_gitlab_test") print "Can clone gitolite-admin?............" - puts "YES".green - rescue + puts "YES".green + rescue print "Can clone gitolite-admin?............" puts "NO".red return end print "UMASK for .gitolite.rc is 0007? ............" - unless open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).empty? - puts "YES".green + if open("#{git_base_path}/../.gitolite.rc").grep(/UMASK([ \t]*)=([ \t>]*)0007/).any? + puts "YES".green else puts "NO".red return @@ -69,16 +69,15 @@ namespace :gitlab do end end - - if Project.count > 0 + if Project.count > 0 puts "Validating projects repositories:".yellow Project.find_each(:batch_size => 100) do |project| print "#{project.name}....." - hook_file = File.join(project.path_to_repo, 'hooks','post-receive') + hook_file = File.join(project.path_to_repo, 'hooks', 'post-receive') unless File.exists?(hook_file) - puts "post-receive file missing".red - next + puts "post-receive file missing".red + return end puts "post-receive file ok".green diff --git a/lib/tasks/gitlab/write_hook.rake b/lib/tasks/gitlab/write_hook.rake index 5e9fc8eb..ccc96aad 100644 --- a/lib/tasks/gitlab/write_hook.rake +++ b/lib/tasks/gitlab/write_hook.rake @@ -4,7 +4,6 @@ namespace :gitlab do task :write_hooks => :environment do gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common") gitlab_hooks_path = Rails.root.join("lib", "hooks") - gitlab_hook_files = ['post-receive'] gitlab_hook_files.each do |file_name| @@ -20,4 +19,3 @@ namespace :gitlab do end end end - diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb index bb124d8b..d450b687 100644 --- a/spec/helpers/tree_helper_spec.rb +++ b/spec/helpers/tree_helper_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe TreeHelper do describe '#markup?' do - %w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type| + %w(textile rdoc org creole mediawiki rst asciidoc pod).each do |type| it "returns true for #{type} files" do markup?("README.#{type}").should be_true end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 08176754..14a373e1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -73,4 +73,30 @@ describe User do user.authentication_token.should_not be_blank end end + + describe "attributes can be changed by a regular user" do + before do + @user = Factory :user + @user.update_attributes(skype: "testskype", linkedin: "testlinkedin") + end + it { @user.skype.should == 'testskype' } + it { @user.linkedin.should == 'testlinkedin' } + end + + describe "attributes that shouldn't be changed by a regular user" do + before do + @user = Factory :user + @user.update_attributes(projects_limit: 50) + end + it { @user.projects_limit.should_not == 50 } + end + + describe "attributes can be changed by an admin user" do + before do + @admin_user = Factory :admin + @admin_user.update_attributes({ skype: "testskype", projects_limit: 50 }, as: :admin) + end + it { @admin_user.skype.should == 'testskype' } + it { @admin_user.projects_limit.should == 50 } + end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index b46380b2..498bbad6 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -111,42 +111,52 @@ describe Gitlab::API do end end - describe "GET /projects/:id/users" do - it "should return project users" do - get api("/projects/#{project.code}/users", user) - + describe "GET /projects/:id/members" do + it "should return project team members" do + get api("/projects/#{project.code}/members", user) response.status.should == 200 - json_response.should be_an Array json_response.count.should == 2 - json_response.first['user']['id'].should == user.id + json_response.first['email'].should == user.email end end - describe "POST /projects/:id/users" do - it "should add users to project" do - expect { - post api("/projects/#{project.code}/users", user), - user_ids: {"0" => user2.id}, project_access: UsersProject::DEVELOPER - }.to change {project.users_projects.where(:project_access => UsersProject::DEVELOPER).count}.by(1) + describe "GET /projects/:id/members/:user_id" do + it "should return project team member" do + get api("/projects/#{project.code}/members/#{user.id}", user) + response.status.should == 200 + json_response['email'].should == user.email + json_response['access_level'].should == UsersProject::MASTER end end - describe "PUT /projects/:id/users" do - it "should update users to new access role" do + describe "POST /projects/:id/members" do + it "should add user to project team" do expect { - put api("/projects/#{project.code}/users", user), - user_ids: {"0" => user3.id}, project_access: UsersProject::MASTER - }.to change {project.users_projects.where(:project_access => UsersProject::MASTER).count}.by(1) + post api("/projects/#{project.code}/members", user), user_id: user2.id, + access_level: UsersProject::DEVELOPER + }.to change { UsersProject.count }.by(1) + + response.status.should == 201 + json_response['email'].should == user2.email + json_response['access_level'].should == UsersProject::DEVELOPER end end - describe "DELETE /projects/:id/users" do - it "should delete users from project" do + describe "PUT /projects/:id/members/:user_id" do + it "should update project team member" do + put api("/projects/#{project.code}/members/#{user3.id}", user), access_level: UsersProject::MASTER + response.status.should == 200 + json_response['email'].should == user3.email + json_response['access_level'].should == UsersProject::MASTER + end + end + + describe "DELETE /projects/:id/members/:user_id" do + it "should remove user from project team" do expect { - delete api("/projects/#{project.code}/users", user), - user_ids: {"0" => user3.id} - }.to change {project.users_projects.count}.by(-1) + delete api("/projects/#{project.code}/members/#{user3.id}", user) + }.to change { UsersProject.count }.by(-1) end end @@ -189,6 +199,27 @@ describe Gitlab::API do end end + describe "GET /projects/:id/repository/commits" do + context "authorized user" do + before { project.add_access(user2, :read) } + + it "should return project commits" do + get api("/projects/#{project.code}/repository/commits", user) + response.status.should == 200 + + json_response.should be_an Array + json_response.first['id'].should == project.commit.id + end + end + + context "unauthorized user" do + it "should not return project commits" do + get api("/projects/#{project.code}/repository/commits") + response.status.should == 401 + end + end + end + describe "GET /projects/:id/snippets/:snippet_id" do it "should return a project snippet" do get api("/projects/#{project.code}/snippets/#{snippet.id}", user) diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb new file mode 100644 index 00000000..f251f392 --- /dev/null +++ b/spec/requests/api/session_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::API do + include ApiHelpers + + let(:user) { Factory :user } + + describe "POST /session" do + context "when valid password" do + it "should return private token" do + post api("/session"), email: user.email, password: '123456' + response.status.should == 201 + + json_response['email'].should == user.email + json_response['private_token'].should == user.private_token + end + end + + context "when invalid password" do + it "should return authentication error" do + post api("/session"), email: user.email, password: '123' + response.status.should == 401 + + json_response['email'].should be_nil + json_response['private_token'].should be_nil + end + end + + context "when empty password" do + it "should return authentication error" do + post api("/session"), email: user.email + response.status.should == 401 + + json_response['email'].should be_nil + json_response['private_token'].should be_nil + end + end + end +end diff --git a/spec/requests/api/ssh_keys_spec.rb b/spec/requests/api/ssh_keys_spec.rb deleted file mode 100644 index 7fb8c920..00000000 --- a/spec/requests/api/ssh_keys_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Keys do - include ApiHelpers - let(:user) { - user = Factory.create :user - user.reset_authentication_token! - user - } - let(:key) { Factory.create :key, { user: user}} - - describe "GET /keys" do - context "when unauthenticated" do - it "should return authentication error" do - get api("/keys") - response.status.should == 401 - end - end - context "when authenticated" do - it "should return array of ssh keys" do - user.keys << key - user.save - get api("/keys", user) - response.status.should == 200 - json_response.should be_an Array - json_response.first["title"].should == key.title - end - end - end - - describe "GET /keys/:id" do - it "should returm single key" do - user.keys << key - user.save - get api("/keys/#{key.id}", user) - response.status.should == 200 - json_response["title"].should == key.title - end - it "should return 404 Not Found within invalid ID" do - get api("/keys/42", user) - response.status.should == 404 - end - end - - describe "POST /keys" do - it "should not create invalid ssh key" do - post api("/keys", user), { title: "invalid key" } - response.status.should == 404 - end - it "should create ssh key" do - key_attrs = Factory.attributes :key - expect { - post api("/keys", user), key_attrs - }.to change{ user.keys.count }.by(1) - end - end - - describe "DELETE /keys/:id" do - it "should delete existed key" do - user.keys << key - user.save - expect { - delete api("/keys/#{key.id}", user) - }.to change{user.keys.count}.by(-1) - end - it "should return 404 Not Found within invalid ID" do - delete api("/keys/42", user) - response.status.should == 404 - end - end - -end - diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index e25fe134..243f70f5 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' describe Gitlab::API do include ApiHelpers - let(:user) { Factory :user } + let(:user) { Factory :user } + let(:key) { Factory :key, user: user } describe "GET /users" do context "when unauthenticated" do @@ -38,4 +39,68 @@ describe Gitlab::API do json_response['email'].should == user.email end end + + describe "GET /user/keys" do + context "when unauthenticated" do + it "should return authentication error" do + get api("/user/keys") + response.status.should == 401 + end + end + + context "when authenticated" do + it "should return array of ssh keys" do + user.keys << key + user.save + get api("/user/keys", user) + response.status.should == 200 + json_response.should be_an Array + json_response.first["title"].should == key.title + end + end + end + + describe "GET /user/keys/:id" do + it "should returm single key" do + user.keys << key + user.save + get api("/user/keys/#{key.id}", user) + response.status.should == 200 + json_response["title"].should == key.title + end + + it "should return 404 Not Found within invalid ID" do + get api("/user/keys/42", user) + response.status.should == 404 + end + end + + describe "POST /user/keys" do + it "should not create invalid ssh key" do + post api("/user/keys", user), { title: "invalid key" } + response.status.should == 404 + end + + it "should create ssh key" do + key_attrs = Factory.attributes :key + expect { + post api("/user/keys", user), key_attrs + }.to change{ user.keys.count }.by(1) + end + end + + describe "DELETE /user/keys/:id" do + it "should delete existed key" do + user.keys << key + user.save + expect { + delete api("/user/keys/#{key.id}", user) + }.to change{user.keys.count}.by(-1) + end + + it "should return 404 Not Found within invalid ID" do + delete api("/user/keys/42", user) + response.status.should == 404 + end + end end