Skip to content
This repository has been archived by the owner on Sep 6, 2022. It is now read-only.

Commit

Permalink
Refactor of initial config clone and server bootup
Browse files Browse the repository at this point in the history
Finally a clean `config.ru` and a clean `fastlane_app` 馃憤
  • Loading branch information
KrauseFx committed Feb 1, 2018
1 parent a3f6f90 commit c621725
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 155 deletions.
53 changes: 2 additions & 51 deletions config.ru
Expand Up @@ -3,56 +3,7 @@ require "bundler"

Bundler.require

if File.exist?(".keys")
require "dotenv"
Dotenv.load(".keys")
end
require_relative "./launch"

# Don't even try to run without this
begin
require "openssl"
rescue LoadError
warn("Error: no such file to load -- openssl. Make sure you have openssl installed")
exit(1)
end

if ENV["RACK_ENV"] == "development"
puts("development mode, aborting on any thread exceptions")
Thread.abort_on_exception = true
end

if ENV["FASTLANE_CI_ENCRYPTION_KEY"].nil?
warn("Error: unable to decrypt sensitive data without environment variable `FASTLANE_CI_ENCRYPTION_KEY` set")
exit(1)
end

if ENV["FASTLANE_CI_USER"].nil? || ENV["FASTLANE_CI_PASSWORD"].nil?
warn("Error: ensure you have your `FASTLANE_CI_USER` and `FASTLANE_CI_PASSWORD`environment variables set")
exit(1)
end

if ENV["FASTLANE_CI_REPO_URL"].nil?
warn("Error: ensure you have your `FASTLANE_CI_REPO_URL` environment variable set")
exit(1)
end

# before running, call `bundle install --path vendor/bundle`
# this isolates the gems for bundler

require "./fastlane_app"

# allow use of `require` for all things under `shared`, helps with some cycle issues
$LOAD_PATH << "shared"

# require all controllers
require_relative "features/dashboard/dashboard_controller"
require_relative "features/login/login_controller"
require_relative "features/project/project_controller"

# Load up all the available controllers
use(FastlaneCI::DashboardController)
use(FastlaneCI::LoginController)
use(FastlaneCI::ProjectController)

# Start the CI app
FastlaneCI::Launch.take_off
run(FastlaneCI::FastlaneApp)
63 changes: 1 addition & 62 deletions fastlane_app.rb
Expand Up @@ -4,16 +4,8 @@
require_relative "./fastfile-parser/fastfile_parser"

# Internal
require_relative "services/config_data_sources/json_project_data_source"
require_relative "services/config_service"
require_relative "services/worker_service"
require_relative "services/user_service"
require_relative "services/data_sources/json_user_data_source"
require_relative "services/data_sources/json_build_data_source"
require_relative "services/code_hosting_sources/git_hub_source"

require_relative "services/services"
require_relative "workers/refresh_config_data_sources_worker"

require_relative "shared/logging_module"
require_relative "shared/fastlane_ci_error" # TODO: move somewhere else

Expand Down Expand Up @@ -41,58 +33,5 @@ class FastlaneApp < Sinatra::Base
get "/favico.ico" do
"nope"
end

# Setup the fastlane.ci GitRepoConfig
ci_config_repo = GitRepoConfig.new(
id: "fastlane-ci-config",
git_url: ENV["FASTLANE_CI_REPO_URL"],
description: "Contains the fastlane.ci configuration",
name: "fastlane ci",
hidden: true
)

# Get the path to where we store fastlane.ci configuration
ci_config_git_repo_path = ci_config_repo.local_repo_path

# Create JSON data sources from the configuration git repo
user_data_source = JSONUserDataSource.new(json_folder_path: ci_config_git_repo_path)
build_data_source = JSONBuildDataSource.new(json_folder_path: ci_config_git_repo_path)

# Start up a UserService from our JSONUserDataSource
USER_SERVICE = FastlaneCI::UserService.new(user_data_source: user_data_source)

# Start up the BuildService
BUILD_SERVICE = FastlaneCI::BuildService.new(build_data_source: build_data_source)

