sensible-cli 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4841010ea567e04f3f358f1f98b9e61aedb419c6f6857f5f2644bc0d9c5d58e3
4
+ data.tar.gz: 16b5ce3ead9edfd3ff17465f8cb4d5c9463ef5bf14e429e02ab8b70f249921c7
5
+ SHA512:
6
+ metadata.gz: dd36abd65d68819f83f817aa4979bda0ad78aa4b173e42c1792500812ac99501d5d0aaccac02d3d84da2c636338769adc41fcba7453266dbbf1b36efc0763b42
7
+ data.tar.gz: 5b1e16c926d168f4498e440161c294587428562b1a8672e2bf7606f111219c32e12fd0c5719e96141530698ae9e8c4cd8d3554aca15c65aebf16a6dffa296f90
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: Another test task
3
+ description: This is a sample task, and this is its description
4
+ install: |
5
+ echo "This is a multi line script"
6
+ echo "I should use a temp file to run"
7
+ notify-send "This is the post task that ran!"
@@ -0,0 +1,4 @@
1
+ ---
2
+ name: This is a sample task in a seperate file
3
+ description: This is a sample task, and this is its description
4
+ install: notify-send "This is the pre task that ran!"
data/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # Sensible
2
+ A small tool to manage projects.
3
+
4
+ ## NOTICE
5
+ The tool is still in very early development, and it's not ready for production use.
6
+
7
+ Everything is subject to change, and the tool is not stable.
8
+
9
+ ## Why?
10
+ The reason I made this tool, is I work on a lot of project at work, and we are a very small team. We work on separate things for the same projects, like I work on the frontend, and the other guy on the database.
11
+ He has his own tooling, and need a bunch of stuff installed and various settings set in files, to run the database. I have also my own tooling, and we often have to spend a lot of time helping with setting up our part of the project for each other so we can run them locally.
12
+
13
+ So to make things easier, I created this tool to help us document and setup each part of the projects, and make sure we have everything required to run each part.
14
+
15
+ It can also be used to document the setup process for a project, and make it easier for new developers to get started with the project.
16
+
17
+ It's inspired by the `flutter doctor` feature in Flutter.
18
+
19
+ It's currently focused for usage in linux environments, but it should work on MacOS as well. It's not tested on Windows, but it should work with WSL. It uses zx by google to run the scripts, and it's only tested on linux.
20
+
21
+ ## Installing
22
+ You can use the binary from the releases page, or you can install it with the RPM package from the same page
23
+
24
+ ```
25
+ gem install sensible-ruby
26
+ ```
27
+
28
+ # Docs
29
+
30
+ ## Sensible file
31
+ The sensible file is a yaml file that defines the packages, requirements and tasks for the project. The sensible file should be in the root of the project and should be named `sensible.yml`. You can run the `sensible init` command to create a new sensible file.
32
+
33
+ The `sensible.yml` should look like this:
34
+
35
+ ```yaml
36
+ ---
37
+ preTasks:
38
+ packages:
39
+ requirements:
40
+ postTasks:
41
+ ```
42
+ This is the basic structure of the sensible file. You can add the packages, requirements, tasks, and postTasks to the file.
43
+
44
+ **All properties are optional, and you can omit them if you don't need them.**
45
+
46
+ ### Packages
47
+ The packages are the packages that should be installed. The packages should be defined in the `packages` section of the sensible file.
48
+
49
+ The packages should be defined like this:
50
+
51
+ ```yaml
52
+ packages:
53
+ - name: <package-name>
54
+ install: <install-command>
55
+ env:
56
+ - <environment 1>
57
+ - <environment 2>
58
+ ```
59
+ The package should be the name of the command the package provides. As an example, if you check if Nodejs is installed `node`, the package name should be `node`.
60
+ It uses the [semver](https://www.npmjs.com/package/semver) npm package to check if the version is in the correct range.
61
+
62
+ The env property is optional, and if it's not defined, the package will be installed in every environment
63
+
64
+ The property `install` is the shell command that install the package.
65
+
66
+ ### Requirements
67
+ The requirements section defines the requirements for the project, like if you need specific settings in a file, or a line in /etc/hosts. They work pretty much like tasks, but it's a way to document the requirements for the project, and structure the setup process.
68
+
69
+ The requirement should be defined like this:
70
+ ```yaml
71
+ # sensible.yml
72
+ requirements:
73
+ - name: <requirement-name>
74
+ check: <shell script to check if the requirement is met>
75
+ install: <shell script to make the requirement met>
76
+ env:
77
+ - <environment 1>
78
+ - <environment 2>
79
+ ```
80
+ The check and install script should be kept as short and simple as possible. If it gets too complex, it should be a task instead.
81
+
82
+ #### Tasks
83
+ Tasks are the files that define the tasks that should be run. The task files should be placed in the `.sensible/tasks` folder in the root of the project.
84
+ The tasks should be defined like this:
85
+
86
+ ```yaml
87
+ # .sensible/tasks/example.yml
88
+ ---
89
+ name: Example task
90
+ description: This is an example task, showing how to define a task
91
+ showOutput: true | false # Default is false
92
+ script: echo "This is an example task"
93
+ ```
94
+ You can use | to make multiline scripts
95
+ ```yaml
96
+ # .sensible/tasks/example.yml
97
+ ---
98
+ name: Example task 2
99
+ description: This is an example task, showing how to define a task
100
+ script: |
101
+ echo "This is an example task"
102
+ echo "This is a multiline script"
103
+ ```
104
+ The script part is pure bash script. You can use any bash command in the script.
105
+
106
+ Tasks has the following properties:
107
+ - name: The name of the task
108
+ - description: A description of the task
109
+ - showOutput: If the output of the task should be shown in the terminal. Default is false
110
+ - script: The bash script that should be run
111
+ - env: The list of environments the task should be run in. The default is `dev`, and if you omit this property, it will be run in every environment.
112
+
113
+ ## Commands
114
+
115
+ ```
116
+ Usage: sensible [options] [command]
117
+
118
+ A simple sensible tool for your projects
119
+
120
+ Options:
121
+ --env <string> Set the environment (default: "dev")
122
+ -f, --sensibleFile <string> Path to the sensible file
123
+ -d, --sensibleFolder <string> Path to the sensible folder (default: ".sensible")
124
+ -p, --prod Set to production mode (default: false)
125
+ -V, --version output the version number
126
+ -h, --help display help for command
127
+
128
+ Commands:
129
+ check [options] Check the project for missing dependencies
130
+ install [options] Install missing dependencies and requirements
131
+ task <task> Manage tasks
132
+ init [options] Create a new sensible file
133
+ help [command] display help for command`
134
+ ```
135
+
136
+ ### `sensible init`
137
+ This command creates a new sensible file in the root of your project. It will create a file called `sensible.yml`.
138
+
139
+
140
+ ### `sensible check`
141
+ This checks the project for missing dependencies. It will check if the packages are installed and if they are in the correct version range. It uses the bash command `command -v` to check if the packages are installed.
142
+ To determine the version of the package, it uses the `<command> --version` and `<command> --V` command.
143
+
144
+ ### `sensible install`
145
+ This command installs missing dependencies and requirements. It will install the packages that are missing and are in the correct version range.
146
+
147
+ It will also run the tasks that are defined in the sensible file.
148
+ The order of the tasks is important. The tasks will be run in the order they are defined in the sensible file.
149
+
150
+ The total order of things are like this:
151
+
152
+ - preTasks
153
+ - packages
154
+ - requirements
155
+ - postTasks
156
+
157
+ ### `sensible task`
158
+ This command manages the tasks that are defined in your project.
159
+
160
+ ```
161
+ Usage: sensible task [command] <task>
162
+
163
+ Manage tasks
164
+
165
+ Arguments:
166
+ task Task to run
167
+
168
+ Options:
169
+ -h, --help display help for command
170
+
171
+ Commands:
172
+ run [options] <task> Run a task
173
+ list [options] List available tasks
174
+ create [options] <task> Create a new task
175
+ ```
176
+
177
+ Tasks will be placed in the `.sensible/tasks` when created, and it looks for them in that folder.
178
+
179
+ #### `sensible task run`
180
+ This will run a single task. Very useful if your project has need to do a lot of things to run it.
181
+
182
+ The last argument for the command is the name of the task you want to run.
183
+
184
+ `sensible run task example`
185
+
186
+ This will run the task `example` in the `.sensible/tasks` folder.
187
+
188
+ Possible use cases for tasks:
189
+ - Setting up a database
190
+ - Running migrations
191
+ - Running tests
192
+ - Running a development server
193
+ - Reinstall node dependencies
194
+ - Clearing cache files
195
+
196
+ #### `sensible task list`
197
+ This simply lists the tasks that are available in the `.sensible/tasks` folder.
198
+
199
+
200
+ ## Environments
201
+ The sensible tool supports multiple environments. The default environment is `dev`. You can set the environment with the `--env` flag.
202
+
203
+ This means you can have different requirements, packages, and tasks for different environments, and you can name them however you want.
204
+
205
+ An example of a command that sets the environment to `prod`:
206
+
207
+ ```bash
208
+ sensible check --env prod # Check the project for missing dependencies in the prod environment
209
+ sensible install --env stage # Install missing dependencies and requirements in the staging environment
210
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/exe/sensible ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ SPEC = %(
4
+ @ A small tool to manage and deploy projects
5
+
6
+ Install and update systems much like ansible but using shell scripts. It uses
7
+ a sensible.yml configuration file to control the process
8
+
9
+ OPTIONS
10
+ -e,env,environment=ENV
11
+ Sets the environment (dev/prod/etc),
12
+
13
+ -f,file=FILE
14
+ Path to sensible configuration file
15
+
16
+ -d,dir,directory=DIR
17
+ Path to sensible directory. Default '.sensible'
18
+
19
+ -v,verbose
20
+ Verbose output
21
+
22
+ COMMANDS
23
+ check!
24
+ Check the project for missing dependencies
25
+
26
+ install!
27
+ Install missing dependencies and requirements
28
+
29
+ task.list!
30
+ List tasks
31
+
32
+ task.run! -- TASK
33
+ Run a single task
34
+
35
+ task.create! -- TASK
36
+ Create a task
37
+
38
+ init!
39
+ Create a new sensible configuration file
40
+ )
41
+
42
+ require_relative '../lib/sensible.rb'
43
+ require 'shellopts'
44
+
45
+ # Monkey patch where version_number is not fetched correctly in ruby 3.4.2
46
+ module ShellOpts
47
+ class ShellOpts
48
+ def version_number
49
+ Sensible::VERSION
50
+ end
51
+ end
52
+ end
53
+
54
+ opts, args = ShellOpts::process(SPEC, ARGV)
55
+ cmd = opts.subcommand!
56
+ #cmd == :task! or args.expect(0) # Ensure no arguments except for task
57
+
58
+ if opts.verbose
59
+ puts "Options"
60
+ puts " env: #{opts.env}"
61
+ puts " file: #{opts.file}"
62
+ puts " dir: #{opts.dir}"
63
+ puts " args: #{args}"
64
+ # puts "Command: #{cmd.to_s.sub(/!$/, "")}"
65
+ end
66
+
67
+ case opts.subcommand
68
+ when :init!
69
+ Sensible::Sensible.init(opts)
70
+ else
71
+ sensible = Sensible::Sensible.new("sensible.yml", opts, args)
72
+
73
+ case opts.subcommand
74
+ when :check!
75
+ sensible.check
76
+ when :install!
77
+ sensible.install
78
+ when :task!
79
+ case cmd.subcommand
80
+ when :list!; sensible.task_list
81
+ when :run!
82
+ arg = args.expect(1) # Expect a single argument to sensible task
83
+ sensible.task_run(arg)
84
+ when :create!
85
+ arg = args.expect(1) # Expect a single argument to sensible task
86
+ sensible.task_create(arg)
87
+ end
88
+ end
89
+ end
90
+
@@ -0,0 +1,68 @@
1
+ require 'tty-prompt'
2
+ require 'tty-spinner'
3
+ require 'pastel'
4
+
5
+ module Sensible
6
+ $pastel = Pastel.new
7
+
8
+ class Logger
9
+ def self.log(message, indent = 0, use_print: false)
10
+ puts message
11
+ end
12
+
13
+ def self.success(message, indent = 0, use_print: false)
14
+ spaceIndent = ""
15
+ indent.times { |i| spaceIndent << " " }
16
+
17
+ if use_print
18
+ print "#{spaceIndent}#{$pastel.green("✔")} #{message}"
19
+ else
20
+ puts "#{spaceIndent}#{$pastel.green("✔")} #{message}"
21
+ end
22
+ end
23
+
24
+ def self.info(message, indent = 0, use_print: false)
25
+ spaceIndent = ""
26
+ indent.times { |i| spaceIndent << " " }
27
+
28
+ if use_print
29
+ print "#{spaceIndent}#{$pastel.blue("i")} #{message}"
30
+ else
31
+ puts "#{spaceIndent}#{$pastel.blue("i")} #{message}"
32
+ end
33
+ end
34
+
35
+ def self.danger(message, indent = 0, use_print: false)
36
+ spaceIndent = ""
37
+ indent.times { |i| spaceIndent << " " }
38
+
39
+ if use_print
40
+ print "#{spaceIndent}#{$pastel.red("✘")} #{message}"
41
+ else
42
+ puts "#{spaceIndent}#{$pastel.red("✘")} #{message}"
43
+ end
44
+ end
45
+
46
+ def self.error(message, indent = 0, use_print: false)
47
+ spaceIndent = ""
48
+ indent.times { |i| spaceIndent << " " }
49
+
50
+ full_message = "#{spaceIndent}#{$pastel.on_red.black(" ERROR ")} #{message}"
51
+
52
+ if use_print
53
+ print full_message
54
+ else
55
+ puts full_message
56
+ end
57
+ end
58
+
59
+ def self.yes?(message, indent = 0)
60
+ prompt = TTY::Prompt.new
61
+
62
+ spaceIndent = ""
63
+ indent.times { |i| spaceIndent << " " }
64
+
65
+ prompt.yes?("#{spaceIndent}#{message}")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'log'
2
+ require 'tty-prompt'
3
+ require 'pastel'
4
+
5
+ module Sensible
6
+ class Package
7
+ attr_reader :sensible
8
+ attr_reader :name
9
+ attr_reader :check
10
+ attr_reader :install
11
+ attr_reader :env
12
+
13
+ def initialize(packageHash, sensible)
14
+ @name = packageHash['name']
15
+ @check = packageHash['check']
16
+ @install = packageHash['install']
17
+ @env = packageHash['env'] || []
18
+
19
+ @sensible = sensible
20
+ end
21
+
22
+
23
+ # Check if the package is installed
24
+ def do_check
25
+ if @check
26
+ result = `#{@check}`
27
+ return $?.success?
28
+ else
29
+ # If check is not set, then infer that it's a system package
30
+ result = `rpm -q #{@name}`
31
+
32
+ if result.include? 'is not installed'
33
+ return false
34
+ else
35
+ return true
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ # Install the package
42
+ def do_install
43
+ if @install
44
+ system(@install, out: File::NULL)
45
+ return $?.success?
46
+ else
47
+ system("sudo", "dnf", "install", "-y", @name, out: File::NULL, err: File::NULL)
48
+ return $?.success?
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'task'
2
+ require_relative 'requirement'
3
+ require_relative 'package'
4
+
5
+ module Sensible
6
+ class Parse
7
+
8
+ # Parse the package list from sensible.yml
9
+ def self.parse_sensible_packages(sensible_hash_list, sensible)
10
+ list = []
11
+ for pkg in sensible_hash_list
12
+ list.append(Package.new(pkg, sensible))
13
+ end
14
+ return list
15
+ end
16
+
17
+ # Parse the task list from sensible.yml
18
+ def self.parse_sensible_tasks(sensible_hash_list, sensible)
19
+ list = []
20
+ for task in sensible_hash_list
21
+ list.append(Task.new(tash, sensible))
22
+ end
23
+ return list
24
+ end
25
+
26
+ # Parse the requirement list from sensible.yml
27
+ def self.parse_sensible_requirements(sensible_hash_list, sensible)
28
+ list = []
29
+ for pkg in sensible_hash_list
30
+ list.append(Requirement.new(pkg, sensible))
31
+ end
32
+ return list
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'package'
2
+
3
+ module Sensible
4
+ class Requirement < Package
5
+ # Pretty much the same as package right now
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ require_relative 'package'
2
+
3
+ module Sensible
4
+ class Task < Package
5
+ attr_reader :file_name
6
+ attr_reader :description
7
+ attr_accessor :show_output
8
+
9
+ def initialize(taskHash, file_name, sensible)
10
+ super(taskHash, sensible)
11
+ @file_name = file_name
12
+ @description = taskHash['description']
13
+ @show_output = taskHash['showOutput']
14
+ end
15
+
16
+ def do_check
17
+ do_verify()
18
+
19
+ # If check is not set, always run the task
20
+ if @check == nil
21
+ return false
22
+ end
23
+
24
+ # If there is no check, default to false, to force task to install every time
25
+ system(@check, out: File::NULL)
26
+ return $?.success?
27
+ end
28
+
29
+ def do_install
30
+ # TODO: Handle the show output property!
31
+
32
+ if @install.include?("\n")
33
+ temp_path = "/tmp/sensible"
34
+ temp_file_name = "install.sh"
35
+ temp_file_path = "#{temp_path}/#{temp_file_name}"
36
+
37
+ # Make sure we have the tmp folder created
38
+ FileUtils.mkdir_p(temp_path)
39
+
40
+ File.open(temp_file_path, "w") do |f|
41
+ f.puts "#!/usr/bin/env bash\n\n"
42
+ f.write(@install)
43
+ end
44
+
45
+ # Make it executable
46
+ File.chmod(0700, temp_file_path)
47
+
48
+ system("#{temp_file_path}", out: File::NULL)
49
+ return $?.success?
50
+ else
51
+ system(@install, out: File::NULL)
52
+ return $?.success?
53
+ end
54
+ end
55
+
56
+ def do_verify
57
+ if !@install
58
+ pastel = Pastel.new
59
+ Logger.error("This is not valid task, #{pastel.bold("install")} property is missing!")
60
+ exit(1)
61
+ return false
62
+ end
63
+
64
+ return true
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sensible
4
+ VERSION = "0.1.0"
5
+ end
data/lib/sensible.rb ADDED
@@ -0,0 +1,286 @@
1
+ # frozen_string_literal: true
2
+ require 'yaml'
3
+ require_relative "sensible/version"
4
+ require_relative "sensible/package"
5
+ require_relative "sensible/log"
6
+ require_relative "sensible/parse"
7
+
8
+ module Sensible
9
+ class Error < StandardError; end
10
+
11
+ class Sensible
12
+ attr_reader :opts
13
+ attr_reader :args
14
+
15
+ attr_reader :preTasks
16
+ attr_reader :packages
17
+ attr_reader :requirements
18
+ attr_reader :postTasks
19
+
20
+ attr_reader :sensible_folder
21
+ attr_reader :tasks_folder
22
+
23
+
24
+ def initialize(sensibleFileName, opts, args)
25
+ @opts = opts
26
+ @args = args
27
+
28
+ @sensible_folder = '.sensible'
29
+ @tasks_folder = 'tasks'
30
+
31
+ file_name = opts.file || sensibleFileName
32
+ unless File.exist?(file_name)
33
+ Logger.error("Required file not found: #{file_name}")
34
+ exit(1)
35
+ end
36
+
37
+ # Load the Sensile file
38
+ sensible_file_data = YAML.load_file(file_name)
39
+
40
+ # Parse packages
41
+ if sensible_file_data['packages']
42
+ @packages = Parse.parse_sensible_packages(sensible_file_data['packages'], self)
43
+ end
44
+
45
+ # Parse packages
46
+ if sensible_file_data['requirements']
47
+ @requirements = Parse.parse_sensible_requirements(sensible_file_data['requirements'], self)
48
+ end
49
+
50
+ if (sensible_file_data['preTasks'])
51
+ @preTasks = sensible_file_data['preTasks']
52
+ end
53
+
54
+ if (sensible_file_data['postTasks'])
55
+ @postTasks = sensible_file_data['postTasks']
56
+ end
57
+ end
58
+
59
+
60
+ # Run all the checks for packages and requirements
61
+ def check
62
+ Logger.log("Checking for installed packages...")
63
+
64
+ for pkg in @packages
65
+ # Do an environment test
66
+ if @opts.env
67
+ # If package env is not define, we expect it should always be installed regardless of environment
68
+ # If user has defined an environment, skip if the set environment isn't in the package enviroment list
69
+ next if pkg.env.any? && !pkg.env.include?(@opts.env)
70
+ else
71
+ # If env contains anything, when env is not defined in opts, skip it, as this is not the correct env
72
+ next if pkg.env.any?
73
+ end
74
+
75
+ if pkg.do_check
76
+ Logger.success("#{pkg.name} is installed")
77
+ else
78
+ Logger.danger("#{pkg.name} was NOT installed")
79
+ end
80
+ end
81
+
82
+ if @requirements != nil
83
+ Logger.log("\nChecking if requirements are met...")
84
+
85
+ for requirement in @requirements
86
+ # Do an environment test
87
+ if @opts.env
88
+ # If package env is not define, we expect it should always be installed regardless of environment
89
+ # If user has defined an environment, skip if the set environment isn't in the package enviroment list
90
+ next if requirement.env.any? && !requirement.env.include?(@opts.env)
91
+ else
92
+ # If env contains anything, when env is not defined in opts, skip it, as this is not the correct env
93
+ next if pkg.env.any?
94
+ end
95
+
96
+ if requirement.do_check
97
+ Logger.success("#{requirement.name}")
98
+ else
99
+ Logger.danger("#{requirement.name}")
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def install
106
+ # Prewarm sudo, to prevent asking too much
107
+ system('sudo -v')
108
+
109
+ # Run pre tasks
110
+ if @preTasks != nil
111
+ Logger.log("Running pre tasks...")
112
+
113
+ for task in @preTasks
114
+ task_run(task)
115
+ end
116
+ Logger.log("")
117
+ end
118
+
119
+ # Install packages
120
+ if @packages != nil
121
+ Logger.log("Installing packages...")
122
+ for pkg in @packages
123
+ # Do an environment test
124
+ if @opts.env
125
+ # If package env is not defined, we expect it should always be installed regardless of environment
126
+ # If user has defined an environment, skip if the set environment isn't in the package enviroment list
127
+ next if pkg.env.any? && !pkg.env.include?(@opts.env)
128
+ else
129
+ # If env contains anything, when env is not defined in opts, skip it, as this is not the correct env
130
+ next if pkg.env.any?
131
+ end
132
+
133
+ if pkg.do_check
134
+ Logger.success("#{pkg.name} is installed")
135
+ else
136
+ Logger.info("Installing: #{pkg.name}\r", use_print: true)
137
+ if pkg.do_install
138
+ Logger.success("#{pkg.name} was installed")
139
+ $stdout.flush
140
+ else
141
+ Logger.danger("#{pkg.name} was not installed")
142
+ $stdout.flush
143
+ end
144
+ end
145
+ end
146
+ Logger.log("")
147
+ end
148
+
149
+ # Handle requirements
150
+ if @requirements != nil
151
+ Logger.log("Handling requirements...")
152
+ for requirement in @requirements
153
+ # Do an environment test
154
+ if @opts.env
155
+ # If package env is not defined, we expect it should always be installed regardless of environment
156
+ # If user has defined an environment, skip if the set environment isn't in the package enviroment list
157
+ next if requirement.env.any? && !requirement.env.include?(@opts.env)
158
+ else
159
+ # If env contains anything, when env is not defined in opts, skip it, as this is not the correct env
160
+ next if requirement.env.any?
161
+ end
162
+
163
+ if requirement.do_check
164
+ Logger.success("#{requirement.name}")
165
+ else
166
+ Logger.info("Handling: #{pkg.name}\r", use_print: true)
167
+ if requirement.do_install
168
+ Logger.success("#{requirement.name}")
169
+ $stdout.flush
170
+ else
171
+ Logger.danger("#{requirement.name}")
172
+ $stdout.flush
173
+ end
174
+ end
175
+ end
176
+ Logger.log("")
177
+ end
178
+
179
+ # Run post tasks
180
+ if @postTasks != nil
181
+ Logger.log("Running post tasks...")
182
+
183
+ for task in @postTasks
184
+ task_run(task)
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ def self.init(opts)
191
+ sensible_file_name = "sensible.yml"
192
+
193
+ if opts.file
194
+ if opts.file.end_with?(".yml")
195
+ sensible_file_name = opts.file
196
+ else
197
+ sensible_file_name = opts.file + ".yml"
198
+ end
199
+ end
200
+
201
+ if not File.exist?(sensible_file_name)
202
+ File.open(sensible_file_name, "w") do |f|
203
+ f.write(<<~EOF)
204
+ ---
205
+ packages:
206
+ requirements:
207
+ EOF
208
+ end
209
+ Logger.success("Created #{sensible_file_name}!")
210
+ else
211
+ Logger.error("Cannot create #{sensible_file_name}, it already exists!")
212
+ end
213
+ end
214
+
215
+ def task_run(task_name)
216
+ # Load and parse the task file
217
+ task_file_path = "#{@sensible_folder}/#{@tasks_folder}/#{task_name}.yml"
218
+
219
+ # If task is not found, exit!
220
+ if not File.exist?(task_file_path)
221
+ pastel = Pastel.new
222
+ Logger.error("Task: #{pastel.bold(task_name)} does not exist!")
223
+ exit(1)
224
+ end
225
+
226
+ task = Task.new(YAML.load_file(task_file_path), "#{task_name}.yml", self)
227
+
228
+ pastel = Pastel.new
229
+ Logger.info("Running task: #{pastel.bold(task_name)}")
230
+
231
+ # Check if we need to rerun the task
232
+ if !task.do_check
233
+ if !task.do_install
234
+ Logger.error("The tasked failed!")
235
+ else
236
+ Logger.success("The task ran succesfully!")
237
+ end
238
+ else
239
+ puts "Task check is already met"
240
+ end
241
+ end
242
+
243
+ def task_list
244
+ # Parse tasks
245
+ tasks_path = "#{@sensible_folder}/#{@tasks_folder}"
246
+ task_files = Dir.children(tasks_path)
247
+
248
+ tasks = []
249
+ for task_file in task_files
250
+ tasks << Task.new(YAML.load_file("#{tasks_path}/#{task_file}"), task_file, self)
251
+ end
252
+
253
+ puts "Here is available tasks inside #{@sensible_folder}/#{tasks_folder}" if @opts.verbose
254
+
255
+ pastel = Pastel.new
256
+ for task in tasks
257
+ Logger.log("#{pastel.blue.bold(task.file_name)}: #{task.name}")
258
+ end
259
+ end
260
+
261
+ def task_create(task_name)
262
+ tasks_path = "#{@sensible_folder}/#{@tasks_folder}"
263
+ task_file_path = "#{tasks_path}/#{task_name}.yml"
264
+
265
+ # Make sure the task don't already exist
266
+ if File.exist?(task_file_path)
267
+ pastel = Pastel.new
268
+ Logger.error("Task: #{pastel.bold(task_name)} already exist!")
269
+ exit(1)
270
+ end
271
+
272
+ # Create the task yaml file
273
+ File.open(task_file_path, "w") do |f|
274
+ f.write(<<~EOF)
275
+ ---
276
+ name:
277
+ description:
278
+ install:
279
+ EOF
280
+ end
281
+
282
+ pastel = Pastel.new
283
+ Logger.success("Created the task: #{pastel.bold("#{task_name}.yml")}")
284
+ end
285
+ end
286
+ end
data/sensible.yml ADDED
@@ -0,0 +1,23 @@
1
+ preTasks:
2
+ - test
3
+
4
+ packages:
5
+ - name: httpd
6
+ - name: htop
7
+ - name: btop
8
+ env:
9
+ - prod
10
+ - name: rvm
11
+ check: which rvm
12
+
13
+ requirements:
14
+ - name: Test requirement
15
+ check: which node
16
+ install: echo "install"
17
+
18
+ - name: Test requirement that fails
19
+ check: exit 1
20
+ install: exit 1
21
+
22
+ postTasks:
23
+ - another
data/sig/sensible.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Sensible
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensible-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mikkel Jensen
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-06-13 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: shellopts
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: tty-which
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: tty-command
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: pastel
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: tty-spinner
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: tty-prompt
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ description: A small tool to manage projects, making shell scripts easier to manage,
97
+ and faster to setup projects!
98
+ email:
99
+ - dasmikko@gmail.com
100
+ executables:
101
+ - sensible
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".rspec"
106
+ - ".sensible/tasks/another.yml"
107
+ - ".sensible/tasks/test.yml"
108
+ - README.md
109
+ - Rakefile
110
+ - exe/sensible
111
+ - lib/sensible.rb
112
+ - lib/sensible/log.rb
113
+ - lib/sensible/package.rb
114
+ - lib/sensible/parse.rb
115
+ - lib/sensible/requirement.rb
116
+ - lib/sensible/task.rb
117
+ - lib/sensible/version.rb
118
+ - sensible.yml
119
+ - sig/sensible.rbs
120
+ homepage: https://github.com/dasmikko/sensible-ruby
121
+ licenses: []
122
+ metadata:
123
+ homepage_uri: https://github.com/dasmikko/sensible-ruby
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 3.1.0
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubygems_version: 3.6.5
139
+ specification_version: 4
140
+ summary: A small tool to manage projects.
141
+ test_files: []