From 5010ac6ed98664d20a9c8b0df487e8d9f6722ec7 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 20 Jul 2017 15:26:38 +0200 Subject: [PATCH 1/4] Require pry --- Capfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Capfile b/Capfile index 50aea0b..9915588 100644 --- a/Capfile +++ b/Capfile @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'pry' + # Load DSL and set up stages require 'capistrano/setup' From 08223b4c760bd052354d60cc37e1e12bdf700fca Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 20 Jul 2017 15:24:50 +0200 Subject: [PATCH 2/4] DRY out the ruby projects. --- .../mixins/applications/hets-rabbitmq-wrapper.rb | 14 +++----------- config/mixins/applications/ontohub-backend.rb | 15 +++------------ config/mixins/ruby_project_config.rb | 11 +++++++++++ config/mixins/ruby_project_requirements.rb | 5 +++++ 4 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 config/mixins/ruby_project_config.rb create mode 100644 config/mixins/ruby_project_requirements.rb diff --git a/config/mixins/applications/hets-rabbitmq-wrapper.rb b/config/mixins/applications/hets-rabbitmq-wrapper.rb index 8ab0194..29cc89e 100644 --- a/config/mixins/applications/hets-rabbitmq-wrapper.rb +++ b/config/mixins/applications/hets-rabbitmq-wrapper.rb @@ -2,25 +2,17 @@ Rake::Task['load:defaults'].invoke Rake::Task['load:defaults'].clear -require 'capistrano/rbenv' -require 'capistrano/bundler' +mixin('ruby_project_requirements') Rake::Task['load:defaults'].reenable Rake::Task['load:defaults'].invoke set :application, 'hets-rabbitmq-wrapper' set :repo_url, 'https://github.com/ontohub/hets-rabbitmq-wrapper.git' +mixin('ruby_project_config') + # Default value for :linked_files is [] # append :linked_files, 'config/settings.yml' # Default value for linked_dirs is [] # append :linked_dirs, 'log' - -set :rbenv_type, :system # or :user -set :rbenv_ruby, File.read('.ruby-version').strip -set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} "\ - "RBENV_VERSION=#{fetch(:rbenv_ruby)} "\ - "#{fetch(:rbenv_path)}/bin/rbenv exec" -set :rbenv_map_bins, %w(rake gem bundle ruby) - -set :bundle_binstubs, -> { shared_path.join('bin') } diff --git a/config/mixins/applications/ontohub-backend.rb b/config/mixins/applications/ontohub-backend.rb index b0db0bf..b8eb44b 100644 --- a/config/mixins/applications/ontohub-backend.rb +++ b/config/mixins/applications/ontohub-backend.rb @@ -2,8 +2,7 @@ Rake::Task['load:defaults'].invoke Rake::Task['load:defaults'].clear -require 'capistrano/rbenv' -require 'capistrano/bundler' +mixin('ruby_project_requirements') require 'capistrano/rails/migrations' Rake::Task['load:defaults'].reenable Rake::Task['load:defaults'].invoke @@ -11,6 +10,8 @@ set :application, 'ontohub-backend' set :repo_url, 'https://github.com/ontohub/ontohub-backend.git' +mixin('ruby_project_config') + # Default value for :linked_files is [] append :linked_files, 'config/database.yml', 'config/secrets.yml', @@ -23,16 +24,6 @@ 'tmp/cache', 'tmp/pids', 'tmp/sockets', 'vendor/bundle' -set :rbenv_type, :system # or :user -# The ruby version is only considered if there is no override (like a -# .ruby-version file) -set :rbenv_ruby, '2.4.1' -set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} "\ - "#{fetch(:rbenv_path)}/bin/rbenv exec" -set :rbenv_map_bins, %w(rake gem bundle ruby rails) - -set :bundle_binstubs, -> { "~#{fetch(:deploy_user)}/bin" } - set :migration_role, :app after :'deploy:updated', :'version:fetch' do diff --git a/config/mixins/ruby_project_config.rb b/config/mixins/ruby_project_config.rb new file mode 100644 index 0000000..cacabe7 --- /dev/null +++ b/config/mixins/ruby_project_config.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +set :rbenv_type, :system # or :user +# The ruby version is only considered if there is no override (like a +# .ruby-version file) +set :rbenv_ruby, '2.4.1' +set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} "\ + "#{fetch(:rbenv_path)}/bin/rbenv exec" +set :rbenv_map_bins, %w(rake gem bundle ruby rails) + +set :bundle_binstubs, -> { "~#{fetch(:deploy_user)}/bin" } diff --git a/config/mixins/ruby_project_requirements.rb b/config/mixins/ruby_project_requirements.rb new file mode 100644 index 0000000..400b21e --- /dev/null +++ b/config/mixins/ruby_project_requirements.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require 'capistrano/rbenv' +require 'capistrano/bundler' +require 'capistrano/rails/migrations' From 8c97ed86925d9370661d5d4d77ee735b359a7df4 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 20 Jul 2017 15:25:41 +0200 Subject: [PATCH 3/4] Modularise the frontend deployment config. --- Capfile | 1 + .../mixins/applications/ontohub-frontend.rb | 112 +---------------- config/mixins/local_repository.rb | 114 ++++++++++++++++++ lib/popen.rb | 10 ++ 4 files changed, 128 insertions(+), 109 deletions(-) create mode 100644 config/mixins/local_repository.rb create mode 100644 lib/popen.rb diff --git a/Capfile b/Capfile index 9915588..5e04f8e 100644 --- a/Capfile +++ b/Capfile @@ -40,6 +40,7 @@ install_plugin Capistrano::SCM::Git # Add the mixin method $:.unshift(File.dirname(__FILE__)) require 'lib/mixin' +require 'lib/popen' # Disable freezing rake tasks require 'lib/disable_immutable_task' diff --git a/config/mixins/applications/ontohub-frontend.rb b/config/mixins/applications/ontohub-frontend.rb index 397b813..673be5f 100644 --- a/config/mixins/applications/ontohub-frontend.rb +++ b/config/mixins/applications/ontohub-frontend.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'open3' - Rake::Task['load:defaults'].invoke Rake::Task['load:defaults'].clear Rake::Task['load:defaults'].reenable @@ -10,93 +8,7 @@ set :application, 'ontohub-frontend' set :repo_url, 'https://github.com/ontohub/ontohub-frontend.git' -set :local_repo_root, File.expand_path('../../../../repos', __FILE__) -set :local_repo_path, File.join(fetch(:local_repo_root), fetch(:application)) - -# We want to build this locally and push the built version to the server, -# se we need to redefine the SCM tasks - -# rubocop:disable Metrics/BlockLength -namespace :git do - # Clone the repo to the local directory if it is not yet cloned. - Rake::Task['git:clone'].clear_actions - task :clone do - run_locally do - `mkdir -p #{fetch(:local_repo_root).to_s}` - Dir.chdir(fetch(:local_repo_root).to_s) do - unless Dir.exist?(fetch(:local_repo_path)) - `git clone #{fetch(:repo_url)}` - end - end - end - end - - # This shall fetch the remote repo to the local directory and update it to the - # given branch/commit. - Rake::Task['git:update'].clear_actions - task :update do - run_locally do - Dir.chdir(fetch(:local_repo_path)) do - `git remote update --prune` - end - end - end - - # This shall only create the release directory, and not push the repository at - # the given commit into it. Later on, the built app will be uploaded there. - Rake::Task['git:create_release'].clear_actions - task create_release: :'git:update' do - on release_roles :all do - with fetch(:git_environmental_variables) do - within repo_path do - execute :mkdir, '-p', release_path - end - end - end - end - - Rake::Task['git:set_current_revision'].clear_actions - task :set_current_revision do - run_locally do - Dir.chdir(fetch(:local_repo_path)) do - current_revision = `git rev-list --max-count=1 #{fetch(:branch)}`.strip - set :current_revision, current_revision - end - end - end -end - -# Find the latest tag in the local repository -# (overwrite the task for the live stage) -Rake::Task['set_latest_tag'].clear_actions -after :'git:update', :set_latest_tag do - run_locally do - Dir.chdir(fetch(:local_repo_path)) do - set :latest_tag, `git tag --list`.lines.last.strip - end - end -end -after :set_latest_tag, :set_deploy_tag - -after :'git:update', :'git:checkout' do - run_locally do - Dir.chdir(fetch(:local_repo_path)) do - ref = fetch(:branch) - # We check out a tag in a live stage - no 'origin/' required - if fetch(:stage).to_s.split('_', 2).first != 'live' - ref = "origin/#{ref}" unless ref.start_with?('origin/') - end - # Clean and reset to allow a checkout - `git clean -fd .` - `git reset --hard` - # Actuall checkout - `git checkout #{ref}` - # Clean again because the .gitignore might have changed and thus, the - # previous cleaning might be inclomplete. - `git clean -fd .` - end - end -end +mixin('local_repository') before :'deploy:publishing', :build_application do run_locally do @@ -106,30 +18,12 @@ env = {'REACT_APP_BACKEND_HOST' => fetch(:backend_url), 'REACT_APP_GRECAPTCHA_SITE_KEY' => fetch(:grecaptcha_site_key)} - Open3.popen2e(env, 'yarn', 'build') do |_stdin, stdout_and_err, wait_thr| - Thread.new { stdout_and_err.each { |l| puts l } } - wait_thr.value - end + popen(env, 'yarn', 'build') end end end -after :build_application, :publish_built_application do - # Deploy the application - on roles(:all) do - remote_base_dir = fetch(:release_path).to_s - local_base_dir = File.join(fetch(:local_repo_path), 'build') - files = Dir.glob(File.join(local_base_dir, '**/*')).reject do |file| - File.directory?(file) - end - files.each do |local_file| - remote_file = local_file.to_s.sub(local_base_dir, remote_base_dir) - remote_dir = File.dirname(remote_file).sub(%r{\A~/}, '') - execute :mkdir, '-p', remote_dir - upload! local_file, remote_dir - end - end -end +after :build_application, :publish_built_application after :'deploy:publishing', :create_symlinks do on roles(:all) do diff --git a/config/mixins/local_repository.rb b/config/mixins/local_repository.rb new file mode 100644 index 0000000..54ea331 --- /dev/null +++ b/config/mixins/local_repository.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +set :local_repo_root, File.expand_path('../../../repos', __FILE__) +set :local_repo_path, File.join(fetch(:local_repo_root), fetch(:application)) + +# We want to build this locally and push the built version to the server, +# se we need to redefine the SCM tasks + +# rubocop:disable Metrics/BlockLength +namespace :git do + # Clone the repo to the local directory if it is not yet cloned. + Rake::Task['git:clone'].clear_actions + task :clone do + run_locally do + `mkdir -p #{fetch(:local_repo_root).to_s}` + Dir.chdir(fetch(:local_repo_root).to_s) do + unless Dir.exist?(fetch(:local_repo_path)) + `git clone #{fetch(:repo_url)} #{fetch(:local_repo_path)}` + end + end + end + end + + # This shall fetch the remote repo to the local directory and update it to the + # given branch/commit. + Rake::Task['git:update'].clear_actions + task :update do + run_locally do + Dir.chdir(fetch(:local_repo_path)) do + `git remote update --prune` + end + end + end + + # This shall only create the release directory, and not push the repository at + # the given commit into it. Later on, the built app will be uploaded there. + Rake::Task['git:create_release'].clear_actions + task create_release: :'git:update' do + on release_roles :all do + with fetch(:git_environmental_variables) do + within repo_path do + execute :mkdir, '-p', release_path + end + end + end + end + + Rake::Task['git:set_current_revision'].clear_actions + task :set_current_revision do + run_locally do + Dir.chdir(fetch(:local_repo_path)) do + current_revision = `git rev-list --max-count=1 #{fetch(:branch)}`.strip + set :current_revision, current_revision + end + end + end +end + +# This is needed because capistrano expects a repo directory to exist. +after :'deploy:check:directories', :create_repo_directory do + on roles(:all) do + execute('mkdir', '-p', repo_path) + end +end + +# Find the latest tag in the local repository +# (overwrite the task for the live stage) +Rake::Task['set_latest_tag'].clear_actions +after :'git:update', :set_latest_tag do + run_locally do + Dir.chdir(fetch(:local_repo_path)) do + set :latest_tag, `git tag --list`.lines.last.strip + end + end +end +after :set_latest_tag, :set_deploy_tag + +after :'git:update', :'git:checkout' do + run_locally do + Dir.chdir(fetch(:local_repo_path)) do + ref = fetch(:branch) + # We check out a tag in a live stage - no 'origin/' required + if fetch(:stage).to_s.split('_', 2).first != 'live' + ref = "origin/#{ref}" unless ref.start_with?('origin/') + end + # Clean and reset to allow a checkout + `git clean -fd .` + `git reset --hard` + # Actuall checkout + `git checkout #{ref}` + # Clean again because the .gitignore might have changed and thus, the + # previous cleaning might be inclomplete. + `git clean -fd .` + end + end +end + +# Deploy the application +desc 'deploy the application' +task :publish_built_application do + on roles(:all) do + remote_base_dir = fetch(:release_path).to_s + local_base_dir = File.join(fetch(:local_repo_path), 'build') + files = Dir.glob(File.join(local_base_dir, '**/*')).reject do |file| + File.directory?(file) + end + files.each do |local_file| + remote_file = local_file.to_s.sub(local_base_dir, remote_base_dir) + remote_dir = File.dirname(remote_file).sub(%r{\A~/}, '') + execute :mkdir, '-p', remote_dir + upload! local_file, remote_dir + end + end +end diff --git a/lib/popen.rb b/lib/popen.rb new file mode 100644 index 0000000..25f644b --- /dev/null +++ b/lib/popen.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'open3' + +def popen(environment, *command) + Open3.popen2e(environment, *command) do |_stdin, stdout_and_err, wait_thr| + Thread.new { stdout_and_err.each { |l| puts l } } + wait_thr.value + end +end From 9985259ff86f22cede5c2a5914ffe408053a88d2 Mon Sep 17 00:00:00 2001 From: Eugen Kuksa Date: Thu, 20 Jul 2017 15:26:29 +0200 Subject: [PATCH 4/4] Add apidoc deployment config. --- Gemfile | 5 ++ Gemfile.lock | 60 ++++++++++++++++++++++ Readme.md | 7 +-- config/deploy/staging_apidoc.rb | 5 ++ config/mixins/applications/apidoc.rb | 75 ++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 config/deploy/staging_apidoc.rb create mode 100644 config/mixins/applications/apidoc.rb diff --git a/Gemfile b/Gemfile index ec537ab..0d80ab7 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,11 @@ source 'https://rubygems.org' +gem 'pry', '~> 0.10.4' + gem 'capistrano', '~> 3.8.2' gem 'capistrano-rails', '~> 1.3.0' gem 'capistrano-rbenv', '~> 2.1.1' + +gem 'graphql-docs', github: 'eugenk/graphql-docs', + branch: 'improve_style_and_fix_nil_fields' diff --git a/Gemfile.lock b/Gemfile.lock index 8eff9b1..1306b04 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,25 @@ +GIT + remote: git://github.com/eugenk/graphql-docs.git + revision: cc3eedee1ea10027c35fad251aa6e9ddc9bdd19d + branch: improve_style_and_fix_nil_fields + specs: + graphql-docs (0.6.2) + commonmarker (~> 0.16) + extended-markdown-filter (~> 0.4) + faraday (< 0.10) + gemoji (= 2.1.0) + graphql (~> 1.4) + html-pipeline (~> 2.2) + sass (~> 3.4) + GEM remote: https://rubygems.org/ specs: + activesupport (5.1.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) airbrussh (1.3.0) sshkit (>= 1.6.1, != 1.7.0) capistrano (3.8.2) @@ -17,14 +36,53 @@ GEM capistrano-rbenv (2.1.1) capistrano (~> 3.1) sshkit (~> 1.3) + coderay (1.1.1) + commonmarker (0.16.8) + ruby-enum (~> 0.5) + concurrent-ruby (1.0.5) + extended-markdown-filter (0.4.9) + html-pipeline (~> 2.0) + nokogiri (~> 1.6) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) + ffi (1.9.18) + gemoji (2.1.0) + graphql (1.6.6) + html-pipeline (2.6.0) + activesupport (>= 2) + nokogiri (>= 1.4) i18n (0.8.4) + method_source (0.8.2) + mini_portile2 (2.2.0) + minitest (5.10.2) + multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (4.1.0) + nokogiri (1.8.0) + mini_portile2 (~> 2.2.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) rake (12.0.0) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + ruby-enum (0.7.1) + i18n + sass (3.5.1) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + slop (3.6.0) sshkit (1.13.1) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) + thread_safe (0.3.6) + tzinfo (1.2.3) + thread_safe (~> 0.1) PLATFORMS ruby @@ -33,6 +91,8 @@ DEPENDENCIES capistrano (~> 3.8.2) capistrano-rails (~> 1.3.0) capistrano-rbenv (~> 2.1.1) + graphql-docs! + pry (~> 0.10.4) BUNDLED WITH 1.14.6 diff --git a/Readme.md b/Readme.md index c3f3802..aec5394 100644 --- a/Readme.md +++ b/Readme.md @@ -15,9 +15,10 @@ where the three place holders can be: * `live` (ontohub.org) * `staging` (staging.ontohub.org) * `` is one of - * `ontohub-frontend` - * `ontohub-backend` - * `hets-rabbitmq-wrapper` + * `apidoc`: The API documentation of the backend + * `ontohub-backend`: The backend + * `ontohub-frontend`: The frontend + * `hets-rabbitmq-wrapper`: The HetsRabbitMQWrapper so to deploy the backend to staging.ontohub.org, you need to execute diff --git a/config/deploy/staging_apidoc.rb b/config/deploy/staging_apidoc.rb new file mode 100644 index 0000000..795028e --- /dev/null +++ b/config/deploy/staging_apidoc.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +mixin 'servers/staging.ontohub.org' +mixin 'environments/production' +mixin 'applications/apidoc' diff --git a/config/mixins/applications/apidoc.rb b/config/mixins/applications/apidoc.rb new file mode 100644 index 0000000..80b8570 --- /dev/null +++ b/config/mixins/applications/apidoc.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +require 'graphql-docs' + +Rake::Task['load:defaults'].invoke +Rake::Task['load:defaults'].clear +Rake::Task['load:defaults'].reenable +Rake::Task['load:defaults'].invoke + +set :application, 'apidoc' +set :repo_url, 'https://github.com/ontohub/ontohub-backend.git' + +mixin('local_repository') + +set :graphql_base_url, '/graphql' + +# rubocop:disable Metrics/BlockLength +before :'deploy:publishing', :build_application do + # rubocop:enable Metrics/BlockLength + run_locally do + Bundler.with_clean_env do + local_repo = fetch(:local_repo_path) + build_dir = File.join(local_repo, 'build') + Dir.chdir(local_repo) do + system('bundle install') + + # Build the REST API documentation into a temp directory + system('bundle exec rails apidoc:prepare') + system('bundle exec rails apidoc:init') + Dir.chdir('apidoc') do + system('yarn build') + end + + # Build the GraphQL documentation into a temp directory + apidoc_graphql_dir = File.join(local_repo, 'apidoc-graphql') + graphql_schema_file = File.join(local_repo, 'tmp/graphql_schema.json') + + popen({'FILE' => graphql_schema_file}, + 'bundle', 'exec', 'rails', 'graphql:write_json') + + GraphQLDocs.build(delete_output: true, + output_dir: apidoc_graphql_dir, + base_url: fetch(:graphql_base_url), + path: graphql_schema_file) + + # Move the generated documentation files to the `build` directory + FileUtils.rm_rf(build_dir) + system("mkdir -p #{build_dir}") + FileUtils.mv(File.join(local_repo, 'apidoc/build'), + File.join(build_dir, 'rest')) + FileUtils.mv(apidoc_graphql_dir, File.join(build_dir, 'graphql')) + + # Add an index page to select the API docs: + index_content = <<~INDEX + + + Ontohub API Documentation + + +

Ontohub API Documentation

+

Please select an API

+
    +
  • GraphQL (with full functionality)
  • +
  • REST (read only actions)
  • +
+ + + INDEX + File.write(File.join(build_dir, 'index.html'), index_content) + end + end + end +end + +after :build_application, :publish_built_application