# Find our fastlane.ci system user
@ci_user = USER_SERVICE.login(email: ENV["FASTLANE_CI_USER"], password: ENV["FASTLANE_CI_PASSWORD"], ci_config_repo: ci_config_repo)

# Start our project data source, TODO: this should be accessed through a ProjectDataService
PROJECT_DATA_SOURCE = FastlaneCI::JSONProjectDataSource.new(git_repo_config: ci_config_repo, user: @ci_user)

# Going ot start our workers
@worker_service = FastlaneCI::WorkerService.new

# Grab a config service that is configured for the CI user
@ci_user_config_service = FastlaneCI::ConfigService.new(ci_user: @ci_user)

# Iterate through all provider credentials and their projects and start a worker for each project
number_of_workers_started = 0
@ci_user.provider_credentials.each do |provider_credential|
projects = @ci_user_config_service.projects(provider_credential: provider_credential)
projects.each do |project|
@worker_service.start_worker_for_provider_credential_and_config(
project: project,
provider_credential: provider_credential
)
number_of_workers_started += 1
end
end
puts("Seems like no workers were started to monitor your projects") if number_of_workers_started == 0 # TODO: use logger

# Initialize the workers
# For now, we're not using a fancy framework that adds multiple heavy dependencies
# including a database, etc.
FastlaneCI::RefreshConfigDataSourcesWorker.new
end
end
6 changes: 3 additions & 3 deletions features/login/login_controller.rb
Expand Up @@ -54,7 +54,7 @@ class LoginController < ControllerBase
post "/login" do
email = params[:email]
password = params[:password]
user = FastlaneApp::USER_SERVICE.login(email: email, password: password)
user = Services.user_service.login(email: email, password: password)
if user.nil?
redirect("#{HOME}/ci_login")
else
Expand All @@ -75,7 +75,7 @@ class LoginController < ControllerBase
post "#{HOME}/create_account" do
email = params[:email]
password = params[:password]
user = FastlaneApp::USER_SERVICE.create_user!(email: email, password: password)
user = Services.user_service.create_user!(email: email, password: password)
if user.nil?
locals = { title: "Create fastlane.ci account", create_failed: true }
erb(:create_account, locals: locals, layout: FastlaneCI.default_layout)
Expand Down Expand Up @@ -116,7 +116,7 @@ class LoginController < ControllerBase
updated_user = true
end

FastlaneApp::USER_SERVICE.update_user!(user: user) if updated_user
Services.user_service.update_user!(user: user) if updated_user

# update session user
session[:user] = user
Expand Down
154 changes: 154 additions & 0 deletions launch.rb
@@ -0,0 +1,154 @@
module FastlaneCI
class Launch
def self.take_off
verify_dependencies
load_dot_env
verify_env_variables
setup_threads
require_fastlane_ci
check_for_existing_setup
prepare_server
# launch_workers
end

def self.require_fastlane_ci
# before running, call `bundle install --path vendor/bundle`
# this isolates the gems for bundler
require "./fastlane_app"

# allow use of `require` for all things under `shared`, helps with some cycle issues
$LOAD_PATH << "shared"
end

def self.load_dot_env
return unless File.exist?(".keys")
require "dotenv"
Dotenv.load(".keys")
end

def self.verify_dependencies
begin
require "openssl"
rescue LoadError
warn("Error: no such file to load -- openssl. Make sure you have openssl installed")
exit(1)
end
end

def self.verify_env_variables
# Don't even try to run without having those
if ENV["FASTLANE_CI_ENCRYPTION_KEY"].nil?
warn("Error: unable to decrypt sensitive data without environment variable `FASTLANE_CI_ENCRYPTION_KEY` set")
exit(1)
end

if ENV["FASTLANE_CI_USER"].nil? || ENV["FASTLANE_CI_PASSWORD"].nil?
warn("Error: ensure you have your `FASTLANE_CI_USER` and `FASTLANE_CI_PASSWORD`environment variables set")
exit(1)
end

if ENV["FASTLANE_CI_REPO_URL"].nil?
warn("Error: ensure you have your `FASTLANE_CI_REPO_URL` environment variable set")
exit(1)
end
end

def self.setup_threads
if ENV["RACK_ENV"] == "development"
puts("development mode, aborting on any thread exceptions")
Thread.abort_on_exception = true
end
end

