Commit 4f529a88 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into issue_13212

parents 45c29fa2 eb9838d3
...@@ -3,6 +3,12 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -3,6 +3,12 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased) v 8.6.0 (unreleased)
- Improve the formatting for the user page bio (Connor Shea) - Improve the formatting for the user page bio (Connor Shea)
- Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Fix avatar stretching by providing a cropping feature (Johann Pardanaud)
- Strip leading and trailing spaces in URL validator (evuez)
- Update documentation to reflect Guest role not being enforced on internal projects
v 8.5.2
- Fix sidebar overlapping content when screen width was below 1200px
- Fix error 500 when commenting on a commit
v 8.5.1 v 8.5.1
- Fix group projects styles - Fix group projects styles
...@@ -23,9 +29,6 @@ v 8.5.1 ...@@ -23,9 +29,6 @@ v 8.5.1
- Update sentry-raven gem to 0.15.6 - Update sentry-raven gem to 0.15.6
- Add build coverage in project's builds page (Steffen Köhler) - Add build coverage in project's builds page (Steffen Köhler)
v 8.5.2
- Fix error 500 when commenting on a commit
v 8.5.0 v 8.5.0
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu) - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
- Cache various Repository methods to improve performance (Yorick Peterse) - Cache various Repository methods to improve performance (Yorick Peterse)
......
...@@ -213,7 +213,6 @@ gem 'jquery-atwho-rails', '~> 1.3.2' ...@@ -213,7 +213,6 @@ gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.0.0' gem 'jquery-rails', '~> 4.0.0'
gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 5.0.0' gem 'jquery-ui-rails', '~> 5.0.0'
gem 'nprogress-rails', '~> 0.1.6.7'
gem 'raphael-rails', '~> 2.1.2' gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.2.0' gem 'request_store', '~> 1.2.0'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
......
...@@ -483,7 +483,6 @@ GEM ...@@ -483,7 +483,6 @@ GEM
newrelic_rpm (3.14.1.311) newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2) nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2) mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
...@@ -964,7 +963,6 @@ DEPENDENCIES ...@@ -964,7 +963,6 @@ DEPENDENCIES
net-ssh (~> 3.0.1) net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14) newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.8.0) octokit (~> 3.8.0)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
......
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
#= require ace/ace #= require ace/ace
#= require ace/ext-searchbox #= require ace/ext-searchbox
#= require underscore #= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require dropzone #= require dropzone
#= require mousetrap #= require mousetrap
#= require mousetrap/pause #= require mousetrap/pause
......
NProgress.configure(showSpinner: false) Turbolinks.enableProgressBar();
defaultClass = 'tanuki-shape' defaultClass = 'tanuki-shape'
pieces = [ pieces = [
......
...@@ -25,12 +25,6 @@ ...@@ -25,12 +25,6 @@
*/ */
@import "framework"; @import "framework";
/*
* NProgress load bar css
*/
@import 'nprogress';
@import 'nprogress-bootstrap';
/* /*
* Font icons * Font icons
*/ */
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
@import "framework/mobile.scss"; @import "framework/mobile.scss";
@import "framework/nav.scss"; @import "framework/nav.scss";
@import "framework/pagination.scss"; @import "framework/pagination.scss";
@import "framework/progress.scss";
@import "framework/panels.scss"; @import "framework/panels.scss";
@import "framework/selects.scss"; @import "framework/selects.scss";
@import "framework/sidebar.scss"; @import "framework/sidebar.scss";
......
html.turbolinks-progress-bar::before {
background-color: $progress-color!important;
height: 2px!important;
box-shadow: 0 0 10px $progress-color, 0 0 5px $progress-color;
}
...@@ -7,7 +7,7 @@ $gl-header-color: #323232; ...@@ -7,7 +7,7 @@ $gl-header-color: #323232;
$gl-link-color: #333c48; $gl-link-color: #333c48;
$md-text-color: #444; $md-text-color: #444;
$md-link-color: #3084bb; $md-link-color: #3084bb;
$nprogress-color: #c0392b; $progress-color: #c0392b;
$gl-font-size: 15px; $gl-font-size: 15px;
$list-font-size: 15px; $list-font-size: 15px;
$sidebar_collapsed_width: 62px; $sidebar_collapsed_width: 62px;
......
.appearance-logo-preview {
max-width: 400px;
margin-bottom: 20px;
}
.appearance-light-logo-preview {
background-color: $background-color;
max-width: 72px;
padding: 10px;
margin-bottom: 10px;
}
...@@ -12,29 +12,10 @@ ...@@ -12,29 +12,10 @@
} }
} }
.todos {
.panel {
border-top: none;
margin-bottom: 0;
}
}
.todo-item { .todo-item {
font-size: $gl-font-size; font-size: $gl-font-size;
padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top); padding-left: $gl-avatar-size + $gl-padding-top;
border-bottom: 1px solid $table-border-color; color: $secondary-text;
color: #7f8fa4;
&.todo-inline {
.avatar {
position: relative;
top: -2px;
}
.todo-title {
line-height: 40px;
}
}
a { a {
color: #4c4e54; color: #4c4e54;
...@@ -48,7 +29,7 @@ ...@@ -48,7 +29,7 @@
@include str-truncated(calc(100% - 174px)); @include str-truncated(calc(100% - 174px));
font-weight: 600; font-weight: 600;
.author_name { .author-name {
color: #333; color: #333;
} }
} }
...@@ -88,17 +69,7 @@ ...@@ -88,17 +69,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
.todo-note-icon {
color: #777;
float: left;
font-size: $gl-font-size;
line-height: 16px;
margin-right: 5px;
}
} }
&:last-child { border:none }
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
......
class Admin::AppearancesController < Admin::ApplicationController
before_action :set_appearance, except: :create
def show
end
def preview
end
def create
@appearance = Appearance.new(appearance_params)
if @appearance.save
redirect_to admin_appearances_path, notice: 'Appearance was successfully created.'
else
render action: 'show'
end
end
def update
if @appearance.update(appearance_params)
redirect_to admin_appearances_path, notice: 'Appearance was successfully updated.'
else
render action: 'show'
end
end
def logo
@appearance.remove_logo!
@appearance.save
redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.'
end
def header_logos
@appearance.remove_header_logo!
@appearance.save
redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_appearance
@appearance = Appearance.last || Appearance.new
end
# Only allow a trusted parameter "white list" through.
def appearance_params
params.require(:appearance).permit(
:title, :description, :logo, :logo_cache, :header_logo, :header_logo_cache,
:updated_by
)
end
end
...@@ -4,12 +4,22 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -4,12 +4,22 @@ class Projects::ForksController < Projects::ApplicationController
before_action :authorize_download_code! before_action :authorize_download_code!
def index def index
@sort = params[:sort] || 'id_desc' base_query = project.forks.includes(:creator)
@all_forks = project.forks.includes(:creator).order_by(@sort)
@public_forks, @protected_forks = @all_forks.partition do |project| @forks = if current_user
can?(current_user, :read_project, project) base_query.where('projects.visibility_level IN (?) OR projects.id IN (?)',
Project.public_and_internal_levels,
current_user.authorized_projects.pluck(:id))
else
base_query.where('projects.visibility_level = ?', Project::PUBLIC)
end end
@total_forks_count = base_query.size
@private_forks_count = @total_forks_count - @forks.size
@public_forks_count = @total_forks_count - @private_forks_count
@sort = params[:sort] || 'id_desc'
@forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE)
end end
def new def new
......
...@@ -55,14 +55,15 @@ class UploadsController < ApplicationController ...@@ -55,14 +55,15 @@ class UploadsController < ApplicationController
"user" => User, "user" => User,
"project" => Project, "project" => Project,
"note" => Note, "note" => Note,
"group" => Group "group" => Group,
"appearance" => Appearance
} }
upload_models[params[:model]] upload_models[params[:model]]
end end
def upload_mount def upload_mount
upload_mounts = %w(avatar attachment file) upload_mounts = %w(avatar attachment file logo header_logo)
if upload_mounts.include?(params[:mounted_as]) if upload_mounts.include?(params[:mounted_as])
params[:mounted_as] params[:mounted_as]
......
module AppearancesHelper module AppearancesHelper
def brand_item
nil
end
def brand_title def brand_title
if brand_item && brand_item.title
brand_item.title
else
'GitLab Community Edition' 'GitLab Community Edition'
end end
end
def brand_image def brand_image
if brand_item.logo?
image_tag brand_item.logo
else
nil nil
end end
end
def brand_text def brand_text
nil markdown(brand_item.description)
end
def brand_item
@appearance ||= Appearance.first
end end
def brand_header_logo def brand_header_logo
if brand_item && brand_item.header_logo?
image_tag brand_item.header_logo
else
render 'shared/logo.svg' render 'shared/logo.svg'
end end
end
end end
class Appearance < ActiveRecord::Base
validates :title, presence: true
validates :description, presence: true
validates :logo, file_size: { maximum: 1.megabyte }
validates :header_logo, file_size: { maximum: 1.megabyte }
mount_uploader :logo, AttachmentUploader
mount_uploader :header_logo, AttachmentUploader
end
...@@ -27,7 +27,7 @@ class Milestone < ActiveRecord::Base ...@@ -27,7 +27,7 @@ class Milestone < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :issues has_many :issues
has_many :labels, through: :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
has_many :merge_requests has_many :merge_requests
has_many :participants, through: :issues, source: :assignee has_many :participants, through: :issues, source: :assignee
......
...@@ -29,8 +29,11 @@ class UrlValidator < ActiveModel::EachValidator ...@@ -29,8 +29,11 @@ class UrlValidator < ActiveModel::EachValidator
end end
def valid_url?(value) def valid_url?(value)
return false if value.nil?
options = default_options.merge(self.options) options = default_options.merge(self.options)
value.strip!
value =~ /\A#{URI.regexp(options[:protocols])}\z/ value =~ /\A#{URI.regexp(options[:protocols])}\z/
end end
end end
= form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f|
- if @appearance.errors.any?
.alert.alert-danger
- @appearance.errors.full_messages.each do |msg|
%p= msg
%fieldset.sign-in
%legend
Sign in/Sign up pages:
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
= f.text_field :title, class: "form-control"
.form-group
= f.label :description, class: 'control-label'
.col-sm-10
= f.text_area :description, class: "form-control", rows: 10
.hint
Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown', 'markdown'), target: '_blank'}.
.form-group
= f.label :logo, class: 'control-label'
.col-sm-10
- if @appearance.logo?
= image_tag @appearance.logo_url, class: 'appearance-logo-preview'
- if @appearance.persisted?
%br
= link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo"
%hr
= f.hidden_field :logo_cache
= f.file_field :logo, class: ""
.hint
Maximum file size is 1MB. Pages are optimized for a 640x360 px logo.
%fieldset.app_logo
%legend
Navigation bar:
.form-group
= f.label :header_logo, 'Header logo', class: 'control-label'
.col-sm-10
- if @appearance.header_logo?
= image_tag @appearance.header_logo_url, class: 'appearance-light-logo-preview'
- if @appearance.persisted?
%br
= link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo"
%hr
= f.hidden_field :header_logo_cache
= f.file_field :header_logo, class: ""
.hint
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
.form-actions
= f.submit 'Save', class: 'btn btn-save'
- if @appearance.persisted?
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
- if @appearance.updated_at
%span.pull-right
Last edit #{time_ago_with_tooltip(@appearance.updated_at)}
- page_title "Preview | Appearance"
%h3.page-title
Appearance settings - Preview
%hr
.ui-box
.title
Sign-in page
%div
.login-page
.container
.content
.login-title
%h1= brand_title
%hr
.container
.content
.row
.col-sm-7
.brand-image
= brand_image
.brand_text
= brand_text
.col-sm-4
.login-box
%h3.page-title Sign in
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
= button_tag "Sign in", class: "btn-create btn"
- page_title "Appearance"
%h3.page-title
Appearance settings
%p.light
You can modify the look and feel of GitLab here
= render 'form'
%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) } %li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) }
.todo-item{class: 'todo-block'} .todo-item.todo-block
= image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:''
.todo-title .todo-title
%span.author_name %span.author-name
= link_to_author todo = link_to_author todo
%span.todo_label %span.todo-label
= todo_action_name(todo) = todo_action_name(todo)
= todo_target_link(todo) = todo_target_link(todo)
......
...@@ -56,6 +56,11 @@ ...@@ -56,6 +56,11 @@
= icon('cog fw') = icon('cog fw')
%span %span
Background Jobs Background Jobs
= nav_link(controller: :appearances) do
= link_to admin_appearances_path, title: 'Appearances' do
= icon('image')
%span
Appearance
= nav_link(controller: :applications) do = nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do = link_to admin_applications_path, title: 'Applications' do
......
...@@ -3,17 +3,16 @@ ...@@ -3,17 +3,16 @@
Too many changes to show. Too many changes to show.
.pull-right .pull-right
- unless diff_hard_limit_enabled? - unless diff_hard_limit_enabled?
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm btn-warning" = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm"
- if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit) - if current_controller?(:commit)
= link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-warning btn-sm" = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm"
= link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-warning btn-sm" = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-sm"
- elsif @merge_request && @merge_request.persisted? - elsif @merge_request && @merge_request.persisted?
= link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-warning btn-sm" = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-sm"
= link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-warning btn-sm" = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm"
%p %p
To preserve performance only To preserve performance only
%strong #{shown_files_count} of #{diffs.size} %strong #{shown_files_count} of #{diffs.size}
files are displayed. files are displayed.
.top-area .top-area
.nav-text .nav-text
- public_count = @public_forks.size - full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private"
- protected_count = @protected_forks.size == #{pluralize(@total_forks_count, 'fork')}: #{full_count_title}
- full_count_title = "#{public_count} public and #{protected_count} private"
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
.nav-controls .nav-controls
= search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short', = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
...@@ -41,17 +39,17 @@ ...@@ -41,17 +39,17 @@
.projects-list-holder .projects-list-holder
- if @public_forks.blank? - if @forks.blank?
%ul.content-list %ul.content-list
%li %li
.nothing-here-block No forks to show .nothing-here-block No forks to show
- else - else
= render 'shared/projects/list', projects: @public_forks, use_creator_avatar: true, = render 'shared/projects/list', projects: @forks, use_creator_avatar: true,
forks: true, show_last_commit_as_description: true forks: true, show_last_commit_as_description: true
- if protected_count > 0 - if @private_forks_count > 0
%ul.projects-list.private-forks-notice %ul.projects-list.private-forks-notice
%li.project-row %li.project-row
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon') = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(protected_count, 'private fork') %strong= pluralize(@private_forks_count, 'private fork')
%span you have no access to. %span you have no access to.
...@@ -156,6 +156,11 @@ Rails.application.routes.draw do ...@@ -156,6 +156,11 @@ Rails.application.routes.draw do
to: "uploads#show", to: "uploads#show",
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ } constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ }
# Appearance
get ":model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ }
# Project markdown uploads # Project markdown uploads
get ":namespace_id/:project_id/:secret/:filename", get ":namespace_id/:project_id/:secret/:filename",
to: "projects/uploads#show", to: "projects/uploads#show",
...@@ -253,6 +258,14 @@ Rails.application.routes.draw do ...@@ -253,6 +258,14 @@ Rails.application.routes.draw do
end end
end end
resource :appearances, path: 'appearance' do
member do
get :preview
delete :logo
delete :header_logos
end
end
resource :application_settings, only: [:show, :update] do resource :application_settings, only: [:show, :update] do
resources :services resources :services
put :reset_runners_token put :reset_runners_token
......
class CreateAppearancesCe < ActiveRecord::Migration
def change
unless table_exists?(:appearances)
create_table :appearances do |t|
t.string :title
t.text :description
t.string :header_logo
t.string :logo
t.timestamps null: false
end
end
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160220123949) do ActiveRecord::Schema.define(version: 20160222153918) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -24,6 +24,15 @@ ActiveRecord::Schema.define(version: 20160220123949) do ...@@ -24,6 +24,15 @@ ActiveRecord::Schema.define(version: 20160220123949) do
t.datetime "updated_at" t.datetime "updated_at"
end end
create_table "appearances", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "header_logo"
t.string "logo"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "application_settings", force: :cascade do |t| create_table "application_settings", force: :cascade do |t|
t.integer "default_projects_limit" t.integer "default_projects_limit"
t.boolean "signup_enabled" t.boolean "signup_enabled"
......
...@@ -16,40 +16,42 @@ ...@@ -16,40 +16,42 @@
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
## CI Documentation ## CI User documentation
- [Quick Start](ci/quick_start/README.md) - [Get started with GitLab CI](ci/quick_start/README.md)
- [Enable or disable GitLab CI](ci/enable_or_disable_ci.md) - [Learn how to enable or disable GitLab CI](ci/enable_or_disable_ci.md)
- [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) - [Learn how `.gitlab-ci.yml` works](ci/yaml/README.md)
- [Configuring runner](ci/runners/README.md) - [Configure a Runner, the application that runs your builds](ci/runners/README.md)
- [Configuring deployment](ci/deployment/README.md) - [Use Docker images with GitLab Runner](ci/docker/using_docker_images.md)
- [Using Docker Images](ci/docker/using_docker_images.md) - [Use CI to build Docker images](ci/docker/using_docker_build.md)
- [Using Docker Build](ci/docker/using_docker_build.md) - [Use variables in your `.gitlab-ci.yml`](ci/variables/README.md)
- [Using Variables](ci/variables/README.md) - [Use SSH keys in your build environment](ci/ssh_keys/README.md)
- [Using SSH keys](ci/ssh_keys/README.md) - [Trigger builds through the API](ci/triggers/README.md)
- [Build artifacts](ci/build_artifacts/README.md)
- [User permissions](ci/permissions/README.md) - [User permissions](ci/permissions/README.md)
- [API](ci/api/README.md) - [API](ci/api/README.md)
- [Triggering builds through the API](ci/triggers/README.md)
- [Build artifacts](ci/build_artifacts/README.md)
### CI Languages ### CI Examples
- [Testing PHP](ci/languages/php.md) - [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
- [Test your PHP applications](ci/examples/php.md)
- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md)
- [Test Clojure applications](ci/examples/test-clojure-application.md)
- [Using `dpl` as deployment tool](ci/deployment/README.md)
- Help your favorite programming language and GitLab by sending a merge request
with a guide for that language.
### CI Services ### CI Services
GitLab CI uses the `services` keyword to define what docker containers should
be linked with your base image. Below is a list of examples you may use:
- [Using MySQL](ci/services/mysql.md) - [Using MySQL](ci/services/mysql.md)
- [Using PostgreSQL](ci/services/postgres.md) - [Using PostgreSQL](ci/services/postgres.md)
- [Using Redis](ci/services/redis.md) - [Using Redis](ci/services/redis.md)
- [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services) - [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services)
### CI Examples
- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md)
- [Test Clojure applications](ci/examples/test-clojure-application.md)
- Help your favorite programming language and GitLab by sending a merge request with a guide for that language.
## Administrator documentation ## Administrator documentation
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough. - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
......
## GitLab CI Documentation ## GitLab CI Documentation
### User documentation ### CI User documentation
* [Quick Start](quick_start/README.md) - [Get started with GitLab CI](quick_start/README.md)
* [Enable or disable GitLab CI](enable_or_disable_ci.md) - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
* [Configuring project (.gitlab-ci.yml)](yaml/README.md) - [Learn how `.gitlab-ci.yml` works](yaml/README.md)
* [Configuring runner](runners/README.md) - [Configure a Runner, the application that runs your builds](runners/README.md)
* [Configuring deployment](deployment/README.md) - [Use Docker images with GitLab Runner](docker/using_docker_images.md)
* [Using Docker Images](docker/using_docker_images.md) - [Use CI to build Docker images](docker/using_docker_build.md)
* [Using Docker Build](docker/using_docker_build.md) - [Use variables in your `.gitlab-ci.yml`](variables/README.md)
* [Using Variables](variables/README.md) - [Use SSH keys in your build environment](ssh_keys/README.md)
* [Using SSH keys](ssh_keys/README.md) - [Trigger builds through the API](triggers/README.md)
* [Triggering builds through the API](triggers/README.md) - [Build artifacts](build_artifacts/README.md)
* [Build artifacts](build_artifacts/README.md) - [User permissions](permissions/README.md)
- [API](api/README.md)
### Languages
### CI Examples
* [Testing PHP](languages/php.md)
- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
### Services - [Test your PHP applications](examples/php.md)
- [Test and deploy Ruby applications to Heroku](examples/test-and-deploy-ruby-application-to-heroku.md)
* [Using MySQL](services/mysql.md) - [Test and deploy Python applications to Heroku](examples/test-and-deploy-python-application-to-heroku.md)
* [Using PostgreSQL](services/postgres.md) - [Test Clojure applications](examples/test-clojure-application.md)
* [Using Redis](services/redis.md) - [Using `dpl` as deployment tool](deployment/README.md)
* [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services) - Help your favorite programming language and GitLab by sending a merge request
with a guide for that language.
### Examples
### CI Services
+ [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
+ [Test and deploy Ruby applications to Heroku](examples/test-and-deploy-ruby-application-to-heroku.md) GitLab CI uses the `services` keyword to define what docker containers should
+ [Test and deploy Python applications to Heroku](examples/test-and-deploy-python-application-to-heroku.md) be linked with your base image. Below is a list of examples you may use:
+ [Test Clojure applications](examples/test-clojure-application.md)
+ Help your favorite programming language and GitLab by sending a merge request with a guide for that language. - [Using MySQL](services/mysql.md)
- [Using PostgreSQL](services/postgres.md)
### Administrator documentation - [Using Redis](services/redis.md)
- [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services)
* [User permissions](permissions/README.md)
* [API](api/README.md)
# Build script examples ## Build script examples
+ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) - [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
+ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) - [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
+ [Test a Clojure application](test-clojure-application.md) - [Test a Clojure application](test-clojure-application.md)
## Languages
This is a list of languages you can test with GitLab CI. Each section has
comprehensive documentation and comes with a test repository hosted on
GitLab.com.
- [Testing PHP](php.md)
### Languages
This is a list of languages you can test with GitLab CI. Each section has
comprehensive documentation and comes with a test repository hosted on
GitLab.com
+ [Testing PHP](php.md)
...@@ -201,6 +201,11 @@ You can access a builds badge image using following link: ...@@ -201,6 +201,11 @@ You can access a builds badge image using following link:
http://example.gitlab.com/namespace/project/badges/branch/build.svg http://example.gitlab.com/namespace/project/badges/branch/build.svg
``` ```
## Examples
Visit the [examples README][examples] to see a list of examples using GitLab
CI with various languages.
## Next steps ## Next steps
Awesome! You started using CI in GitLab! Awesome! You started using CI in GitLab!
...@@ -212,3 +217,4 @@ Visit our various languages examples at <https://gitlab.com/groups/gitlab-exampl ...@@ -212,3 +217,4 @@ Visit our various languages examples at <https://gitlab.com/groups/gitlab-exampl
[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation [runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation
[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/
[examples]: ../examples/README.md
## GitLab CI Services ## GitLab CI Services
GitLab CI uses the `services` keyword to define what docker containers should be GitLab CI uses the `services` keyword to define what docker containers should
linked with your base image. Below is a list of examples you may use. be linked with your base image. Below is a list of examples you may use.
+ [Using MySQL](mysql.md) - [Using MySQL](mysql.md)
+ [Using PostgreSQL](postgres.md) - [Using PostgreSQL](postgres.md)
+ [Using Redis](redis.md) - [Using Redis](redis.md)
+ [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services) - [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services)
...@@ -518,3 +518,10 @@ You can find the link under `/ci/lint` of your gitlab instance. ...@@ -518,3 +518,10 @@ You can find the link under `/ci/lint` of your gitlab instance.
If your commit message contains `[ci skip]`, the commit will be created but the If your commit message contains `[ci skip]`, the commit will be created but the
builds will be skipped. builds will be skipped.
## Examples
Visit the [examples README][examples] to see a list of examples using GitLab
CI with various languages.
[examples]: ../examples/README.md
# Changing the appearance of the login page
GitLab Community Edition offers a way to put your company's identity on the login page of your GitLab server and make it a branded login page.
By default, the page shows the GitLab logo and description.
![default_login_page](branded_login_page/default_login_page.png)
## Changing the appearance of the login page
Navigate to the **Admin** area and go to the **Appearance** page.
Fill in the required details like Title, Description and upload the company logo.
![appearance](branded_login_page/appearance.png)
After saving the page, your GitLab login page will have the details you filled in:
![company_login_page](branded_login_page/custom_sign_in.png)
# Customize the complete sign-in page (GitLab Enterprise Edition only) # Customize the complete sign-in page
Please see [Branded login page](http://doc.gitlab.com/ee/customization/branded_login_page.html) Please see [Branded login page](branded_login_page.md)
# Add a welcome message to the sign-in page (GitLab Community Edition) # Add a welcome message to the sign-in page (GitLab Community Edition)
It is possible to add a markdown-formatted welcome message to your GitLab It is possible to add a markdown-formatted welcome message to your GitLab
sign-in page. Users of GitLab Enterprise Edition should use the [branded login sign-in page. Users of GitLab Enterprise Edition should use the [branded login
page feature](/ee/customization/branded_login_page.html) instead. page feature](branded_login_page.md) instead.
The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI. The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
Admin area > Settings Admin area > Settings
# Development # Development
- [Architecture](architecture.md) of GitLab - [Architecture](architecture.md) of GitLab
- [Shell commands](shell_commands.md) in the GitLab codebase - [Benchmarking](benchmarking.md)
- [Rake tasks](rake_tasks.md) for development
- [CI setup](ci_setup.md) for testing GitLab - [CI setup](ci_setup.md) for testing GitLab
- [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md)
- [Migration Style Guide](migration_style_guide.md) for creating safe migrations
- [Rake tasks](rake_tasks.md) for development
- [Shell commands](shell_commands.md) in the GitLab codebase
- [Sidekiq debugging](sidekiq_debugging.md) - [Sidekiq debugging](sidekiq_debugging.md)
- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements - [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
- [Migration Style Guide](migration_style_guide.md) for creating safe migrations
- [How to dump production data to staging](dump_db.md)
- [Benchmarking](benchmarking.md)
# Gotchas
The purpose of this guide is to document potential "gotchas" that contributors
might encounter or should avoid during development of GitLab CE and EE.
## Don't `describe` symbols
Consider the following model spec:
```ruby
require 'rails_helper'
describe User do
describe :to_param do
it 'converts the username to a param' do
user = described_class.new(username: 'John Smith')
expect(user.to_param).to eq 'john-smith'
end
end
end
```
When run, this spec doesn't do what we might expect:
```sh
spec/models/user_spec.rb|6 error| Failure/Error: u = described_class.new NoMethodError: undefined method `new' for :to_param:Symbol
```
### Solution
Except for the top-level `describe` block, always provide a String argument to
`describe`.
## Don't `rescue Exception`
See ["Why is it bad style to `rescue Exception => e` in Ruby?"][Exception].
_**Note:** This rule is [enforced automatically by
Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L911-914)._
[Exception]: http://stackoverflow.com/q/10048173/223897
## Don't use inline CoffeeScript in views
Using the inline `:coffee` or `:coffeescript` Haml filters comes with a
performance overhead.
_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/initializers/haml.rb)
in an initializer._
### Further reading
- Pull Request: [Replace CoffeeScript block into JavaScript in Views](https://git.io/vztMu)
- Stack Overflow: [Performance implications of using :coffescript filter inside HAML templates?](http://stackoverflow.com/a/17571242/223897)
## ID-based CSS selectors need to be a bit more specific
Normally, because HTML `id` attributes need to be unique to the page, it's
perfectly fine to write some JavaScript like the following:
```javascript
$('#js-my-selector').hide();
```
However, there's a feature of GitLab's Markdown processing that [automatically
adds anchors to header elements][ToC Processing], with the `id` attribute being
automatically generated based on the content of the header.
Unfortunately, this feature makes it possible for user-generated content to
create a header element with the same `id` attribute we're using in our
selector, potentially breaking the JavaScript behavior. A user could break the
above example with the following Markdown:
```markdown
## JS My Selector
```
Which gets converted to the following HTML:
```html
<h2>
<a id="js-my-selector" class="anchor" href="#js-my-selector" aria-hidden="true"></a>
JS My Selector
</h2>
```
[ToC Processing]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/lib/banzai/filter/table_of_contents_filter.rb#L31-37
### Solution
The current recommended fix for this is to make our selectors slightly more
specific:
```javascript
$('div#js-my-selector').hide();
```
### Further reading
- Issue: [Merge request ToC anchor conflicts with tabs](https://gitlab.com/gitlab-org/gitlab-ce/issues/3908)
- Merge Request: [Make tab target selectors less naive](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2023)
- Merge Request: [Make cross-project reference's clipboard target less naive](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2024)
...@@ -6,7 +6,7 @@ If a user is both in a project group and in the project itself, the highest perm ...@@ -6,7 +6,7 @@ If a user is both in a project group and in the project itself, the highest perm
If a user is a GitLab administrator they receive all permissions. If a user is a GitLab administrator they receive all permissions.
On public projects the Guest role is not enforced. On public and internal projects the Guest role is not enforced.
All users will be able to create issues, leave comments, and pull or download the project code. All users will be able to create issues, leave comments, and pull or download the project code.
To add or import a user, you can follow the [project users and members To add or import a user, you can follow the [project users and members
......
Feature: Admin Appearance
Scenario: Create new appearance
Given I sign in as an admin
And I visit admin appearance page
When submit form with new appearance
Then I should be redirected to admin appearance page
And I should see newly created appearance
Scenario: Preview appearance
Given application has custom appearance
And I sign in as an admin
When I visit admin appearance page
And I click preview button
Then I should see a customized appearance
Scenario: Custom sign-in page
Given application has custom appearance
When I visit login page
Then I should see a customized appearance
Scenario: Appearance logo
Given application has custom appearance
And I sign in as an admin
And I visit admin appearance page
When I attach a logo
Then I should see a logo
And I remove the logo
Then I should see logo removed
Scenario: Header logos
Given application has custom appearance
And I sign in as an admin
And I visit admin appearance page
When I attach header logos
Then I should see header logos
And I remove the header logos
Then I should see header logos removed
...@@ -13,6 +13,7 @@ Feature: Project Milestone ...@@ -13,6 +13,7 @@ Feature: Project Milestone
Given I visit project "Shop" milestones page Given I visit project "Shop" milestones page
And I click link "v2.2" And I click link "v2.2"
Then I should see the labels "bug", "enhancement" and "feature" Then I should see the labels "bug", "enhancement" and "feature"
And I should see the "bug" label listed only once
@javascript @javascript
Scenario: Listing labels from labels tab Scenario: Listing labels from labels tab
......
class Spinach::Features::AdminAppearance < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
step 'submit form with new appearance' do
fill_in 'appearance_title', with: 'MyCompany'
fill_in 'appearance_description', with: 'dev server'
click_button 'Save'
end
step 'I should be redirected to admin appearance page' do
expect(current_path).to eq admin_appearances_path
expect(page).to have_content 'Appearance settings'
end
step 'I should see newly created appearance' do
expect(page).to have_field('appearance_title', with: 'MyCompany')
expect(page).to have_field('appearance_description', with: 'dev server')
expect(page).to have_content 'Last edit'
end
step 'I click preview button' do
click_link "Preview"
end
step 'application has custom appearance' do
create(:appearance)
end
step 'I should see a customized appearance' do
expect(page).to have_content appearance.title
expect(page).to have_content appearance.description
end
step 'I attach a logo' do
attach_file(:appearance_logo, Rails.root.join('spec', 'fixtures', 'dk.png'))
click_button 'Save'
end
step 'I attach header logos' do
attach_file(:appearance_header_logo, Rails.root.join('spec', 'fixtures', 'dk.png'))
click_button 'Save'
end
step 'I should see a logo' do
expect(page).to have_xpath('//img[@src="/uploads/appearance/logo/1/dk.png"]')
end
step 'I should see header logos' do
expect(page).to have_xpath('//img[@src="/uploads/appearance/header_logo/1/dk.png"]')
end
step 'I remove the logo' do
click_link 'Remove logo'
end
step 'I remove the header logos' do
click_link 'Remove header logo'
end
step 'I should see logo removed' do
expect(page).not_to have_xpath('//img[@src="/uploads/appearance/logo/1/gitlab_logo.png"]')
end
step 'I should see header logos removed' do
expect(page).not_to have_xpath('//img[@src="/uploads/appearance/header_logo/1/header_logo_light.png"]')
end
def appearance
Appearance.last
end
end
...@@ -41,6 +41,12 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps ...@@ -41,6 +41,12 @@ class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
end end
end end
step 'I should see the "bug" label listed only once' do
page.within('#tab-labels') do
expect(page).to have_content('bug', count: 1)
end
end
step 'I click link "v2.2"' do step 'I click link "v2.2"' do
click_link "v2.2" click_link "v2.2"
end end
......
...@@ -7,6 +7,10 @@ module SharedPaths ...@@ -7,6 +7,10 @@ module SharedPaths
visit new_project_path visit new_project_path
end end
step 'I visit login page' do
visit new_user_session_path
end
# ---------------------------------------- # ----------------------------------------
# User # User
# ---------------------------------------- # ----------------------------------------
...@@ -187,6 +191,10 @@ module SharedPaths ...@@ -187,6 +191,10 @@ module SharedPaths
visit admin_groups_path visit admin_groups_path
end end
step 'I visit admin appearance page' do
visit admin_appearances_path
end
step 'I visit admin teams page' do step 'I visit admin teams page' do
visit admin_teams_path visit admin_teams_path
end end
......
...@@ -7,6 +7,8 @@ module Banzai ...@@ -7,6 +7,8 @@ module Banzai
# #
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
class SanitizationFilter < HTML::Pipeline::SanitizationFilter class SanitizationFilter < HTML::Pipeline::SanitizationFilter
UNSAFE_PROTOCOLS = %w(javascript :javascript data vbscript).freeze
def whitelist def whitelist
whitelist = super whitelist = super
...@@ -43,8 +45,8 @@ module Banzai ...@@ -43,8 +45,8 @@ module Banzai
# Allow any protocol in `a` elements... # Allow any protocol in `a` elements...
whitelist[:protocols].delete('a') whitelist[:protocols].delete('a')
# ...but then remove links with the `javascript` protocol # ...but then remove links with unsafe protocols
whitelist[:transformers].push(remove_javascript_links) whitelist[:transformers].push(remove_unsafe_links)
# Remove `rel` attribute from `a` elements # Remove `rel` attribute from `a` elements
whitelist[:transformers].push(remove_rel) whitelist[:transformers].push(remove_rel)
...@@ -55,14 +57,14 @@ module Banzai ...@@ -55,14 +57,14 @@ module Banzai
whitelist whitelist
end end
def remove_javascript_links def remove_unsafe_links
lambda do |env| lambda do |env|
node = env[:node] node = env[:node]
return unless node.name == 'a' return unless node.name == 'a'
return unless node.has_attribute?('href') return unless node.has_attribute?('href')
if node['href'].start_with?('javascript', ':javascript') if node['href'].start_with?(*UNSAFE_PROTOCOLS)
node.remove_attribute('href') node.remove_attribute('href')
end end
end end
......
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :appearance do
title "MepMep"
description "This is my Community Edition instance"
end
end
...@@ -13,7 +13,7 @@ feature 'Issue filtering by Milestone', feature: true do ...@@ -13,7 +13,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project) visit_issues(project)
filter_by_milestone(Milestone::None.title) filter_by_milestone(Milestone::None.title)
expect(page).to have_css('.title', count: 1) expect(page).to have_css('.issue .title', count: 1)
end end
scenario 'filters by a specific Milestone', js: true do scenario 'filters by a specific Milestone', js: true do
...@@ -23,7 +23,7 @@ feature 'Issue filtering by Milestone', feature: true do ...@@ -23,7 +23,7 @@ feature 'Issue filtering by Milestone', feature: true do
visit_issues(project) visit_issues(project)
filter_by_milestone(milestone.title) filter_by_milestone(milestone.title)
expect(page).to have_css('.title', count: 1) expect(page).to have_css('.issue .title', count: 1)
end end
def visit_issues(project) def visit_issues(project)
......
...@@ -156,13 +156,27 @@ describe Banzai::Filter::SanitizationFilter, lib: true do ...@@ -156,13 +156,27 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
} }
protocols.each do |name, data| protocols.each do |name, data|
it "handles #{name}" do it "disallows #{name}" do
doc = filter(data[:input]) doc = filter(data[:input])
expect(doc.to_html).to eq data[:output] expect(doc.to_html).to eq data[:output]
end end
end end
it 'disallows data links' do
input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>'
output = filter(input)
expect(output.to_html).to eq '<a>XSS</a>'
end
it 'disallows vbscript links' do
input = '<a href="vbscript:alert(document.domain)">XSS</a>'
output = filter(input)
expect(output.to_html).to eq '<a>XSS</a>'
end
it 'allows non-standard anchor schemes' do it 'allows non-standard anchor schemes' do
exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>} exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>}
act = filter(exp) act = filter(exp)
......
require 'rails_helper'
RSpec.describe Appearance, type: :model do
subject { create(:appearance) }
it { is_expected.to be_valid }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:description) }
end
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
require 'spec_helper' require 'spec_helper'
describe ProjectHook, models: true do describe ProjectHook, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
describe '.push_hooks' do describe '.push_hooks' do
it 'should return hooks for push events only' do it 'should return hooks for push events only' do
hook = create(:project_hook, push_events: true) hook = create(:project_hook, push_events: true)
......
...@@ -18,20 +18,14 @@ ...@@ -18,20 +18,14 @@
require 'spec_helper' require 'spec_helper'
describe ProjectHook, models: true do describe WebHook, models: true do
describe "Associations" do
it { is_expected.to belong_to :project }
end
describe "Mass assignment" do
end
describe "Validations" do describe "Validations" do
it { is_expected.to validate_presence_of(:url) } it { is_expected.to validate_presence_of(:url) }
context "url format" do describe 'url' do
it { is_expected.to allow_value("http://example.com").for(:url) } it { is_expected.to allow_value("http://example.com").for(:url) }
it { is_expected.to allow_value("https://excample.com").for(:url) } it { is_expected.to allow_value("https://example.com").for(:url) }
it { is_expected.to allow_value(" https://example.com ").for(:url) }
it { is_expected.to allow_value("http://test.com/api").for(:url) } it { is_expected.to allow_value("http://test.com/api").for(:url) }
it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) } it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) }
it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) } it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) }
...@@ -39,6 +33,12 @@ describe ProjectHook, models: true do ...@@ -39,6 +33,12 @@ describe ProjectHook, models: true do
it { is_expected.not_to allow_value("example.com").for(:url) } it { is_expected.not_to allow_value("example.com").for(:url) }
it { is_expected.not_to allow_value("ftp://example.com").for(:url) } it { is_expected.not_to allow_value("ftp://example.com").for(:url) }
it { is_expected.not_to allow_value("herp-and-derp").for(:url) } it { is_expected.not_to allow_value("herp-and-derp").for(:url) }
it 'strips :url before saving it' do
hook = create(:project_hook, url: ' https://example.com ')
expect(hook.url).to eq('https://example.com')
end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment