Merge branch 'jej-pages-to-ce' into 'master'
Adding GitLab Pages to CE Closes #14605, gitlab-com/infrastructure#1058, gitlab-ee#1333, and #323 See merge request !8463
|
@ -0,0 +1 @@
|
||||||
|
0.2.4
|
3
Gemfile
|
@ -47,6 +47,9 @@ gem 'rqrcode-rails3', '~> 0.1.7'
|
||||||
gem 'attr_encrypted', '~> 3.0.0'
|
gem 'attr_encrypted', '~> 3.0.0'
|
||||||
gem 'u2f', '~> 0.2.1'
|
gem 'u2f', '~> 0.2.1'
|
||||||
|
|
||||||
|
# GitLab Pages
|
||||||
|
gem 'validates_hostname', '~> 1.0.6'
|
||||||
|
|
||||||
# Browser detection
|
# Browser detection
|
||||||
gem 'browser', '~> 2.2'
|
gem 'browser', '~> 2.2'
|
||||||
|
|
||||||
|
|
|
@ -785,6 +785,9 @@ GEM
|
||||||
get_process_mem (~> 0)
|
get_process_mem (~> 0)
|
||||||
unicorn (>= 4, < 6)
|
unicorn (>= 4, < 6)
|
||||||
uniform_notifier (1.10.0)
|
uniform_notifier (1.10.0)
|
||||||
|
validates_hostname (1.0.6)
|
||||||
|
activerecord (>= 3.0)
|
||||||
|
activesupport (>= 3.0)
|
||||||
version_sorter (2.1.0)
|
version_sorter (2.1.0)
|
||||||
virtus (1.0.5)
|
virtus (1.0.5)
|
||||||
axiom-types (~> 0.1)
|
axiom-types (~> 0.1)
|
||||||
|
@ -998,6 +1001,7 @@ DEPENDENCIES
|
||||||
unf (~> 0.1.4)
|
unf (~> 0.1.4)
|
||||||
unicorn (~> 5.1.0)
|
unicorn (~> 5.1.0)
|
||||||
unicorn-worker-killer (~> 0.4.4)
|
unicorn-worker-killer (~> 0.4.4)
|
||||||
|
validates_hostname (~> 1.0.6)
|
||||||
version_sorter (~> 2.1.0)
|
version_sorter (~> 2.1.0)
|
||||||
virtus (~> 1.0.1)
|
virtus (~> 1.0.1)
|
||||||
vmstat (~> 2.3.0)
|
vmstat (~> 2.3.0)
|
||||||
|
|
|
@ -109,6 +109,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
:plantuml_url,
|
:plantuml_url,
|
||||||
:max_artifacts_size,
|
:max_artifacts_size,
|
||||||
:max_attachment_size,
|
:max_attachment_size,
|
||||||
|
:max_pages_size,
|
||||||
:metrics_enabled,
|
:metrics_enabled,
|
||||||
:metrics_host,
|
:metrics_host,
|
||||||
:metrics_method_call_threshold,
|
:metrics_method_call_threshold,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
class Projects::PagesController < Projects::ApplicationController
|
||||||
|
layout 'project_settings'
|
||||||
|
|
||||||
|
before_action :authorize_read_pages!, only: [:show]
|
||||||
|
before_action :authorize_update_pages!, except: [:show]
|
||||||
|
|
||||||
|
def show
|
||||||
|
@domains = @project.pages_domains.order(:domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
project.remove_pages
|
||||||
|
project.pages_domains.destroy_all
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
redirect_to(namespace_project_pages_path(@project.namespace, @project),
|
||||||
|
notice: 'Pages were removed')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,49 @@
|
||||||
|
class Projects::PagesDomainsController < Projects::ApplicationController
|
||||||
|
layout 'project_settings'
|
||||||
|
|
||||||
|
before_action :authorize_update_pages!, except: [:show]
|
||||||
|
before_action :domain, only: [:show, :destroy]
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@domain = @project.pages_domains.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@domain = @project.pages_domains.create(pages_domain_params)
|
||||||
|
|
||||||
|
if @domain.valid?
|
||||||
|
redirect_to namespace_project_pages_path(@project.namespace, @project)
|
||||||
|
else
|
||||||
|
render 'new'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@domain.destroy
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
redirect_to(namespace_project_pages_path(@project.namespace, @project),
|
||||||
|
notice: 'Domain was removed')
|
||||||
|
end
|
||||||
|
format.js
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pages_domain_params
|
||||||
|
params.require(:pages_domain).permit(
|
||||||
|
:certificate,
|
||||||
|
:key,
|
||||||
|
:domain
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def domain
|
||||||
|
@domain ||= @project.pages_domains.find_by(domain: params[:id].to_s)
|
||||||
|
end
|
||||||
|
end
|
|
@ -256,7 +256,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_id
|
def project_id
|
||||||
pipeline.project_id
|
gl_project_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_name
|
def project_name
|
||||||
|
@ -451,6 +451,7 @@ module Ci
|
||||||
build_data = Gitlab::DataBuilder::Build.build(self)
|
build_data = Gitlab::DataBuilder::Build.build(self)
|
||||||
project.execute_hooks(build_data.dup, :build_hooks)
|
project.execute_hooks(build_data.dup, :build_hooks)
|
||||||
project.execute_services(build_data.dup, :build_hooks)
|
project.execute_services(build_data.dup, :build_hooks)
|
||||||
|
PagesService.new(build_data).execute
|
||||||
project.running_or_pending_build_count(force: true)
|
project.running_or_pending_build_count(force: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ class Namespace < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
|
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
|
||||||
|
Gitlab::PagesTransfer.new.rename_namespace(path_was, path)
|
||||||
|
|
||||||
remove_exports!
|
remove_exports!
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
class PagesDomain < ActiveRecord::Base
|
||||||
|
belongs_to :project
|
||||||
|
|
||||||
|
validates :domain, hostname: true
|
||||||
|
validates_uniqueness_of :domain, case_sensitive: false
|
||||||
|
validates :certificate, certificate: true, allow_nil: true, allow_blank: true
|
||||||
|
validates :key, certificate_key: true, allow_nil: true, allow_blank: true
|
||||||
|
|
||||||
|
validate :validate_pages_domain
|
||||||
|
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
|
||||||
|
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? }
|
||||||
|
|
||||||
|
attr_encrypted :key,
|
||||||
|
mode: :per_attribute_iv_and_salt,
|
||||||
|
insecure_mode: true,
|
||||||
|
key: Gitlab::Application.secrets.db_key_base,
|
||||||
|
algorithm: 'aes-256-cbc'
|
||||||
|
|
||||||
|
after_create :update
|
||||||
|
after_save :update
|
||||||
|
after_destroy :update
|
||||||
|
|
||||||
|
def to_param
|
||||||
|
domain
|
||||||
|
end
|
||||||
|
|
||||||
|
def url
|
||||||
|
return unless domain
|
||||||
|
|
||||||
|
if certificate
|
||||||
|
"https://#{domain}"
|
||||||
|
else
|
||||||
|
"http://#{domain}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_matching_key?
|
||||||
|
return false unless x509
|
||||||
|
return false unless pkey
|
||||||
|
|
||||||
|
# We compare the public key stored in certificate with public key from certificate key
|
||||||
|
x509.check_private_key(pkey)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_intermediates?
|
||||||
|
return false unless x509
|
||||||
|
|
||||||
|
# self-signed certificates doesn't have the certificate chain
|
||||||
|
return true if x509.verify(x509.public_key)
|
||||||
|
|
||||||
|
store = OpenSSL::X509::Store.new
|
||||||
|
store.set_default_paths
|
||||||
|
|
||||||
|
# This forces to load all intermediate certificates stored in `certificate`
|
||||||
|
Tempfile.open('certificate_chain') do |f|
|
||||||
|
f.write(certificate)
|
||||||
|
f.flush
|
||||||
|
store.add_file(f.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
store.verify(x509)
|
||||||
|
rescue OpenSSL::X509::StoreError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def expired?
|
||||||
|
return false unless x509
|
||||||
|
current = Time.new
|
||||||
|
current < x509.not_before || x509.not_after < current
|
||||||
|
end
|
||||||
|
|
||||||
|
def subject
|
||||||
|
return unless x509
|
||||||
|
x509.subject.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def certificate_text
|
||||||
|
@certificate_text ||= x509.try(:to_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def update
|
||||||
|
::Projects::UpdatePagesConfigurationService.new(project).execute
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_matching_key
|
||||||
|
unless has_matching_key?
|
||||||
|
self.errors.add(:key, "doesn't match the certificate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_intermediates
|
||||||
|
unless has_intermediates?
|
||||||
|
self.errors.add(:certificate, 'misses intermediates')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_pages_domain
|
||||||
|
return unless domain
|
||||||
|
if domain.downcase.ends_with?(".#{Settings.pages.host}".downcase)
|
||||||
|
self.errors.add(:domain, "*.#{Settings.pages.host} is restricted")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def x509
|
||||||
|
return unless certificate
|
||||||
|
@x509 ||= OpenSSL::X509::Certificate.new(certificate)
|
||||||
|
rescue OpenSSL::X509::CertificateError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def pkey
|
||||||
|
return unless key
|
||||||
|
@pkey ||= OpenSSL::PKey::RSA.new(key)
|
||||||
|
rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
|
@ -53,6 +53,8 @@ class Project < ActiveRecord::Base
|
||||||
update_column(:last_activity_at, self.created_at)
|
update_column(:last_activity_at, self.created_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
after_destroy :remove_pages
|
||||||
|
|
||||||
# update visibility_level of forks
|
# update visibility_level of forks
|
||||||
after_update :update_forks_visibility_level
|
after_update :update_forks_visibility_level
|
||||||
def update_forks_visibility_level
|
def update_forks_visibility_level
|
||||||
|
@ -148,6 +150,7 @@ class Project < ActiveRecord::Base
|
||||||
has_many :lfs_objects, through: :lfs_objects_projects
|
has_many :lfs_objects, through: :lfs_objects_projects
|
||||||
has_many :project_group_links, dependent: :destroy
|
has_many :project_group_links, dependent: :destroy
|
||||||
has_many :invited_groups, through: :project_group_links, source: :group
|
has_many :invited_groups, through: :project_group_links, source: :group
|
||||||
|
has_many :pages_domains, dependent: :destroy
|
||||||
has_many :todos, dependent: :destroy
|
has_many :todos, dependent: :destroy
|
||||||
has_many :notification_settings, dependent: :destroy, as: :source
|
has_many :notification_settings, dependent: :destroy, as: :source
|
||||||
|
|
||||||
|
@ -955,6 +958,7 @@ class Project < ActiveRecord::Base
|
||||||
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||||
|
|
||||||
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
|
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
|
||||||
|
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Expires various caches before a project is renamed.
|
# Expires various caches before a project is renamed.
|
||||||
|
@ -1156,6 +1160,45 @@ class Project < ActiveRecord::Base
|
||||||
ensure_runners_token!
|
ensure_runners_token!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pages_deployed?
|
||||||
|
Dir.exist?(public_pages_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_url
|
||||||
|
# The hostname always needs to be in downcased
|
||||||
|
# All web servers convert hostname to lowercase
|
||||||
|
host = "#{namespace.path}.#{Settings.pages.host}".downcase
|
||||||
|
|
||||||
|
# The host in URL always needs to be downcased
|
||||||
|
url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix|
|
||||||
|
"#{prefix}#{namespace.path}."
|
||||||
|
end.downcase
|
||||||
|
|
||||||
|
# If the project path is the same as host, we serve it as group page
|
||||||
|
return url if host == path
|
||||||
|
|
||||||
|
"#{url}/#{path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_path
|
||||||
|
File.join(Settings.pages.path, path_with_namespace)
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_pages_path
|
||||||
|
File.join(pages_path, 'public')
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_pages
|
||||||
|
# 1. We rename pages to temporary directory
|
||||||
|
# 2. We wait 5 minutes, due to NFS caching
|
||||||
|
# 3. We asynchronously remove pages with force
|
||||||
|
temp_path = "#{path}.#{SecureRandom.hex}.deleted"
|
||||||
|
|
||||||
|
if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path)
|
||||||
|
PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def wiki
|
def wiki
|
||||||
@wiki ||= ProjectWiki.new(self, self.owner)
|
@wiki ||= ProjectWiki.new(self, self.owner)
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,6 +110,9 @@ class ProjectPolicy < BasePolicy
|
||||||
can! :admin_pipeline
|
can! :admin_pipeline
|
||||||
can! :admin_environment
|
can! :admin_environment
|
||||||
can! :admin_deployment
|
can! :admin_deployment
|
||||||
|
can! :admin_pages
|
||||||
|
can! :read_pages
|
||||||
|
can! :update_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_access!
|
def public_access!
|
||||||
|
@ -136,6 +139,7 @@ class ProjectPolicy < BasePolicy
|
||||||
can! :remove_fork_project
|
can! :remove_fork_project
|
||||||
can! :destroy_merge_request
|
can! :destroy_merge_request
|
||||||
can! :destroy_issue
|
can! :destroy_issue
|
||||||
|
can! :remove_pages
|
||||||
end
|
end
|
||||||
|
|
||||||
def team_member_owner_access!
|
def team_member_owner_access!
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
class PagesService
|
||||||
|
attr_reader :data
|
||||||
|
|
||||||
|
def initialize(data)
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
return unless Settings.pages.enabled
|
||||||
|
return unless data[:build_name] == 'pages'
|
||||||
|
return unless data[:build_status] == 'success'
|
||||||
|
|
||||||
|
PagesWorker.perform_async(:deploy, data[:build_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -64,6 +64,9 @@ module Projects
|
||||||
# Move uploads
|
# Move uploads
|
||||||
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
|
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
|
||||||
|
|
||||||
|
# Move pages
|
||||||
|
Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
|
||||||
|
|
||||||
project.old_path_with_namespace = old_path
|
project.old_path_with_namespace = old_path
|
||||||
|
|
||||||
SystemHooksService.new.execute_hooks_for(project, :transfer)
|
SystemHooksService.new.execute_hooks_for(project, :transfer)
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
module Projects
|
||||||
|
class UpdatePagesConfigurationService < BaseService
|
||||||
|
attr_reader :project
|
||||||
|
|
||||||
|
def initialize(project)
|
||||||
|
@project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
update_file(pages_config_file, pages_config.to_json)
|
||||||
|
reload_daemon
|
||||||
|
success
|
||||||
|
rescue => e
|
||||||
|
error(e.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def pages_config
|
||||||
|
{
|
||||||
|
domains: pages_domains_config
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_domains_config
|
||||||
|
project.pages_domains.map do |domain|
|
||||||
|
{
|
||||||
|
domain: domain.domain,
|
||||||
|
certificate: domain.certificate,
|
||||||
|
key: domain.key,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reload_daemon
|
||||||
|
# GitLab Pages daemon constantly watches for modification time of `pages.path`
|
||||||
|
# It reloads configuration when `pages.path` is modified
|
||||||
|
update_file(pages_update_file, SecureRandom.hex(64))
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_path
|
||||||
|
@pages_path ||= project.pages_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_config_file
|
||||||
|
File.join(pages_path, 'config.json')
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_update_file
|
||||||
|
File.join(::Settings.pages.path, '.update')
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_file(file, data)
|
||||||
|
unless data
|
||||||
|
FileUtils.remove(file, force: true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
temp_file = "#{file}.#{SecureRandom.hex(16)}"
|
||||||
|
File.open(temp_file, 'w') do |f|
|
||||||
|
f.write(data)
|
||||||
|
end
|
||||||
|
FileUtils.move(temp_file, file, force: true)
|
||||||
|
ensure
|
||||||
|
# In case if the updating fails
|
||||||
|
FileUtils.remove(temp_file, force: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,164 @@
|
||||||
|
module Projects
|
||||||
|
class UpdatePagesService < BaseService
|
||||||
|
BLOCK_SIZE = 32.kilobytes
|
||||||
|
MAX_SIZE = 1.terabyte
|
||||||
|
SITE_PATH = 'public/'
|
||||||
|
|
||||||
|
attr_reader :build
|
||||||
|
|
||||||
|
def initialize(project, build)
|
||||||
|
@project, @build = project, build
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
# Create status notifying the deployment of pages
|
||||||
|
@status = create_status
|
||||||
|
@status.enqueue!
|
||||||
|
@status.run!
|
||||||
|
|
||||||
|
raise 'missing pages artifacts' unless build.artifacts_file?
|
||||||
|
raise 'pages are outdated' unless latest?
|
||||||
|
|
||||||
|
# Create temporary directory in which we will extract the artifacts
|
||||||
|
FileUtils.mkdir_p(tmp_path)
|
||||||
|
Dir.mktmpdir(nil, tmp_path) do |archive_path|
|
||||||
|
extract_archive!(archive_path)
|
||||||
|
|
||||||
|
# Check if we did extract public directory
|
||||||
|
archive_public_path = File.join(archive_path, 'public')
|
||||||
|
raise 'pages miss the public folder' unless Dir.exist?(archive_public_path)
|
||||||
|
raise 'pages are outdated' unless latest?
|
||||||
|
|
||||||
|
deploy_page!(archive_public_path)
|
||||||
|
success
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
error(e.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def success
|
||||||
|
@status.success
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message, http_status = nil)
|
||||||
|
@status.allow_failure = !latest?
|
||||||
|
@status.description = message
|
||||||
|
@status.drop
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_status
|
||||||
|
GenericCommitStatus.new(
|
||||||
|
project: project,
|
||||||
|
pipeline: build.pipeline,
|
||||||
|
user: build.user,
|
||||||
|
ref: build.ref,
|
||||||
|
stage: 'deploy',
|
||||||
|
name: 'pages:deploy'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_archive!(temp_path)
|
||||||
|
if artifacts.ends_with?('.tar.gz') || artifacts.ends_with?('.tgz')
|
||||||
|
extract_tar_archive!(temp_path)
|
||||||
|
elsif artifacts.ends_with?('.zip')
|
||||||
|
extract_zip_archive!(temp_path)
|
||||||
|
else
|
||||||
|
raise 'unsupported artifacts format'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_tar_archive!(temp_path)
|
||||||
|
results = Open3.pipeline(%W(gunzip -c #{artifacts}),
|
||||||
|
%W(dd bs=#{BLOCK_SIZE} count=#{blocks}),
|
||||||
|
%W(tar -x -C #{temp_path} #{SITE_PATH}),
|
||||||
|
err: '/dev/null')
|
||||||
|
raise 'pages failed to extract' unless results.compact.all?(&:success?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_zip_archive!(temp_path)
|
||||||
|
raise 'missing artifacts metadata' unless build.artifacts_metadata?
|
||||||
|
|
||||||
|
# Calculate page size after extract
|
||||||
|
public_entry = build.artifacts_metadata_entry(SITE_PATH, recursive: true)
|
||||||
|
|
||||||
|
if public_entry.total_size > max_size
|
||||||
|
raise "artifacts for pages are too large: #{public_entry.total_size}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Requires UnZip at least 6.00 Info-ZIP.
|
||||||
|
# -n never overwrite existing files
|
||||||
|
# We add * to end of SITE_PATH, because we want to extract SITE_PATH and all subdirectories
|
||||||
|
site_path = File.join(SITE_PATH, '*')
|
||||||
|
unless system(*%W(unzip -n #{artifacts} #{site_path} -d #{temp_path}))
|
||||||
|
raise 'pages failed to extract'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def deploy_page!(archive_public_path)
|
||||||
|
# Do atomic move of pages
|
||||||
|
# Move and removal may not be atomic, but they are significantly faster then extracting and removal
|
||||||
|
# 1. We move deployed public to previous public path (file removal is slow)
|
||||||
|
# 2. We move temporary public to be deployed public
|
||||||
|
# 3. We remove previous public path
|
||||||
|
FileUtils.mkdir_p(pages_path)
|
||||||
|
begin
|
||||||
|
FileUtils.move(public_path, previous_public_path)
|
||||||
|
rescue
|
||||||
|
end
|
||||||
|
FileUtils.move(archive_public_path, public_path)
|
||||||
|
ensure
|
||||||
|
FileUtils.rm_r(previous_public_path, force: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest?
|
||||||
|
# check if sha for the ref is still the most recent one
|
||||||
|
# this helps in case when multiple deployments happens
|
||||||
|
sha == latest_sha
|
||||||
|
end
|
||||||
|
|
||||||
|
def blocks
|
||||||
|
# Calculate dd parameters: we limit the size of pages
|
||||||
|
1 + max_size / BLOCK_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_size
|
||||||
|
current_application_settings.max_pages_size.megabytes || MAX_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
|
def tmp_path
|
||||||
|
@tmp_path ||= File.join(::Settings.pages.path, 'tmp')
|
||||||
|
end
|
||||||
|
|
||||||
|
def pages_path
|
||||||
|
@pages_path ||= project.pages_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_path
|
||||||
|
@public_path ||= File.join(pages_path, 'public')
|
||||||
|
end
|
||||||
|
|
||||||
|
def previous_public_path
|
||||||
|
@previous_public_path ||= File.join(pages_path, "public.#{SecureRandom.hex}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def ref
|
||||||
|
build.ref
|
||||||
|
end
|
||||||
|
|
||||||
|
def artifacts
|
||||||
|
build.artifacts_file.path
|
||||||
|
end
|
||||||
|
|
||||||
|
def latest_sha
|
||||||
|
project.commit(build.ref).try(:sha).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def sha
|
||||||
|
build.sha
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
# UrlValidator
|
||||||
|
#
|
||||||
|
# Custom validator for private keys.
|
||||||
|
#
|
||||||
|
# class Project < ActiveRecord::Base
|
||||||
|
# validates :certificate_key, certificate_key: true
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
class CertificateKeyValidator < ActiveModel::EachValidator
|
||||||
|
def validate_each(record, attribute, value)
|
||||||
|
unless valid_private_key_pem?(value)
|
||||||
|
record.errors.add(attribute, "must be a valid PEM private key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def valid_private_key_pem?(value)
|
||||||
|
return false unless value
|
||||||
|
pkey = OpenSSL::PKey::RSA.new(value)
|
||||||
|
pkey.private?
|
||||||
|
rescue OpenSSL::PKey::PKeyError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
# UrlValidator
|
||||||
|
#
|
||||||
|
# Custom validator for private keys.
|
||||||
|
#
|
||||||
|
# class Project < ActiveRecord::Base
|
||||||
|
# validates :certificate_key, certificate: true
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
class CertificateValidator < ActiveModel::EachValidator
|
||||||
|
def validate_each(record, attribute, value)
|
||||||
|
unless valid_certificate_pem?(value)
|
||||||
|
record.errors.add(attribute, "must be a valid PEM certificate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def valid_certificate_pem?(value)
|
||||||
|
return false unless value
|
||||||
|
OpenSSL::X509::Certificate.new(value).present?
|
||||||
|
rescue OpenSSL::X509::CertificateError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
|
@ -186,6 +186,14 @@
|
||||||
= f.text_area :help_page_text, class: 'form-control', rows: 4
|
= f.text_area :help_page_text, class: 'form-control', rows: 4
|
||||||
.help-block Markdown enabled
|
.help-block Markdown enabled
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend Pages
|
||||||
|
.form-group
|
||||||
|
= f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'control-label col-sm-2'
|
||||||
|
.col-sm-10
|
||||||
|
= f.number_field :max_pages_size, class: 'form-control'
|
||||||
|
.help-block Zero for unlimited
|
||||||
|
|
||||||
%fieldset
|
%fieldset
|
||||||
%legend Continuous Integration
|
%legend Continuous Integration
|
||||||
.form-group
|
.form-group
|
||||||
|
|
|
@ -34,3 +34,7 @@
|
||||||
= link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
|
= link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
|
||||||
%span
|
%span
|
||||||
CI/CD Pipelines
|
CI/CD Pipelines
|
||||||
|
= nav_link(controller: :pages) do
|
||||||
|
= link_to namespace_project_pages_path(@project.namespace, @project), title: 'Pages', data: {placement: 'right'} do
|
||||||
|
%span
|
||||||
|
Pages
|
||||||
|
|
|
@ -133,6 +133,7 @@
|
||||||
%hr
|
%hr
|
||||||
= link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
|
= link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
|
||||||
= f.submit 'Save changes', class: "btn btn-save"
|
= f.submit 'Save changes', class: "btn btn-save"
|
||||||
|
|
||||||
.row.prepend-top-default
|
.row.prepend-top-default
|
||||||
%hr
|
%hr
|
||||||
.row.prepend-top-default
|
.row.prepend-top-default
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
- if @project.pages_deployed?
|
||||||
|
.panel.panel-default
|
||||||
|
.panel-heading
|
||||||
|
Access pages
|
||||||
|
.panel-body
|
||||||
|
%p
|
||||||
|
%strong
|
||||||
|
Congratulations! Your pages are served under:
|
||||||
|
|
||||||
|
%p= link_to @project.pages_url, @project.pages_url
|
||||||
|
|
||||||
|
- @project.pages_domains.each do |domain|
|
||||||
|
%p= link_to domain.url, domain.url
|
|
@ -0,0 +1,12 @@
|
||||||
|
- if @project.pages_deployed?
|
||||||
|
- if can?(current_user, :remove_pages, @project)
|
||||||
|
.panel.panel-default.panel.panel-danger
|
||||||
|
.panel-heading Remove pages
|
||||||
|
.errors-holder
|
||||||
|
.panel-body
|
||||||
|
%p
|
||||||
|
Removing the pages will prevent from exposing them to outside world.
|
||||||
|
.form-actions
|
||||||
|
= link_to 'Remove pages', namespace_project_pages_path(@project.namespace, @project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove"
|
||||||
|
- else
|
||||||
|
.nothing-here-block Only the project owner can remove pages
|
|
@ -0,0 +1,4 @@
|
||||||
|
.panel.panel-default
|
||||||
|
.nothing-here-block
|
||||||
|
GitLab Pages are disabled.
|
||||||
|
Ask your system's administrator to enable it.
|
|
@ -0,0 +1,17 @@
|
||||||
|
- if can?(current_user, :update_pages, @project) && @domains.any?
|
||||||
|
.panel.panel-default
|
||||||
|
.panel-heading
|
||||||
|
Domains (#{@domains.count})
|
||||||
|
%ul.well-list
|
||||||
|
- @domains.each do |domain|
|
||||||
|
%li
|
||||||
|
.pull-right
|
||||||
|
= link_to 'Details', namespace_project_pages_domain_path(@project.namespace, @project, domain), class: "btn btn-sm btn-grouped"
|
||||||
|
= link_to 'Remove', namespace_project_pages_domain_path(@project.namespace, @project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
|
||||||
|
.clearfix
|
||||||
|
%span= link_to domain.domain, domain.url
|
||||||
|
%p
|
||||||
|
- if domain.subject
|
||||||
|
%span.label.label-gray Certificate: #{domain.subject}
|
||||||
|
- if domain.expired?
|
||||||
|
%span.label.label-danger Expired
|
|
@ -0,0 +1,7 @@
|
||||||
|
- if can?(current_user, :update_pages, @project)
|
||||||
|
.panel.panel-default
|
||||||
|
.panel-heading
|
||||||
|
Domains
|
||||||
|
.nothing-here-block
|
||||||
|
Support for domains and certificates is disabled.
|
||||||
|
Ask your system's administrator to enable it.
|
|
@ -0,0 +1,8 @@
|
||||||
|
- unless @project.pages_deployed?
|
||||||
|
.panel.panel-info
|
||||||
|
.panel-heading
|
||||||
|
Configure pages
|
||||||
|
.panel-body
|
||||||
|
%p
|
||||||
|
Learn how to upload your static site and have it served by
|
||||||
|
GitLab by following the #{link_to "documentation on GitLab Pages", "http://doc.gitlab.com/ee/pages/README.html", target: :blank}.
|
|
@ -0,0 +1,26 @@
|
||||||
|
- page_title 'Pages'
|
||||||
|
%h3.page_title
|
||||||
|
Pages
|
||||||
|
|
||||||
|
- if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https)
|
||||||
|
= link_to new_namespace_project_pages_domain_path(@project.namespace, @project), class: 'btn btn-new pull-right', title: 'New Domain' do
|
||||||
|
%i.fa.fa-plus
|
||||||
|
New Domain
|
||||||
|
|
||||||
|
%p.light
|
||||||
|
With GitLab Pages you can host your static websites on GitLab.
|
||||||
|
Combined with the power of GitLab CI and the help of GitLab Runner
|
||||||
|
you can deploy static pages for your individual projects, your user or your group.
|
||||||
|
|
||||||
|
%hr.clearfix
|
||||||
|
|
||||||
|
- if Gitlab.config.pages.enabled
|
||||||
|
= render 'access'
|
||||||
|
= render 'use'
|
||||||
|
- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https
|
||||||
|
= render 'list'
|
||||||
|
- else
|
||||||
|
= render 'no_domains'
|
||||||
|
= render 'destroy'
|
||||||
|
- else
|
||||||
|
= render 'disabled'
|
|
@ -0,0 +1,34 @@
|
||||||
|
= form_for [@project.namespace.becomes(Namespace), @project, @domain], html: { class: 'form-horizontal fieldset-form' } do |f|
|
||||||
|
- if @domain.errors.any?
|
||||||
|
#error_explanation
|
||||||
|
.alert.alert-danger
|
||||||
|
- @domain.errors.full_messages.each do |msg|
|
||||||
|
%p= msg
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :domain, class: 'control-label' do
|
||||||
|
Domain
|
||||||
|
.col-sm-10
|
||||||
|
= f.text_field :domain, required: true, autocomplete: 'off', class: 'form-control'
|
||||||
|
|
||||||
|
- if Gitlab.config.pages.external_https
|
||||||
|
.form-group
|
||||||
|
= f.label :certificate, class: 'control-label' do
|
||||||
|
Certificate (PEM)
|
||||||
|
.col-sm-10
|
||||||
|
= f.text_area :certificate, rows: 5, class: 'form-control'
|
||||||
|
%span.help-inline Upload a certificate for your domain with all intermediates
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.label :key, class: 'control-label' do
|
||||||
|
Key (PEM)
|
||||||
|
.col-sm-10
|
||||||
|
= f.text_area :key, rows: 5, class: 'form-control'
|
||||||
|
%span.help-inline Upload a private key for your certificate
|
||||||
|
- else
|
||||||
|
.nothing-here-block
|
||||||
|
Support for custom certificates is disabled.
|
||||||
|
Ask your system's administrator to enable it.
|
||||||
|
|
||||||
|
.form-actions
|
||||||
|
= f.submit 'Create New Domain', class: "btn btn-save"
|
|
@ -0,0 +1,6 @@
|
||||||
|
- page_title 'New Pages Domain'
|
||||||
|
%h3.page_title
|
||||||
|
New Pages Domain
|
||||||
|
%hr.clearfix
|
||||||
|
%div
|
||||||
|
= render 'form'
|
|
@ -0,0 +1,30 @@
|
||||||
|
- page_title "#{@domain.domain}", 'Pages Domains'
|
||||||
|
|
||||||
|
%h3.page-title
|
||||||
|
Pages Domain
|
||||||
|
|
||||||
|
.table-holder
|
||||||
|
%table.table
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
Domain
|
||||||
|
%td
|
||||||
|
= link_to @domain.domain, @domain.url
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
DNS
|
||||||
|
%td
|
||||||
|
%p
|
||||||
|
To access the domain create a new DNS record:
|
||||||
|
%pre
|
||||||
|
#{@domain.domain} CNAME #{@domain.project.namespace.path}.#{Settings.pages.host}.
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
Certificate
|
||||||
|
%td
|
||||||
|
- if @domain.certificate_text
|
||||||
|
%pre
|
||||||
|
= @domain.certificate_text
|
||||||
|
- else
|
||||||
|
.light
|
||||||
|
missing
|
|
@ -0,0 +1,23 @@
|
||||||
|
class PagesWorker
|
||||||
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options queue: :pages, retry: false
|
||||||
|
|
||||||
|
def perform(action, *arg)
|
||||||
|
send(action, *arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def deploy(build_id)
|
||||||
|
build = Ci::Build.find_by(id: build_id)
|
||||||
|
result = Projects::UpdatePagesService.new(build.project, build).execute
|
||||||
|
if result[:status] == :success
|
||||||
|
result = Projects::UpdatePagesConfigurationService.new(build.project).execute
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove(namespace_path, project_path)
|
||||||
|
full_path = File.join(Settings.pages.path, namespace_path, project_path)
|
||||||
|
FileUtils.rm_r(full_path, force: true)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Added GitLab Pages to CE
|
||||||
|
merge_request: 8463
|
||||||
|
author:
|
|
@ -153,6 +153,21 @@ production: &base
|
||||||
# The location where LFS objects are stored (default: shared/lfs-objects).
|
# The location where LFS objects are stored (default: shared/lfs-objects).
|
||||||
# storage_path: shared/lfs-objects
|
# storage_path: shared/lfs-objects
|
||||||
|
|
||||||
|
## GitLab Pages
|
||||||
|
pages:
|
||||||
|
enabled: false
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
# path: shared/pages
|
||||||
|
|
||||||
|
# The domain under which the pages are served:
|
||||||
|
# http://group.example.com/project
|
||||||
|
# or project path can be a group page: group.example.com
|
||||||
|
host: example.com
|
||||||
|
port: 80 # Set to 443 if you serve the pages with HTTPS
|
||||||
|
https: false # Set to true if you serve the pages with HTTPS
|
||||||
|
# external_http: "1.1.1.1:80" # If defined, enables custom domain support in GitLab Pages
|
||||||
|
# external_https: "1.1.1.1:443" # If defined, enables custom domain and certificate support in GitLab Pages
|
||||||
|
|
||||||
## Mattermost
|
## Mattermost
|
||||||
## For enabling Add to Mattermost button
|
## For enabling Add to Mattermost button
|
||||||
mattermost:
|
mattermost:
|
||||||
|
|
|
@ -6,7 +6,7 @@ class Settings < Settingslogic
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def gitlab_on_standard_port?
|
def gitlab_on_standard_port?
|
||||||
gitlab.port.to_i == (gitlab.https ? 443 : 80)
|
on_standard_port?(gitlab)
|
||||||
end
|
end
|
||||||
|
|
||||||
def host_without_www(url)
|
def host_without_www(url)
|
||||||
|
@ -14,7 +14,7 @@ class Settings < Settingslogic
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_gitlab_ci_url
|
def build_gitlab_ci_url
|
||||||
if gitlab_on_standard_port?
|
if on_standard_port?(gitlab)
|
||||||
custom_port = nil
|
custom_port = nil
|
||||||
else
|
else
|
||||||
custom_port = ":#{gitlab.port}"
|
custom_port = ":#{gitlab.port}"
|
||||||
|
@ -27,6 +27,10 @@ class Settings < Settingslogic
|
||||||
].join('')
|
].join('')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_pages_url
|
||||||
|
base_url(pages).join('')
|
||||||
|
end
|
||||||
|
|
||||||
def build_gitlab_shell_ssh_path_prefix
|
def build_gitlab_shell_ssh_path_prefix
|
||||||
user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}"
|
user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}"
|
||||||
|
|
||||||
|
@ -42,11 +46,11 @@ class Settings < Settingslogic
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_base_gitlab_url
|
def build_base_gitlab_url
|
||||||
base_gitlab_url.join('')
|
base_url(gitlab).join('')
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_gitlab_url
|
def build_gitlab_url
|
||||||
(base_gitlab_url + [gitlab.relative_url_root]).join('')
|
(base_url(gitlab) + [gitlab.relative_url_root]).join('')
|
||||||
end
|
end
|
||||||
|
|
||||||
# check that values in `current` (string or integer) is a contant in `modul`.
|
# check that values in `current` (string or integer) is a contant in `modul`.
|
||||||
|
@ -74,15 +78,19 @@ class Settings < Settingslogic
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def base_gitlab_url
|
def base_url(config)
|
||||||
custom_port = gitlab_on_standard_port? ? nil : ":#{gitlab.port}"
|
custom_port = on_standard_port?(config) ? nil : ":#{config.port}"
|
||||||
[ gitlab.protocol,
|
[ config.protocol,
|
||||||
"://",
|
"://",
|
||||||
gitlab.host,
|
config.host,
|
||||||
custom_port
|
custom_port
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def on_standard_port?(config)
|
||||||
|
config.port.to_i == (config.https ? 443 : 80)
|
||||||
|
end
|
||||||
|
|
||||||
# Extract the host part of the given +url+.
|
# Extract the host part of the given +url+.
|
||||||
def host(url)
|
def host(url)
|
||||||
url = url.downcase
|
url = url.downcase
|
||||||
|
@ -254,6 +262,20 @@ Settings.registry['issuer'] ||= nil
|
||||||
Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
|
Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
|
||||||
Settings.registry['path'] = File.expand_path(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry'), Rails.root)
|
Settings.registry['path'] = File.expand_path(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry'), Rails.root)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Pages
|
||||||
|
#
|
||||||
|
Settings['pages'] ||= Settingslogic.new({})
|
||||||
|
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
|
||||||
|
Settings.pages['path'] = File.expand_path(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"), Rails.root)
|
||||||
|
Settings.pages['https'] = false if Settings.pages['https'].nil?
|
||||||
|
Settings.pages['host'] ||= "example.com"
|
||||||
|
Settings.pages['port'] ||= Settings.pages.https ? 443 : 80
|
||||||
|
Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http"
|
||||||
|
Settings.pages['url'] ||= Settings.send(:build_pages_url)
|
||||||
|
Settings.pages['external_http'] ||= false if Settings.pages['external_http'].nil?
|
||||||
|
Settings.pages['external_https'] ||= false if Settings.pages['external_https'].nil?
|
||||||
|
|
||||||
#
|
#
|
||||||
# Git LFS
|
# Git LFS
|
||||||
#
|
#
|
||||||
|
|
|
@ -39,6 +39,10 @@ constraints(ProjectUrlConstrainer.new) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resource :pages, only: [:show, :destroy] do
|
||||||
|
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: /[^\/]+/ }
|
||||||
|
end
|
||||||
|
|
||||||
resources :compare, only: [:index, :create] do
|
resources :compare, only: [:index, :create] do
|
||||||
collection do
|
collection do
|
||||||
get :diff_for_path
|
get :diff_for_path
|
||||||
|
|
|
@ -50,3 +50,4 @@
|
||||||
- [reactive_caching, 1]
|
- [reactive_caching, 1]
|
||||||
- [cronjob, 1]
|
- [cronjob, 1]
|
||||||
- [default, 1]
|
- [default, 1]
|
||||||
|
- [pages, 1]
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
class AddPagesSizeToApplicationSettings < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column_with_default :application_settings, :max_pages_size, :integer, default: 100, allow_null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column(:application_settings, :max_pages_size)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
class CreatePagesDomain < ActiveRecord::Migration
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def change
|
||||||
|
create_table :pages_domains do |t|
|
||||||
|
t.integer :project_id
|
||||||
|
t.text :certificate
|
||||||
|
t.text :encrypted_key
|
||||||
|
t.string :encrypted_key_iv
|
||||||
|
t.string :encrypted_key_salt
|
||||||
|
t.string :domain
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :pages_domains, :domain, unique: true
|
||||||
|
end
|
||||||
|
end
|
18
db/schema.rb
|
@ -98,17 +98,18 @@ ActiveRecord::Schema.define(version: 20170204181513) do
|
||||||
t.text "help_page_text_html"
|
t.text "help_page_text_html"
|
||||||
t.text "shared_runners_text_html"
|
t.text "shared_runners_text_html"
|
||||||
t.text "after_sign_up_text_html"
|
t.text "after_sign_up_text_html"
|
||||||
t.boolean "sidekiq_throttling_enabled", default: false
|
|
||||||
t.string "sidekiq_throttling_queues"
|
|
||||||
t.decimal "sidekiq_throttling_factor"
|
|
||||||
t.boolean "housekeeping_enabled", default: true, null: false
|
t.boolean "housekeeping_enabled", default: true, null: false
|
||||||
t.boolean "housekeeping_bitmaps_enabled", default: true, null: false
|
t.boolean "housekeeping_bitmaps_enabled", default: true, null: false
|
||||||
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
|
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
|
||||||
t.integer "housekeeping_full_repack_period", default: 50, null: false
|
t.integer "housekeeping_full_repack_period", default: 50, null: false
|
||||||
t.integer "housekeeping_gc_period", default: 200, null: false
|
t.integer "housekeeping_gc_period", default: 200, null: false
|
||||||
|
t.boolean "sidekiq_throttling_enabled", default: false
|
||||||
|
t.string "sidekiq_throttling_queues"
|
||||||
|
t.decimal "sidekiq_throttling_factor"
|
||||||
t.boolean "html_emails_enabled", default: true
|
t.boolean "html_emails_enabled", default: true
|
||||||
t.string "plantuml_url"
|
t.string "plantuml_url"
|
||||||
t.boolean "plantuml_enabled"
|
t.boolean "plantuml_enabled"
|
||||||
|
t.integer "max_pages_size", default: 100, null: false
|
||||||
t.integer "terminal_max_session_time", default: 0, null: false
|
t.integer "terminal_max_session_time", default: 0, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -857,6 +858,17 @@ ActiveRecord::Schema.define(version: 20170204181513) do
|
||||||
add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
|
add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
|
||||||
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
|
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
|
||||||
|
|
||||||
|
create_table "pages_domains", force: :cascade do |t|
|
||||||
|
t.integer "project_id"
|
||||||
|
t.text "certificate"
|
||||||
|
t.text "encrypted_key"
|
||||||
|
t.string "encrypted_key_iv"
|
||||||
|
t.string "encrypted_key_salt"
|
||||||
|
t.string "domain"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
|
||||||
|
|
||||||
create_table "personal_access_tokens", force: :cascade do |t|
|
create_table "personal_access_tokens", force: :cascade do |t|
|
||||||
t.integer "user_id", null: false
|
t.integer "user_id", null: false
|
||||||
t.string "token", null: false
|
t.string "token", null: false
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
||||||
- [Container Registry](user/project/container_registry.md) Learn how to use GitLab Container Registry.
|
- [Container Registry](user/project/container_registry.md) Learn how to use GitLab Container Registry.
|
||||||
- [GitLab basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
|
- [GitLab basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
|
||||||
|
- [GitLab Pages](user/project/pages/index.md) Using GitLab Pages.
|
||||||
- [Importing to GitLab](workflow/importing/README.md) Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
|
- [Importing to GitLab](workflow/importing/README.md) Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz and SVN into GitLab.
|
||||||
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
|
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
|
||||||
- [Markdown](user/markdown.md) GitLab's advanced formatting system.
|
- [Markdown](user/markdown.md) GitLab's advanced formatting system.
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
|
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
|
||||||
- [Git LFS configuration](workflow/lfs/lfs_administration.md)
|
- [Git LFS configuration](workflow/lfs/lfs_administration.md)
|
||||||
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
|
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
|
||||||
|
- [GitLab Pages configuration](administration/pages/index.md) Configure GitLab Pages.
|
||||||
- [GitLab performance monitoring with InfluxDB](administration/monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
|
- [GitLab performance monitoring with InfluxDB](administration/monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
|
||||||
- [GitLab performance monitoring with Prometheus](administration/monitoring/performance/prometheus.md) Configure GitLab and Prometheus for measuring performance metrics.
|
- [GitLab performance monitoring with Prometheus](administration/monitoring/performance/prometheus.md) Configure GitLab and Prometheus for measuring performance metrics.
|
||||||
- [Request Profiling](administration/monitoring/performance/request_profiling.md) Get a detailed profile on slow requests.
|
- [Request Profiling](administration/monitoring/performance/request_profiling.md) Get a detailed profile on slow requests.
|
||||||
|
|
|
@ -66,4 +66,4 @@ Read more on high-availability configuration:
|
||||||
configure custom domains with custom SSL, which would not be possible
|
configure custom domains with custom SSL, which would not be possible
|
||||||
if SSL was terminated at the load balancer.
|
if SSL was terminated at the load balancer.
|
||||||
|
|
||||||
[gitlab-pages]: http://docs.gitlab.com/ee/pages/administration.html
|
[gitlab-pages]: ../pages/index.md
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
# GitLab Pages Administration
|
||||||
|
|
||||||
|
> **Notes:**
|
||||||
|
- [Introduced][ee-80] in GitLab EE 8.3.
|
||||||
|
- Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
|
||||||
|
- GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
|
||||||
|
- This guide is for Omnibus GitLab installations. If you have installed
|
||||||
|
GitLab from source, follow the [Pages source installation document](source.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This document describes how to set up the _latest_ GitLab Pages feature. Make
|
||||||
|
sure to read the [changelog](#changelog) if you are upgrading to a new GitLab
|
||||||
|
version as it may include new features and changes needed to be made in your
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
If you are looking for ways to upload your static content in GitLab Pages, you
|
||||||
|
probably want to read the [user documentation][pages-userguide].
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
GitLab Pages makes use of the [GitLab Pages daemon], a simple HTTP server
|
||||||
|
written in Go that can listen on an external IP address and provide support for
|
||||||
|
custom domains and custom certificates. It supports dynamic certificates through
|
||||||
|
SNI and exposes pages using HTTP2 by default.
|
||||||
|
You are encouraged to read its [README][pages-readme] to fully understand how
|
||||||
|
it works.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
In the case of custom domains, the Pages daemon needs to listen on ports `80`
|
||||||
|
and/or `443`. For that reason, there is some flexibility in the way which you
|
||||||
|
can set it up:
|
||||||
|
|
||||||
|
1. Run the pages daemon in the same server as GitLab, listening on a secondary IP
|
||||||
|
1. Run the pages daemon in a separate server. In that case, the
|
||||||
|
[Pages path](#change-storage-path) must also be present in the server that
|
||||||
|
the pages daemon is installed, so you will have to share it via network.
|
||||||
|
1. Run the pages daemon in the same server as GitLab, listening on the same IP
|
||||||
|
but on different ports. In that case, you will have to proxy the traffic with
|
||||||
|
a loadbalancer. If you choose that route note that you should use TCP load
|
||||||
|
balancing for HTTPS. If you use TLS-termination (HTTPS-load balancing) the
|
||||||
|
pages will not be able to be served with user provided certificates. For
|
||||||
|
HTTP it's OK to use HTTP or TCP load balancing.
|
||||||
|
|
||||||
|
In this document, we will proceed assuming the first option.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before proceeding with the Pages configuration, you will need to:
|
||||||
|
|
||||||
|
1. Have a separate domain under which the GitLab Pages will be served. In this
|
||||||
|
document we assume that to be `example.io`.
|
||||||
|
1. Configure a **wildcard DNS record**.
|
||||||
|
1. (Optional) Have a **wildcard certificate** for that domain if you decide to
|
||||||
|
serve Pages under HTTPS.
|
||||||
|
1. (Optional but recommended) Enable [Shared runners](../ci/runners/README.md)
|
||||||
|
so that your users don't have to bring their own.
|
||||||
|
|
||||||
|
### DNS configuration
|
||||||
|
|
||||||
|
GitLab Pages expect to run on their own virtual host. In your DNS server/provider
|
||||||
|
you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the
|
||||||
|
host that GitLab runs. For example, an entry would look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
*.example.io. 1800 IN A 1.2.3.4
|
||||||
|
```
|
||||||
|
|
||||||
|
where `example.io` is the domain under which GitLab Pages will be served
|
||||||
|
and `1.2.3.4` is the IP address of your GitLab instance.
|
||||||
|
|
||||||
|
> **Note:**
|
||||||
|
You should not use the GitLab domain to serve user pages. For more information
|
||||||
|
see the [security section](#security).
|
||||||
|
|
||||||
|
[wiki-wildcard-dns]: https://en.wikipedia.org/wiki/Wildcard_DNS_record
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Depending on your needs, you can install GitLab Pages in four different ways.
|
||||||
|
|
||||||
|
### Option 1. Custom domains with HTTPS support
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `https://page.example.io` and `https://page.com` | yes | redirects to HTTPS | yes | yes |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled AND pages has external IP support enabled.
|
||||||
|
In that case, the pages daemon is running, NGINX still proxies requests to
|
||||||
|
the daemon but the daemon is also able to receive requests from the outside
|
||||||
|
world. Custom domains and TLS are supported.
|
||||||
|
|
||||||
|
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pages_external_url "https://example.io"
|
||||||
|
nginx['listen_addresses'] = ['1.1.1.1']
|
||||||
|
pages_nginx['enable'] = false
|
||||||
|
gitlab_pages['cert'] = "/etc/gitlab/ssl/example.io.crt"
|
||||||
|
gitlab_pages['cert_key'] = "/etc/gitlab/ssl/example.io.key"
|
||||||
|
gitlab_pages['external_http'] = '1.1.1.2:80'
|
||||||
|
gitlab_pages['external_https'] = '1.1.1.2:443'
|
||||||
|
```
|
||||||
|
|
||||||
|
where `1.1.1.1` is the primary IP address that GitLab is listening to and
|
||||||
|
`1.1.1.2` the secondary IP where the GitLab Pages daemon listens to.
|
||||||
|
|
||||||
|
1. [Reconfigure GitLab][reconfigure]
|
||||||
|
|
||||||
|
### Option 2. Custom domains without HTTPS support
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `http://page.example.io` and `http://page.com` | no | yes | no | yes |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled AND pages has external IP support enabled.
|
||||||
|
In that case, the pages daemon is running, NGINX still proxies requests to
|
||||||
|
the daemon but the daemon is also able to receive requests from the outside
|
||||||
|
world. Custom domains and TLS are supported.
|
||||||
|
|
||||||
|
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pages_external_url "http://example.io"
|
||||||
|
nginx['listen_addresses'] = ['1.1.1.1']
|
||||||
|
pages_nginx['enable'] = false
|
||||||
|
gitlab_pages['external_http'] = '1.1.1.2:80'
|
||||||
|
```
|
||||||
|
|
||||||
|
where `1.1.1.1` is the primary IP address that GitLab is listening to and
|
||||||
|
`1.1.1.2` the secondary IP where the GitLab Pages daemon listens to.
|
||||||
|
|
||||||
|
1. [Reconfigure GitLab][reconfigure]
|
||||||
|
|
||||||
|
### Option 3. Wildcard HTTPS domain without custom domains
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `https://page.example.io` | yes | no | no | no |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled and NGINX will proxy all requests to the
|
||||||
|
daemon. Pages daemon doesn't listen to the outside world.
|
||||||
|
|
||||||
|
1. Place the certificate and key inside `/etc/gitlab/ssl`
|
||||||
|
1. In `/etc/gitlab/gitlab.rb` specify the following configuration:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pages_external_url 'https://example.io'
|
||||||
|
|
||||||
|
pages_nginx['redirect_http_to_https'] = true
|
||||||
|
pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt"
|
||||||
|
pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
where `pages-nginx.crt` and `pages-nginx.key` are the SSL cert and key,
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
1. [Reconfigure GitLab][reconfigure]
|
||||||
|
|
||||||
|
### Option 4. Wildcard HTTP domain without custom domains
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `http://page.example.io` | no | no | no | no |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled and NGINX will proxy all requests to the
|
||||||
|
daemon. Pages daemon doesn't listen to the outside world.
|
||||||
|
|
||||||
|
1. Set the external URL for GitLab Pages in `/etc/gitlab/gitlab.rb`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
pages_external_url 'http://example.io'
|
||||||
|
```
|
||||||
|
|
||||||
|
1. [Reconfigure GitLab][reconfigure]
|
||||||
|
|
||||||
|
## Change storage path
|
||||||
|
|
||||||
|
Follow the steps below to change the default path where GitLab Pages' contents
|
||||||
|
are stored.
|
||||||
|
|
||||||
|
1. Pages are stored by default in `/var/opt/gitlab/gitlab-rails/shared/pages`.
|
||||||
|
If you wish to store them in another location you must set it up in
|
||||||
|
`/etc/gitlab/gitlab.rb`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gitlab_rails['pages_path'] = "/mnt/storage/pages"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. [Reconfigure GitLab][reconfigure]
|
||||||
|
|
||||||
|
## Set maximum pages size
|
||||||
|
|
||||||
|
The maximum size of the unpacked archive per project can be configured in the
|
||||||
|
Admin area under the Application settings in the **Maximum size of pages (MB)**.
|
||||||
|
The default is 100MB.
|
||||||
|
|
||||||
|
## Backup
|
||||||
|
|
||||||
|
Pages are part of the [regular backup][backup] so there is nothing to configure.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
You should strongly consider running GitLab pages under a different hostname
|
||||||
|
than GitLab to prevent XSS attacks.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
GitLab Pages were first introduced in GitLab EE 8.3. Since then, many features
|
||||||
|
where added, like custom CNAME and TLS support, and many more are likely to
|
||||||
|
come. Below is a brief changelog. If no changes were introduced or a version is
|
||||||
|
missing from the changelog, assume that the documentation is the same as the
|
||||||
|
latest previous version.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**GitLab 8.17 ([documentation][8-17-docs])**
|
||||||
|
|
||||||
|
- GitLab Pages were ported to Community Edition in GitLab 8.17.
|
||||||
|
- Documentation was refactored to be more modular and easy to follow.
|
||||||
|
|
||||||
|
**GitLab 8.5 ([documentation][8-5-docs])**
|
||||||
|
|
||||||
|
- In GitLab 8.5 we introduced the [gitlab-pages][] daemon which is now the
|
||||||
|
recommended way to set up GitLab Pages.
|
||||||
|
- The [NGINX configs][] have changed to reflect this change. So make sure to
|
||||||
|
update them.
|
||||||
|
- Custom CNAME and TLS certificates support.
|
||||||
|
- Documentation was moved to one place.
|
||||||
|
|
||||||
|
**GitLab 8.3 ([documentation][8-3-docs])**
|
||||||
|
|
||||||
|
- GitLab Pages feature was introduced.
|
||||||
|
|
||||||
|
[8-3-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md
|
||||||
|
[8-5-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md
|
||||||
|
[8-17-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable-ce/doc/administration/pages/index.md
|
||||||
|
[backup]: ../raketasks/backup_restore.md
|
||||||
|
[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
|
||||||
|
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
|
||||||
|
[ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173
|
||||||
|
[gitlab pages daemon]: https://gitlab.com/gitlab-org/gitlab-pages
|
||||||
|
[NGINX configs]: https://gitlab.com/gitlab-org/gitlab-ee/tree/8-5-stable-ee/lib/support/nginx
|
||||||
|
[pages-readme]: https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md
|
||||||
|
[pages-userguide]: ../../user/project/pages/index.md
|
||||||
|
[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
|
||||||
|
[restart]: ../administration/restart_gitlab.md#installations-from-source
|
||||||
|
[gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4
|
|
@ -0,0 +1,323 @@
|
||||||
|
# GitLab Pages administration for source installations
|
||||||
|
|
||||||
|
This is the documentation for configuring a GitLab Pages when you have installed
|
||||||
|
GitLab from source and not using the Omnibus packages.
|
||||||
|
|
||||||
|
You are encouraged to read the [Omnibus documentation](index.md) as it provides
|
||||||
|
some invaluable information to the configuration of GitLab Pages. Please proceed
|
||||||
|
to read it before going forward with this guide.
|
||||||
|
|
||||||
|
We also highly recommend that you use the Omnibus GitLab packages, as we
|
||||||
|
optimize them specifically for GitLab, and we will take care of upgrading GitLab
|
||||||
|
Pages to the latest supported version.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
[Read the Omnibus overview section.](index.md#overview)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
[Read the Omnibus prerequisites section.](index.md#prerequisites)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Depending on your needs, you can install GitLab Pages in four different ways.
|
||||||
|
|
||||||
|
### Option 1. Custom domains with HTTPS support
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `https://page.example.io` and `https://page.com` | yes | redirects to HTTPS | yes | yes |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled AND pages has external IP support enabled.
|
||||||
|
In that case, the pages daemon is running, NGINX still proxies requests to
|
||||||
|
the daemon but the daemon is also able to receive requests from the outside
|
||||||
|
world. Custom domains and TLS are supported.
|
||||||
|
|
||||||
|
1. Install the Pages daemon:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/git
|
||||||
|
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
|
||||||
|
cd gitlab-pages
|
||||||
|
sudo -u git -H git checkout v0.2.4
|
||||||
|
sudo -u git -H make
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `gitlab.yml` to look like the example below. You need to change the
|
||||||
|
`host` to the FQDN under which GitLab Pages will be served. Set
|
||||||
|
`external_http` and `external_https` to the secondary IP on which the pages
|
||||||
|
daemon will listen for connections:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
## GitLab Pages
|
||||||
|
pages:
|
||||||
|
enabled: true
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
# path: shared/pages
|
||||||
|
|
||||||
|
host: example.io
|
||||||
|
port: 443
|
||||||
|
https: true
|
||||||
|
|
||||||
|
external_http: 1.1.1.2:80
|
||||||
|
external_https: 1.1.1.2:443
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
|
||||||
|
order to enable the pages daemon. In `gitlab_pages_options` the
|
||||||
|
`-pages-domain`, `-listen-http` and `-listen-https` must match the `host`,
|
||||||
|
`external_http` and `external_https` settings that you set above respectively.
|
||||||
|
The `-root-cert` and `-root-key` settings are the wildcard TLS certificates
|
||||||
|
of the `example.io` domain:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitlab_pages_enabled=true
|
||||||
|
gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 1.1.1.2:80 -listen-https 1.1.1.2:443 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy the `gitlab-pages-ssl` Nginx configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf
|
||||||
|
sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `gitlab-pages-ssl` with `gitlab-pages` if you are not using SSL.
|
||||||
|
|
||||||
|
1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace
|
||||||
|
`0.0.0.0` with `1.1.1.1`, where `1.1.1.1` the primary IP where GitLab
|
||||||
|
listens to.
|
||||||
|
1. Restart NGINX
|
||||||
|
1. [Restart GitLab][restart]
|
||||||
|
|
||||||
|
### Option 2. Custom domains without HTTPS support
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `http://page.example.io` and `http://page.com` | no | yes | no | yes |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled AND pages has external IP support enabled.
|
||||||
|
In that case, the pages daemon is running, NGINX still proxies requests to
|
||||||
|
the daemon but the daemon is also able to receive requests from the outside
|
||||||
|
world. Custom domains and TLS are supported.
|
||||||
|
|
||||||
|
1. Install the Pages daemon:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/git
|
||||||
|
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
|
||||||
|
cd gitlab-pages
|
||||||
|
sudo -u git -H git checkout v0.2.4
|
||||||
|
sudo -u git -H make
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `gitlab.yml` to look like the example below. You need to change the
|
||||||
|
`host` to the FQDN under which GitLab Pages will be served. Set
|
||||||
|
`external_http` to the secondary IP on which the pages daemon will listen
|
||||||
|
for connections:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pages:
|
||||||
|
enabled: true
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
# path: shared/pages
|
||||||
|
|
||||||
|
host: example.io
|
||||||
|
port: 80
|
||||||
|
https: false
|
||||||
|
|
||||||
|
external_http: 1.1.1.2:80
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
|
||||||
|
order to enable the pages daemon. In `gitlab_pages_options` the
|
||||||
|
`-pages-domain` and `-listen-http` must match the `host` and `external_http`
|
||||||
|
settings that you set above respectively:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitlab_pages_enabled=true
|
||||||
|
gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 1.1.1.2:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy the `gitlab-pages-ssl` Nginx configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf
|
||||||
|
sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `gitlab-pages-ssl` with `gitlab-pages` if you are not using SSL.
|
||||||
|
|
||||||
|
1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace
|
||||||
|
`0.0.0.0` with `1.1.1.1`, where `1.1.1.1` the primary IP where GitLab
|
||||||
|
listens to.
|
||||||
|
1. Restart NGINX
|
||||||
|
1. [Restart GitLab][restart]
|
||||||
|
|
||||||
|
### Option 3. Wildcard HTTPS domain without custom domains
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `https://page.example.io` | yes | no | no | no |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled and NGINX will proxy all requests to the
|
||||||
|
daemon. Pages daemon doesn't listen to the outside world.
|
||||||
|
|
||||||
|
1. Install the Pages daemon:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/git
|
||||||
|
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
|
||||||
|
cd gitlab-pages
|
||||||
|
sudo -u git -H git checkout v0.2.4
|
||||||
|
sudo -u git -H make
|
||||||
|
```
|
||||||
|
1. In `gitlab.yml`, set the port to `443` and https to `true`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
## GitLab Pages
|
||||||
|
pages:
|
||||||
|
enabled: true
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
# path: shared/pages
|
||||||
|
|
||||||
|
host: example.io
|
||||||
|
port: 443
|
||||||
|
https: true
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy the `gitlab-pages-ssl` Nginx configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf
|
||||||
|
sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `gitlab-pages-ssl` with `gitlab-pages` if you are not using SSL.
|
||||||
|
|
||||||
|
1. Restart NGINX
|
||||||
|
1. [Restart GitLab][restart]
|
||||||
|
|
||||||
|
### Option 4. Wildcard HTTP domain without custom domains
|
||||||
|
|
||||||
|
| URL scheme | Wildcard certificate | Custom domain with HTTP support | Custom domain with HTTPS support | Secondary IP |
|
||||||
|
| --- |:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|
| `http://page.example.io` | no | no | no | no |
|
||||||
|
|
||||||
|
Pages enabled, daemon is enabled and NGINX will proxy all requests to the
|
||||||
|
daemon. Pages daemon doesn't listen to the outside world.
|
||||||
|
|
||||||
|
1. Install the Pages daemon:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd /home/git
|
||||||
|
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-pages.git
|
||||||
|
cd gitlab-pages
|
||||||
|
sudo -u git -H git checkout v0.2.4
|
||||||
|
sudo -u git -H make
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Go to the GitLab installation directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/git/gitlab
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Edit `gitlab.yml` and under the `pages` setting, set `enabled` to `true` and
|
||||||
|
the `host` to the FQDN under which GitLab Pages will be served:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
## GitLab Pages
|
||||||
|
pages:
|
||||||
|
enabled: true
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
# path: shared/pages
|
||||||
|
|
||||||
|
host: example.io
|
||||||
|
port: 80
|
||||||
|
https: false
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Copy the `gitlab-pages-ssl` Nginx configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp lib/support/nginx/gitlab-pages-ssl /etc/nginx/sites-available/gitlab-pages-ssl.conf
|
||||||
|
sudo ln -sf /etc/nginx/sites-{available,enabled}/gitlab-pages-ssl.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `gitlab-pages-ssl` with `gitlab-pages` if you are not using SSL.
|
||||||
|
|
||||||
|
1. Restart NGINX
|
||||||
|
1. [Restart GitLab][restart]
|
||||||
|
|
||||||
|
## NGINX caveats
|
||||||
|
|
||||||
|
>**Note:**
|
||||||
|
The following information applies only for installations from source.
|
||||||
|
|
||||||
|
Be extra careful when setting up the domain name in the NGINX config. You must
|
||||||
|
not remove the backslashes.
|
||||||
|
|
||||||
|
If your GitLab pages domain is `example.io`, replace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
server_name ~^.*\.YOUR_GITLAB_PAGES\.DOMAIN$;
|
||||||
|
```
|
||||||
|
|
||||||
|
with:
|
||||||
|
|
||||||
|
```
|
||||||
|
server_name ~^.*\.example\.io$;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using a subdomain, make sure to escape all dots (`.`) except from
|
||||||
|
the first one with a backslash (\). For example `pages.example.io` would be:
|
||||||
|
|
||||||
|
```
|
||||||
|
server_name ~^.*\.pages\.example\.io$;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Change storage path
|
||||||
|
|
||||||
|
Follow the steps below to change the default path where GitLab Pages' contents
|
||||||
|
are stored.
|
||||||
|
|
||||||
|
1. Pages are stored by default in `/home/git/gitlab/shared/pages`.
|
||||||
|
If you wish to store them in another location you must set it up in
|
||||||
|
`gitlab.yml` under the `pages` section:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pages:
|
||||||
|
enabled: true
|
||||||
|
# The location where pages are stored (default: shared/pages).
|
||||||
|
path: /mnt/storage/pages
|
||||||
|
```
|
||||||
|
|
||||||
|
1. [Restart GitLab][restart]
|
||||||
|
|
||||||
|
## Set maximum Pages size
|
||||||
|
|
||||||
|
The maximum size of the unpacked archive per project can be configured in the
|
||||||
|
Admin area under the Application settings in the **Maximum size of pages (MB)**.
|
||||||
|
The default is 100MB.
|
||||||
|
|
||||||
|
## Backup
|
||||||
|
|
||||||
|
Pages are part of the [regular backup][backup] so there is nothing to configure.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
You should strongly consider running GitLab pages under a different hostname
|
||||||
|
than GitLab to prevent XSS attacks.
|
||||||
|
|
||||||
|
[backup]: ../raketasks/backup_restore.md
|
||||||
|
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
|
||||||
|
[ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173
|
||||||
|
[gitlab pages daemon]: https://gitlab.com/gitlab-org/gitlab-pages
|
||||||
|
[NGINX configs]: https://gitlab.com/gitlab-org/gitlab-ee/tree/8-5-stable-ee/lib/support/nginx
|
||||||
|
[pages-readme]: https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md
|
||||||
|
[pages-userguide]: ../../user/project/pages/index.md
|
||||||
|
[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
|
||||||
|
[restart]: ../administration/restart_gitlab.md#installations-from-source
|
||||||
|
[gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4
|
|
@ -1319,6 +1319,35 @@ with an API call.
|
||||||
|
|
||||||
[Read more in the triggers documentation.](../triggers/README.md)
|
[Read more in the triggers documentation.](../triggers/README.md)
|
||||||
|
|
||||||
|
### pages
|
||||||
|
|
||||||
|
`pages` is a special job that is used to upload static content to GitLab that
|
||||||
|
can be used to serve your website. It has a special syntax, so the two
|
||||||
|
requirements below must be met:
|
||||||
|
|
||||||
|
1. Any static content must be placed under a `public/` directory
|
||||||
|
1. `artifacts` with a path to the `public/` directory must be defined
|
||||||
|
|
||||||
|
The example below simply moves all files from the root of the project to the
|
||||||
|
`public/` directory. The `.public` workaround is so `cp` doesn't also copy
|
||||||
|
`public/` to itself in an infinite loop:
|
||||||
|
|
||||||
|
```
|
||||||
|
pages:
|
||||||
|
stage: deploy
|
||||||
|
script:
|
||||||
|
- mkdir .public
|
||||||
|
- cp -r * .public
|
||||||
|
- mv .public public
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
```
|
||||||
|
|
||||||
|
Read more on [GitLab Pages user documentation](../../pages/README.md).
|
||||||
|
|
||||||
## Validate the .gitlab-ci.yml
|
## Validate the .gitlab-ci.yml
|
||||||
|
|
||||||
Each instance of GitLab CI has an embedded debug tool called Lint.
|
Each instance of GitLab CI has an embedded debug tool called Lint.
|
||||||
|
|
|
@ -313,6 +313,9 @@ sudo usermod -aG redis git
|
||||||
# Change the permissions of the directory where CI artifacts are stored
|
# Change the permissions of the directory where CI artifacts are stored
|
||||||
sudo chmod -R u+rwX shared/artifacts/
|
sudo chmod -R u+rwX shared/artifacts/
|
||||||
|
|
||||||
|
# Change the permissions of the directory where GitLab Pages are stored
|
||||||
|
sudo chmod -R ug+rwX shared/pages/
|
||||||
|
|
||||||
# Copy the example Unicorn config
|
# Copy the example Unicorn config
|
||||||
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
|
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
|
||||||
|
|
||||||
|
@ -484,6 +487,10 @@ Make sure to edit the config file to match your setup. Also, ensure that you mat
|
||||||
# or else sudo rm -f /etc/nginx/sites-enabled/default
|
# or else sudo rm -f /etc/nginx/sites-enabled/default
|
||||||
sudo editor /etc/nginx/sites-available/gitlab
|
sudo editor /etc/nginx/sites-available/gitlab
|
||||||
|
|
||||||
|
If you intend to enable GitLab pages, there is a separate Nginx config you need
|
||||||
|
to use. Read all about the needed configuration at the
|
||||||
|
[GitLab Pages administration guide](../administration/pages/index.md).
|
||||||
|
|
||||||
**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
|
**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details.
|
||||||
|
|
||||||
### Test Configuration
|
### Test Configuration
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
This document was moved to [user/project/pages](../user/project/pages/index.md).
|
|
@ -0,0 +1 @@
|
||||||
|
This document was moved to [administration/pages](../administration/pages/index.md).
|
|
@ -84,6 +84,29 @@ Deleting tmp directories...[DONE]
|
||||||
Deleting old backups... [SKIPPING]
|
Deleting old backups... [SKIPPING]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Exclude specific directories from the backup
|
||||||
|
|
||||||
|
You can choose what should be backed up by adding the environment variable `SKIP`.
|
||||||
|
The available options are:
|
||||||
|
|
||||||
|
* `db`
|
||||||
|
* `uploads` (attachments)
|
||||||
|
* `repositories`
|
||||||
|
* `builds` (CI build output logs)
|
||||||
|
* `artifacts` (CI build artifacts)
|
||||||
|
* `lfs` (LFS objects)
|
||||||
|
* `pages` (pages content)
|
||||||
|
|
||||||
|
Use a comma to specify several options at the same time:
|
||||||
|
|
||||||
|
```
|
||||||
|
# use this command if you've installed GitLab with the Omnibus package
|
||||||
|
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
|
||||||
|
|
||||||
|
# if you've installed GitLab from source
|
||||||
|
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=production
|
||||||
|
```
|
||||||
|
|
||||||
## Upload backups to remote (cloud) storage
|
## Upload backups to remote (cloud) storage
|
||||||
|
|
||||||
Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates.
|
Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates.
|
||||||
|
|
|
@ -91,7 +91,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
|
||||||
|
|
||||||
1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
|
1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
|
||||||
1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
|
1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
|
||||||
1. [GitLab Pages Documentation](https://docs.gitlab.com/ee/pages/README.html)
|
1. [GitLab Pages Documentation](https://docs.gitlab.com/ce/user/project/pages/)
|
||||||
|
|
||||||
#### 2.2. GitLab Issues
|
#### 2.2. GitLab Issues
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ Move on to understanding some of GitLab's more advanced features. You can make u
|
||||||
- Get to know the [GitLab API](https://docs.gitlab.com/ee/api/README.html), its capabilities and shortcomings
|
- Get to know the [GitLab API](https://docs.gitlab.com/ee/api/README.html), its capabilities and shortcomings
|
||||||
- Learn how to [migrate from SVN to Git](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
|
- Learn how to [migrate from SVN to Git](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
|
||||||
- Set up [GitLab CI](https://docs.gitlab.com/ee/ci/quick_start/README.html)
|
- Set up [GitLab CI](https://docs.gitlab.com/ee/ci/quick_start/README.html)
|
||||||
- Create your first [GitLab Page](https://docs.gitlab.com/ee/pages/administration.html)
|
- Create your first [GitLab Page](https://docs.gitlab.com/ce/administration/pages/)
|
||||||
- Get to know the GitLab Codebase by reading through the source code:
|
- Get to know the GitLab Codebase by reading through the source code:
|
||||||
- Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
|
- Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
|
||||||
and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
|
and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
|
||||||
|
|
|
@ -62,11 +62,14 @@ The following table depicts the various user permission levels in a project.
|
||||||
| Manage runners | | | | ✓ | ✓ |
|
| Manage runners | | | | ✓ | ✓ |
|
||||||
| Manage build triggers | | | | ✓ | ✓ |
|
| Manage build triggers | | | | ✓ | ✓ |
|
||||||
| Manage variables | | | | ✓ | ✓ |
|
| Manage variables | | | | ✓ | ✓ |
|
||||||
|
| Manage pages | | | | ✓ | ✓ |
|
||||||
|
| Manage pages domains and certificates | | | | ✓ | ✓ |
|
||||||
| Switch visibility level | | | | | ✓ |
|
| Switch visibility level | | | | | ✓ |
|
||||||
| Transfer project to another namespace | | | | | ✓ |
|
| Transfer project to another namespace | | | | | ✓ |
|
||||||
| Remove project | | | | | ✓ |
|
| Remove project | | | | | ✓ |
|
||||||
| Force push to protected branches [^3] | | | | | |
|
| Force push to protected branches [^3] | | | | | |
|
||||||
| Remove protected branches [^3] | | | | | |
|
| Remove protected branches [^3] | | | | | |
|
||||||
|
| Remove pages | | | | | ✓ |
|
||||||
|
|
||||||
## Group
|
## Group
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 101 KiB |
|
@ -0,0 +1,435 @@
|
||||||
|
# GitLab Pages
|
||||||
|
|
||||||
|
> **Notes:**
|
||||||
|
> - This feature was [introduced][ee-80] in GitLab EE 8.3.
|
||||||
|
> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
|
||||||
|
> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
|
||||||
|
> - This document is about the user guide. To learn how to enable GitLab Pages
|
||||||
|
> across your GitLab instance, visit the [administrator documentation](../../../administration/pages/index.md).
|
||||||
|
|
||||||
|
With GitLab Pages you can host for free your static websites on GitLab.
|
||||||
|
Combined with the power of [GitLab CI] and the help of [GitLab Runner] you can
|
||||||
|
deploy static pages for your individual projects, your user or your group.
|
||||||
|
|
||||||
|
Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlab-com) for specific
|
||||||
|
information, if you are using GitLab.com to host your website.
|
||||||
|
|
||||||
|
## Getting started with GitLab Pages
|
||||||
|
|
||||||
|
> **Note:**
|
||||||
|
> In the rest of this document we will assume that the general domain name that
|
||||||
|
> is used for GitLab Pages is `example.io`.
|
||||||
|
|
||||||
|
In general there are two types of pages one might create:
|
||||||
|
|
||||||
|
- Pages per user (`username.example.io`) or per group (`groupname.example.io`)
|
||||||
|
- Pages per project (`username.example.io/projectname` or `groupname.example.io/projectname`)
|
||||||
|
|
||||||
|
In GitLab, usernames and groupnames are unique and we often refer to them
|
||||||
|
as namespaces. There can be only one namespace in a GitLab instance. Below you
|
||||||
|
can see the connection between the type of GitLab Pages, what the project name
|
||||||
|
that is created on GitLab looks like and the website URL it will be ultimately
|
||||||
|
be served on.
|
||||||
|
|
||||||
|
| Type of GitLab Pages | The name of the project created in GitLab | Website URL |
|
||||||
|
| -------------------- | ------------ | ----------- |
|
||||||
|
| User pages | `username.example.io` | `http(s)://username.example.io` |
|
||||||
|
| Group pages | `groupname.example.io` | `http(s)://groupname.example.io` |
|
||||||
|
| Project pages owned by a user | `projectname` | `http(s)://username.example.io/projectname` |
|
||||||
|
| Project pages owned by a group | `projectname` | `http(s)://groupname.example.io/projectname`|
|
||||||
|
|
||||||
|
> **Warning:**
|
||||||
|
> There are some known [limitations](#limitations) regarding namespaces served
|
||||||
|
> under the general domain name and HTTPS. Make sure to read that section.
|
||||||
|
|
||||||
|
### GitLab Pages requirements
|
||||||
|
|
||||||
|
In brief, this is what you need to upload your website in GitLab Pages:
|
||||||
|
|
||||||
|
1. Find out the general domain name that is used for GitLab Pages
|
||||||
|
(ask your administrator). This is very important, so you should first make
|
||||||
|
sure you get that right.
|
||||||
|
1. Create a project
|
||||||
|
1. Push a [`.gitlab-ci.yml` file](../ci/yaml/README.md) in the root directory
|
||||||
|
of your repository with a specific job named [`pages`][pages]
|
||||||
|
1. Set up a GitLab Runner to build your website
|
||||||
|
|
||||||
|
> **Note:**
|
||||||
|
> If [shared runners](../ci/runners/README.md) are enabled by your GitLab
|
||||||
|
> administrator, you should be able to use them instead of bringing your own.
|
||||||
|
|
||||||
|
### User or group Pages
|
||||||
|
|
||||||
|
For user and group pages, the name of the project should be specific to the
|
||||||
|
username or groupname and the general domain name that is used for GitLab Pages.
|
||||||
|
Head over your GitLab instance that supports GitLab Pages and create a
|
||||||
|
repository named `username.example.io`, where `username` is your username on
|
||||||
|
GitLab. If the first part of the project name doesn't match exactly your
|
||||||
|
username, it won’t work, so make sure to get it right.
|
||||||
|
|
||||||
|
To create a group page, the steps are the same like when creating a website for
|
||||||
|
users. Just make sure that you are creating the project within the group's
|
||||||
|
namespace.
|
||||||
|
|
||||||
|
![Create a user-based pages project](img/pages_create_user_page.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
After you push some static content to your repository and GitLab Runner uploads
|
||||||
|
the artifacts to GitLab CI, you will be able to access your website under
|
||||||
|
`http(s)://username.example.io`. Keep reading to find out how.
|
||||||
|
|
||||||
|
>**Note:**
|
||||||
|
If your username/groupname contains a dot, for example `foo.bar`, you will not
|
||||||
|
be able to use the wildcard domain HTTPS, read more at [limitations](#limitations).
|
||||||
|
|
||||||
|
### Project Pages
|
||||||
|
|
||||||
|
GitLab Pages for projects can be created by both user and group accounts.
|
||||||
|
The steps to create a project page for a user or a group are identical:
|
||||||
|
|
||||||
|
1. Create a new project
|
||||||
|
1. Push a [`.gitlab-ci.yml` file](../ci/yaml/README.md) in the root directory
|
||||||
|
of your repository with a specific job named [`pages`][pages].
|
||||||
|
1. Set up a GitLab Runner to build your website
|
||||||
|
|
||||||
|
A user's project will be served under `http(s)://username.example.io/projectname`
|
||||||
|
whereas a group's project under `http(s)://groupname.example.io/projectname`.
|
||||||
|
|
||||||
|
### Explore the contents of `.gitlab-ci.yml`
|
||||||
|
|
||||||
|
The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that
|
||||||
|
gives you absolute control over the build process. You can actually watch your
|
||||||
|
website being built live by following the CI build traces.
|
||||||
|
|
||||||
|
> **Note:**
|
||||||
|
> Before reading this section, make sure you familiarize yourself with GitLab CI
|
||||||
|
> and the specific syntax of[`.gitlab-ci.yml`](../ci/yaml/README.md) by
|
||||||
|
> following our [quick start guide](../ci/quick_start/README.md).
|
||||||
|
|
||||||
|
To make use of GitLab Pages, the contents of `.gitlab-ci.yml` must follow the
|
||||||
|
rules below:
|
||||||
|
|
||||||
|
1. A special job named [`pages`][pages] must be defined
|
||||||
|
1. Any static content which will be served by GitLab Pages must be placed under
|
||||||
|
a `public/` directory
|
||||||
|
1. `artifacts` with a path to the `public/` directory must be defined
|
||||||
|
|
||||||
|
In its simplest form, `.gitlab-ci.yml` looks like:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pages:
|
||||||
|
script:
|
||||||
|
- my_commands
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
```
|
||||||
|
|
||||||
|
When the Runner reaches to build the `pages` job, it executes whatever is
|
||||||
|
defined in the `script` parameter and if the build completes with a non-zero
|
||||||
|
exit status, it then uploads the `public/` directory to GitLab Pages.
|
||||||
|
|
||||||
|
The `public/` directory should contain all the static content of your website.
|
||||||
|
Depending on how you plan to publish your website, the steps defined in the
|
||||||
|
[`script` parameter](../ci/yaml/README.md#script) may differ.
|
||||||
|
|
||||||
|
Be aware that Pages are by default branch/tag agnostic and their deployment
|
||||||
|
relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
|
||||||
|
`pages` job with the [`only` parameter](../ci/yaml/README.md#only-and-except),
|
||||||
|
whenever a new commit is pushed to whatever branch or tag, the Pages will be
|
||||||
|
overwritten. In the example below, we limit the Pages to be deployed whenever
|
||||||
|
a commit is pushed only on the `master` branch:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pages:
|
||||||
|
script:
|
||||||
|
- my_commands
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
```
|
||||||
|
|
||||||
|
We then tell the Runner to treat the `public/` directory as `artifacts` and
|
||||||
|
upload it to GitLab. And since all these parameters were all under a `pages`
|
||||||
|
job, the contents of the `public` directory will be served by GitLab Pages.
|
||||||
|
|
||||||
|
#### How `.gitlab-ci.yml` looks like when the static content is in your repository
|
||||||
|
|
||||||
|
Supposedly your repository contained the following files:
|
||||||
|
|
||||||
|
```
|
||||||
|
├── index.html
|
||||||
|
├── css
|
||||||
|
│ └── main.css
|
||||||
|
└── js
|
||||||
|
└── main.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the `.gitlab-ci.yml` example below simply moves all files from the root
|
||||||
|
directory of the project to the `public/` directory. The `.public` workaround
|
||||||
|
is so `cp` doesn't also copy `public/` to itself in an infinite loop:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pages:
|
||||||
|
script:
|
||||||
|
- mkdir .public
|
||||||
|
- cp -r * .public
|
||||||
|
- mv .public public
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
```
|
||||||
|
|
||||||
|
#### How `.gitlab-ci.yml` looks like when using a static generator
|
||||||
|
|
||||||
|
In general, GitLab Pages support any kind of [static site generator][staticgen],
|
||||||
|
since `.gitlab-ci.yml` can be configured to run any possible command.
|
||||||
|
|
||||||
|
In the root directory of your Git repository, place the source files of your
|
||||||
|
favorite static generator. Then provide a `.gitlab-ci.yml` file which is
|
||||||
|
specific to your static generator.
|
||||||
|
|
||||||
|
The example below, uses [Jekyll] to build the static site:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: ruby:2.1 # the script will run in Ruby 2.1 using the Docker image ruby:2.1
|
||||||
|
|
||||||
|
pages: # the build job must be named pages
|
||||||
|
script:
|
||||||
|
- gem install jekyll # we install jekyll
|
||||||
|
- jekyll build -d public/ # we tell jekyll to build the site for us
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public # this is where the site will live and the Runner uploads it in GitLab
|
||||||
|
only:
|
||||||
|
- master # this script is only affecting the master branch
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, we used the Docker executor and in the first line we specified the base
|
||||||
|
image against which our builds will run.
|
||||||
|
|
||||||
|
You have to make sure that the generated static files are ultimately placed
|
||||||
|
under the `public` directory, that's why in the `script` section we run the
|
||||||
|
`jekyll` command that builds the website and puts all content in the `public/`
|
||||||
|
directory. Depending on the static generator of your choice, this command will
|
||||||
|
differ. Search in the documentation of the static generator you will use if
|
||||||
|
there is an option to explicitly set the output directory. If there is not
|
||||||
|
such an option, you can always add one more line under `script` to rename the
|
||||||
|
resulting directory in `public/`.
|
||||||
|
|
||||||
|
We then tell the Runner to treat the `public/` directory as `artifacts` and
|
||||||
|
upload it to GitLab.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
See the [jekyll example project][pages-jekyll] to better understand how this
|
||||||
|
works.
|
||||||
|
|
||||||
|
For a list of Pages projects, see the [example projects](#example-projects) to
|
||||||
|
get you started.
|
||||||
|
|
||||||
|
#### How to set up GitLab Pages in a repository where there's also actual code
|
||||||
|
|
||||||
|
Remember that GitLab Pages are by default branch/tag agnostic and their
|
||||||
|
deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
|
||||||
|
the `pages` job with the [`only` parameter](../ci/yaml/README.md#only-and-except),
|
||||||
|
whenever a new commit is pushed to a branch that will be used specifically for
|
||||||
|
your pages.
|
||||||
|
|
||||||
|
That way, you can have your project's code in the `master` branch and use an
|
||||||
|
orphan branch (let's name it `pages`) that will host your static generator site.
|
||||||
|
|
||||||
|
You can create a new empty branch like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout --orphan pages
|
||||||
|
```
|
||||||
|
|
||||||
|
The first commit made on this new branch will have no parents and it will be
|
||||||
|
the root of a new history totally disconnected from all the other branches and
|
||||||
|
commits. Push the source files of your static generator in the `pages` branch.
|
||||||
|
|
||||||
|
Below is a copy of `.gitlab-ci.yml` where the most significant line is the last
|
||||||
|
one, specifying to execute everything in the `pages` branch:
|
||||||
|
|
||||||
|
```
|
||||||
|
image: ruby:2.1
|
||||||
|
|
||||||
|
pages:
|
||||||
|
script:
|
||||||
|
- gem install jekyll
|
||||||
|
- jekyll build -d public/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
only:
|
||||||
|
- pages
|
||||||
|
```
|
||||||
|
|
||||||
|
See an example that has different files in the [`master` branch][jekyll-master]
|
||||||
|
and the source files for Jekyll are in a [`pages` branch][jekyll-pages] which
|
||||||
|
also includes `.gitlab-ci.yml`.
|
||||||
|
|
||||||
|
[jekyll-master]: https://gitlab.com/pages/jekyll-branched/tree/master
|
||||||
|
[jekyll-pages]: https://gitlab.com/pages/jekyll-branched/tree/pages
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
|
So you have successfully deployed your website, congratulations! Let's check
|
||||||
|
what more you can do with GitLab Pages.
|
||||||
|
|
||||||
|
### Example projects
|
||||||
|
|
||||||
|
Below is a list of example projects for GitLab Pages with a plain HTML website
|
||||||
|
or various static site generators. Contributions are very welcome.
|
||||||
|
|
||||||
|
- [Plain HTML](https://gitlab.com/pages/plain-html)
|
||||||
|
- [Jekyll](https://gitlab.com/pages/jekyll)
|
||||||
|
- [Hugo](https://gitlab.com/pages/hugo)
|
||||||
|
- [Middleman](https://gitlab.com/pages/middleman)
|
||||||
|
- [Hexo](https://gitlab.com/pages/hexo)
|
||||||
|
- [Brunch](https://gitlab.com/pages/brunch)
|
||||||
|
- [Metalsmith](https://gitlab.com/pages/metalsmith)
|
||||||
|
- [Harp](https://gitlab.com/pages/harp)
|
||||||
|
|
||||||
|
Visit the GitLab Pages group for a full list of example projects:
|
||||||
|
<https://gitlab.com/groups/pages>.
|
||||||
|
|
||||||
|
### Add a custom domain to your Pages website
|
||||||
|
|
||||||
|
If this setting is enabled by your GitLab administrator, you should be able to
|
||||||
|
see the **New Domain** button when visiting your project's settings through the
|
||||||
|
gear icon in the top right and then navigating to **Pages**.
|
||||||
|
|
||||||
|
![New domain button](img/pages_new_domain_button.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You can add multiple domains pointing to your website hosted under GitLab.
|
||||||
|
Once the domain is added, you can see it listed under the **Domains** section.
|
||||||
|
|
||||||
|
![Pages multiple domains](img/pages_multiple_domains.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
As a last step, you need to configure your DNS and add a CNAME pointing to your
|
||||||
|
user/group page. Click on the **Details** button of a domain for further
|
||||||
|
instructions.
|
||||||
|
|
||||||
|
![Pages DNS details](img/pages_dns_details.png)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
>**Note:**
|
||||||
|
Currently there is support only for custom domains on per-project basis. That
|
||||||
|
means that if you add a custom domain (`example.com`) for your user website
|
||||||
|
(`username.example.io`), a project that is served under `username.example.io/foo`,
|
||||||
|
will not be accessible under `example.com/foo`.
|
||||||
|
|
||||||
|
### Secure your custom domain website with TLS
|
||||||
|
|
||||||
|
When you add a new custom domain, you also have the chance to add a TLS
|
||||||
|
certificate. If this setting is enabled by your GitLab administrator, you
|
||||||
|
should be able to see the option to upload the public certificate and the
|
||||||
|
private key when adding a new domain.
|
||||||
|
|
||||||
|
![Pages upload cert](img/pages_upload_cert.png)
|
||||||
|
|
||||||
|
### Custom error codes pages
|
||||||
|
|
||||||
|
You can provide your own 403 and 404 error pages by creating the `403.html` and
|
||||||
|
`404.html` files respectively in the root directory of the `public/` directory
|
||||||
|
that will be included in the artifacts. Usually this is the root directory of
|
||||||
|
your project, but that may differ depending on your static generator
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
If the case of `404.html`, there are different scenarios. For example:
|
||||||
|
|
||||||
|
- If you use project Pages (served under `/projectname/`) and try to access
|
||||||
|
`/projectname/non/exsiting_file`, GitLab Pages will try to serve first
|
||||||
|
`/projectname/404.html`, and then `/404.html`.
|
||||||
|
- If you use user/group Pages (served under `/`) and try to access
|
||||||
|
`/non/existing_file` GitLab Pages will try to serve `/404.html`.
|
||||||
|
- If you use a custom domain and try to access `/non/existing_file`, GitLab
|
||||||
|
Pages will try to serve only `/404.html`.
|
||||||
|
|
||||||
|
### Remove the contents of your pages
|
||||||
|
|
||||||
|
If you ever feel the need to purge your Pages content, you can do so by going
|
||||||
|
to your project's settings through the gear icon in the top right, and then
|
||||||
|
navigating to **Pages**. Hit the **Remove pages** button and your Pages website
|
||||||
|
will be deleted. Simple as that.
|
||||||
|
|
||||||
|
![Remove pages](img/pages_remove.png)
|
||||||
|
|
||||||
|
## GitLab Pages on GitLab.com
|
||||||
|
|
||||||
|
If you are using GitLab.com to host your website, then:
|
||||||
|
|
||||||
|
- The general domain name for GitLab Pages on GitLab.com is `gitlab.io`.
|
||||||
|
- Custom domains and TLS support are enabled.
|
||||||
|
- Shared runners are enabled by default, provided for free and can be used to
|
||||||
|
build your website. If you want you can still bring your own Runner.
|
||||||
|
|
||||||
|
The rest of the guide still applies.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
When using Pages under the general domain of a GitLab instance (`*.example.io`),
|
||||||
|
you _cannot_ use HTTPS with sub-subdomains. That means that if your
|
||||||
|
username/groupname contains a dot, for example `foo.bar`, the domain
|
||||||
|
`https://foo.bar.example.io` will _not_ work. This is a limitation of the
|
||||||
|
[HTTP Over TLS protocol][rfc]. HTTP pages will continue to work provided you
|
||||||
|
don't redirect HTTP to HTTPS.
|
||||||
|
|
||||||
|
[rfc]: https://tools.ietf.org/html/rfc2818#section-3.1 "HTTP Over TLS RFC"
|
||||||
|
|
||||||
|
## Redirects in GitLab Pages
|
||||||
|
|
||||||
|
Since you cannot use any custom server configuration files, like `.htaccess` or
|
||||||
|
any `.conf` file for that matter, if you want to redirect a web page to another
|
||||||
|
location, you can use the [HTTP meta refresh tag][metarefresh].
|
||||||
|
|
||||||
|
Some static site generators provide plugins for that functionality so that you
|
||||||
|
don't have to create and edit HTML files manually. For example, Jekyll has the
|
||||||
|
[redirect-from plugin](https://github.com/jekyll/jekyll-redirect-from).
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
### Can I download my generated pages?
|
||||||
|
|
||||||
|
Sure. All you need to do is download the artifacts archive from the build page.
|
||||||
|
|
||||||
|
### Can I use GitLab Pages if my project is private?
|
||||||
|
|
||||||
|
Yes. GitLab Pages don't care whether you set your project's visibility level
|
||||||
|
to private, internal or public.
|
||||||
|
|
||||||
|
### Do I need to create a user/group website before creating a project website?
|
||||||
|
|
||||||
|
No, you don't. You can create your project first and it will be accessed under
|
||||||
|
`http(s)://namespace.example.io/projectname`.
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
For a list of known issues, visit GitLab's [public issue tracker].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[jekyll]: http://jekyllrb.com/
|
||||||
|
[ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80
|
||||||
|
[ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173
|
||||||
|
[pages-daemon]: https://gitlab.com/gitlab-org/gitlab-pages
|
||||||
|
[gitlab ci]: https://about.gitlab.com/gitlab-ci
|
||||||
|
[gitlab runner]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner
|
||||||
|
[pages]: ../ci/yaml/README.md#pages
|
||||||
|
[staticgen]: https://www.staticgen.com/
|
||||||
|
[pages-jekyll]: https://gitlab.com/pages/jekyll
|
||||||
|
[metarefresh]: https://en.wikipedia.org/wiki/Meta_refresh
|
||||||
|
[public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Pages
|
||||||
|
[ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605
|
|
@ -53,6 +53,13 @@ Feature: Project Active Tab
|
||||||
And no other sub navs should be active
|
And no other sub navs should be active
|
||||||
And the active main tab should be Settings
|
And the active main tab should be Settings
|
||||||
|
|
||||||
|
Scenario: On Project Settings/Pages
|
||||||
|
Given I visit my project's settings page
|
||||||
|
And I click the "Pages" tab
|
||||||
|
Then the active sub nav should be Pages
|
||||||
|
And no other sub navs should be active
|
||||||
|
And the active main tab should be Settings
|
||||||
|
|
||||||
Scenario: On Project Members
|
Scenario: On Project Members
|
||||||
Given I visit my project's members page
|
Given I visit my project's members page
|
||||||
Then the active sub nav should be Members
|
Then the active sub nav should be Members
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
Feature: Project Pages
|
||||||
|
Background:
|
||||||
|
Given I sign in as a user
|
||||||
|
And I own a project
|
||||||
|
|
||||||
|
Scenario: Pages are disabled
|
||||||
|
Given pages are disabled
|
||||||
|
When I visit the Project Pages
|
||||||
|
Then I should see that GitLab Pages are disabled
|
||||||
|
|
||||||
|
Scenario: I can see the pages usage if not deployed
|
||||||
|
Given pages are enabled
|
||||||
|
When I visit the Project Pages
|
||||||
|
Then I should see the usage of GitLab Pages
|
||||||
|
|
||||||
|
Scenario: I can access the pages if deployed
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are deployed
|
||||||
|
When I visit the Project Pages
|
||||||
|
Then I should be able to access the Pages
|
||||||
|
|
||||||
|
Scenario: I should message that domains support is disabled
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are deployed
|
||||||
|
And support for external domains is disabled
|
||||||
|
When I visit the Project Pages
|
||||||
|
Then I should see that support for domains is disabled
|
||||||
|
|
||||||
|
Scenario: I should see a new domain button
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are exposed on external HTTP address
|
||||||
|
When I visit the Project Pages
|
||||||
|
And I should be able to add a New Domain
|
||||||
|
|
||||||
|
Scenario: I should be able to add a new domain
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are exposed on external HTTP address
|
||||||
|
When I visit add a new Pages Domain
|
||||||
|
And I fill the domain
|
||||||
|
And I click on "Create New Domain"
|
||||||
|
Then I should see a new domain added
|
||||||
|
|
||||||
|
Scenario: I should be able to add a new domain for project in group namespace
|
||||||
|
Given I own a project in some group namespace
|
||||||
|
And pages are enabled
|
||||||
|
And pages are exposed on external HTTP address
|
||||||
|
When I visit add a new Pages Domain
|
||||||
|
And I fill the domain
|
||||||
|
And I click on "Create New Domain"
|
||||||
|
Then I should see a new domain added
|
||||||
|
|
||||||
|
Scenario: I should be denied to add the same domain twice
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are exposed on external HTTP address
|
||||||
|
And pages domain is added
|
||||||
|
When I visit add a new Pages Domain
|
||||||
|
And I fill the domain
|
||||||
|
And I click on "Create New Domain"
|
||||||
|
Then I should see error message that domain already exists
|
||||||
|
|
||||||
|
Scenario: I should message that certificates support is disabled when trying to add a new domain
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are exposed on external HTTP address
|
||||||
|
And pages domain is added
|
||||||
|
When I visit add a new Pages Domain
|
||||||
|
Then I should see that support for certificates is disabled
|
||||||
|
|
||||||
|
Scenario: I should be able to add a new domain with certificate
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are exposed on external HTTPS address
|
||||||
|
When I visit add a new Pages Domain
|
||||||
|
And I fill the domain
|
||||||
|
And I fill the certificate and key
|
||||||
|
And I click on "Create New Domain"
|
||||||
|
Then I should see a new domain added
|
||||||
|
|
||||||
|
Scenario: I can remove the pages if deployed
|
||||||
|
Given pages are enabled
|
||||||
|
And pages are deployed
|
||||||
|
When I visit the Project Pages
|
||||||
|
And I click Remove Pages
|
||||||
|
Then The Pages should get removed
|
|
@ -35,6 +35,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
|
||||||
click_link('Deploy Keys')
|
click_link('Deploy Keys')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
step 'I click the "Pages" tab' do
|
||||||
|
click_link('Pages')
|
||||||
|
end
|
||||||
|
|
||||||
step 'the active sub nav should be Members' do
|
step 'the active sub nav should be Members' do
|
||||||
ensure_active_sub_nav('Members')
|
ensure_active_sub_nav('Members')
|
||||||
end
|
end
|
||||||
|
@ -47,6 +51,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
|
||||||
ensure_active_sub_nav('Deploy Keys')
|
ensure_active_sub_nav('Deploy Keys')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
step 'the active sub nav should be Pages' do
|
||||||
|
ensure_active_sub_nav('Pages')
|
||||||
|
end
|
||||||
|
|
||||||
# Sub Tabs: Commits
|
# Sub Tabs: Commits
|
||||||
|
|
||||||
step 'I click the "Compare" tab' do
|
step 'I click the "Compare" tab' do
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
class Spinach::Features::ProjectPages < Spinach::FeatureSteps
|
||||||
|
include SharedAuthentication
|
||||||
|
include SharedPaths
|
||||||
|
include SharedProject
|
||||||
|
|
||||||
|
step 'pages are enabled' do
|
||||||
|
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
|
||||||
|
allow(Gitlab.config.pages).to receive(:host).and_return('example.com')
|
||||||
|
allow(Gitlab.config.pages).to receive(:port).and_return(80)
|
||||||
|
allow(Gitlab.config.pages).to receive(:https).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'pages are disabled' do
|
||||||
|
allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I visit the Project Pages' do
|
||||||
|
visit namespace_project_pages_path(@project.namespace, @project)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see that GitLab Pages are disabled' do
|
||||||
|
expect(page).to have_content('GitLab Pages are disabled')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see the usage of GitLab Pages' do
|
||||||
|
expect(page).to have_content('Configure pages')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'pages are deployed' do
|
||||||
|
pipeline = @project.ensure_pipeline('HEAD', @project.commit('HEAD').sha)
|
||||||
|
build = build(:ci_build,
|
||||||
|
project: @project,
|
||||||
|
pipeline: pipeline,
|
||||||
|
ref: 'HEAD',
|
||||||
|
artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'),
|
||||||
|
artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
|
||||||
|
)
|
||||||
|
result = ::Projects::UpdatePagesService.new(@project, build).execute
|
||||||
|
expect(result[:status]).to eq(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should be able to access the Pages' do
|
||||||
|
expect(page).to have_content('Access pages')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see that support for domains is disabled' do
|
||||||
|
expect(page).to have_content('Support for domains and certificates is disabled')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'support for external domains is disabled' do
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_http).and_return(nil)
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'pages are exposed on external HTTP address' do
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_http).and_return('1.1.1.1:80')
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'pages are exposed on external HTTPS address' do
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_http).and_return('1.1.1.1:80')
|
||||||
|
allow(Gitlab.config.pages).to receive(:external_https).and_return('1.1.1.1:443')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should be able to add a New Domain' do
|
||||||
|
expect(page).to have_content('New Domain')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I visit add a new Pages Domain' do
|
||||||
|
visit new_namespace_project_pages_domain_path(@project.namespace, @project)
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I fill the domain' do
|
||||||
|
fill_in 'Domain', with: 'my.test.domain.com'
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I click on "Create New Domain"' do
|
||||||
|
click_button 'Create New Domain'
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see a new domain added' do
|
||||||
|
expect(page).to have_content('Domains (1)')
|
||||||
|
expect(page).to have_content('my.test.domain.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'pages domain is added' do
|
||||||
|
@project.pages_domains.create!(domain: 'my.test.domain.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see error message that domain already exists' do
|
||||||
|
expect(page).to have_content('Domain has already been taken')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see that support for certificates is disabled' do
|
||||||
|
expect(page).to have_content('Support for custom certificates is disabled')
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I fill the certificate and key' do
|
||||||
|
fill_in 'Certificate (PEM)', with: '-----BEGIN CERTIFICATE-----
|
||||||
|
MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0
|
||||||
|
LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ
|
||||||
|
MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||||
|
gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa
|
||||||
|
SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT
|
||||||
|
nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w
|
||||||
|
DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD
|
||||||
|
VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh
|
||||||
|
IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ
|
||||||
|
joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese
|
||||||
|
5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg
|
||||||
|
YHi2yesCrOvVXt+lgPTd
|
||||||
|
-----END CERTIFICATE-----'
|
||||||
|
|
||||||
|
fill_in 'Key (PEM)', with: '-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN
|
||||||
|
SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t
|
||||||
|
PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB
|
||||||
|
kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd
|
||||||
|
j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/
|
||||||
|
uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR
|
||||||
|
5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O
|
||||||
|
AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K
|
||||||
|
EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh
|
||||||
|
Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C
|
||||||
|
m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH
|
||||||
|
EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx
|
||||||
|
63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi
|
||||||
|
nNp/xedE1YxutQ==
|
||||||
|
-----END PRIVATE KEY-----'
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I click Remove Pages' do
|
||||||
|
click_link 'Remove pages'
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'The Pages should get removed' do
|
||||||
|
expect(@project.pages_deployed?).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,12 @@ module SharedProject
|
||||||
@project.team << [@user, :master]
|
@project.team << [@user, :master]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
step "I own a project in some group namespace" do
|
||||||
|
@group = create(:group, name: 'some group')
|
||||||
|
@project = create(:project, namespace: @group)
|
||||||
|
@project.team << [@user, :master]
|
||||||
|
end
|
||||||
|
|
||||||
step "project exists in some group namespace" do
|
step "project exists in some group namespace" do
|
||||||
@group = create(:group, name: 'some group')
|
@group = create(:group, name: 'some group')
|
||||||
@project = create(:project, :repository, namespace: @group, public_builds: false)
|
@project = create(:project, :repository, namespace: @group, public_builds: false)
|
||||||
|
|
|
@ -57,6 +57,7 @@ module API
|
||||||
requires :shared_runners_text, type: String, desc: 'Shared runners text '
|
requires :shared_runners_text, type: String, desc: 'Shared runners text '
|
||||||
end
|
end
|
||||||
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have"
|
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have"
|
||||||
|
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
|
||||||
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
|
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
|
||||||
optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
|
optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
|
||||||
given metrics_enabled: ->(val) { val } do
|
given metrics_enabled: ->(val) { val } do
|
||||||
|
@ -116,7 +117,7 @@ module API
|
||||||
:send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
|
:send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
|
||||||
:after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
|
:after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
|
||||||
:home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
|
:home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
|
||||||
:shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay,
|
:shared_runners_enabled, :max_artifacts_size, :max_pages_size, :container_registry_token_expire_delay,
|
||||||
:metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
|
:metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
|
||||||
:akismet_enabled, :admin_notification_email, :sentry_enabled,
|
:akismet_enabled, :admin_notification_email, :sentry_enabled,
|
||||||
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
|
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Backup
|
module Backup
|
||||||
class Manager
|
class Manager
|
||||||
ARCHIVES_TO_BACKUP = %w[uploads builds artifacts lfs registry]
|
ARCHIVES_TO_BACKUP = %w[uploads builds artifacts pages lfs registry]
|
||||||
FOLDERS_TO_BACKUP = %w[repositories db]
|
FOLDERS_TO_BACKUP = %w[repositories db]
|
||||||
FILE_NAME_SUFFIX = '_gitlab_backup.tar'
|
FILE_NAME_SUFFIX = '_gitlab_backup.tar'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
require 'backup/files'
|
||||||
|
|
||||||
|
module Backup
|
||||||
|
class Pages < Files
|
||||||
|
def initialize
|
||||||
|
super('pages', Gitlab.config.pages.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_files_dir
|
||||||
|
Dir.mkdir(app_files_dir, 0700)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
module Gitlab
|
||||||
|
class PagesTransfer < ProjectTransfer
|
||||||
|
def root_dir
|
||||||
|
Gitlab.config.pages.path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,35 @@
|
||||||
|
module Gitlab
|
||||||
|
class ProjectTransfer
|
||||||
|
def move_project(project_path, namespace_path_was, namespace_path)
|
||||||
|
new_namespace_folder = File.join(root_dir, namespace_path)
|
||||||
|
FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder)
|
||||||
|
from = File.join(root_dir, namespace_path_was, project_path)
|
||||||
|
to = File.join(root_dir, namespace_path, project_path)
|
||||||
|
move(from, to, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def rename_project(path_was, path, namespace_path)
|
||||||
|
base_dir = File.join(root_dir, namespace_path)
|
||||||
|
move(path_was, path, base_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rename_namespace(path_was, path)
|
||||||
|
move(path_was, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def root_dir
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def move(path_was, path, base_dir = nil)
|
||||||
|
base_dir = root_dir unless base_dir
|
||||||
|
from = File.join(base_dir, path_was)
|
||||||
|
to = File.join(base_dir, path)
|
||||||
|
FileUtils.mv(from, to)
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,33 +1,5 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class UploadsTransfer
|
class UploadsTransfer < ProjectTransfer
|
||||||
def move_project(project_path, namespace_path_was, namespace_path)
|
|
||||||
new_namespace_folder = File.join(root_dir, namespace_path)
|
|
||||||
FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder)
|
|
||||||
from = File.join(root_dir, namespace_path_was, project_path)
|
|
||||||
to = File.join(root_dir, namespace_path, project_path)
|
|
||||||
move(from, to, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename_project(path_was, path, namespace_path)
|
|
||||||
base_dir = File.join(root_dir, namespace_path)
|
|
||||||
move(path_was, path, base_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename_namespace(path_was, path)
|
|
||||||
move(path_was, path)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def move(path_was, path, base_dir = nil)
|
|
||||||
base_dir = root_dir unless base_dir
|
|
||||||
from = File.join(base_dir, path_was)
|
|
||||||
to = File.join(base_dir, path)
|
|
||||||
FileUtils.mv(from, to)
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def root_dir
|
def root_dir
|
||||||
File.join(Rails.root, "public", "uploads")
|
File.join(Rails.root, "public", "uploads")
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,6 +42,11 @@ gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse 2> /dev/null && pwd)
|
||||||
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
|
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
|
||||||
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
|
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
|
||||||
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
|
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
|
||||||
|
gitlab_pages_enabled=false
|
||||||
|
gitlab_pages_dir=$(cd $app_root/../gitlab-pages 2> /dev/null && pwd)
|
||||||
|
gitlab_pages_pid_path="$pid_path/gitlab-pages.pid"
|
||||||
|
gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090"
|
||||||
|
gitlab_pages_log="$app_root/log/gitlab-pages.log"
|
||||||
shell_path="/bin/bash"
|
shell_path="/bin/bash"
|
||||||
|
|
||||||
# Read configuration variable file if it is present
|
# Read configuration variable file if it is present
|
||||||
|
@ -89,13 +94,20 @@ check_pids(){
|
||||||
mpid=0
|
mpid=0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [ "$gitlab_pages_enabled" = true ]; then
|
||||||
|
if [ -f "$gitlab_pages_pid_path" ]; then
|
||||||
|
gppid=$(cat "$gitlab_pages_pid_path")
|
||||||
|
else
|
||||||
|
gppid=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
## Called when we have started the two processes and are waiting for their pid files.
|
## Called when we have started the two processes and are waiting for their pid files.
|
||||||
wait_for_pids(){
|
wait_for_pids(){
|
||||||
# We are sleeping a bit here mostly because sidekiq is slow at writing its pid
|
# We are sleeping a bit here mostly because sidekiq is slow at writing its pid
|
||||||
i=0;
|
i=0;
|
||||||
while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do
|
while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ] || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; }; do
|
||||||
sleep 0.1;
|
sleep 0.1;
|
||||||
i=$((i+1))
|
i=$((i+1))
|
||||||
if [ $((i%10)) = 0 ]; then
|
if [ $((i%10)) = 0 ]; then
|
||||||
|
@ -144,7 +156,15 @@ check_status(){
|
||||||
mail_room_status="-1"
|
mail_room_status="-1"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then
|
if [ "$gitlab_pages_enabled" = true ]; then
|
||||||
|
if [ $gppid -ne 0 ]; then
|
||||||
|
kill -0 "$gppid" 2>/dev/null
|
||||||
|
gitlab_pages_status="$?"
|
||||||
|
else
|
||||||
|
gitlab_pages_status="-1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; }; then
|
||||||
gitlab_status=0
|
gitlab_status=0
|
||||||
else
|
else
|
||||||
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
|
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
|
||||||
|
@ -186,12 +206,19 @@ check_stale_pids(){
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [ "$gitlab_pages_enabled" = true ] && [ "$gppid" != "0" ] && [ "$gitlab_pages_status" != "0" ]; then
|
||||||
|
echo "Removing stale GitLab Pages job dispatcher pid. This is most likely caused by GitLab Pages crashing the last time it ran."
|
||||||
|
if ! rm "$gitlab_pages_pid_path"; then
|
||||||
|
echo "Unable to remove stale pid, exiting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
## If no parts of the service is running, bail out.
|
## If no parts of the service is running, bail out.
|
||||||
exit_if_not_running(){
|
exit_if_not_running(){
|
||||||
check_stale_pids
|
check_stale_pids
|
||||||
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
|
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
|
||||||
echo "GitLab is not running."
|
echo "GitLab is not running."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
@ -213,6 +240,9 @@ start_gitlab() {
|
||||||
if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then
|
if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then
|
||||||
echo "Starting GitLab MailRoom"
|
echo "Starting GitLab MailRoom"
|
||||||
fi
|
fi
|
||||||
|
if [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" != "0" ]; then
|
||||||
|
echo "Starting GitLab Pages"
|
||||||
|
fi
|
||||||
|
|
||||||
# Then check if the service is running. If it is: don't start again.
|
# Then check if the service is running. If it is: don't start again.
|
||||||
if [ "$web_status" = "0" ]; then
|
if [ "$web_status" = "0" ]; then
|
||||||
|
@ -252,6 +282,16 @@ start_gitlab() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$gitlab_pages_enabled" = true ]; then
|
||||||
|
if [ "$gitlab_pages_status" = "0" ]; then
|
||||||
|
echo "The GitLab Pages is already running with pid $spid, not restarting"
|
||||||
|
else
|
||||||
|
$app_root/bin/daemon_with_pidfile $gitlab_pages_pid_path \
|
||||||
|
$gitlab_pages_dir/gitlab-pages $gitlab_pages_options \
|
||||||
|
>> $gitlab_pages_log 2>&1 &
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Wait for the pids to be planted
|
# Wait for the pids to be planted
|
||||||
wait_for_pids
|
wait_for_pids
|
||||||
# Finally check the status to tell wether or not GitLab is running
|
# Finally check the status to tell wether or not GitLab is running
|
||||||
|
@ -278,13 +318,17 @@ stop_gitlab() {
|
||||||
echo "Shutting down GitLab MailRoom"
|
echo "Shutting down GitLab MailRoom"
|
||||||
RAILS_ENV=$RAILS_ENV bin/mail_room stop
|
RAILS_ENV=$RAILS_ENV bin/mail_room stop
|
||||||
fi
|
fi
|
||||||
|
if [ "$gitlab_pages_status" = "0" ]; then
|
||||||
|
echo "Shutting down gitlab-pages"
|
||||||
|
kill -- $(cat $gitlab_pages_pid_path)
|
||||||
|
fi
|
||||||
|
|
||||||
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
|
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
|
||||||
while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do
|
while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; do
|
||||||
sleep 1
|
sleep 1
|
||||||
check_status
|
check_status
|
||||||
printf "."
|
printf "."
|
||||||
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
|
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
|
||||||
printf "\n"
|
printf "\n"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
@ -298,6 +342,7 @@ stop_gitlab() {
|
||||||
if [ "$mail_room_enabled" = true ]; then
|
if [ "$mail_room_enabled" = true ]; then
|
||||||
rm "$mail_room_pid_path" 2>/dev/null
|
rm "$mail_room_pid_path" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
rm -f "$gitlab_pages_pid_path"
|
||||||
|
|
||||||
print_status
|
print_status
|
||||||
}
|
}
|
||||||
|
@ -305,7 +350,7 @@ stop_gitlab() {
|
||||||
## Prints the status of GitLab and its components.
|
## Prints the status of GitLab and its components.
|
||||||
print_status() {
|
print_status() {
|
||||||
check_status
|
check_status
|
||||||
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
|
if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
|
||||||
echo "GitLab is not running."
|
echo "GitLab is not running."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
@ -331,7 +376,14 @@ print_status() {
|
||||||
printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n"
|
printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then
|
if [ "$gitlab_pages_enabled" = true ]; then
|
||||||
|
if [ "$gitlab_pages_status" = "0" ]; then
|
||||||
|
echo "The GitLab Pages with pid $mpid is running."
|
||||||
|
else
|
||||||
|
printf "The GitLab Pages is \033[31mnot running\033[0m.\n"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; }; then
|
||||||
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
|
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -362,7 +414,7 @@ reload_gitlab(){
|
||||||
## Restarts Sidekiq and Unicorn.
|
## Restarts Sidekiq and Unicorn.
|
||||||
restart_gitlab(){
|
restart_gitlab(){
|
||||||
check_status
|
check_status
|
||||||
if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then
|
if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; then
|
||||||
stop_gitlab
|
stop_gitlab
|
||||||
fi
|
fi
|
||||||
start_gitlab
|
start_gitlab
|
||||||
|
|
|
@ -47,6 +47,30 @@ gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
|
||||||
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
|
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
|
||||||
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
|
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
|
||||||
|
|
||||||
|
# The GitLab Pages Daemon needs either a separate IP address on which it will
|
||||||
|
# listen or use different ports than 80 or 443 that will be forwarded to GitLab
|
||||||
|
# Pages Daemon.
|
||||||
|
#
|
||||||
|
# To enable HTTP support for custom domains add the `-listen-http` directive
|
||||||
|
# in `gitlab_pages_options` below.
|
||||||
|
# The value of -listen-http must be set to `gitlab.yml > pages > external_http`
|
||||||
|
# as well. For example:
|
||||||
|
#
|
||||||
|
# -listen-http 1.1.1.1:80
|
||||||
|
#
|
||||||
|
# To enable HTTPS support for custom domains add the `-listen-https`,
|
||||||
|
# `-root-cert` and `-root-key` directives in `gitlab_pages_options` below.
|
||||||
|
# The value of -listen-https must be set to `gitlab.yml > pages > external_https`
|
||||||
|
# as well. For example:
|
||||||
|
#
|
||||||
|
# -listen-https 1.1.1.1:443 -root-cert /path/to/example.com.crt -root-key /path/to/example.com.key
|
||||||
|
#
|
||||||
|
# The -pages-domain must be specified the same as in `gitlab.yml > pages > host`.
|
||||||
|
# Set `gitlab_pages_enabled=true` if you want to enable the Pages feature.
|
||||||
|
gitlab_pages_enabled=false
|
||||||
|
gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090"
|
||||||
|
gitlab_pages_log="$app_root/log/gitlab-pages.log"
|
||||||
|
|
||||||
# mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
|
# mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
|
||||||
# This is required for the Reply by email feature.
|
# This is required for the Reply by email feature.
|
||||||
# The default is "false"
|
# The default is "false"
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
## GitLab
|
||||||
|
##
|
||||||
|
|
||||||
|
## Pages serving host
|
||||||
|
server {
|
||||||
|
listen 0.0.0.0:80;
|
||||||
|
listen [::]:80 ipv6only=on;
|
||||||
|
|
||||||
|
## Replace this with something like pages.gitlab.com
|
||||||
|
server_name ~^.*\.YOUR_GITLAB_PAGES\.DOMAIN$;
|
||||||
|
|
||||||
|
## Individual nginx logs for GitLab pages
|
||||||
|
access_log /var/log/nginx/gitlab_pages_access.log;
|
||||||
|
error_log /var/log/nginx/gitlab_pages_error.log;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
# The same address as passed to GitLab Pages: `-listen-proxy`
|
||||||
|
proxy_pass http://localhost:8090/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define custom error pages
|
||||||
|
error_page 403 /403.html;
|
||||||
|
error_page 404 /404.html;
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
## GitLab
|
||||||
|
##
|
||||||
|
|
||||||
|
## Redirects all HTTP traffic to the HTTPS host
|
||||||
|
server {
|
||||||
|
## Either remove "default_server" from the listen line below,
|
||||||
|
## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
|
||||||
|
## to be served if you visit any address that your server responds to, eg.
|
||||||
|
## the ip address of the server (http://x.x.x.x/)
|
||||||
|
listen 0.0.0.0:80;
|
||||||
|
listen [::]:80 ipv6only=on;
|
||||||
|
|
||||||
|
## Replace this with something like pages.gitlab.com
|
||||||
|
server_name ~^.*\.YOUR_GITLAB_PAGES\.DOMAIN$;
|
||||||
|
server_tokens off; ## Don't show the nginx version number, a security best practice
|
||||||
|
|
||||||
|
return 301 https://$http_host$request_uri;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/gitlab_pages_access.log;
|
||||||
|
error_log /var/log/nginx/gitlab_pages_access.log;
|
||||||
|
}
|
||||||
|
|
||||||
|
## Pages serving host
|
||||||
|
server {
|
||||||
|
listen 0.0.0.0:443 ssl;
|
||||||
|
listen [::]:443 ipv6only=on ssl http2;
|
||||||
|
|
||||||
|
## Replace this with something like pages.gitlab.com
|
||||||
|
server_name ~^.*\.YOUR_GITLAB_PAGES\.DOMAIN$;
|
||||||
|
server_tokens off; ## Don't show the nginx version number, a security best practice
|
||||||
|
|
||||||
|
## Strong SSL Security
|
||||||
|
## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /etc/nginx/ssl/gitlab-pages.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/gitlab-pages.key;
|
||||||
|
|
||||||
|
# GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
|
||||||
|
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
|
||||||
|
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 5m;
|
||||||
|
|
||||||
|
## See app/controllers/application_controller.rb for headers set
|
||||||
|
|
||||||
|
## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL.
|
||||||
|
## Replace with your ssl_trusted_certificate. For more info see:
|
||||||
|
## - https://medium.com/devops-programming/4445f4862461
|
||||||
|
## - https://www.ruby-forum.com/topic/4419319
|
||||||
|
## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx
|
||||||
|
# ssl_stapling on;
|
||||||
|
# ssl_stapling_verify on;
|
||||||
|
# ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt;
|
||||||
|
|
||||||
|
## [Optional] Generate a stronger DHE parameter:
|
||||||
|
## sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
|
||||||
|
##
|
||||||
|
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
|
||||||
|
|
||||||
|
## Individual nginx logs for GitLab pages
|
||||||
|
access_log /var/log/nginx/gitlab_pages_access.log;
|
||||||
|
error_log /var/log/nginx/gitlab_pages_error.log;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
# The same address as passed to GitLab Pages: `-listen-proxy`
|
||||||
|
proxy_pass http://localhost:8090/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define custom error pages
|
||||||
|
error_page 403 /403.html;
|
||||||
|
error_page 404 /404.html;
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ namespace :gitlab do
|
||||||
Rake::Task["gitlab:backup:uploads:create"].invoke
|
Rake::Task["gitlab:backup:uploads:create"].invoke
|
||||||
Rake::Task["gitlab:backup:builds:create"].invoke
|
Rake::Task["gitlab:backup:builds:create"].invoke
|
||||||
Rake::Task["gitlab:backup:artifacts:create"].invoke
|
Rake::Task["gitlab:backup:artifacts:create"].invoke
|
||||||
|
Rake::Task["gitlab:backup:pages:create"].invoke
|
||||||
Rake::Task["gitlab:backup:lfs:create"].invoke
|
Rake::Task["gitlab:backup:lfs:create"].invoke
|
||||||
Rake::Task["gitlab:backup:registry:create"].invoke
|
Rake::Task["gitlab:backup:registry:create"].invoke
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ namespace :gitlab do
|
||||||
Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads')
|
Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads')
|
||||||
Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds')
|
Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds')
|
||||||
Rake::Task['gitlab:backup:artifacts:restore'].invoke unless backup.skipped?('artifacts')
|
Rake::Task['gitlab:backup:artifacts:restore'].invoke unless backup.skipped?('artifacts')
|
||||||
|
Rake::Task["gitlab:backup:pages:restore"].invoke unless backup.skipped?("pages")
|
||||||
Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs')
|
Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs')
|
||||||
Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry')
|
Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry')
|
||||||
Rake::Task['gitlab:shell:setup'].invoke
|
Rake::Task['gitlab:shell:setup'].invoke
|
||||||
|
@ -159,6 +161,25 @@ namespace :gitlab do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
namespace :pages do
|
||||||
|
task create: :environment do
|
||||||
|
$progress.puts "Dumping pages ... ".blue
|
||||||
|
|
||||||
|
if ENV["SKIP"] && ENV["SKIP"].include?("pages")
|
||||||
|
$progress.puts "[SKIPPED]".cyan
|
||||||
|
else
|
||||||
|
Backup::Pages.new.dump
|
||||||
|
$progress.puts "done".green
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
task restore: :environment do
|
||||||
|
$progress.puts "Restoring pages ... ".blue
|
||||||
|
Backup::Pages.new.restore
|
||||||
|
$progress.puts "done".green
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
namespace :lfs do
|
namespace :lfs do
|
||||||
task create: :environment do
|
task create: :environment do
|
||||||
$progress.puts "Dumping lfs objects ... ".color(:blue)
|
$progress.puts "Dumping lfs objects ... ".color(:blue)
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Projects::PagesDomainsController do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
|
||||||
|
let(:request_params) do
|
||||||
|
{
|
||||||
|
namespace_id: project.namespace,
|
||||||
|
project_id: project
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
project.team << [user, :master]
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET show' do
|
||||||
|
let!(:pages_domain) { create(:pages_domain, project: project) }
|
||||||
|
|
||||||
|
it "displays the 'show' page" do
|
||||||
|
get(:show, request_params.merge(id: pages_domain.domain))
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(response).to render_template('show')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET new' do
|
||||||
|
it "displays the 'new' page" do
|
||||||
|
get(:new, request_params)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(response).to render_template('new')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST create' do
|
||||||
|
let(:pages_domain_params) do
|
||||||
|
build(:pages_domain, :with_certificate, :with_key).slice(:key, :certificate, :domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new pages domain" do
|
||||||
|
expect do
|
||||||
|
post(:create, request_params.merge(pages_domain: pages_domain_params))
|
||||||
|
end.to change { PagesDomain.count }.by(1)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(namespace_project_pages_path(project.namespace, project))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE destroy' do
|
||||||
|
let!(:pages_domain) { create(:pages_domain, project: project) }
|
||||||
|
|
||||||
|
it "deletes the pages domain" do
|
||||||
|
expect do
|
||||||
|
delete(:destroy, request_params.merge(id: pages_domain.domain))
|
||||||
|
end.to change { PagesDomain.count }.by(-1)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(namespace_project_pages_path(project.namespace, project))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,153 @@
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :pages_domain, class: 'PagesDomain' do
|
||||||
|
domain 'my.domain.com'
|
||||||
|
|
||||||
|
trait :with_certificate do
|
||||||
|
certificate '-----BEGIN CERTIFICATE-----
|
||||||
|
MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0
|
||||||
|
LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ
|
||||||
|
MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||||
|
gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa
|
||||||
|
SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT
|
||||||
|
nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w
|
||||||
|
DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD
|
||||||
|
VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh
|
||||||
|
IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ
|
||||||
|
joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese
|
||||||
|
5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg
|
||||||
|
YHi2yesCrOvVXt+lgPTd
|
||||||
|
-----END CERTIFICATE-----'
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :with_key do
|
||||||
|
key '-----BEGIN PRIVATE KEY-----
|
||||||
|
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN
|
||||||
|
SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t
|
||||||
|
PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB
|
||||||
|
kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd
|
||||||
|
j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/
|
||||||
|
uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR
|
||||||
|
5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O
|
||||||
|
AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K
|
||||||
|
EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh
|
||||||
|
Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C
|
||||||
|
m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH
|
||||||
|
EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx
|
||||||
|
63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi
|
||||||
|
nNp/xedE1YxutQ==
|
||||||
|
-----END PRIVATE KEY-----'
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :with_missing_chain do
|
||||||
|
# This certificate is signed with different key
|
||||||
|
# And misses the CA to build trust chain
|
||||||
|
certificate '-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDGTCCAgGgAwIBAgIBAjANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdUZXN0
|
||||||
|
IENBMB4XDTE2MDIxMjE0MjMwMFoXDTE3MDIxMTE0MjMwMFowHTEbMBkGA1UEAxMS
|
||||||
|
dGVzdC1jZXJ0aWZpY2F0ZS0yMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||||
|
AQEAw8RWetIUT0YymSuKvBpClzDv/jQdX0Ch+2iF7f4Lm3lcmoUuXgyhl/WRe5K9
|
||||||
|
ONuMHPQlZbeavEbvWb0BsU7geInhsjd/zAu3EP17jfSIXToUdSD20wcSG/yclLdZ
|
||||||
|
qhb6NCtHTJKFUI8BktoS7kafkdvmeem/UJFzlvcA6VMyGDkS8ZN39a45R1jGmPEl
|
||||||
|
Yk0g1jW7lSKcBLjU1O/Csv59LyWXqBP6jR1vB8ijlUf1IyK8gOk7NHF13GHl7Z3A
|
||||||
|
/8zwuEt/pB3yK92o71P+FnSEcJ23zcAalz6H9ajVTzRr/AXttineBNVYnEuPXW+V
|
||||||
|
Rsboe+bBO/e4pVKXnQ1F3aMT7QIDAQABo28wbTAMBgNVHRMBAf8EAjAAMB0GA1Ud
|
||||||
|
DgQWBBSFwo3rhc26lD8ZVaBVcUY1NyCOLDALBgNVHQ8EBAMCBeAwEQYJYIZIAYb4
|
||||||
|
QgEBBAQDAgZAMB4GCWCGSAGG+EIBDQQRFg94Y2EgY2VydGlmaWNhdGUwDQYJKoZI
|
||||||
|
hvcNAQEFBQADggEBABppUhunuT7qArM9gZ2gLgcOK8qyZWU8AJulvloaCZDvqGVs
|
||||||
|
Qom0iEMBrrt5+8bBevNiB49Tz7ok8NFgLzrlEnOw6y6QGjiI/g8sRKEiXl+ZNX8h
|
||||||
|
s8VN6arqT348OU8h2BixaXDmBF/IqZVApGhR8+B4fkCt0VQmdzVuHGbOQXMWJCpl
|
||||||
|
WlU8raZoPIqf6H/8JA97pM/nk/3CqCoHsouSQv+jGY4pSL22RqsO0ylIM0LDBbmF
|
||||||
|
m4AEaojTljX1tMJAF9Rbiw/omam5bDPq2JWtosrz/zB69y5FaQjc6FnCk0M4oN/+
|
||||||
|
VM+d42lQAgoq318A84Xu5vRh1KCAJuztkhNbM+w=
|
||||||
|
-----END CERTIFICATE-----'
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :with_trusted_chain do
|
||||||
|
# This contains
|
||||||
|
# [Intermediate #2 (SHA-2)] 'Comodo RSA Domain Validation Secure Server CA'
|
||||||
|
# [Intermediate #1 (SHA-2)] 'COMODO RSA Certification Authority'
|
||||||
|
certificate '-----BEGIN CERTIFICATE-----
|
||||||
|
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
|
||||||
|
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||||
|
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
|
||||||
|
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
|
||||||
|
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
|
||||||
|
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
|
||||||
|
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
|
||||||
|
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||||
|
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
|
||||||
|
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
|
||||||
|
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
|
||||||
|
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
|
||||||
|
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
|
||||||
|
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
|
||||||
|
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
|
||||||
|
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
|
||||||
|
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
|
||||||
|
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
|
||||||
|
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
|
||||||
|
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
|
||||||
|
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
|
||||||
|
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
|
||||||
|
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
|
||||||
|
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
|
||||||
|
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
|
||||||
|
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
|
||||||
|
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
|
||||||
|
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
|
||||||
|
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
|
||||||
|
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
|
||||||
|
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
|
||||||
|
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
|
||||||
|
+AZxAeKCINT+b72x
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
|
||||||
|
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
|
||||||
|
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
|
||||||
|
eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
|
||||||
|
gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
||||||
|
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
|
||||||
|
VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
|
||||||
|
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
|
||||||
|
AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
|
||||||
|
2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
|
||||||
|
ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
|
||||||
|
4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
|
||||||
|
m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
|
||||||
|
vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
|
||||||
|
8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
|
||||||
|
IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
|
||||||
|
KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
|
||||||
|
GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
|
||||||
|
s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
|
||||||
|
JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
|
||||||
|
AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
|
||||||
|
MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
|
||||||
|
bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
|
||||||
|
Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
|
||||||
|
zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
|
||||||
|
Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
|
||||||
|
Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
|
||||||
|
B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
|
||||||
|
PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
|
||||||
|
pu/xO28QOG8=
|
||||||
|
-----END CERTIFICATE-----'
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :with_expired_certificate do
|
||||||
|
certificate '-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBsDCCARmgAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDExNleHBp
|
||||||
|
cmVkLWNlcnRpZmljYXRlMB4XDTE1MDIxMjE0MzMwMFoXDTE2MDIwMTE0MzMwMFow
|
||||||
|
HjEcMBoGA1UEAxMTZXhwaXJlZC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEF
|
||||||
|
AAOBjQAwgYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2ge
|
||||||
|
NR1qlNFaSvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLyS
|
||||||
|
NT438kdTnY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEA
|
||||||
|
ATANBgkqhkiG9w0BAQUFAAOBgQBNj+vWvneyW1KkbVK+b/cVmnYPSfbkHrYK6m8X
|
||||||
|
Hq9LkWn6WP4EHsesHyslgTQZF8C7kVLTbLn2noLnOE+Mp3vcWlZxl3Yk6aZMhKS+
|
||||||
|
Iy6oRpHaCF/2obZdIdgf9rlyz0fkqyHJc9GkioSoOhJZxEV2SgAkap8yS0sX2tJ9
|
||||||
|
ZDXgrA==
|
||||||
|
-----END CERTIFICATE-----'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,60 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'Pages', feature: true do
|
||||||
|
given(:project) { create(:empty_project) }
|
||||||
|
given(:user) { create(:user) }
|
||||||
|
given(:role) { :master }
|
||||||
|
|
||||||
|
background do
|
||||||
|
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
|
||||||
|
|
||||||
|
project.team << [user, role]
|
||||||
|
|
||||||
|
login_as(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'no pages deployed' do
|
||||||
|
scenario 'does not see anything to destroy' do
|
||||||
|
visit namespace_project_pages_path(project.namespace, project)
|
||||||
|
|
||||||
|
expect(page).not_to have_link('Remove pages')
|
||||||
|
expect(page).not_to have_text('Only the project owner can remove pages')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is the owner' do
|
||||||
|
background do
|
||||||
|
project.namespace.update(owner: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when pages deployed' do
|
||||||
|
background do
|
||||||
|
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'sees "Remove pages" link' do
|
||||||
|
visit namespace_project_pages_path(project.namespace, project)
|
||||||
|
|
||||||
|
expect(page).to have_link('Remove pages')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'no pages deployed'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user is not the owner' do
|
||||||
|
context 'when pages deployed' do
|
||||||
|
background do
|
||||||
|
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'sees "Only the project owner can remove pages" text' do
|
||||||
|
visit namespace_project_pages_path(project.namespace, project)
|
||||||
|
|
||||||
|
expect(page).to have_text('Only the project owner can remove pages')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'no pages deployed'
|
||||||
|
end
|
||||||
|
end
|
|
@ -192,6 +192,7 @@ project:
|
||||||
- environments
|
- environments
|
||||||
- deployments
|
- deployments
|
||||||
- project_feature
|
- project_feature
|
||||||
|
- pages_domains
|
||||||
- authorized_users
|
- authorized_users
|
||||||
- project_authorizations
|
- project_authorizations
|
||||||
- route
|
- route
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Gitlab::UploadsTransfer, lib: true do
|
describe Gitlab::ProjectTransfer, lib: true do
|
||||||
before do
|
before do
|
||||||
@root_dir = File.join(Rails.root, "public", "uploads")
|
@root_dir = File.join(Rails.root, "public", "uploads")
|
||||||
@upload_transfer = Gitlab::UploadsTransfer.new
|
@project_transfer = Gitlab::ProjectTransfer.new
|
||||||
|
allow(@project_transfer).to receive(:root_dir).and_return(@root_dir)
|
||||||
|
|
||||||
@project_path_was = "test_project_was"
|
@project_path_was = "test_project_was"
|
||||||
@project_path = "test_project"
|
@project_path = "test_project"
|
||||||
|
@ -21,7 +22,7 @@ describe Gitlab::UploadsTransfer, lib: true do
|
||||||
describe '#move_project' do
|
describe '#move_project' do
|
||||||
it "moves project upload to another namespace" do
|
it "moves project upload to another namespace" do
|
||||||
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
|
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
|
||||||
@upload_transfer.move_project(@project_path, @namespace_path_was, @namespace_path)
|
@project_transfer.move_project(@project_path, @namespace_path_was, @namespace_path)
|
||||||
|
|
||||||
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
||||||
expect(Dir.exist?(expected_path)).to be_truthy
|
expect(Dir.exist?(expected_path)).to be_truthy
|
||||||
|
@ -31,7 +32,7 @@ describe Gitlab::UploadsTransfer, lib: true do
|
||||||
describe '#rename_project' do
|
describe '#rename_project' do
|
||||||
it "renames project" do
|
it "renames project" do
|
||||||
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was))
|
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was))
|
||||||
@upload_transfer.rename_project(@project_path_was, @project_path, @namespace_path)
|
@project_transfer.rename_project(@project_path_was, @project_path, @namespace_path)
|
||||||
|
|
||||||
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
||||||
expect(Dir.exist?(expected_path)).to be_truthy
|
expect(Dir.exist?(expected_path)).to be_truthy
|
||||||
|
@ -41,7 +42,7 @@ describe Gitlab::UploadsTransfer, lib: true do
|
||||||
describe '#rename_namespace' do
|
describe '#rename_namespace' do
|
||||||
it "renames namespace" do
|
it "renames namespace" do
|
||||||
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
|
FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
|
||||||
@upload_transfer.rename_namespace(@namespace_path_was, @namespace_path)
|
@project_transfer.rename_namespace(@namespace_path_was, @namespace_path)
|
||||||
|
|
||||||
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
expected_path = File.join(@root_dir, @namespace_path, @project_path)
|
||||||
expect(Dir.exist?(expected_path)).to be_truthy
|
expect(Dir.exist?(expected_path)).to be_truthy
|
|
@ -0,0 +1,168 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe PagesDomain, models: true do
|
||||||
|
describe 'associations' do
|
||||||
|
it { is_expected.to belong_to(:project) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :validate_domain do
|
||||||
|
subject { build(:pages_domain, domain: domain) }
|
||||||
|
|
||||||
|
context 'is unique' do
|
||||||
|
let(:domain) { 'my.domain.com' }
|
||||||
|
|
||||||
|
it { is_expected.to validate_uniqueness_of(:domain) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'valid domain' do
|
||||||
|
let(:domain) { 'my.domain.com' }
|
||||||
|
|
||||||
|
it { is_expected.to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'valid hexadecimal-looking domain' do
|
||||||
|
let(:domain) { '0x12345.com'}
|
||||||
|
|
||||||
|
it { is_expected.to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'no domain' do
|
||||||
|
let(:domain) { nil }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'invalid domain' do
|
||||||
|
let(:domain) { '0123123' }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'domain from .example.com' do
|
||||||
|
let(:domain) { 'my.domain.com' }
|
||||||
|
|
||||||
|
before { allow(Settings.pages).to receive(:host).and_return('domain.com') }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'validate certificate' do
|
||||||
|
subject { domain }
|
||||||
|
|
||||||
|
context 'when only certificate is specified' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when only key is specified' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_key) }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with matching key' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate, :with_key) }
|
||||||
|
|
||||||
|
it { is_expected.to be_valid }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for not matching key' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :url do
|
||||||
|
subject { domain.url }
|
||||||
|
|
||||||
|
context 'without the certificate' do
|
||||||
|
let(:domain) { build(:pages_domain) }
|
||||||
|
|
||||||
|
it { is_expected.to eq('http://my.domain.com') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a certificate' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
it { is_expected.to eq('https://my.domain.com') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :has_matching_key? do
|
||||||
|
subject { domain.has_matching_key? }
|
||||||
|
|
||||||
|
context 'for matching key' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate, :with_key) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for invalid key' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_missing_chain, :with_key) }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :has_intermediates? do
|
||||||
|
subject { domain.has_intermediates? }
|
||||||
|
|
||||||
|
context 'for self signed' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for missing certificate chain' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_missing_chain) }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for trusted certificate chain' do
|
||||||
|
# We only validate that we can to rebuild the trust chain, for certificates
|
||||||
|
# We assume that 'AddTrustExternalCARoot' needed to validate the chain is in trusted store.
|
||||||
|
# It will be if ca-certificates is installed on Debian/Ubuntu/Alpine
|
||||||
|
|
||||||
|
let(:domain) { build(:pages_domain, :with_trusted_chain) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :expired? do
|
||||||
|
subject { domain.expired? }
|
||||||
|
|
||||||
|
context 'for valid' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for expired' do
|
||||||
|
let(:domain) { build(:pages_domain, :with_expired_certificate) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :subject do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
subject { domain.subject }
|
||||||
|
|
||||||
|
it { is_expected.to eq('/CN=test-certificate') }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :certificate_text do
|
||||||
|
let(:domain) { build(:pages_domain, :with_certificate) }
|
||||||
|
|
||||||
|
subject { domain.certificate_text }
|
||||||
|
|
||||||
|
# We test only existence of output, since the output is long
|
||||||
|
it { is_expected.not_to be_empty }
|
||||||
|
end
|
||||||
|
end
|
|
@ -60,6 +60,7 @@ describe Project, models: true do
|
||||||
it { is_expected.to have_many(:runners) }
|
it { is_expected.to have_many(:runners) }
|
||||||
it { is_expected.to have_many(:variables) }
|
it { is_expected.to have_many(:variables) }
|
||||||
it { is_expected.to have_many(:triggers) }
|
it { is_expected.to have_many(:triggers) }
|
||||||
|
it { is_expected.to have_many(:pages_domains) }
|
||||||
it { is_expected.to have_many(:labels).class_name('ProjectLabel').dependent(:destroy) }
|
it { is_expected.to have_many(:labels).class_name('ProjectLabel').dependent(:destroy) }
|
||||||
it { is_expected.to have_many(:users_star_projects).dependent(:destroy) }
|
it { is_expected.to have_many(:users_star_projects).dependent(:destroy) }
|
||||||
it { is_expected.to have_many(:environments).dependent(:destroy) }
|
it { is_expected.to have_many(:environments).dependent(:destroy) }
|
||||||
|
@ -1067,6 +1068,22 @@ describe Project, models: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#pages_deployed?' do
|
||||||
|
let(:project) { create :empty_project }
|
||||||
|
|
||||||
|
subject { project.pages_deployed? }
|
||||||
|
|
||||||
|
context 'if public folder does exist' do
|
||||||
|
before { allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true) }
|
||||||
|
|
||||||
|
it { is_expected.to be_truthy }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "if public folder doesn't exist" do
|
||||||
|
it { is_expected.to be_falsey }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.search' do
|
describe '.search' do
|
||||||
let(:project) { create(:empty_project, description: 'kitten mittens') }
|
let(:project) { create(:empty_project, description: 'kitten mittens') }
|
||||||
|
|
||||||
|
@ -1844,4 +1861,31 @@ describe Project, models: true do
|
||||||
def enable_lfs
|
def enable_lfs
|
||||||
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
|
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#pages_url' do
|
||||||
|
let(:group) { create :group, name: group_name }
|
||||||
|
let(:project) { create :empty_project, namespace: group, name: project_name }
|
||||||
|
let(:domain) { 'Example.com' }
|
||||||
|
|
||||||
|
subject { project.pages_url }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Settings.pages).to receive(:host).and_return(domain)
|
||||||
|
allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'group page' do
|
||||||
|
let(:group_name) { 'Group' }
|
||||||
|
let(:project_name) { 'group.example.com' }
|
||||||
|
|
||||||
|
it { is_expected.to eq("http://group.example.com") }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'project page' do
|
||||||
|
let(:group_name) { 'Group' }
|
||||||
|
let(:project_name) { 'Project' }
|
||||||
|
|
||||||
|
it { is_expected.to eq("http://group.example.com/project") }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,35 +27,42 @@ describe 'project routing' do
|
||||||
# let(:actions) { [:index] }
|
# let(:actions) { [:index] }
|
||||||
# let(:controller) { 'issues' }
|
# let(:controller) { 'issues' }
|
||||||
# end
|
# end
|
||||||
|
#
|
||||||
|
# # Different controller name and path
|
||||||
|
# it_behaves_like 'RESTful project resources' do
|
||||||
|
# let(:controller) { 'pages_domains' }
|
||||||
|
# let(:controller_path) { 'pages/domains' }
|
||||||
|
# end
|
||||||
shared_examples 'RESTful project resources' do
|
shared_examples 'RESTful project resources' do
|
||||||
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
|
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
|
||||||
|
let(:controller_path) { controller }
|
||||||
|
|
||||||
it 'to #index' do
|
it 'to #index' do
|
||||||
expect(get("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
|
expect(get("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #create' do
|
it 'to #create' do
|
||||||
expect(post("/gitlab/gitlabhq/#{controller}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
|
expect(post("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #new' do
|
it 'to #new' do
|
||||||
expect(get("/gitlab/gitlabhq/#{controller}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
|
expect(get("/gitlab/gitlabhq/#{controller_path}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #edit' do
|
it 'to #edit' do
|
||||||
expect(get("/gitlab/gitlabhq/#{controller}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
|
expect(get("/gitlab/gitlabhq/#{controller_path}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #show' do
|
it 'to #show' do
|
||||||
expect(get("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
|
expect(get("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #update' do
|
it 'to #update' do
|
||||||
expect(put("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
|
expect(put("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'to #destroy' do
|
it 'to #destroy' do
|
||||||
expect(delete("/gitlab/gitlabhq/#{controller}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
|
expect(delete("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -539,4 +546,20 @@ describe 'project routing' do
|
||||||
'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq')
|
'projects/avatars#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe Projects::PagesDomainsController, 'routing' do
|
||||||
|
it_behaves_like 'RESTful project resources' do
|
||||||
|
let(:actions) { [:show, :new, :create, :destroy] }
|
||||||
|
let(:controller) { 'pages_domains' }
|
||||||
|
let(:controller_path) { 'pages/domains' }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'to #destroy with a valid domain name' do
|
||||||
|
expect(delete('/gitlab/gitlabhq/pages/domains/my.domain.com')).to route_to('projects/pages_domains#destroy', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'my.domain.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'to #show with a valid domain' do
|
||||||
|
expect(get('/gitlab/gitlabhq/pages/domains/my.domain.com')).to route_to('projects/pages_domains#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'my.domain.com')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe PagesService, services: true do
|
||||||
|
let(:build) { create(:ci_build) }
|
||||||
|
let(:data) { Gitlab::DataBuilder::Build.build(build) }
|
||||||
|
let(:service) { PagesService.new(data) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'execute asynchronously for pages job' do
|
||||||
|
before { build.name = 'pages' }
|
||||||
|
|
||||||
|
context 'on success' do
|
||||||
|
before { build.success }
|
||||||
|
|
||||||
|
it 'executes worker' do
|
||||||
|
expect(PagesWorker).to receive(:perform_async)
|
||||||
|
service.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
%w(pending running failed canceled).each do |status|
|
||||||
|
context "on #{status}" do
|
||||||
|
before { build.status = status }
|
||||||
|
|
||||||
|
it 'does not execute worker' do
|
||||||
|
expect(PagesWorker).not_to receive(:perform_async)
|
||||||
|
service.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for other jobs' do
|
||||||
|
before do
|
||||||
|
build.name = 'other job'
|
||||||
|
build.success
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not execute worker' do
|
||||||
|
expect(PagesWorker).not_to receive(:perform_async)
|
||||||
|
service.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,6 +9,8 @@ describe Projects::TransferService, services: true do
|
||||||
before do
|
before do
|
||||||
allow_any_instance_of(Gitlab::UploadsTransfer).
|
allow_any_instance_of(Gitlab::UploadsTransfer).
|
||||||
to receive(:move_project).and_return(true)
|
to receive(:move_project).and_return(true)
|
||||||
|
allow_any_instance_of(Gitlab::PagesTransfer).
|
||||||
|
to receive(:move_project).and_return(true)
|
||||||
group.add_owner(user)
|
group.add_owner(user)
|
||||||
@result = transfer_project(project, user, group)
|
@result = transfer_project(project, user, group)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Projects::UpdatePagesConfigurationService, services: true do
|
||||||
|
let(:project) { create(:empty_project) }
|
||||||
|
subject { described_class.new(project) }
|
||||||
|
|
||||||
|
describe "#update" do
|
||||||
|
let(:file) { Tempfile.new('pages-test') }
|
||||||
|
|
||||||
|
after do
|
||||||
|
file.close
|
||||||
|
file.unlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the .update file' do
|
||||||
|
# Access this reference to ensure scoping works
|
||||||
|
Projects::Settings # rubocop:disable Lint/Void
|
||||||
|
expect(subject).to receive(:pages_config_file).and_return(file.path)
|
||||||
|
expect(subject).to receive(:reload_daemon).and_call_original
|
||||||
|
|
||||||
|
expect(subject.execute).to eq({ status: :success })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,80 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe Projects::UpdatePagesService do
|
||||||
|
let(:project) { create :project }
|
||||||
|
let(:pipeline) { create :ci_pipeline, project: project, sha: project.commit('HEAD').sha }
|
||||||
|
let(:build) { create :ci_build, pipeline: pipeline, ref: 'HEAD' }
|
||||||
|
let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') }
|
||||||
|
|
||||||
|
subject { described_class.new(project, build) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.remove_pages
|
||||||
|
end
|
||||||
|
|
||||||
|
%w(tar.gz zip).each do |format|
|
||||||
|
context "for valid #{format}" do
|
||||||
|
let(:file) { fixture_file_upload(Rails.root + "spec/fixtures/pages.#{format}") }
|
||||||
|
let(:empty_file) { fixture_file_upload(Rails.root + "spec/fixtures/pages_empty.#{format}") }
|
||||||
|
let(:metadata) do
|
||||||
|
filename = Rails.root + "spec/fixtures/pages.#{format}.meta"
|
||||||
|
fixture_file_upload(filename) if File.exist?(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
build.update_attributes(artifacts_file: file)
|
||||||
|
build.update_attributes(artifacts_metadata: metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'succeeds' do
|
||||||
|
expect(project.pages_deployed?).to be_falsey
|
||||||
|
expect(execute).to eq(:success)
|
||||||
|
expect(project.pages_deployed?).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'limits pages size' do
|
||||||
|
stub_application_setting(max_pages_size: 1)
|
||||||
|
expect(execute).not_to eq(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'removes pages after destroy' do
|
||||||
|
expect(PagesWorker).to receive(:perform_in)
|
||||||
|
expect(project.pages_deployed?).to be_falsey
|
||||||
|
expect(execute).to eq(:success)
|
||||||
|
expect(project.pages_deployed?).to be_truthy
|
||||||
|
project.destroy
|
||||||
|
expect(project.pages_deployed?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails if sha on branch is not latest' do
|
||||||
|
pipeline.update_attributes(sha: 'old_sha')
|
||||||
|
build.update_attributes(artifacts_file: file)
|
||||||
|
expect(execute).not_to eq(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails for empty file fails' do
|
||||||
|
build.update_attributes(artifacts_file: empty_file)
|
||||||
|
expect(execute).not_to eq(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails to remove project pages when no pages is deployed' do
|
||||||
|
expect(PagesWorker).not_to receive(:perform_in)
|
||||||
|
expect(project.pages_deployed?).to be_falsey
|
||||||
|
project.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails if no artifacts' do
|
||||||
|
expect(execute).not_to eq(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails for invalid archive' do
|
||||||
|
build.update_attributes(artifacts_file: invalid_file)
|
||||||
|
expect(execute).not_to eq(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
subject.execute[:status]
|
||||||
|
end
|
||||||
|
end
|
|
@ -28,7 +28,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def reenable_backup_sub_tasks
|
def reenable_backup_sub_tasks
|
||||||
%w{db repo uploads builds artifacts lfs registry}.each do |subtask|
|
%w{db repo uploads builds artifacts pages lfs registry}.each do |subtask|
|
||||||
Rake::Task["gitlab:backup:#{subtask}:create"].reenable
|
Rake::Task["gitlab:backup:#{subtask}:create"].reenable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -71,6 +71,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
expect(Rake::Task['gitlab:backup:builds:restore']).to receive(:invoke)
|
expect(Rake::Task['gitlab:backup:builds:restore']).to receive(:invoke)
|
||||||
expect(Rake::Task['gitlab:backup:uploads:restore']).to receive(:invoke)
|
expect(Rake::Task['gitlab:backup:uploads:restore']).to receive(:invoke)
|
||||||
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive(:invoke)
|
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive(:invoke)
|
||||||
|
expect(Rake::Task['gitlab:backup:pages:restore']).to receive(:invoke)
|
||||||
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive(:invoke)
|
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive(:invoke)
|
||||||
expect(Rake::Task['gitlab:backup:registry:restore']).to receive(:invoke)
|
expect(Rake::Task['gitlab:backup:registry:restore']).to receive(:invoke)
|
||||||
expect(Rake::Task['gitlab:shell:setup']).to receive(:invoke)
|
expect(Rake::Task['gitlab:shell:setup']).to receive(:invoke)
|
||||||
|
@ -202,7 +203,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
|
|
||||||
it 'sets correct permissions on the tar contents' do
|
it 'sets correct permissions on the tar contents' do
|
||||||
tar_contents, exit_status = Gitlab::Popen.popen(
|
tar_contents, exit_status = Gitlab::Popen.popen(
|
||||||
%W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz registry.tar.gz}
|
%W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz pages.tar.gz lfs.tar.gz registry.tar.gz}
|
||||||
)
|
)
|
||||||
expect(exit_status).to eq(0)
|
expect(exit_status).to eq(0)
|
||||||
expect(tar_contents).to match('db/')
|
expect(tar_contents).to match('db/')
|
||||||
|
@ -210,14 +211,15 @@ describe 'gitlab:app namespace rake task' do
|
||||||
expect(tar_contents).to match('repositories/')
|
expect(tar_contents).to match('repositories/')
|
||||||
expect(tar_contents).to match('builds.tar.gz')
|
expect(tar_contents).to match('builds.tar.gz')
|
||||||
expect(tar_contents).to match('artifacts.tar.gz')
|
expect(tar_contents).to match('artifacts.tar.gz')
|
||||||
|
expect(tar_contents).to match('pages.tar.gz')
|
||||||
expect(tar_contents).to match('lfs.tar.gz')
|
expect(tar_contents).to match('lfs.tar.gz')
|
||||||
expect(tar_contents).to match('registry.tar.gz')
|
expect(tar_contents).to match('registry.tar.gz')
|
||||||
expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz|registry.tar.gz)\/$/)
|
expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|pages.tar.gz|artifacts.tar.gz|registry.tar.gz)\/$/)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes temp directories' do
|
it 'deletes temp directories' do
|
||||||
temp_dirs = Dir.glob(
|
temp_dirs = Dir.glob(
|
||||||
File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs,registry}')
|
File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,pages,lfs,registry}')
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(temp_dirs).to be_empty
|
expect(temp_dirs).to be_empty
|
||||||
|
@ -304,7 +306,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
|
|
||||||
it "does not contain skipped item" do
|
it "does not contain skipped item" do
|
||||||
tar_contents, _exit_status = Gitlab::Popen.popen(
|
tar_contents, _exit_status = Gitlab::Popen.popen(
|
||||||
%W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz registry.tar.gz}
|
%W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz pages.tar.gz lfs.tar.gz registry.tar.gz}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(tar_contents).to match('db/')
|
expect(tar_contents).to match('db/')
|
||||||
|
@ -312,6 +314,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
expect(tar_contents).to match('builds.tar.gz')
|
expect(tar_contents).to match('builds.tar.gz')
|
||||||
expect(tar_contents).to match('artifacts.tar.gz')
|
expect(tar_contents).to match('artifacts.tar.gz')
|
||||||
expect(tar_contents).to match('lfs.tar.gz')
|
expect(tar_contents).to match('lfs.tar.gz')
|
||||||
|
expect(tar_contents).to match('pages.tar.gz')
|
||||||
expect(tar_contents).to match('registry.tar.gz')
|
expect(tar_contents).to match('registry.tar.gz')
|
||||||
expect(tar_contents).not_to match('repositories/')
|
expect(tar_contents).not_to match('repositories/')
|
||||||
end
|
end
|
||||||
|
@ -327,6 +330,7 @@ describe 'gitlab:app namespace rake task' do
|
||||||
expect(Rake::Task['gitlab:backup:uploads:restore']).not_to receive :invoke
|
expect(Rake::Task['gitlab:backup:uploads:restore']).not_to receive :invoke
|
||||||
expect(Rake::Task['gitlab:backup:builds:restore']).to receive :invoke
|
expect(Rake::Task['gitlab:backup:builds:restore']).to receive :invoke
|
||||||
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive :invoke
|
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive :invoke
|
||||||
|
expect(Rake::Task['gitlab:backup:pages:restore']).to receive :invoke
|
||||||
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive :invoke
|
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive :invoke
|
||||||
expect(Rake::Task['gitlab:backup:registry:restore']).to receive :invoke
|
expect(Rake::Task['gitlab:backup:registry:restore']).to receive :invoke
|
||||||
expect(Rake::Task['gitlab:shell:setup']).to receive :invoke
|
expect(Rake::Task['gitlab:shell:setup']).to receive :invoke
|
||||||
|
|