repo_manager 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemfiles +115 -0
- data/.gitattributes +1 -0
- data/.gitignore +7 -0
- data/.rspec +3 -0
- data/.yardopts +11 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +99 -0
- data/Guardfile +63 -0
- data/HISTORY.markdown +12 -0
- data/LICENSE +20 -0
- data/README.markdown +192 -0
- data/Rakefile +94 -0
- data/TODO.markdown +15 -0
- data/VERSION +1 -0
- data/bin/repo +151 -0
- data/cucumber.yml +28 -0
- data/examples/pc_saved_game_backup/.gitignore +2 -0
- data/examples/pc_saved_game_backup/INSTALL.markdown +420 -0
- data/examples/pc_saved_game_backup/README.markdown +108 -0
- data/examples/pc_saved_game_backup/remote/.gitignore +2 -0
- data/examples/pc_saved_game_backup/repo_manager/Gemfile +12 -0
- data/examples/pc_saved_game_backup/repo_manager/Gemfile.lock +66 -0
- data/examples/pc_saved_game_backup/repo_manager/assets/.gitignore +2 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/aruba.rb +15 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/env.rb +11 -0
- data/examples/pc_saved_game_backup/repo_manager/features/support/steps.rb +3 -0
- data/examples/pc_saved_game_backup/repo_manager/features/tasks/update.feature +144 -0
- data/examples/pc_saved_game_backup/repo_manager/global/default/asset.conf +2 -0
- data/examples/pc_saved_game_backup/repo_manager/repo.conf +64 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/.gitignore +0 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/remote.rb +57 -0
- data/examples/pc_saved_game_backup/repo_manager/tasks/update.rb +65 -0
- data/examples/pc_saved_game_backup/saved_games/hearts/save1 +1 -0
- data/examples/pc_saved_game_backup/saved_games/hearts/save2 +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/my_profile.ini +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/saves/save1 +1 -0
- data/examples/pc_saved_game_backup/saved_games/mines/saves/save2 +1 -0
- data/features/actions/git.feature +296 -0
- data/features/actions/help.feature +53 -0
- data/features/actions/list.feature +624 -0
- data/features/actions/path.feature +195 -0
- data/features/actions/status.feature +261 -0
- data/features/actions/task.feature +127 -0
- data/features/assets/configuration.feature +204 -0
- data/features/assets/rendering.feature +42 -0
- data/features/assets/user_attributes.feature +98 -0
- data/features/bin.feature +42 -0
- data/features/logger.feature +218 -0
- data/features/settings.feature +240 -0
- data/features/support/aruba.rb +15 -0
- data/features/support/env.rb +11 -0
- data/features/support/steps.rb +3 -0
- data/features/tasks/add/asset.feature +178 -0
- data/features/tasks/generate/init.feature +56 -0
- data/lib/repo_manager.rb +36 -0
- data/lib/repo_manager/actions.rb +8 -0
- data/lib/repo_manager/actions/action_helper.rb +39 -0
- data/lib/repo_manager/actions/app_action.rb +30 -0
- data/lib/repo_manager/actions/base_action.rb +296 -0
- data/lib/repo_manager/actions/git_action.rb +113 -0
- data/lib/repo_manager/actions/help_action.rb +52 -0
- data/lib/repo_manager/actions/list_action.rb +123 -0
- data/lib/repo_manager/actions/path_action.rb +22 -0
- data/lib/repo_manager/actions/status_action.rb +192 -0
- data/lib/repo_manager/actions/task_action.rb +71 -0
- data/lib/repo_manager/app.rb +116 -0
- data/lib/repo_manager/assets.rb +3 -0
- data/lib/repo_manager/assets/app_asset.rb +15 -0
- data/lib/repo_manager/assets/asset_accessors.rb +67 -0
- data/lib/repo_manager/assets/asset_configuration.rb +137 -0
- data/lib/repo_manager/assets/asset_manager.rb +72 -0
- data/lib/repo_manager/assets/base_asset.rb +199 -0
- data/lib/repo_manager/assets/repo_asset.rb +30 -0
- data/lib/repo_manager/core.rb +2 -0
- data/lib/repo_manager/core/array.rb +21 -0
- data/lib/repo_manager/core/hash.rb +83 -0
- data/lib/repo_manager/errors.rb +10 -0
- data/lib/repo_manager/extensions/hash.rb +86 -0
- data/lib/repo_manager/git.rb +2 -0
- data/lib/repo_manager/git/lib.rb +69 -0
- data/lib/repo_manager/git/status.rb +196 -0
- data/lib/repo_manager/logger.rb +39 -0
- data/lib/repo_manager/settings.rb +98 -0
- data/lib/repo_manager/tasks.rb +3 -0
- data/lib/repo_manager/tasks/add/asset.rb +213 -0
- data/lib/repo_manager/tasks/generate/init.rb +42 -0
- data/lib/repo_manager/tasks/generate/templates/config/repo.conf.tt +61 -0
- data/lib/repo_manager/tasks/generate/templates/init/assets/.gitignore +0 -0
- data/lib/repo_manager/tasks/generate/templates/init/global/default/asset.conf +2 -0
- data/lib/repo_manager/tasks/generate/templates/init/tasks/.gitignore +0 -0
- data/lib/repo_manager/tasks/task_manager.rb +166 -0
- data/lib/repo_manager/tasks/thor_helper.rb +29 -0
- data/lib/repo_manager/test/asset_steps.rb +19 -0
- data/lib/repo_manager/test/base_steps.rb +152 -0
- data/lib/repo_manager/test/repo_api.rb +41 -0
- data/lib/repo_manager/test/repo_steps.rb +83 -0
- data/lib/repo_manager/test/test_api.rb +88 -0
- data/lib/repo_manager/views.rb +2 -0
- data/lib/repo_manager/views/app_view.rb +15 -0
- data/lib/repo_manager/views/base_view.rb +137 -0
- data/lib/repo_manager/views/templates/css/basic.css +26 -0
- data/lib/repo_manager/views/templates/default.erb +40 -0
- data/lib/repo_manager/views/templates/default.slim +37 -0
- data/lib/repo_manager/views/view_helper.rb +55 -0
- data/repo_manager.gemspec +75 -0
- data/spec/basic_app/actions/action_helper_spec.rb +54 -0
- data/spec/basic_app/assets/base_asset_spec.rb +210 -0
- data/spec/basic_app/core_spec.rb +78 -0
- data/spec/basic_app/settings_spec.rb +64 -0
- data/spec/basic_app/views/view_helper_spec.rb +28 -0
- data/spec/basic_gem/aruba_helper_spec.rb +33 -0
- data/spec/basic_gem/basic_gem_spec.rb +84 -0
- data/spec/basic_gem/gemspec_spec.rb +68 -0
- data/spec/repo_manager/git_spec.rb +31 -0
- data/spec/spec_helper.rb +25 -0
- metadata +472 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'aruba/api'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
APP_BIN_PATH = File.join(FileUtils.pwd, 'bin', 'repo')
|
|
5
|
+
|
|
6
|
+
module Aruba
|
|
7
|
+
module Api
|
|
8
|
+
|
|
9
|
+
# override aruba avoid 'current_ruby' call and make sure
|
|
10
|
+
# that binary run on Win32 without the binstubs
|
|
11
|
+
def detect_ruby(cmd)
|
|
12
|
+
cmd = cmd.gsub(/^repo/, "ruby -S #{APP_BIN_PATH}")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
@announce
|
|
2
|
+
Feature: Task to generate asset configurations
|
|
3
|
+
|
|
4
|
+
Generate config files automatically by searching each top level folder
|
|
5
|
+
contained in the given command line FOLDER. If a '.git' folder exists,
|
|
6
|
+
then that top level folder will be used to generate a new config file.
|
|
7
|
+
|
|
8
|
+
Help:
|
|
9
|
+
|
|
10
|
+
repo help add:asset
|
|
11
|
+
|
|
12
|
+
repo help add:assets
|
|
13
|
+
|
|
14
|
+
USAGE :
|
|
15
|
+
|
|
16
|
+
repo add:asset WORKING_FOLDER
|
|
17
|
+
|
|
18
|
+
repo add:assets TOP_LEVEL_FOLDER
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
-r, [--refresh] # Refresh existing blank attributes
|
|
22
|
+
-f, [--filter=one two three] # List of regex folder name filters
|
|
23
|
+
|
|
24
|
+
Runtime options:
|
|
25
|
+
-s, [--skip] # Skip files that already exist
|
|
26
|
+
-q, [--quiet] # Suppress status output
|
|
27
|
+
-p, [--pretend] # Run but do not make any changes
|
|
28
|
+
-f, [--force] # Overwrite files that already exist
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
|
|
32
|
+
cond add:assets c:/users/robert/documents/
|
|
33
|
+
cond add:assets ~/workspace/delphi
|
|
34
|
+
cond add:assets ~/workspace --filter guard-*,repo_manager-*
|
|
35
|
+
|
|
36
|
+
General Notes:
|
|
37
|
+
|
|
38
|
+
* task is not recursive and only looks for .git folder in direct children of the top level folder
|
|
39
|
+
* task will skip existing asset names and existing asset paths unless using the '--refresh' switch
|
|
40
|
+
* add '.condenser' file to application path to have Condenser always skip this application
|
|
41
|
+
|
|
42
|
+
Example output (~/repo_manager/assets/asset.conf):
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
path: some/path/my_repo_name
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Background: Test repositories and a valid config file
|
|
49
|
+
Given a repo in folder "workspace/repo1_path" with the following:
|
|
50
|
+
| filename | status | content |
|
|
51
|
+
| .gitignore | C | |
|
|
52
|
+
And a repo in folder "workspace/repo2_path" with the following:
|
|
53
|
+
| filename | status | content |
|
|
54
|
+
| .gitignore | C | |
|
|
55
|
+
And a directory named "workspace/not_a_repo"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Scenario: Point at a top level folder that contains two repos and on non repo folder
|
|
59
|
+
Given a file named "repo.conf" with:
|
|
60
|
+
"""
|
|
61
|
+
---
|
|
62
|
+
folders:
|
|
63
|
+
assets : assets
|
|
64
|
+
"""
|
|
65
|
+
And a directory named "assets"
|
|
66
|
+
When I run `repo add:assets workspace` interactively
|
|
67
|
+
When I type "y"
|
|
68
|
+
Then the exit status should be 0
|
|
69
|
+
And the output should contain:
|
|
70
|
+
"""
|
|
71
|
+
Found 2 asset(s)
|
|
72
|
+
"""
|
|
73
|
+
And the file "assets/repo1_path/asset.conf" should match:
|
|
74
|
+
"""
|
|
75
|
+
path: .*/workspace/repo1_path
|
|
76
|
+
"""
|
|
77
|
+
And the file "assets/repo2_path/asset.conf" should match:
|
|
78
|
+
"""
|
|
79
|
+
path: .*/workspace/repo2_path
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
Scenario: Point at a single working folder
|
|
83
|
+
Given a file named "repo.conf" with:
|
|
84
|
+
"""
|
|
85
|
+
---
|
|
86
|
+
folders:
|
|
87
|
+
assets : assets
|
|
88
|
+
"""
|
|
89
|
+
And a directory named "assets"
|
|
90
|
+
When I run `repo add:asset workspace/repo1_path` interactively
|
|
91
|
+
When I type "y"
|
|
92
|
+
Then the exit status should be 0
|
|
93
|
+
And the output should contain:
|
|
94
|
+
"""
|
|
95
|
+
Found 1 asset(s)
|
|
96
|
+
"""
|
|
97
|
+
And the file "assets/repo1_path/asset.conf" should match:
|
|
98
|
+
"""
|
|
99
|
+
path: .*/workspace/repo1_path
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
Scenario: Point at a single working folder relative to repo.conf
|
|
103
|
+
Given a file named "repo_manager/repo.conf" with:
|
|
104
|
+
"""
|
|
105
|
+
---
|
|
106
|
+
folders:
|
|
107
|
+
assets : assets
|
|
108
|
+
"""
|
|
109
|
+
And a directory named "repo_manager/assets"
|
|
110
|
+
When I run `repo add:asset workspace/repo1_path` interactively
|
|
111
|
+
When I type "y"
|
|
112
|
+
Then the exit status should be 0
|
|
113
|
+
And the output should contain:
|
|
114
|
+
"""
|
|
115
|
+
Found 1 asset(s)
|
|
116
|
+
"""
|
|
117
|
+
And the file "repo_manager/assets/repo1_path/asset.conf" should match:
|
|
118
|
+
"""
|
|
119
|
+
path: .*/workspace/repo1_path
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
Scenario: Point at an invalid working folder
|
|
123
|
+
Given a file named "repo.conf" with:
|
|
124
|
+
"""
|
|
125
|
+
---
|
|
126
|
+
folders:
|
|
127
|
+
assets : assets
|
|
128
|
+
"""
|
|
129
|
+
And a directory named "assets"
|
|
130
|
+
When I run `repo add:asset workspace/not_a_repo` interactively
|
|
131
|
+
Then the exit status should be 1
|
|
132
|
+
And the output should not contain:
|
|
133
|
+
"""
|
|
134
|
+
Found 1 asset(s)
|
|
135
|
+
"""
|
|
136
|
+
And the output should contain:
|
|
137
|
+
"""
|
|
138
|
+
unable to find '.git' folder
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
Scenario: Point at a single working folder and give it a non-default name
|
|
142
|
+
Given a file named "repo.conf" with:
|
|
143
|
+
"""
|
|
144
|
+
---
|
|
145
|
+
folders:
|
|
146
|
+
assets : assets
|
|
147
|
+
"""
|
|
148
|
+
And a directory named "assets"
|
|
149
|
+
When I run `repo add:asset workspace/repo1_path --name=repo1` interactively
|
|
150
|
+
When I type "y"
|
|
151
|
+
Then the exit status should be 0
|
|
152
|
+
And the output should contain:
|
|
153
|
+
"""
|
|
154
|
+
Found 1 asset(s)
|
|
155
|
+
"""
|
|
156
|
+
And the file "assets/repo1/asset.conf" should match:
|
|
157
|
+
"""
|
|
158
|
+
path: .*/workspace/repo1_path
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
Scenario: Attempting to add an asset that exists under a different name
|
|
162
|
+
Given a file named "repo.conf" with:
|
|
163
|
+
"""
|
|
164
|
+
---
|
|
165
|
+
folders:
|
|
166
|
+
assets : assets
|
|
167
|
+
"""
|
|
168
|
+
And a directory named "assets"
|
|
169
|
+
And the folder "assets" with the following asset configurations:
|
|
170
|
+
| name | path |
|
|
171
|
+
| repo1_path | workspace/repo1_path |
|
|
172
|
+
When I run `repo add:asset workspace/repo1_path --name repo1` interactively
|
|
173
|
+
When I type "y"
|
|
174
|
+
Then the exit status should be 1
|
|
175
|
+
And its output should contain:
|
|
176
|
+
"""
|
|
177
|
+
asset already exists under a different name
|
|
178
|
+
"""
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
@announce
|
|
2
|
+
Feature: Generate init task
|
|
3
|
+
|
|
4
|
+
End-user generation of a repo_manager configuration
|
|
5
|
+
|
|
6
|
+
Example commands:
|
|
7
|
+
|
|
8
|
+
repo task generate:init .repo_manager
|
|
9
|
+
repo task generate:init .
|
|
10
|
+
repo task generate:init repo_manager --force --verbose
|
|
11
|
+
|
|
12
|
+
Scenario: Specify path on the command line
|
|
13
|
+
When I run `repo task generate:init nodefault --no-config --verbose`
|
|
14
|
+
Then the exit status should be 0
|
|
15
|
+
Then the output should contain:
|
|
16
|
+
"""
|
|
17
|
+
creating initial file structure
|
|
18
|
+
"""
|
|
19
|
+
And the following files should exist:
|
|
20
|
+
| nodefault/assets/.gitignore |
|
|
21
|
+
| nodefault/global/default/asset.conf |
|
|
22
|
+
| nodefault/tasks/.gitignore |
|
|
23
|
+
|
|
24
|
+
Scenario: Path not specified
|
|
25
|
+
When I run `repo task generate:init --no-config`
|
|
26
|
+
Then the exit status should be 1
|
|
27
|
+
Then the output should contain:
|
|
28
|
+
"""
|
|
29
|
+
repo init requires at least 1 argument
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
Scenario: Config file not found
|
|
33
|
+
When I run `repo task generate:init . --config=BadConfig.conf`
|
|
34
|
+
Then the exit status should be 1
|
|
35
|
+
Then the output should contain:
|
|
36
|
+
"""
|
|
37
|
+
config file not found
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
Scenario: A file exists at destination and is not overwritten when answering 'No'
|
|
41
|
+
Given a file named ".gitignore" with:
|
|
42
|
+
"""
|
|
43
|
+
.my.file.3234134
|
|
44
|
+
"""
|
|
45
|
+
When I run `repo task generate:init . --no-config` interactively
|
|
46
|
+
When I type "n"
|
|
47
|
+
Then the exit status should be 0
|
|
48
|
+
And the following files should exist:
|
|
49
|
+
| .gitignore |
|
|
50
|
+
And the file ".gitignore" should contain:
|
|
51
|
+
"""
|
|
52
|
+
.my.file.3234134
|
|
53
|
+
"""
|
|
54
|
+
And the following files should exist:
|
|
55
|
+
| assets/.gitignore |
|
|
56
|
+
|
data/lib/repo_manager.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# require all files here
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
require 'repo_manager/core'
|
|
4
|
+
require 'repo_manager/errors'
|
|
5
|
+
require 'repo_manager/assets'
|
|
6
|
+
require 'repo_manager/views'
|
|
7
|
+
require 'repo_manager/actions'
|
|
8
|
+
require 'repo_manager/git'
|
|
9
|
+
require 'repo_manager/app'
|
|
10
|
+
require 'repo_manager/settings'
|
|
11
|
+
require 'repo_manager/logger'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Master namespace
|
|
15
|
+
module RepoManager
|
|
16
|
+
|
|
17
|
+
# Contents of the VERSION file
|
|
18
|
+
#
|
|
19
|
+
# Example format: 0.0.1
|
|
20
|
+
#
|
|
21
|
+
# @return [String] the contents of the version file in #.#.# format
|
|
22
|
+
def self.version
|
|
23
|
+
version_info_file = File.join(File.dirname(__FILE__), *%w[.. VERSION])
|
|
24
|
+
File.open(version_info_file, "r") do |f|
|
|
25
|
+
f.read.strip
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Platform constants
|
|
30
|
+
unless defined?(RepoManager::WINDOWS)
|
|
31
|
+
WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/i
|
|
32
|
+
CYGWIN = RbConfig::CONFIG['host_os'] =~ /cygwin/i
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
require 'repo_manager/actions/base_action'
|
|
2
|
+
require 'repo_manager/actions/app_action'
|
|
3
|
+
require 'repo_manager/actions/help_action'
|
|
4
|
+
require 'repo_manager/actions/list_action'
|
|
5
|
+
require 'repo_manager/actions/task_action'
|
|
6
|
+
require 'repo_manager/actions/path_action'
|
|
7
|
+
require 'repo_manager/actions/git_action'
|
|
8
|
+
require 'repo_manager/actions/status_action'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module RepoManager
|
|
6
|
+
module ActionHelper
|
|
7
|
+
|
|
8
|
+
def shell_quote(string)
|
|
9
|
+
return "" if string.nil? or string.empty?
|
|
10
|
+
if windows?
|
|
11
|
+
%{"#{string}"}
|
|
12
|
+
else
|
|
13
|
+
string.split("'").map{|m| "'#{m}'" }.join("\\'")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def windows?
|
|
18
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/i
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return[String] the relative path from the CWD
|
|
22
|
+
def relative_path(path)
|
|
23
|
+
return unless path
|
|
24
|
+
|
|
25
|
+
path = Pathname.new(File.expand_path(path, FileUtils.pwd))
|
|
26
|
+
cwd = Pathname.new(FileUtils.pwd)
|
|
27
|
+
|
|
28
|
+
if windows?
|
|
29
|
+
# c:/home D:/path/here will faile with ArgumentError: different prefix
|
|
30
|
+
return path.to_s if path.to_s.capitalize[0] != cwd.to_s.capitalize[0]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
path = path.relative_path_from(cwd)
|
|
34
|
+
path = "./#{path}" unless path.absolute? || path.to_s.match(/^\./)
|
|
35
|
+
path.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
####################################################
|
|
2
|
+
# The file is was originally cloned from "Basic App"
|
|
3
|
+
# More information on "Basic App" can be found in the
|
|
4
|
+
# "Basic App" repository.
|
|
5
|
+
#
|
|
6
|
+
# See http://github.com/robertwahler
|
|
7
|
+
####################################################
|
|
8
|
+
module RepoManager
|
|
9
|
+
|
|
10
|
+
# An abstract superclass for basic action functionality specific to an
|
|
11
|
+
# application implementation. Put application specific code here.
|
|
12
|
+
class AppAction < BaseAction
|
|
13
|
+
|
|
14
|
+
# Used by asset factory to create assets. Override in app_action.rb or a
|
|
15
|
+
# descendant to set the class to be instantiated by by the AssetManager.
|
|
16
|
+
#
|
|
17
|
+
# @return [Symbol] asset type
|
|
18
|
+
def asset_type
|
|
19
|
+
:repo_asset
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# alias for items/assets
|
|
23
|
+
#
|
|
24
|
+
# @return [Array] of repos
|
|
25
|
+
def repos
|
|
26
|
+
items
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
####################################################
|
|
2
|
+
# The file is was originally cloned from "Basic App"
|
|
3
|
+
# More information on "Basic App" can be found in the
|
|
4
|
+
# "Basic App" repository.
|
|
5
|
+
#
|
|
6
|
+
# See http://github.com/robertwahler
|
|
7
|
+
####################################################
|
|
8
|
+
|
|
9
|
+
require 'repo_manager/assets/asset_manager'
|
|
10
|
+
|
|
11
|
+
module RepoManager
|
|
12
|
+
|
|
13
|
+
# An abstract superclass for basic action functionality
|
|
14
|
+
class BaseAction
|
|
15
|
+
# main configuration hash
|
|
16
|
+
attr_reader :configuration
|
|
17
|
+
|
|
18
|
+
# options hash, read from configuration hash
|
|
19
|
+
attr_reader :options
|
|
20
|
+
|
|
21
|
+
# args as passed on command line
|
|
22
|
+
attr_reader :args
|
|
23
|
+
|
|
24
|
+
# filename to template for rendering
|
|
25
|
+
attr_accessor :template
|
|
26
|
+
|
|
27
|
+
# filename to write output
|
|
28
|
+
attr_accessor :output
|
|
29
|
+
|
|
30
|
+
# numeric exit code set from return of process method
|
|
31
|
+
attr_reader :exit_code
|
|
32
|
+
|
|
33
|
+
# bin wrapper option parser object
|
|
34
|
+
attr_accessor :option_parser
|
|
35
|
+
|
|
36
|
+
def initialize(args=[], configuration={})
|
|
37
|
+
@configuration = configuration
|
|
38
|
+
@options = configuration[:options] || {}
|
|
39
|
+
@args = args
|
|
40
|
+
logger.debug "initialize with args: #{args.inspect}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Parse generic action options for all decendant actions
|
|
44
|
+
#
|
|
45
|
+
# @return [OptionParser] for use by decendant actions
|
|
46
|
+
def parse_options(parser_configuration = {})
|
|
47
|
+
raise_on_invalid_option = parser_configuration.has_key?(:raise_on_invalid_option) ? parser_configuration[:raise_on_invalid_option] : true
|
|
48
|
+
parse_base_options = parser_configuration.has_key?(:parse_base_options) ? parser_configuration[:parse_base_options] : true
|
|
49
|
+
logger.debug "base_action parsing args: #{args.inspect}, raise_on_invalid_option: #{raise_on_invalid_option}, parse_base_options: #{parse_base_options}"
|
|
50
|
+
|
|
51
|
+
@option_parser ||= OptionParser.new
|
|
52
|
+
|
|
53
|
+
option_parser.banner = help + "\n\nOptions:"
|
|
54
|
+
|
|
55
|
+
if parse_base_options
|
|
56
|
+
option_parser.on("--template [NAME]", "Use a template to render output. (default=default.slim)") do |t|
|
|
57
|
+
options[:template] = t.nil? ? "default.slim" : t
|
|
58
|
+
@template = options[:template]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
option_parser.on("--output FILENAME", "Render output directly to a file") do |f|
|
|
62
|
+
options[:output] = f
|
|
63
|
+
@output = options[:output]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
option_parser.on("--force", "Overwrite file output without prompting") do |f|
|
|
67
|
+
options[:force] = f
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
option_parser.on("-r", "--repos a1,a2,a3", "--asset a1,a2,a3", "--filter a1,a2,a3", Array, "List of regex asset name filters") do |list|
|
|
71
|
+
options[:filter] = list
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# NOTE: OptionParser will add short options, there is no way to stop '-m' from being the same as '--match'
|
|
75
|
+
option_parser.on("--match [MODE]", "Asset filter match mode. MODE=ALL (default), FIRST, EXACT, or ONE (fails if more than 1 match)") do |m|
|
|
76
|
+
options[:match] = m || "ALL"
|
|
77
|
+
options[:match].upcase!
|
|
78
|
+
unless ["ALL", "FIRST", "EXACT", "ONE"].include?(options[:match])
|
|
79
|
+
puts "invalid match mode option: #{options[:match]}"
|
|
80
|
+
exit 1
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# allow decendants to add options
|
|
86
|
+
yield option_parser if block_given?
|
|
87
|
+
|
|
88
|
+
# reprocess args for known options, see binary wrapper for first pass
|
|
89
|
+
# (first pass doesn't know about action specific options), find all
|
|
90
|
+
# action options that may come after the action/subcommand (options
|
|
91
|
+
# before subcommand have already been processed) and its args
|
|
92
|
+
logger.debug "(BaseAction) args before reprocessing: #{args.inspect}"
|
|
93
|
+
begin
|
|
94
|
+
option_parser.order!(args)
|
|
95
|
+
rescue OptionParser::InvalidOption => e
|
|
96
|
+
if raise_on_invalid_option
|
|
97
|
+
puts "option error: #{e}"
|
|
98
|
+
puts option_parser
|
|
99
|
+
exit 1
|
|
100
|
+
else
|
|
101
|
+
# parse and consume until we hit an unknown option (not arg), put it back so it
|
|
102
|
+
# can be shifted into the new array
|
|
103
|
+
e.recover(args)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
logger.debug "(BaseAction) args before unknown collection: #{args.inspect}"
|
|
107
|
+
|
|
108
|
+
unknown_args = []
|
|
109
|
+
while unknown_arg = args.shift
|
|
110
|
+
logger.debug "(BaseAction) unknown_arg: #{unknown_arg.inspect}"
|
|
111
|
+
unknown_args << unknown_arg
|
|
112
|
+
begin
|
|
113
|
+
# consume options and stop at an arg
|
|
114
|
+
option_parser.order!(args)
|
|
115
|
+
rescue OptionParser::InvalidOption => e
|
|
116
|
+
if raise_on_invalid_option
|
|
117
|
+
puts "option error: #{e}"
|
|
118
|
+
puts option_parser
|
|
119
|
+
exit 1
|
|
120
|
+
else
|
|
121
|
+
# parse and consume until we hit an unknown option (not arg), put it back so it
|
|
122
|
+
# can be shifted into the new array
|
|
123
|
+
e.recover(args)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
logger.debug "(BaseAction) args after unknown collection: #{args.inspect}"
|
|
128
|
+
|
|
129
|
+
@args = unknown_args.dup
|
|
130
|
+
logger.debug "(BaseAction) args after reprocessing: #{args.inspect}"
|
|
131
|
+
|
|
132
|
+
option_parser
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def execute
|
|
136
|
+
before_execute
|
|
137
|
+
parse_options
|
|
138
|
+
@exit_code = process
|
|
139
|
+
after_execute
|
|
140
|
+
@exit_code
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# handle "assets to items" transformations, if any, and write to output
|
|
144
|
+
def process
|
|
145
|
+
write_to_output(render)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# TODO: add exception handler and pass return values
|
|
149
|
+
def write_to_output(content)
|
|
150
|
+
if output
|
|
151
|
+
logger.debug "write_to_output called with output : #{output}"
|
|
152
|
+
if overwrite_output?
|
|
153
|
+
logger.debug "writing output to : #{output}"
|
|
154
|
+
File.open(output, 'wb') {|f| f.write(content) }
|
|
155
|
+
else
|
|
156
|
+
logger.info "existing file not overwritten. To overwrite automatically, use the '--force' option."
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
logger.debug "base_action writing to STDOUT"
|
|
160
|
+
print content
|
|
161
|
+
end
|
|
162
|
+
return 0
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# TODO: create items/app_item class with at least the 'name' accessor
|
|
166
|
+
#
|
|
167
|
+
# assets: raw configuration handling system for items
|
|
168
|
+
def assets
|
|
169
|
+
return @assets if @assets
|
|
170
|
+
@assets = AssetManager.new.assets(asset_options)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Used by asset factory to create assets. Override in app_action.rb or a
|
|
174
|
+
# descendant to set the class to be instantiated by by the AssetManager.
|
|
175
|
+
#
|
|
176
|
+
# @return [Symbol] asset type
|
|
177
|
+
def asset_type
|
|
178
|
+
:app_asset
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# asset options separated from assets to make it easier to override assets
|
|
182
|
+
def asset_options
|
|
183
|
+
# include all base action options
|
|
184
|
+
result = options.dup
|
|
185
|
+
|
|
186
|
+
# anything left on the command line should be filters as all options have
|
|
187
|
+
# been consumed, for pass through options, filters must be ignored by overwritting them
|
|
188
|
+
filters = args.dup
|
|
189
|
+
filters += result[:filter] if result[:filter]
|
|
190
|
+
result = result.merge(:filter => filters) unless filters.empty?
|
|
191
|
+
|
|
192
|
+
# asset type to create
|
|
193
|
+
type = result[:type] || asset_type
|
|
194
|
+
result = result.merge(:type => type)
|
|
195
|
+
|
|
196
|
+
# optional key: :assets_folder, absolute path or relative to config file if :base_folder is specified
|
|
197
|
+
result = result.merge(:assets_folder => configuration[:folders][:assets]) if configuration[:folders]
|
|
198
|
+
|
|
199
|
+
# optional key: :base_folder is the folder that contains the main config file
|
|
200
|
+
result = result.merge(:base_folder => File.dirname(configuration[:configuration_filename])) if configuration[:configuration_filename]
|
|
201
|
+
|
|
202
|
+
result
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# items to be rendered, defaults to assets, override to suit
|
|
206
|
+
#
|
|
207
|
+
# @return [Array] of items to be rendered
|
|
208
|
+
def items
|
|
209
|
+
assets
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Render items result to a string
|
|
213
|
+
#
|
|
214
|
+
# @return [String] suitable for displaying on STDOUT or writing to a file
|
|
215
|
+
def render(view_options=configuration)
|
|
216
|
+
logger.debug "base_action rendering"
|
|
217
|
+
result = ""
|
|
218
|
+
if template
|
|
219
|
+
logger.debug "base_action rendering with template : #{template}"
|
|
220
|
+
view = AppView.new(items, view_options)
|
|
221
|
+
view.template = template
|
|
222
|
+
result = view.render
|
|
223
|
+
else
|
|
224
|
+
items.each_with_index do |item, index|
|
|
225
|
+
result += "\n" unless index == 0
|
|
226
|
+
result += item.name.green + ":\n"
|
|
227
|
+
if item.respond_to?(:attributes)
|
|
228
|
+
attributes = item.attributes.dup
|
|
229
|
+
result += attributes.recursively_stringify_keys!.to_conf.gsub(/\s+$/, '') # strip trailing whitespace from YAML
|
|
230
|
+
result += "\n"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
result
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Convert method comments block to help text
|
|
238
|
+
#
|
|
239
|
+
# @return [String] suitable for displaying on STDOUT
|
|
240
|
+
def help(help_options={})
|
|
241
|
+
comment_starting_with = help_options[:comment_starting_with] || ""
|
|
242
|
+
located_in_file = help_options[:located_in_file] || __FILE__
|
|
243
|
+
text = File.read(located_in_file)
|
|
244
|
+
|
|
245
|
+
result = text.match(/(^\s*#\s*#{comment_starting_with}.*)^\s*class .* AppAction/m)
|
|
246
|
+
result = $1
|
|
247
|
+
result = result.gsub(/ @example/, '')
|
|
248
|
+
result = result.gsub(/ @return \[Number\]/, ' Exit code:')
|
|
249
|
+
result = result.gsub(/ @return .*/, '')
|
|
250
|
+
result = result.gsub(/ @see .*$/, '')
|
|
251
|
+
|
|
252
|
+
# strip the leading whitespace, the '#' and space
|
|
253
|
+
result = result.gsub(/^\s*# ?/, '')
|
|
254
|
+
|
|
255
|
+
# strip surrounding whitespace
|
|
256
|
+
result.strip
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# @return [Boolean] true if output doesn't exist or it is OK to overwrite
|
|
260
|
+
def overwrite_output?
|
|
261
|
+
return true unless File.exists?(output)
|
|
262
|
+
|
|
263
|
+
if options[:force]
|
|
264
|
+
logger.debug "overwriting output with --force option"
|
|
265
|
+
return true
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
unless STDOUT.isatty
|
|
269
|
+
logger.debug "TTY not detected, skipping overwrite prompt"
|
|
270
|
+
return false
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
result = false
|
|
274
|
+
print "File '#{output}' exists. Would you like overwrite? [y/n]: "
|
|
275
|
+
case gets.strip
|
|
276
|
+
when 'Y', 'y', 'yes'
|
|
277
|
+
logger.debug "user answered yes to overwrite prompt"
|
|
278
|
+
result = true
|
|
279
|
+
else
|
|
280
|
+
logger.debug "user answered no to overwrite prompt"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
result
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# callbacks
|
|
287
|
+
def before_execute
|
|
288
|
+
logger.debug "callback: before_execute"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def after_execute
|
|
292
|
+
logger.debug "callback: after_execute"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
end
|
|
296
|
+
end
|