diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2015-11-03 21:28:07 +0100 |
---|---|---|
committer | James Edwards-Jones <jedwardsjones@gitlab.com> | 2017-01-31 22:50:39 +0000 |
commit | 120f9abaa15ce0feec1dc457ad3dc3787e4fbfc6 (patch) | |
tree | 4bb8bf5f7e47613ea967555e01fc2c7e27e994c6 | |
parent | 659cceb0e8694b58a8b665de3f338245244ef114 (diff) | |
download | gitlab-ce-120f9abaa15ce0feec1dc457ad3dc3787e4fbfc6.tar.gz |
Add GitLab Pages
- The pages are created when build artifacts for `pages` job are uploaded
- Pages serve the content under: http://group.pages.domain.com/project
- Pages can be used to serve the group page, special project named as host: group.pages.domain.com
- User can provide own 403 and 404 error pages by creating 403.html and 404.html in group page project
- Pages can be explicitly removed from the project by clicking Remove Pages in Project Settings
- The size of pages is limited by Application Setting: max pages size, which limits the maximum size of unpacked archive (default: 100MB)
- The public/ is extracted from artifacts and content is served as static pages
- Pages asynchronous worker use `dd` to limit the unpacked tar size
- Pages needs to be explicitly enabled and domain needs to be specified in gitlab.yml
- Pages are part of backups
- Pages notify the deployment status using Commit Status API
- Pages use a new sidekiq queue: pages
- Pages use a separate nginx config which needs to be explicitly added
26 files changed, 441 insertions, 6 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 543d5eac504..df8682e246e 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -109,6 +109,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :plantuml_url, :max_artifacts_size, :max_attachment_size, + :max_pages_size, :metrics_enabled, :metrics_host, :metrics_method_call_threshold, diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 444ff837bb3..123dc179e73 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -151,6 +151,16 @@ class ProjectsController < Projects::ApplicationController end end + def remove_pages + return access_denied! unless can?(current_user, :remove_pages, @project) + + @project.remove_pages + + respond_to do |format| + format.html { redirect_to project_path(@project) } + end + end + def housekeeping ::Projects::HousekeepingService.new(@project).execute diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 5fe8ddf69d7..095a346f337 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -256,7 +256,7 @@ module Ci end def project_id - pipeline.project_id + gl_project_id end def project_name @@ -457,6 +457,7 @@ module Ci build_data = Gitlab::DataBuilder::Build.build(self) project.execute_hooks(build_data.dup, :build_hooks) project.execute_services(build_data.dup, :build_hooks) + UpdatePagesService.new(build_data).execute project.running_or_pending_build_count(force: true) end diff --git a/app/models/project.rb b/app/models/project.rb index 37f4705adbd..48ff5ec7fc7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -53,6 +53,8 @@ class Project < ActiveRecord::Base update_column(:last_activity_at, self.created_at) end + after_destroy :remove_pages + # update visibility_level of forks after_update :update_forks_visibility_level def update_forks_visibility_level @@ -1160,6 +1162,29 @@ class Project < ActiveRecord::Base ensure_runners_token! end + def pages_url + if Dir.exist?(public_pages_path) + host = "#{namespace.path}.#{Settings.pages.domain}" + + # If the project path is the same as host, leave the short version + return "http://#{host}" if host == path + + "http://#{host}/#{path}" + end + 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 + FileUtils.rm_r(pages_path, force: true) + end + def wiki @wiki ||= ProjectWiki.new(self, self.owner) end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 71ef8901932..63bc639688d 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -136,6 +136,7 @@ class ProjectPolicy < BasePolicy can! :remove_fork_project can! :destroy_merge_request can! :destroy_issue + can! :remove_pages end def team_member_owner_access! diff --git a/app/services/update_pages_service.rb b/app/services/update_pages_service.rb new file mode 100644 index 00000000000..818bb94a293 --- /dev/null +++ b/app/services/update_pages_service.rb @@ -0,0 +1,15 @@ +class UpdatePagesService + 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(data[:build_id]) + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 558bbe07b16..125a805a897 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -187,6 +187,14 @@ .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 %legend Continuous Integration .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index ec944d4ffb7..89e2d4046b8 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -133,6 +133,41 @@ %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" = f.submit 'Save changes', class: "btn btn-save" + + - if Settings.pages.enabled + .pages-settings + .panel.panel-default + .panel-heading Pages + .errors-holder + .panel-body + - if @project.pages_url + %strong + Congratulations. Your pages are served at: + %p= link_to @project.pages_url, @project.pages_url + - else + %p + To publish pages create .gitlab-ci.yml with + %strong pages job + and send public/ folder to GitLab. + %p + Use existing tools: + %ul + %li + %pre + :plain + pages: + image: jekyll + script: jekyll build + artifacts: + paths: + - public + + - if @project.pages_url && can?(current_user, :remove_pages, @project) + .form-actions + = link_to 'Remove pages', remove_pages_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to remove pages for this project?" }, + method: :post, class: "btn btn-warning" + .row.prepend-top-default %hr .row.prepend-top-default diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb new file mode 100644 index 00000000000..9aa3030264b --- /dev/null +++ b/app/workers/pages_worker.rb @@ -0,0 +1,123 @@ +class PagesWorker + include Sidekiq::Worker + include Gitlab::CurrentSettings + + BLOCK_SIZE = 32.kilobytes + MAX_SIZE = 1.terabyte + + sidekiq_options queue: :pages + + def perform(build_id) + @build_id = build_id + return unless valid? + + # Create status notifying the deployment of pages + @status = GenericCommitStatus.new( + project: project, + commit: build.commit, + user: build.user, + ref: build.ref, + stage: 'deploy', + name: 'pages:deploy' + ) + @status.run! + + FileUtils.mkdir_p(tmp_path) + + # Calculate dd parameters: we limit the size of pages + max_size = current_application_settings.max_pages_size.megabytes + max_size ||= MAX_SIZE + blocks = 1 + max_size / BLOCK_SIZE + + # Create temporary directory in which we will extract the artifacts + Dir.mktmpdir(nil, tmp_path) do |temp_path| + # We manually extract the archive and limit the archive size with dd + results = Open3.pipeline(%W(gunzip -c #{artifacts}), + %W(dd bs=#{BLOCK_SIZE} count=#{blocks}), + %W(tar -x -C #{temp_path} public/)) + return unless results.compact.all?(&:success?) + + # Check if we did extract public directory + temp_public_path = File.join(temp_path, 'public') + return unless Dir.exists?(temp_public_path) + + FileUtils.mkdir_p(pages_path) + + # Lock file for time of deployment to prevent the two processes from doing the concurrent deployment + File.open(lock_path, File::RDWR|File::CREAT, 0644) do |f| + f.flock(File::LOCK_EX) + return unless valid? + + # 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 + if File.exists?(public_path) + FileUtils.move(public_path, previous_public_path) + end + FileUtils.move(temp_public_path, public_path) + end + + if File.exists?(previous_public_path) + FileUtils.rm_r(previous_public_path, force: true) + end + + @status.success + end + ensure + @status.drop if @status && @status.active? + end + + private + + def valid? + # check if sha for the ref is still the most recent one + # this helps in case when multiple deployments happens + build && build.artifacts_file? && sha == latest_sha + end + + def build + @build ||= Ci::Build.find_by(id: @build_id) + end + + def project + @project ||= build.project + 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 lock_path + @lock_path ||= File.join(pages_path, 'deploy.lock') + 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 diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 42e5f105d46..d41280624ae 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -153,6 +153,17 @@ production: &base # The location where LFS objects are stored (default: 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 + domain: example.com + ## Mattermost ## For enabling Add to Mattermost button mattermost: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 4f33aad8693..0c06b73ba36 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -254,6 +254,12 @@ Settings.registry['issuer'] ||= nil 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) +# Pages +Settings['pages'] ||= Settingslogic.new({}) +Settings.pages['enabled'] = false if Settings.pages['enabled'].nil? +Settings.pages['path'] = File.expand_path('shared/pages/', Rails.root) +Settings.pages['domain'] ||= "example.com" + # # Git LFS # diff --git a/config/routes/project.rb b/config/routes/project.rb index f36febc6e04..cd56f6281f5 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -329,6 +329,7 @@ constraints(ProjectUrlConstrainer.new) do post :archive post :unarchive post :housekeeping + post :remove_pages post :toggle_star post :preview_markdown post :export diff --git a/db/migrate/20151215132013_add_pages_size_to_application_settings.rb b/db/migrate/20151215132013_add_pages_size_to_application_settings.rb new file mode 100644 index 00000000000..e7fb73190e8 --- /dev/null +++ b/db/migrate/20151215132013_add_pages_size_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddPagesSizeToApplicationSettings < ActiveRecord::Migration + def up + add_column :application_settings, :max_pages_size, :integer, default: 100, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 5efb4f6595c..15f378b28ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -61,6 +61,7 @@ ActiveRecord::Schema.define(version: 20170130204620) do t.boolean "shared_runners_enabled", default: true, null: false t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" + t.integer "max_pages_size", default: 100, null: false t.boolean "require_two_factor_authentication", default: false t.integer "two_factor_grace_period", default: 48 t.boolean "metrics_enabled", default: false diff --git a/doc/README.md b/doc/README.md index 909740211a6..f036febb7b9 100644 --- a/doc/README.md +++ b/doc/README.md @@ -12,6 +12,7 @@ - [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. - [GitLab basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab. +- [GitLab Pages](pages/README.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 and exporting projects between instances](user/project/settings/import_export.md). - [Markdown](user/markdown.md) GitLab's advanced formatting system. diff --git a/doc/install/installation.md b/doc/install/installation.md index 425c5d93efb..c78e469055d 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -313,6 +313,9 @@ sudo usermod -aG redis git # Change the permissions of the directory where CI artifacts are stored sudo chmod -R u+rwX shared/artifacts/ + # Change the permissions of the directory where CI artifacts are stored + sudo chmod -R ug+rwX shared/pages/ + # Copy the example Unicorn config sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb @@ -484,6 +487,16 @@ 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 sudo editor /etc/nginx/sites-available/gitlab +Copy the GitLab pages site config: + + sudo cp lib/support/nginx/gitlab-pages /etc/nginx/sites-available/gitlab-pages + sudo ln -s /etc/nginx/sites-available/gitlab-pages /etc/nginx/sites-enabled/gitlab-pages + + # Change YOUR_GITLAB_PAGES\.DOMAIN to the fully-qualified + # domain name under which the pages will be served. + # The . (dot) replace with \. (backslash+dot) + sudo editor /etc/nginx/sites-available/gitlab-pages + **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 diff --git a/doc/pages/README.md b/doc/pages/README.md new file mode 100644 index 00000000000..d08c34c3ebc --- /dev/null +++ b/doc/pages/README.md @@ -0,0 +1,12 @@ +# GitLab Pages + +To start using GitLab Pages add to your project .gitlab-ci.yml with special pages job. + + pages: + image: jekyll + script: jekyll build + artifacts: + paths: + - public + +TODO diff --git a/lib/backup/pages.rb b/lib/backup/pages.rb new file mode 100644 index 00000000000..215ded93bfe --- /dev/null +++ b/lib/backup/pages.rb @@ -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 diff --git a/lib/support/nginx/gitlab-pages b/lib/support/nginx/gitlab-pages new file mode 100644 index 00000000000..0eeb0cd1917 --- /dev/null +++ b/lib/support/nginx/gitlab-pages @@ -0,0 +1,27 @@ +## Pages serving host +server { + listen 0.0.0.0:80; + listen [::]:80 ipv6only=on; + + ## Replace this with something like pages.gitlab.com + server_name ~^(?<group>.*)\.YOUR_GITLAB_PAGES\.DOMAIN$; + root /home/git/gitlab/shared/pages/${group}; + + ## Individual nginx logs for GitLab pages + access_log /var/log/nginx/gitlab_pages_access.log; + error_log /var/log/nginx/gitlab_pages_error.log; + + # 1. Try to get /project/ to => shared/pages/${group}/public/ or index.html + # 2. Try to get / to => shared/pages/${group}/${host}/public/ or index.html + location ~ ^/([^/]*)(/.*)?$ { + try_files "/$1/public$2" + "/$1/public$2/index.html" + "/${host}/public/${uri}" + "/${host}/public/${uri}/index.html" + =404; + } + + # Define custom error pages + error_page 403 /403.html; + error_page 404 /404.html; +} diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index a9f1255e8cf..ffab6f492fb 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -13,6 +13,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:uploads:create"].invoke Rake::Task["gitlab:backup:builds: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: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:builds:restore'].invoke unless backup.skipped?('builds') 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:registry:restore'].invoke unless backup.skipped?('registry') Rake::Task['gitlab:shell:setup'].invoke @@ -159,6 +161,25 @@ namespace :gitlab do 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 task create: :environment do $progress.puts "Dumping lfs objects ... ".color(:blue) diff --git a/shared/pages/.gitkeep b/shared/pages/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/shared/pages/.gitkeep diff --git a/spec/fixtures/pages.tar.gz b/spec/fixtures/pages.tar.gz Binary files differnew file mode 100644 index 00000000000..d0e89378b3e --- /dev/null +++ b/spec/fixtures/pages.tar.gz diff --git a/spec/fixtures/pages_empty.tar.gz b/spec/fixtures/pages_empty.tar.gz Binary files differnew file mode 100644 index 00000000000..5c2afa1a8f6 --- /dev/null +++ b/spec/fixtures/pages_empty.tar.gz diff --git a/spec/services/update_pages_service_spec.rb b/spec/services/update_pages_service_spec.rb new file mode 100644 index 00000000000..ed392cd94ee --- /dev/null +++ b/spec/services/update_pages_service_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe UpdatePagesService, services: true do + let(:build) { create(:ci_build) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + let(:service) { UpdatePagesService.new(data) } + + context 'execute asynchronously for pages job' do + before { build.name = 'pages' } + + context 'on success' do + before { build.success } + + it 'should execute 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 'should not execute worker' do + expect(PagesWorker).to_not receive(:perform_async) + service.execute + end + end + end + end + + context 'for other jobs' do + before do + build.name = 'other job' + build.success + end + + it 'should not execute worker' do + expect(PagesWorker).to_not receive(:perform_async) + service.execute + end + end +end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index bc751d20ce1..df8a47893f9 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -28,7 +28,7 @@ describe 'gitlab:app namespace rake task' do end 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 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:uploads: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:registry:restore']).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 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(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('builds.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('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 it 'deletes temp directories' do 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 @@ -304,7 +306,7 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do 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/') @@ -312,6 +314,7 @@ describe 'gitlab:app namespace rake task' do expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('artifacts.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).not_to match('repositories/') 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:builds: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:registry:restore']).to receive :invoke expect(Rake::Task['gitlab:shell:setup']).to receive :invoke diff --git a/spec/workers/pages_worker_spec.rb b/spec/workers/pages_worker_spec.rb new file mode 100644 index 00000000000..158a4b3ba8d --- /dev/null +++ b/spec/workers/pages_worker_spec.rb @@ -0,0 +1,58 @@ +require "spec_helper" + +describe PagesWorker do + let(:project) { create :project } + let(:commit) { create :ci_commit, project: project, sha: project.commit('HEAD').sha } + let(:build) { create :ci_build, commit: commit, ref: 'HEAD' } + let(:worker) { PagesWorker.new } + let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/pages.tar.gz', 'application/octet-stream') } + let(:empty_file) { fixture_file_upload(Rails.root + 'spec/fixtures/pages_empty.tar.gz', 'application/octet-stream') } + let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'application/octet-stream') } + + before do + project.remove_pages + end + + context 'for valid file' do + before { build.update_attributes(artifacts_file: file) } + + it 'succeeds' do + expect(project.pages_url).to be_nil + expect(worker.perform(build.id)).to be_truthy + expect(project.pages_url).to_not be_nil + end + + it 'limits pages size' do + stub_application_setting(max_pages_size: 1) + expect(worker.perform(build.id)).to_not be_truthy + end + + it 'removes pages after destroy' do + expect(project.pages_url).to be_nil + expect(worker.perform(build.id)).to be_truthy + expect(project.pages_url).to_not be_nil + project.destroy + expect(Dir.exist?(project.public_pages_path)).to be_falsey + end + end + + it 'fails if no artifacts' do + expect(worker.perform(build.id)).to_not be_truthy + end + + it 'fails for empty file fails' do + build.update_attributes(artifacts_file: empty_file) + expect(worker.perform(build.id)).to_not be_truthy + end + + it 'fails for invalid archive' do + build.update_attributes(artifacts_file: invalid_file) + expect(worker.perform(build.id)).to_not be_truthy + end + + it 'fails if sha on branch is not latest' do + commit.update_attributes(sha: 'old_sha') + build.update_attributes(artifacts_file: file) + expect(worker.perform(build.id)).to_not be_truthy + end +end |