geb 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +37 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE +21 -0
- data/README.md +384 -0
- data/Rakefile +21 -0
- data/bin/geb +21 -0
- data/lib/geb/cli.rb +40 -0
- data/lib/geb/commands/build.rb +59 -0
- data/lib/geb/commands/init.rb +70 -0
- data/lib/geb/commands/release.rb +54 -0
- data/lib/geb/commands/remote.rb +48 -0
- data/lib/geb/commands/server.rb +77 -0
- data/lib/geb/commands/upload.rb +48 -0
- data/lib/geb/commands/version.rb +36 -0
- data/lib/geb/config.rb +103 -0
- data/lib/geb/defaults.rb +44 -0
- data/lib/geb/git.rb +112 -0
- data/lib/geb/page.rb +217 -0
- data/lib/geb/partial.rb +150 -0
- data/lib/geb/samples/basic/assets/css/site.css +7 -0
- data/lib/geb/samples/basic/assets/images/android-chrome-192x192.png +0 -0
- data/lib/geb/samples/basic/assets/images/android-chrome-512x512.png +0 -0
- data/lib/geb/samples/basic/assets/images/apple-touch-icon.png +0 -0
- data/lib/geb/samples/basic/assets/images/favicon-16x16.png +0 -0
- data/lib/geb/samples/basic/assets/images/favicon-32x32.png +0 -0
- data/lib/geb/samples/basic/assets/images/favicon.ico +0 -0
- data/lib/geb/samples/basic/assets/images/hero.png +0 -0
- data/lib/geb/samples/basic/assets/images/og-thumb.png +0 -0
- data/lib/geb/samples/basic/assets/images/twitter-thumb.png +0 -0
- data/lib/geb/samples/basic/assets/js/site.js +5 -0
- data/lib/geb/samples/basic/geb.config.yml +70 -0
- data/lib/geb/samples/basic/index.html +11 -0
- data/lib/geb/samples/basic/page.html +11 -0
- data/lib/geb/samples/basic/shared/partials/_analytics.html +9 -0
- data/lib/geb/samples/basic/shared/partials/_footer.html +3 -0
- data/lib/geb/samples/basic/shared/partials/_global_assets.html +19 -0
- data/lib/geb/samples/basic/shared/partials/_header.html +0 -0
- data/lib/geb/samples/basic/shared/partials/_meta_tags.html +34 -0
- data/lib/geb/samples/basic/shared/templates/_blog_post.html +0 -0
- data/lib/geb/samples/basic/shared/templates/_site.html +19 -0
- data/lib/geb/samples/basic/site.webmanifest +1 -0
- data/lib/geb/samples/bootstrap_jquery/assets/css/site.css +7 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/android-chrome-192x192.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/android-chrome-512x512.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/apple-touch-icon.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/favicon-16x16.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/favicon-32x32.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/favicon.ico +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/hero.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/og-thumb.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/images/twitter-thumb.png +0 -0
- data/lib/geb/samples/bootstrap_jquery/assets/js/site.js +5 -0
- data/lib/geb/samples/bootstrap_jquery/blog/blog_post_1.html +35 -0
- data/lib/geb/samples/bootstrap_jquery/blog/blog_post_2.html +35 -0
- data/lib/geb/samples/bootstrap_jquery/blog/blog_post_3.html +35 -0
- data/lib/geb/samples/bootstrap_jquery/blog/index.html +17 -0
- data/lib/geb/samples/bootstrap_jquery/geb.config.yml +69 -0
- data/lib/geb/samples/bootstrap_jquery/index.html +11 -0
- data/lib/geb/samples/bootstrap_jquery/page.html +11 -0
- data/lib/geb/samples/bootstrap_jquery/shared/partials/_analytics.html +9 -0
- data/lib/geb/samples/bootstrap_jquery/shared/partials/_footer.html +3 -0
- data/lib/geb/samples/bootstrap_jquery/shared/partials/_global_assets.html +19 -0
- data/lib/geb/samples/bootstrap_jquery/shared/partials/_header.html +0 -0
- data/lib/geb/samples/bootstrap_jquery/shared/partials/_meta_tags.html +34 -0
- data/lib/geb/samples/bootstrap_jquery/shared/templates/_blog_post.html +0 -0
- data/lib/geb/samples/bootstrap_jquery/shared/templates/_site.html +19 -0
- data/lib/geb/samples/bootstrap_jquery/site.webmanifest +1 -0
- data/lib/geb/samples/geb.config.yml +70 -0
- data/lib/geb/server.rb +138 -0
- data/lib/geb/site/build.rb +189 -0
- data/lib/geb/site/core.rb +229 -0
- data/lib/geb/site/release.rb +70 -0
- data/lib/geb/site/remote.rb +142 -0
- data/lib/geb/site/template.rb +208 -0
- data/lib/geb/site.rb +83 -0
- data/lib/geb/template.rb +166 -0
- data/lib/geb/utilities.rb +110 -0
- data/lib/geb.rb +36 -0
- data/lib/seth.rb +50 -0
- data/sig/geb.rbs +4 -0
- data/test/api tests/test_cli.rb +200 -0
- data/test/api tests/test_config.rb +330 -0
- data/test/api tests/test_defaults.rb +62 -0
- data/test/api tests/test_git.rb +105 -0
- data/test/api tests/test_page.rb +320 -0
- data/test/api tests/test_partial.rb +152 -0
- data/test/api tests/test_server.rb +416 -0
- data/test/api tests/test_site.rb +1315 -0
- data/test/api tests/test_template.rb +249 -0
- data/test/api tests/test_utilities.rb +162 -0
- data/test/command tests/test_geb_build.rb +199 -0
- data/test/command tests/test_geb_init.rb +312 -0
- data/test/command tests/test_geb_release.rb +166 -0
- data/test/command tests/test_geb_remote.rb +66 -0
- data/test/command tests/test_geb_server.rb +122 -0
- data/test/command tests/test_geb_upload.rb +96 -0
- data/test/command tests/test_geb_version.rb +58 -0
- data/test/support/geb_api_test.rb +37 -0
- data/test/support/geb_cli_test.rb +275 -0
- data/test/support/geb_minitest_ext.rb +35 -0
- data/test/support/geb_test_helpers.rb +84 -0
- data/test/support/geb_web_server_proxy.rb +128 -0
- data/test/test_helper.rb +61 -0
- metadata +301 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Site building functionality, building assets and pages
|
4
|
+
#
|
5
|
+
# @title Geb - Site - Build Module
|
6
|
+
# @author Edin Mustajbegovic <edin@actiontwelve.com>
|
7
|
+
# @copyright 2024 Edin Mustajbegovic
|
8
|
+
# @license MIT
|
9
|
+
#
|
10
|
+
# @see https://github.com/mainfram-work/geb for more information
|
11
|
+
|
12
|
+
module Geb
|
13
|
+
class Site
|
14
|
+
module Build
|
15
|
+
|
16
|
+
class SiteNotLoadedError < Geb::Error
|
17
|
+
MESSAGE = "Site not loaded.".freeze
|
18
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
19
|
+
end # class SiteNotLoadedError < Geb::Error
|
20
|
+
|
21
|
+
class FailedToOutputSite < Geb::Error
|
22
|
+
MESSAGE = "Failed to output site.".freeze
|
23
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
24
|
+
end # class FailedToOutputSite < Geb::Error
|
25
|
+
|
26
|
+
# build the site
|
27
|
+
def build
|
28
|
+
|
29
|
+
# make sure the site is laoded, if not, raise an error
|
30
|
+
raise SiteNotLoadedError.new("Could not build the site.") unless @loaded
|
31
|
+
|
32
|
+
# build the assets and pages
|
33
|
+
# it is important to build pages first as there may be pages in the assets directory
|
34
|
+
build_pages()
|
35
|
+
build_assets()
|
36
|
+
|
37
|
+
end # def build
|
38
|
+
|
39
|
+
# build the assets for the site
|
40
|
+
# @raise SiteNotLoaded if the site is not loaded
|
41
|
+
def build_assets
|
42
|
+
|
43
|
+
# make sure the site is laoded, if not, raise an error
|
44
|
+
raise SiteNotLoadedError.new("Could not build assets.") unless @loaded
|
45
|
+
|
46
|
+
# get the site asset and output assets directory
|
47
|
+
site_assets_dir = get_site_assets_directory()
|
48
|
+
outout_assets_dir = File.join(get_site_output_directory(), site_assets_dir.gsub(@site_path, ""))
|
49
|
+
|
50
|
+
Geb.log "Building assets for #{site_name}\n\n"
|
51
|
+
|
52
|
+
# step through all the asset files and copy them to the output directory
|
53
|
+
Dir.glob("#{site_assets_dir}/**/*").each do |asset_file|
|
54
|
+
|
55
|
+
# skip directories
|
56
|
+
next if File.directory?(asset_file)
|
57
|
+
|
58
|
+
# get the relative path of the asset file and the destination path
|
59
|
+
asset_relative_path = asset_file.gsub(site_assets_dir, "")
|
60
|
+
asset_full_destination_path = File.join(outout_assets_dir, asset_relative_path)
|
61
|
+
|
62
|
+
# check if the destination asset file exists
|
63
|
+
if File.exist?(asset_full_destination_path)
|
64
|
+
|
65
|
+
Geb.log " - skipping asset: #{asset_relative_path}"
|
66
|
+
|
67
|
+
else
|
68
|
+
|
69
|
+
Geb.log " - processing asset: #{asset_relative_path}"
|
70
|
+
|
71
|
+
# create the output directory for the asset file
|
72
|
+
output_dir = File.join(outout_assets_dir, File.dirname(asset_relative_path))
|
73
|
+
FileUtils.mkdir_p(output_dir)
|
74
|
+
|
75
|
+
# copy the asset file to the output directory
|
76
|
+
FileUtils.cp(asset_file, output_dir)
|
77
|
+
|
78
|
+
end # if else
|
79
|
+
|
80
|
+
end # Dir.glob
|
81
|
+
Geb.log "\nDone building assets for #{site_name}"
|
82
|
+
|
83
|
+
end # def build_assets
|
84
|
+
|
85
|
+
# build the pages for the site
|
86
|
+
# @raise SiteNotLoaded if the site is not loaded
|
87
|
+
def build_pages
|
88
|
+
|
89
|
+
# make sure the site is laoded, if not, raise an error
|
90
|
+
raise SiteNotLoadedError.new("Could not build pages.") unless @loaded
|
91
|
+
|
92
|
+
# expire page template and partial caches
|
93
|
+
Geb::Template.expire_cache
|
94
|
+
Geb::Partial.expire_cache
|
95
|
+
|
96
|
+
# find all HTML files in the site path that don't start with an underscore, with extention .html and .htm
|
97
|
+
page_files = get_page_files(@site_path,
|
98
|
+
@site_config.page_extensions(),
|
99
|
+
@site_config.template_and_partial_identifier(),
|
100
|
+
[get_site_output_directory(), get_site_release_directory()])
|
101
|
+
|
102
|
+
Geb.log "Building #{page_files.length} pages for #{site_name}"
|
103
|
+
|
104
|
+
# create a temporary directory
|
105
|
+
Dir.mktmpdir do |tmp_dir|
|
106
|
+
|
107
|
+
# iterate over the HTML files and build the pages
|
108
|
+
page_files.each do |page_file|
|
109
|
+
|
110
|
+
# create a new page object and buid the page into the temporary directory
|
111
|
+
page = Geb::Page.new(self, page_file)
|
112
|
+
page.build(tmp_dir)
|
113
|
+
|
114
|
+
end # html_files.each
|
115
|
+
Geb.log "\nDone building #{page_files.length} pages for #{site_name}"
|
116
|
+
|
117
|
+
# attempt to write the site to the output directory
|
118
|
+
begin
|
119
|
+
|
120
|
+
# clear the output directory
|
121
|
+
Geb.log_start "Clearing site output folder #{get_site_output_directory()} ... "
|
122
|
+
clear_site_output_directory()
|
123
|
+
Geb.log "done."
|
124
|
+
|
125
|
+
# copy the files to the output directory
|
126
|
+
Geb.log_start "Outputting site to #{get_site_output_directory()} ... "
|
127
|
+
output_site(tmp_dir)
|
128
|
+
Geb.log "done."
|
129
|
+
|
130
|
+
rescue => e
|
131
|
+
raise FailedToOutputSite.new(e.message)
|
132
|
+
end # begin rescue
|
133
|
+
|
134
|
+
end # Dir.mktmpdir
|
135
|
+
|
136
|
+
end # def build_pages
|
137
|
+
|
138
|
+
# get the site output directory
|
139
|
+
# @return [String] the site output directory
|
140
|
+
def get_site_output_directory
|
141
|
+
return File.join(@site_path, @site_config.output_dir, Geb::Defaults::LOCAL_OUTPUT_DIR)
|
142
|
+
end # def get_site_output_directory
|
143
|
+
|
144
|
+
# get the page files in the specified path, with specified extentions and ignoring files that match the pattern
|
145
|
+
# @param path [String] the path to the files
|
146
|
+
# @param exts [Array] the extentions to look for, default is Geb::Defaults.PAGE_EXTENSIONS
|
147
|
+
# @param ignore_files_exp [Regexp] the pattern to ignore files, default is Geb::Defaults.TEMPLATE_AND_PARTIAL_IDENTIFIER
|
148
|
+
# @param ignore_directories [Array] the directories to ignore, default is []
|
149
|
+
# @return [Array] the array of matched file paths
|
150
|
+
# @note the ignore_files_exp and ignore_directories are used to ignore files that are not pages
|
151
|
+
def get_page_files(path, exts = Geb::Defaults::PAGE_EXTENSIONS, ignore_files_exp = Geb::Defaults::TEMPLATE_AND_PARTIAL_IDENTIFIER, ignore_directories = [])
|
152
|
+
|
153
|
+
# get all files in the path with the specified extentions
|
154
|
+
files = Dir.glob("#{path}/**/*{#{exts.join(',')}}")
|
155
|
+
|
156
|
+
# reject files that match the ignore pattern and that are within the output or release directories
|
157
|
+
files.reject! do |file|
|
158
|
+
File.basename(file) =~ ignore_files_exp ||
|
159
|
+
ignore_directories.any? { |dir| file.start_with?(dir) }
|
160
|
+
end # files.reject!
|
161
|
+
|
162
|
+
# return the array of matched file paths
|
163
|
+
return files
|
164
|
+
|
165
|
+
end # def get_page_files
|
166
|
+
|
167
|
+
# clear the site output directory
|
168
|
+
# @return [Nil]
|
169
|
+
def clear_site_output_directory
|
170
|
+
FileUtils.rm_rf(Dir.glob("#{get_site_output_directory()}/*"))
|
171
|
+
end # def clear_site_output_directory
|
172
|
+
|
173
|
+
# output the site from specified directory to the output directory. The specified directory is typically
|
174
|
+
# a temporary directory where the site has been built.
|
175
|
+
# @param output_dir [String] the directory to output
|
176
|
+
# @return [Nil]
|
177
|
+
def output_site(output_dir)
|
178
|
+
FileUtils.cp_r("#{output_dir}/.", get_site_output_directory())
|
179
|
+
end # def output_site
|
180
|
+
|
181
|
+
# get the site assets directory
|
182
|
+
# @return [String] the site assets directory
|
183
|
+
def get_site_assets_directory
|
184
|
+
return File.join(@site_path, @site_config.assets_dir)
|
185
|
+
end # def get_site_assets_directory
|
186
|
+
|
187
|
+
end # module Build
|
188
|
+
end # class Site
|
189
|
+
end # module Geb
|
@@ -0,0 +1,229 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Site core functionality, loading, validation and creation
|
4
|
+
#
|
5
|
+
# @title Geb - Site - Core Module
|
6
|
+
# @author Edin Mustajbegovic <edin@actiontwelve.com>
|
7
|
+
# @copyright 2024 Edin Mustajbegovic
|
8
|
+
# @license MIT
|
9
|
+
#
|
10
|
+
# @see https://github.com/mainfram-work/geb for more information
|
11
|
+
|
12
|
+
module Geb
|
13
|
+
class Site
|
14
|
+
module Core
|
15
|
+
|
16
|
+
class DirectoryExistsError < Geb::Error
|
17
|
+
MESSAGE = "Site folder already exists, please choose a different name or location.\nIf you want to use the existing site folder, use the --force option.".freeze
|
18
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
19
|
+
end # class DirectoryExistsError < Geb::Error
|
20
|
+
|
21
|
+
class SiteAlreadyValidated < Geb::Error
|
22
|
+
MESSAGE = "Proposed site and template have not been validated. This is an internal error".freeze
|
23
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
24
|
+
end # class SiteAlreadyValidated < Geb::Error
|
25
|
+
|
26
|
+
class InvalidTemplate < Geb::Error
|
27
|
+
MESSAGE = "Invalid template site. Make sure the specified path is a directory and contains a valid geb.config.yml file.".freeze
|
28
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
29
|
+
end # class InvalidTemplate < Geb::Error
|
30
|
+
|
31
|
+
class UnvalidatedSiteAndTemplate < Geb::Error
|
32
|
+
MESSAGE = "You are trying to create an unvalidated site. This is an internal error".freeze
|
33
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
34
|
+
end # class UnvalidatedSiteAndTemplate < Geb::Error
|
35
|
+
|
36
|
+
class SiteNotFoundError < Geb::Error
|
37
|
+
MESSAGE = "Could not find geb config file.".freeze
|
38
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
39
|
+
end # class SiteNotFoundError < Geb::Error
|
40
|
+
|
41
|
+
# validate the site path and template path, and set the validated flag
|
42
|
+
# it executes the following validations:
|
43
|
+
# - make sure the site is not already validated
|
44
|
+
# - make sure the site path is valid, consider force option
|
45
|
+
# - if template path is nil, use the default template
|
46
|
+
# - if template path is a URL, validate the URL and download the template
|
47
|
+
# - if template path is a directory, check if it has a geb.config.yml file
|
48
|
+
# @param site_path [String] the path to the site folder
|
49
|
+
# @param template_path [String] the path to the site template, default is nil, can be a URL, directory or a bundled template identifier
|
50
|
+
# @param skip_template [Boolean] skip the template validation, default is false
|
51
|
+
# @param force [Boolean] force the site creation, default is false
|
52
|
+
# @raise SiteAlreadyValidated if the site has already been validated
|
53
|
+
# @raise DirectoryExistsError if the site folder already exists and force option is not set
|
54
|
+
# @raise InvalidTemplate if the template path is invalid
|
55
|
+
# @raise InvalidTemplateURL if the template URL is invalid
|
56
|
+
# @return [Nil]
|
57
|
+
def validate(site_path, template_path = nil, skip_template = false, force = false)
|
58
|
+
|
59
|
+
# raise an error if the site has already been validated
|
60
|
+
raise SiteAlreadyValidated.new if @validated
|
61
|
+
|
62
|
+
Geb.log_start "Validating site path #{site_path} ... "
|
63
|
+
# raise error if site folder already exists and force option is not set
|
64
|
+
raise DirectoryExistsError.new if site_directory_exists?(site_path) && !force
|
65
|
+
@site_path = site_path
|
66
|
+
Geb.log('done.')
|
67
|
+
|
68
|
+
Geb.log("Skipping template validation as told.") if skip_template
|
69
|
+
|
70
|
+
# check if we are skipping the template
|
71
|
+
unless skip_template
|
72
|
+
|
73
|
+
# initialize the template directory path.
|
74
|
+
template_dir = nil
|
75
|
+
|
76
|
+
# if the template path is nil, use the first bundled template name
|
77
|
+
if template_path.nil? || template_path.empty?
|
78
|
+
|
79
|
+
Geb.log "No template specified, using default: #{Geb::Defaults::DEFAULT_TEMPLATE}."
|
80
|
+
template_dir = Geb::Defaults::DEFAULT_TEMPLATE_DIR
|
81
|
+
|
82
|
+
end # if
|
83
|
+
|
84
|
+
# check if the template path is a URL
|
85
|
+
if is_url?(template_path) && template_dir.nil?
|
86
|
+
|
87
|
+
# check if the template URL is valid and download it if it is
|
88
|
+
valid_template_url = validate_template_url(template_path)
|
89
|
+
template_dir = download_template_from_url(valid_template_url)
|
90
|
+
|
91
|
+
end # if
|
92
|
+
|
93
|
+
# check if the template path is a bundled template
|
94
|
+
if template_dir.nil? && is_bundled_template?(template_path)
|
95
|
+
|
96
|
+
template_dir = File.join(Geb::Defaults::BUNDLED_TEMPLATES_DIR, template_path)
|
97
|
+
Geb.log "Specified template is a Geb sample: #{template_path}, using it as site template."
|
98
|
+
|
99
|
+
end # if
|
100
|
+
|
101
|
+
# set the template dir to specified template path if template dir is still nil
|
102
|
+
template_dir = template_path if template_dir.nil? # this is the case when the template is a local directory
|
103
|
+
|
104
|
+
# check if the template path is a directory and ontains a geb.config.yml file
|
105
|
+
Geb.log_start "Validating template path #{template_dir.to_s} ... "
|
106
|
+
raise InvalidTemplate.new if template_dir.nil?
|
107
|
+
raise InvalidTemplate.new unless template_directory_exists?(template_dir)
|
108
|
+
raise InvalidTemplate.new unless Geb::Config.site_directory_has_config?(template_dir)
|
109
|
+
Geb.log "done."
|
110
|
+
|
111
|
+
# set the template path
|
112
|
+
@template_path = template_dir
|
113
|
+
|
114
|
+
end # unless skip_template
|
115
|
+
|
116
|
+
# set the validated flag
|
117
|
+
@validated = true
|
118
|
+
|
119
|
+
end # def validate
|
120
|
+
|
121
|
+
# create the site. It assumes and checks that the site has been validated first.
|
122
|
+
# the reason we don't just call validate from here is that we want to separate
|
123
|
+
# the validation from the creation for CLI UI purposes.
|
124
|
+
# performs the following steps
|
125
|
+
# - raise an error if the site has not been validated
|
126
|
+
# - create the site folder, if it exists, just skip it
|
127
|
+
# - copy the template files to the site folder if the template path is set
|
128
|
+
# - create the output folders
|
129
|
+
# @raise UnvalidatedSiteAndTemplate if the site has not been validated
|
130
|
+
# @return [Nil]
|
131
|
+
def create
|
132
|
+
|
133
|
+
# raise an error if the site has not been validated
|
134
|
+
raise UnvalidatedSiteAndTemplate.new unless @validated
|
135
|
+
|
136
|
+
# check if the folder already exists, if we are here and it does, just skip it, validation would have considered a force option
|
137
|
+
Geb.log_start "Creating site folder: #{@site_path} ... "
|
138
|
+
if site_directory_exists?(@site_path)
|
139
|
+
Geb.log "skipped, folder already exists."
|
140
|
+
else
|
141
|
+
Dir.mkdir(@site_path)
|
142
|
+
Geb.log "done."
|
143
|
+
end # if
|
144
|
+
|
145
|
+
Geb.log("Skipping template creation as told.") if @template_path.nil?
|
146
|
+
|
147
|
+
# check if we are skipping the template, if not copy the template files
|
148
|
+
copy_template_from_path unless @template_path.nil?
|
149
|
+
|
150
|
+
# check if the site has a geb config file, if not, copy the default one
|
151
|
+
if Geb::Config.site_directory_has_config?(@site_path)
|
152
|
+
Geb.log "Config file already exists, no need to create it."
|
153
|
+
else
|
154
|
+
Geb.log_start "Creating default geb config file ... "
|
155
|
+
FileUtils.cp(File.join(Geb::Defaults::BUNDLED_TEMPLATES_DIR, Geb::Defaults::SITE_CONFIG_FILENAME), @site_path)
|
156
|
+
Geb.log "done."
|
157
|
+
end # if else
|
158
|
+
|
159
|
+
# load the site config
|
160
|
+
@site_config = Geb::Config.new(self)
|
161
|
+
|
162
|
+
# create the assets folder if it does not exist
|
163
|
+
if File.directory?(File.join(@site_path, @site_config.assets_dir))
|
164
|
+
Geb.log "Assets folder already exists, no need to create it."
|
165
|
+
else
|
166
|
+
Geb.log_start "Creating assets folder since it wasn't created by the template ... "
|
167
|
+
FileUtils.mkdir_p(File.join(@site_path, @site_config.assets_dir))
|
168
|
+
Geb.log "done."
|
169
|
+
end # if else
|
170
|
+
|
171
|
+
# create the output folders
|
172
|
+
Geb.log_start "Creating: local and release output folders ..."
|
173
|
+
FileUtils.mkdir_p(File.join(@site_path, @site_config.output_dir, Geb::Defaults::LOCAL_OUTPUT_DIR))
|
174
|
+
FileUtils.mkdir_p(File.join(@site_path, @site_config.output_dir, Geb::Defaults::RELEASE_OUTPUT_DIR))
|
175
|
+
Geb.log "done."
|
176
|
+
|
177
|
+
end # def create
|
178
|
+
|
179
|
+
# load a site from a site path
|
180
|
+
# it checks if the site path has a geb config file, if not, it goes up the chain to find it
|
181
|
+
# @param site_path [String] the path to the site folder
|
182
|
+
# @raise SiteNotFoundError if the site path is not found
|
183
|
+
# @return [Nil]
|
184
|
+
def load(site_path)
|
185
|
+
|
186
|
+
Geb.log_start "Loading site from path #{site_path} ... "
|
187
|
+
|
188
|
+
# set the site path candidate
|
189
|
+
site_path_candidate = site_path
|
190
|
+
|
191
|
+
# check if the site has a geb config file, if not go up the chain to find it
|
192
|
+
until site_path_candidate == '/'
|
193
|
+
|
194
|
+
# check if the site path has a geb config file
|
195
|
+
if Geb::Config.site_directory_has_config?(site_path_candidate)
|
196
|
+
|
197
|
+
# set the site path
|
198
|
+
@site_path = site_path_candidate
|
199
|
+
|
200
|
+
# load the site configuration
|
201
|
+
@site_config = Geb::Config.new(self)
|
202
|
+
|
203
|
+
# set the loaded flag and break the loop
|
204
|
+
@loaded = true
|
205
|
+
break
|
206
|
+
|
207
|
+
end # if
|
208
|
+
|
209
|
+
# go up the chain
|
210
|
+
site_path_candidate = File.expand_path('..', site_path_candidate)
|
211
|
+
|
212
|
+
end # until
|
213
|
+
|
214
|
+
# raise an error if the site path is not found
|
215
|
+
raise SiteNotFoundError.new("#{site_path} is not and is not in a geb site.") unless @loaded
|
216
|
+
|
217
|
+
Geb.log "done."
|
218
|
+
Geb.log "Found geb site at path #{@site_path} as #{site_name}."
|
219
|
+
|
220
|
+
end # def load
|
221
|
+
|
222
|
+
# check if the site directory exists
|
223
|
+
def site_directory_exists?(site_path)
|
224
|
+
File.directory?(site_path)
|
225
|
+
end # def site_directory_exists?
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Site release functionality, releasing the site, packaging site templates and assets
|
4
|
+
#
|
5
|
+
# @title Geb - Site - Release Module
|
6
|
+
# @author Edin Mustajbegovic <edin@actiontwelve.com>
|
7
|
+
# @copyright 2024 Edin Mustajbegovic
|
8
|
+
# @license MIT
|
9
|
+
#
|
10
|
+
# @see https://github.com/mainfram-work/geb for more information
|
11
|
+
|
12
|
+
module Geb
|
13
|
+
class Site
|
14
|
+
module Release
|
15
|
+
|
16
|
+
# release the site
|
17
|
+
def release
|
18
|
+
|
19
|
+
# build the site first
|
20
|
+
build();
|
21
|
+
|
22
|
+
# get the site local and release directory
|
23
|
+
site_release_directory = get_site_release_directory()
|
24
|
+
|
25
|
+
# clear the output directory
|
26
|
+
Geb.log_start "Clearing site release folder #{site_release_directory} ... "
|
27
|
+
clear_site_release_directory()
|
28
|
+
Geb.log "done."
|
29
|
+
|
30
|
+
# copy the files to the output directory
|
31
|
+
Geb.log_start "Releasing site to #{site_release_directory} ... "
|
32
|
+
copy_site_to_release_directory()
|
33
|
+
Geb.log "done."
|
34
|
+
|
35
|
+
end # def release
|
36
|
+
|
37
|
+
# get the template archive path
|
38
|
+
# @return [String] the template archive path within the release directory
|
39
|
+
def get_template_archive_release_path
|
40
|
+
return File.join(@site_path, @site_config.output_dir, Geb::Defaults::RELEASE_OUTPUT_DIR, Geb::Defaults::TEMPLATE_ARCHIVE_FILENAME)
|
41
|
+
end # def get_template_archive_release_path
|
42
|
+
|
43
|
+
# get the site release directory
|
44
|
+
# @return [String] the site release directory
|
45
|
+
def get_site_release_directory
|
46
|
+
return File.join(@site_path, @site_config.output_dir, Geb::Defaults::RELEASE_OUTPUT_DIR)
|
47
|
+
end # def get_site_release_directory
|
48
|
+
|
49
|
+
# clear the site release directory
|
50
|
+
# @return [Nil]
|
51
|
+
def clear_site_release_directory
|
52
|
+
FileUtils.rm_rf(Dir.glob("#{get_site_release_directory()}/*"))
|
53
|
+
end # def clear_site_release_directory
|
54
|
+
|
55
|
+
# output the site from local output to release directory.
|
56
|
+
# @return [Nil]
|
57
|
+
def copy_site_to_release_directory
|
58
|
+
FileUtils.cp_r("#{get_site_output_directory()}/.", get_site_release_directory())
|
59
|
+
end # def output_site
|
60
|
+
|
61
|
+
# check if the site has been released.
|
62
|
+
# The site is considered released if the release directory exists and is not empty.
|
63
|
+
# @return [Boolean] true if the site has been released, false otherwise
|
64
|
+
def released?
|
65
|
+
return Dir.exist?(get_site_release_directory()) && !Dir.empty?(get_site_release_directory())
|
66
|
+
end # def released?
|
67
|
+
|
68
|
+
end # module Build
|
69
|
+
end # class Site
|
70
|
+
end # module Geb
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Site remote functionality, things like ssh, scp, rsync, etc.
|
4
|
+
#
|
5
|
+
# @title Geb - Site - Remote
|
6
|
+
# @author Edin Mustajbegovic <edin@actiontwelve.com>
|
7
|
+
# @copyright 2024 Edin Mustajbegovic
|
8
|
+
# @license MIT
|
9
|
+
#
|
10
|
+
# @see https://github.com/mainfram-work/geb for more information
|
11
|
+
|
12
|
+
module Geb
|
13
|
+
class Site
|
14
|
+
module Remote
|
15
|
+
|
16
|
+
class RemoteURINotConfigured < Geb::Error
|
17
|
+
MESSAGE = "Remote URI not configured in geb.config.yml".freeze
|
18
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
19
|
+
end # class RemoteURINotConfigured < Geb::Error
|
20
|
+
|
21
|
+
class RemotePathNotConfigured < Geb::Error
|
22
|
+
MESSAGE = "Remote Path is not configured in geb.config.yml".freeze
|
23
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
24
|
+
end # class RemotePathNotConfigured < Geb::Error
|
25
|
+
|
26
|
+
class SiteNotReleasedError < Geb::Error
|
27
|
+
MESSAGE = "Site not released. Please run 'geb release' first.".freeze
|
28
|
+
def initialize(e = ""); super(e, MESSAGE); end
|
29
|
+
end
|
30
|
+
|
31
|
+
# launch a remote session
|
32
|
+
# @return [Nil]
|
33
|
+
# @raise SiteNotLoadedError if the site is not loaded
|
34
|
+
# @raise RemoteURINotConfigured if the remote uri is not configured
|
35
|
+
def launch_remote
|
36
|
+
|
37
|
+
# raise an error if the site is not loaded
|
38
|
+
raise Geb::Site::SiteNotFoundError.new("Site not loaded") unless @loaded
|
39
|
+
|
40
|
+
# make sure the remote uri is configured
|
41
|
+
raise RemoteURINotConfigured.new unless @site_config.remote_uri
|
42
|
+
|
43
|
+
# Temporarily disable reporting exceptions in threads
|
44
|
+
original_thread_report_on_exception_setting = Thread.report_on_exception
|
45
|
+
Thread.report_on_exception = false
|
46
|
+
|
47
|
+
Geb.log "About to start an ssh session with the remote server #{@site_config.remote_uri}."
|
48
|
+
|
49
|
+
# attempt to launch a remote session
|
50
|
+
begin
|
51
|
+
Open3.capture3("ssh", @site_config.remote_uri)
|
52
|
+
rescue Interrupt, IOError
|
53
|
+
Geb.log "Remote session interrupted."
|
54
|
+
rescue
|
55
|
+
Geb.log "Remote session interrupted."
|
56
|
+
ensure
|
57
|
+
# restore the original thread report on exception setting
|
58
|
+
Thread.report_on_exception = original_thread_report_on_exception_setting
|
59
|
+
end # begin ... rescue
|
60
|
+
|
61
|
+
end # def launch_remote
|
62
|
+
|
63
|
+
# upload the site release to the remote server
|
64
|
+
# @return [Nil]
|
65
|
+
# @raise SiteNotReleasedError if the site has not been released
|
66
|
+
# @raise RemoteURINotConfigured if the remote uri is not configured
|
67
|
+
# @raise RemotePathNotConfigured if the remote path is not configured
|
68
|
+
def upload_release_to_remote()
|
69
|
+
|
70
|
+
# check if the release directory is empty
|
71
|
+
raise SiteNotReleasedError.new unless released?
|
72
|
+
|
73
|
+
# make sure the remote uri and remote path are configured
|
74
|
+
raise RemoteURINotConfigured.new unless @site_config.remote_uri
|
75
|
+
raise RemotePathNotConfigured.new unless @site_config.remote_path
|
76
|
+
|
77
|
+
# Temporarily disable reporting exceptions in threads
|
78
|
+
original_thread_report_on_exception_setting = Thread.report_on_exception
|
79
|
+
Thread.report_on_exception = false
|
80
|
+
|
81
|
+
|
82
|
+
Geb.log "About to upload the site release to remote server2."
|
83
|
+
|
84
|
+
# attempt to upload the release to the remote server
|
85
|
+
begin
|
86
|
+
|
87
|
+
# build up the command arguments
|
88
|
+
command_exclude_pattern = '*.DS_Store'
|
89
|
+
command_source_directory = File.join(get_site_release_directory, '/')
|
90
|
+
command_remote_uri = @site_config.remote_uri + ":" + @site_config.remote_path
|
91
|
+
|
92
|
+
# build the rsync command
|
93
|
+
rsync_command = [
|
94
|
+
'rsync',
|
95
|
+
'-av',
|
96
|
+
'-e', 'ssh',
|
97
|
+
"--exclude=#{command_exclude_pattern}",
|
98
|
+
command_source_directory,
|
99
|
+
command_remote_uri
|
100
|
+
]
|
101
|
+
|
102
|
+
Geb.log " - upload command: #{rsync_command.join(' ')}"
|
103
|
+
|
104
|
+
# execute the rsync command
|
105
|
+
Open3.popen3(*rsync_command) do |stdin, stdout, stderr, wait_thr|
|
106
|
+
|
107
|
+
# create threads to read the stdout
|
108
|
+
stdout_thread = Thread.new do
|
109
|
+
stdout.each_line { |line| Geb.log " - #{line}" }
|
110
|
+
end
|
111
|
+
|
112
|
+
# create threads to read the stderr
|
113
|
+
stderr_thread = Thread.new do
|
114
|
+
stderr.each_line { |line| Geb.log " - (error) #{line}" }
|
115
|
+
end
|
116
|
+
|
117
|
+
# wait for the threads to finish
|
118
|
+
stdout_thread.join
|
119
|
+
stderr_thread.join
|
120
|
+
|
121
|
+
# get the status of the command
|
122
|
+
status = wait_thr.value
|
123
|
+
|
124
|
+
# log the status of the command
|
125
|
+
{ stdout: stdout, stderr: stderr, status: status }
|
126
|
+
|
127
|
+
end # Open3.popen3
|
128
|
+
|
129
|
+
rescue Interrupt, IOError
|
130
|
+
Geb.log "Upload interrupted."
|
131
|
+
rescue
|
132
|
+
Geb.log "Upload interrupted."
|
133
|
+
ensure
|
134
|
+
# restore the original thread report on exception setting
|
135
|
+
Thread.report_on_exception = original_thread_report_on_exception_setting
|
136
|
+
end # begin ... rescue
|
137
|
+
|
138
|
+
end # def upload_release_to_remote
|
139
|
+
|
140
|
+
end # module Remote
|
141
|
+
end # class Site
|
142
|
+
end # module Geb
|