# Check if fastlane.ci already ran on this machine
# and with that, have the initial `users.json`, etc.
# If not, this is where we do the initial clone
def self.check_for_existing_setup
# TODO: check if it already exists
unless self.ci_config_repo.exists?
self.trigger_initial_ci_setup
end

Services.ci_config_repo = self.ci_config_repo
end

def self.ci_config_repo
# Setup the fastlane.ci GitRepoConfig
@_ci_config_repo ||= GitRepoConfig.new(
id: "fastlane-ci-config",
git_url: ENV["FASTLANE_CI_REPO_URL"],
description: "Contains the fastlane.ci configuration",
name: "fastlane ci",
hidden: true
)
end

# We can't actually launch the server here
# as it seems like it has to happen in `config.ru`
def self.prepare_server
# require all controllers
require_relative "features/dashboard/dashboard_controller"
require_relative "features/login/login_controller"
require_relative "features/project/project_controller"

# Load up all the available controllers
FastlaneCI::FastlaneApp.use(FastlaneCI::DashboardController)
FastlaneCI::FastlaneApp.use(FastlaneCI::LoginController)
FastlaneCI::FastlaneApp.use(FastlaneCI::ProjectController)
end

def self.launch_workers
# Iterate through all provider credentials and their projects and start a worker for each project
number_of_workers_started = 0
Services.ci_user.provider_credentials.each do |provider_credential|
projects = Services.config_service.projects(provider_credential: provider_credential)
projects.each do |project|
Services.worker_service.start_worker_for_provider_credential_and_config(
project: project,
provider_credential: provider_credential
)
number_of_workers_started += 1
end
end

# TODO: use logger if possible
puts("Seems like no workers were started to monitor your projects") if number_of_workers_started == 0

# Initialize the workers
# For now, we're not using a fancy framework that adds multiple heavy dependencies
# including a database, etc.
FastlaneCI::RefreshConfigDataSourcesWorker.new
end

# Verify that fastlane.ci is already set up on this machine.
# If that's not the case, we have to make sure to trigger the initial clone
def self.trigger_initial_ci_setup
puts "No config repo cloned yet, doing that now" # TODO: use logger if possible

# This happens on the first launch of CI
# We don't have access to the config directory yet
# So we'll use ENV variables that are used for the initial clone only
#
# Long term, we'll have a nice onboarding flow, where you can enter those credentials
# as part of a web UI. But for containers (e.g. Google Cloud App Engine)
# we'll have to support ENV variables also, for the initial clone, so that's the code below
# Clone the repo, and login the user
provider_credential = GitHubProviderCredential.new(email: ENV["FASTLANE_CI_INITIAL_CLONE_EMAIL"],
api_token: ENV["FASTLANE_CI_INITIAL_CLONE_API_TOKEN"])
# Trigger the initial clone
FastlaneCI::JSONProjectDataSource.new(
git_repo_config: ci_config_repo,
provider_credential: provider_credential
)
puts "Successfully did the initial clone on this machine"
rescue StandardError => ex
puts("Something went wrong on the initial clone")

if ENV["FASTLANE_CI_INITIAL_CLONE_API_TOKEN"].to_s.length == 0 || ENV["FASTLANE_CI_INITIAL_CLONE_EMAIL"].to_s.length == 0
puts("Make sure to provide your `FASTLANE_CI_INITIAL_CLONE_EMAIL` and `FASTLANE_CI_INITIAL_CLONE_API_TOKEN` ENV variables")
end

raise ex
end
end
end
2 changes: 1 addition & 1 deletion services/config_service.rb
Expand Up @@ -12,7 +12,7 @@ class ConfigService
attr_accessor :ci_user
attr_accessor :active_code_hosts # dictionary of active_code_hosting_key to CodeHosting

def initialize(project_data_source: FastlaneCI::FastlaneApp::PROJECT_DATA_SOURCE, ci_user: nil)
def initialize(project_data_source: FastlaneCI::Services.project_data_source, ci_user: nil)
self.project_data_source = project_data_source
self.ci_user = ci_user
self.active_code_hosts = {}
Expand Down

0 comments on commit c621725

Please sign in to comment.