runbook 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fe4b7ea15d7d74edf1a660fda55c9f8360a16f7e5032acadc427039240b9cf6
4
- data.tar.gz: c72e2c91cd75587c87ed4b9e5921e1be050ea6466c59e1d903fefcf7f6770bfd
3
+ metadata.gz: 3cb2a40c659a212a9fda6d78d1e94b07ba51625896cd6e4f4a4b6668e42bbc4e
4
+ data.tar.gz: 065708ff0b3bd93dfdcc35b60730138053e64af9e78768176282f5dc59236b66
5
5
  SHA512:
6
- metadata.gz: 129f995881810339793273c74d00a872b62cd865f8712fe7db063356c95bdae7f090537d244383b00fbf81cf2de8555e59627248b9e4a6284320aa9594508ef1
7
- data.tar.gz: b1c2ef5526b2225d02a246c2d4f5d8e657c599ab04faf96f192084ea4f11d61863f455abc91136a17174dd7645f1112231fa7614ed83862d4444debbc0d2ca07
6
+ metadata.gz: 87559cf0bee8e17052a2639033a96481fba11963e50c596f138e64fc72b54328288202bfbe3576da6417eada36f219123ba54d726ab0cc6b28176f31552d21ae
7
+ data.tar.gz: a0b414a4f0b8fa499cc46362d03d2dfb814cf65a46decbd796ffc45e7ecbd0f68db7a2a89168a3ace032814893ef649fa3a1d36982315f548688f4ce7e470b3c
data/.travis.yml CHANGED
@@ -2,4 +2,8 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.2.3
5
+ - 2.2.10
6
+ - 2.4.6
7
+ - 2.5.5
8
+ - 2.6.3
5
9
  before_install: gem install bundler -v 1.15.4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ This log maintains a list of all substantive changes to Runbook. The log include
4
4
 
5
5
  ## master
6
6
 
7
+ ## `v0.14.0` (2019-08-15)
8
+
9
+ ### Fixes:
10
+
11
+ * Relax gem dependencies to be compatible with gems up to the next major version
12
+
13
+ ### New Features
14
+
15
+ * Alias `install` cli command to `init`
16
+ * Add `Runbook.config` alias for `Runbook.configuration`
17
+
7
18
  ## `v0.13.0` (2019-07-10)
8
19
 
9
20
  ### Potentially Breaking Changes:
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Runbook
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/runbook.svg)](https://badge.fury.io/rb/runbook)
4
+ [![Build Status](https://travis-ci.org/braintree/runbook.svg?branch=master)](https://travis-ci.org/braintree/runbook)
5
+
6
+ _See our [blog post](https://medium.com/braintree-product-technology/https-medium-com-braintree-product-technology-runbook-be6f072cfc0d) for the philosophy behind Runbook and an overview of its features._
4
7
 
5
8
  Runbook provides a DSL for specifying a series of steps to execute an operation. Once your runbook is specified, you can use it to generate a formatted representation of the book or to execute the runbook interactively. For example, you can export your runbook to markdown or use the same runbook to execute commands on remote servers.
6
9
 
@@ -26,7 +29,7 @@ Lastly, Runbook provides an extendable interface for augmenting the DSL and defi
26
29
  * **Dynamic Control Flow** - Runbooks can start execution at any step and can skip steps based on user input.
27
30
  * **Resumable** - Runbooks save their state at each step. If your runbook encounters an error, you can resume your runbook at the previous step after addressing the error.
28
31
  * **Noop and Auto Modes** - Runbooks can be executed in noop mode. This allows you to see what a runbook will do before it executes. Runbooks can be run in auto mode to eliminate the need for human interaction.
29
- * **Execution Lifecycle Hooks** - Runbook provides before, after, around hooks to augment its execution behavior.
32
+ * **Execution Lifecycle Hooks** - Runbook provides before, after, and around hooks to augment its execution behavior.
30
33
  * **Tmux Integration** - Runbook integrates with [tmux](https://github.com/tmux/tmux). You can define terminal pane layouts and send commands to terminal panes.
31
34
  * **Generators** - Runbook provides commands to generate runbooks, extensions, and runbook projects. You can define your own generators for easy, customized runbook creation.
32
35
  * **Extendable DSL** - Runbook's DSL is designed to be extendable. You can extend its DSL to add your own behavior.
@@ -107,9 +110,9 @@ Install the Runbook gem:
107
110
 
108
111
  $ bundle install
109
112
 
110
- Install Runbook into your project:
113
+ Initialize Runbook in your project:
111
114
 
112
- $ bundle exec runbook install
115
+ $ bundle exec runbook init
113
116
 
114
117
  ## Contents
115
118
 
@@ -137,38 +140,40 @@ Install Runbook into your project:
137
140
  * [1.1.2.15 Wait](#wait)
138
141
  * [1.1.2.16 Tmux Layouts](#tmux-layouts)
139
142
  * [1.1.3 Setters](#setters)
140
- * [2. Configuration](#configuration)
141
- * [2.1 Configuration Files](#configuration-files)
142
- * [3. Working With Runbooks](#working-with-runbooks)
143
- * [3.1 From Within Your Project](#from-within-your-project)
144
- * [3.2 Via The Command Line](#via-the-command-line)
145
- * [3.3 Self-executable](#self-executable)
143
+ * [2. Working With Runbooks](#working-with-runbooks)
144
+ * [2.1 Via The Command Line](#via-the-command-line)
145
+ * [2.2 From Within Your Project](#from-within-your-project)
146
+ * [2.3 Self-executable](#self-executable)
147
+ * [3. Configuration](#configuration)
148
+ * [3.1 Configuration Files](#configuration-files)
146
149
  * [4. Best Practices](#best-practices)
147
150
  * [4.1 Iterative Automation](#iterative-automation)
148
151
  * [4.2 Parameterizing Runbooks](#parameterizing-runbooks)
149
- * [4.3 Execution Best Practices](#execution-best-practices)
150
- * [4.4 Composing Runbooks](#composing-runbooks)
151
- * [4.5 Deep Nesting](#deep-nesting)
152
- * [4.6 Load Vs. Eval](#load-vs-eval)
153
- * [4.7 Passing State](#passing-state)
152
+ * [4.3 Passing State](#passing-state)
153
+ * [4.4 Execution Best Practices](#execution-best-practices)
154
+ * [4.5 Remote Command Execution](#remote-command-execution)
155
+ * [4.6 Composing Runbooks](#composing-runbooks)
156
+ * [4.7 Deep Nesting](#deep-nesting)
157
+ * [4.8 Load Vs. Eval](#load-vs-eval)
154
158
  * [5. Generators](#generators)
155
159
  * [5.1 Predefined Generators](#predefined-generators)
156
160
  * [5.2 Custom Generators](#custom-generators)
157
161
  * [6. Extending Runbook](#extending-runbook)
158
- * [6.1 Adding Runs and Views](#adding-runs-and-views)
159
- * [6.2 DSL Extensions](#dsl-extensions)
160
- * [6.3 Adding New Statements](#adding-new-statements)
161
- * [6.4 Adding Run and View Functionality](#adding-run-and-view-functionality)
162
+ * [6.1 Adding New Statements](#adding-new-statements)
163
+ * [6.2 Adding Run and View Functionality](#adding-run-and-view-functionality)
164
+ * [6.3 DSL Extensions](#dsl-extensions)
165
+ * [6.4 Adding Runs and Views](#adding-runs-and-views)
162
166
  * [6.5 Augmenting Functionality With Hooks](#augmenting-functionality-with-hooks)
163
167
  * [6.6 Adding New Run Behaviors](#adding-new-run-behaviors)
164
168
  * [6.7 Adding to Runbook's Run Metadata](#adding-to-runbooks-run-metadata)
165
169
  * [6.8 Adding to Runbook's Configuration](#adding-to-runbooks-configuration)
166
- * [7. Known Issues](#known-issues)
167
- * [8. Development](#development)
168
- * [9. Contributing](#contributing)
169
- * [10. Feature Requests](#feature-requests)
170
- * [11. License](#license)
171
- * [12. Code of Conduct](#code-of-conduct)
170
+ * [7. Testing](#testing)
171
+ * [8. Known Issues](#known-issues)
172
+ * [9. Development](#development)
173
+ * [10. Contributing](#contributing)
174
+ * [11. Feature Requests](#feature-requests)
175
+ * [12. License](#license)
176
+ * [13. Code of Conduct](#code-of-conduct)
172
177
 
173
178
  ## Runbook Anatomy
174
179
 
@@ -539,51 +544,10 @@ step do
539
544
  end
540
545
  ```
541
546
 
542
- ## Configuration
543
-
544
- Runbook is configured using its configuration object. Below is an example of how to configure Runbook.
545
-
546
- ```ruby
547
- Runbook.configure do |config|
548
- config.ssh_kit.umask = "077"
549
- config.ssh_kit.default_runner_config = {in: :groups, limit: 5}
550
- config.ssh_kit.default_env = {rails_env: :staging}
551
-
552
- config.enable_sudo_prompt = true
553
- config.use_same_sudo_password = true
554
- end
555
- ```
556
-
557
- If the `ssh_kit` configuration looks familiar, that's because it's an SSHKit Configuration object. Any configuration options set on `SSHKit.config` can be set on `config.ssh_kit`.
558
-
559
- ### Configuration Files
560
-
561
- Runbook loads configuration from a number of predefined files. Runbook will attempt to load configuration from the following locations on startup: `/etc/runbook.conf`, a `Runbookfile` in a parent directory from the current directory, a `.runbook.conf` file in the current user's home directory, a file specified with `--config` on the command line, any configuration specified in a runbook. Runbook will also load configuration from these files in this order of preference, respectively. That is, configuration values specified at the project level (`Runbookfile`) will override configuration values set at the global level (`/etc/runbook.conf`), etc.
562
-
563
547
  ## Working With Runbooks
564
548
 
565
549
  You can integrate with Runbook in several different ways. You can create your own project or incorporate Runbook into your existing projects. You can use Runbook via the command line. And you can even create self-executing runbooks.
566
550
 
567
- ### From Within Your Project
568
-
569
- Runbooks can be executed using the `Runbook::Viewer` and `Runbook::Runner` classes.
570
-
571
- #### Executing a runbook using `Runbook::Viewer`
572
-
573
- ```ruby
574
- Runbook::Viewer.new(book).generate(view: :markdown)
575
- ```
576
-
577
- In this case book is a `Runbook::Entities::Book` and `:markdown` refers to the specific view type (`Runbook::Views::Markdown`).
578
-
579
- #### Executing a runbook using `Runbook::Runner`
580
-
581
- ```ruby
582
- Runbook::Runner.new(book).run(run: :ssh_kit, noop: false, auto: false, paranoid: true, start_at: "0")
583
- ```
584
-
585
- This will execute `book` using the `Runbook::Runs::SSHKit` run type. It will not run the book in `noop` mode. It will not run the book in `auto` mode. It will run the book in `paranoid` mode. And it will start at the beginning of the book. Noop mode runs the book without side-effects outside of printing what it will execute. Auto mode will skip any prompts in the runbook. If there are any required prompts in the runbook (such as the `ask` statement), then the run will fail. Paranoid mode will prompt the user for whether they should continue at every step. Finally `start_at` can be used to skip parts of the runbook or to restart at a certain point in the event of failures, stopping and starting the runbook, etc.
586
-
587
551
  ### Via The Command Line
588
552
 
589
553
  Runbook can be used to write stand-alone runbook files that can be executed via the command line. Below is a list of examples of how to use Runbook via the command line.
@@ -637,6 +601,26 @@ the runbook at runtime.
637
601
  $ HOSTS="appbox{01..30}.prod" ENV="production" runbook exec --start-at 1.2.1 my_runbook.rb
638
602
  ```
639
603
 
604
+ ### From Within Your Project
605
+
606
+ Runbooks can be executed using the `Runbook::Viewer` and `Runbook::Runner` classes. Using these classes, you can invoke runbooks from within your existing codebase. This could be ideal for several reasons. It allows you to maintain a consistent interface with other system tasks such as rake tasks or cap tasks. It allows you to perform setup or sanity check functionality before executing your runbooks. And it allows you to load an environment to be accessed within your runbooks, such as providing access to a canonical list of servers or shared business logic.
607
+
608
+ #### Executing a runbook using `Runbook::Viewer`
609
+
610
+ ```ruby
611
+ Runbook::Viewer.new(book).generate(view: :markdown)
612
+ ```
613
+
614
+ In this case book is a `Runbook::Entities::Book` and `:markdown` refers to the specific view type (`Runbook::Views::Markdown`).
615
+
616
+ #### Executing a runbook using `Runbook::Runner`
617
+
618
+ ```ruby
619
+ Runbook::Runner.new(book).run(run: :ssh_kit, noop: false, auto: false, paranoid: true, start_at: "0")
620
+ ```
621
+
622
+ This will execute `book` using the `Runbook::Runs::SSHKit` run type. It will not run the book in `noop` mode. It will not run the book in `auto` mode. It will run the book in `paranoid` mode. And it will start at the beginning of the book. Noop mode runs the book without side-effects outside of printing what it will execute. Auto mode will skip any prompts in the runbook. If there are any required prompts in the runbook (such as the `ask` statement), then the run will fail. Paranoid mode will prompt the user for whether they should continue at every step. Finally `start_at` can be used to skip parts of the runbook or to restart at a certain point in the event of failures, stopping and starting the runbook, etc.
623
+
640
624
  ### Self-executable
641
625
 
642
626
  Runbooks can be written to be self-executable
@@ -673,6 +657,27 @@ runbook = Runbook.books.last # Runbooks register themselves to Runbook.books whe
673
657
  Runbook::Runner.new(runbook).run(auto: true)
674
658
  ```
675
659
 
660
+ ## Configuration
661
+
662
+ Runbook is configured using its configuration object. Below is an example of how to configure Runbook.
663
+
664
+ ```ruby
665
+ Runbook.configure do |config|
666
+ config.ssh_kit.umask = "077"
667
+ config.ssh_kit.default_runner_config = {in: :groups, limit: 5}
668
+ config.ssh_kit.default_env = {rails_env: :staging}
669
+
670
+ config.enable_sudo_prompt = true
671
+ config.use_same_sudo_password = true
672
+ end
673
+ ```
674
+
675
+ If the `ssh_kit` configuration looks familiar, that's because it's an SSHKit Configuration object. Any configuration options set on `SSHKit.config` can be set on `config.ssh_kit`.
676
+
677
+ ### Configuration Files
678
+
679
+ Runbook loads configuration from a number of predefined files. Runbook will attempt to load configuration from the following locations on startup: `/etc/runbook.conf`, a `Runbookfile` in a parent directory from the current directory, a `.runbook.conf` file in the current user's home directory, a file specified with `--config` on the command line, any configuration specified in a runbook. Runbook will also load configuration from these files in this order of preference, respectively. That is, configuration values specified at the project level (`Runbookfile`) will override configuration values set at the global level (`/etc/runbook.conf`), etc.
680
+
676
681
  ## Best Practices
677
682
 
678
683
  The following are best practices when developing your own runbooks.
@@ -695,12 +700,60 @@ rails_env = `facter rails_env`
695
700
  customer_list = File.read("/tmp/customer_list.txt")
696
701
  ```
697
702
 
703
+ ### Passing State
704
+
705
+ Runbook provides a number of different mechanisms for passing state throughout a runbook. For any data that is known at compile time, local variables can be used because Runbooks are lexically scoped.
706
+
707
+ ```ruby
708
+ home_planet = "Krypton"
709
+ Runbook.book "Book Using Local Variables" do
710
+ hometown = "Smallville"
711
+
712
+ section "My Biography" do
713
+ step do
714
+ note "Home Planet: #{home_planet}"
715
+ note "Home Town: #{hometown}"
716
+ end
717
+ end
718
+ end
719
+ ```
720
+
721
+ When looking to pass data generated at runtime, for example data from `ruby_command`, `ask`, or `capture` statements, Runbook persists and synchronizes instance variables for these commands.
722
+
723
+ ```ruby
724
+ Runbook.book "Book Using Instance Variables" do
725
+ section "The Transported Man" do
726
+ step do
727
+ ask "Who's the greatest magician?", into: :greatest, default: "Alfred Borden"
728
+ ruby_command { @magician = "Robert Angier" }
729
+ end
730
+
731
+ step do
732
+ ruby_command {
733
+ note "Magician: #{@magician}"
734
+ note "Greatest Magician: #{@greatest}"
735
+ }
736
+ end
737
+ end
738
+ end
739
+ ```
740
+
741
+ Instance variables are only passed between statements such as `ruby_command`. They should not be set on entities such as steps, sections, or books. Instance variables are persisted using `metadata[:repo]`. They are copied to the repo after each statement finishes executing and copied from the repo before each statement starts executing. Because instance variables utilize the repo, they are persisted if the runbook is stopped and restarted at the same step.
742
+
743
+ Be careful with your naming of instance variables as it is possible to clobber the step's DSL methods because they share the same namespace.
744
+
698
745
  ### Execution Best Practices
699
746
 
700
747
  As a best practice, Runbooks should always be nooped before they are run. This will allow you to catch runtime errors such as using the ask statement when running in auto mode, typos in your runbooks, and to visually confirm what will be executed.
701
748
 
702
749
  Additionally, it can be nice to have a generated view of the runbook you are executing to have a good high-level overview of the steps in the runbook.
703
750
 
751
+ ### Remote Command Execution
752
+
753
+ Runbook uses [SSHKit](https://github.com/capistrano/sshkit) for remote command execution. When specifying `servers`, you are specifying the target host to execute the command. If you want to use a non-standard port or login using a different user than your current user, then you can specify the `server` as `lucy@host1.prod:2345`. Alternatively, you can use an ssh config file such as `~/.ssh/config` to specify the user and port used to ssh to a given host. See [Capistrano's SSH setup instructions](https://capistranorb.com/documentation/getting-started/installation/#ssh) for further support on setting up SSH to execute commands on remote hosts.
754
+
755
+ The `user` setter designates the user you will sudo as once sshed to the remote host. Runbook supports password-protected sudo execution. That is, if your server requires a password to execute commands as another user, Runbook will allow you to enter your password when prompted. The `enable_sudo_prompt` configuration value controls this behavior. Enabling the sudo password prompt requires that your commands execute using a tty, which can lead to unexpected behavior when executing certain commands. Enabling `use_same_sudo_password` will use the same password accross different hosts and users instead of re-prompting for each unique user/host combo.
756
+
704
757
  ### Composing Runbooks
705
758
 
706
759
  Runbooks can be composed using the `add` keyword. Below is an example of composing a runbook from smaller, reusable components.
@@ -720,6 +773,8 @@ Runbook.book "Update configuration" do
720
773
  end
721
774
  ```
722
775
 
776
+ If you want to parameterize these runbook snippets, you can place them in a ruby function that takes arguments and generates the desired entity or statement. If these snippets set information that is used by the runbook, such as with `capture` statements, it is a good practice to parameterize where the result is stored. This lets the snippet fit different contexts and makes clear what data is being returned from the snippet.
777
+
723
778
  ### Deep Nesting
724
779
 
725
780
  Because the Runbook DSL is declarative, it is generally discouraged to develop elaborate nested decision trees. For example, it is discouraged to use the `ask` statement to gather user feedback, branch on this information in a `ruby_command`, and follow completely separate sets of steps. This is because deep nesting eliminates the benefits of the declarative DSL. You can no longer noop the deeply nested structure for example.
@@ -759,48 +814,6 @@ runbook = eval(File.read("my_runbook.rb"))
759
814
 
760
815
  Loading your runbook file is more ideal, but adds slight complexity. This method is prefered because the Ruby mechanism for retrieving source code does not work for code that has been `eval`ed. This means that you will not see `ruby_command` code blocks in view and noop output when using the `eval` method. You will see an "Unable to retrieve source code" message instead.
761
816
 
762
- ### Passing State
763
-
764
- Runbook provides a number of different mechanisms for passing state throughout a runbook. For any data that is known at compile time, local variables can be used because Runbooks are lexically scoped.
765
-
766
- ```ruby
767
- home_planet = "Krypton"
768
- Runbook.book "Book Using Local Variables" do
769
- hometown = "Smallville"
770
-
771
- section "My Biography" do
772
- step do
773
- note "Home Planet: #{home_planet}"
774
- note "Home Town: #{hometown}"
775
- end
776
- end
777
- end
778
- ```
779
-
780
- When looking to pass data generated at runtime, for example data from `ruby_command`, `ask`, or `capture` statements, Runbook persists and synchronizes instance variables for these commands.
781
-
782
- ```ruby
783
- Runbook.book "Book Using Instance Variables" do
784
- section "The Transported Man" do
785
- step do
786
- ask "Who's the greatest magician?", into: :greatest, default: "Alfred Borden"
787
- ruby_command { @magician = "Robert Angier" }
788
- end
789
-
790
- step do
791
- ruby_command {
792
- note "Magician: #{@magician}"
793
- note "Greatest Magician: #{@greatest}"
794
- }
795
- end
796
- end
797
- end
798
- ```
799
-
800
- Instance variables are only passed between statements such as `ruby_command`. They should not be set on entities such as steps, sections, or books. Instance variables are persisted using `metadata[:repo]`. They are copied to the repo after each statement finishes executing and copied from the repo before each statement starts executing. Because instance variables utilize the repo, they are persisted if the runbook is stopped and restarted at the same step.
801
-
802
- Be careful with your naming of instance variables as it is possible to clobber the step's DSL methods because they share the same namespace.
803
-
804
817
  ## Generators
805
818
 
806
819
  Runbook provides a number of generators accessible via the command line that can be used to generate code for new runbooks, Runbook projects, and Runbook extensions. Additionally, Runbook provides a generator generator so you can define your own custom generators.
@@ -849,28 +862,47 @@ Generate your own generator using the `generate generator` command
849
862
 
850
863
  Runbook can be extended to add custom functionality.
851
864
 
852
- ### Adding Runs and Views
865
+ ### Adding New Statements
853
866
 
854
- You can add new run and view types by defining modules under `Runbook:::Runs` and `Runbook::Views` respectively. They will automatically be accessible from the command line or via the `Runner` and `Viewer` classes. See `lib/runbook/runs/ssh_kit.rb` or `lib/runbook/views/markdown.rb` for examples of how to implement runs and views.
867
+ In order to add a new statement to your DSL, create a class under `Runbook::Statements` that inherits from `Runbook::Statement`. This statement will be initialized with all arguments passed to the corresponding keyword in the DSL. Remember to also add a corresponding method to runs and views so your new statement can be interpretted in each context.
855
868
 
856
869
  ```ruby
857
- module Runbook::Views
858
- module Yaml
859
- include Runbook::View
870
+ module Runbook::Statements
871
+ class Diagram < Runbook::Statement
872
+ attr_reader :alt_text, :url
860
873
 
861
- # handler names correspond to the entity or statement class name
862
- # Everything is underscored and "::" is replaced by "__"
863
- def self.runbook__entities__book(object, output, metadata)
864
- output << "---\n"
865
- output << "book:\n"
866
- output << " title: #{object.title}\n"
874
+ def initialize(alt_text, url)
875
+ @alt_text = alt_text
876
+ @url = url
867
877
  end
878
+ end
879
+ end
880
+ ```
868
881
 
869
- # Add other handlers here
882
+ In the above example a keyword `diagram` will be added to the step dsl and its arguments will be used to initialize the Diagram object.
883
+
884
+ New statements can be generated using the statement generator.
885
+
886
+ $ runbook generate statement diagram --root lib/runbook/extensions
887
+
888
+ ### Adding Run and View Functionality
889
+
890
+ You can add handlers for new statements and entities to your runs and views by prepending the modules with the new desired functionality.
891
+
892
+ ```ruby
893
+ module MyRunbook::Extensions
894
+ module Diagram
895
+ def self.runbook__entities__diagram(object, output, metadata)
896
+ output << "![#{object.alt_text}](#{object.url})"
897
+ end
870
898
  end
899
+
900
+ Runbook::Views::Markdown.prepend(Diagram)
871
901
  end
872
902
  ```
873
903
 
904
+ If you are not modifying existing methods, you can simply re-open the module to add new methods.
905
+
874
906
  ### DSL Extensions
875
907
 
876
908
  You can add arbitrary keywords to your entity DSLs. For example, you could add an alias to Runbook's Book DSL as follows:
@@ -889,43 +921,32 @@ module MyRunbook::Extensions
889
921
  end
890
922
  ```
891
923
 
892
- ### Adding New Statements
893
-
894
- In order to add a new statement to your DSL, create a class under `Runbook::Statements` that inherits from `Runbook::Statement`. This statement will be initialized with all arguments passed to the corresponding keyword in the DSL. Remember to also add a corresponding method to runs and views so your new statement can be interpretted in each context.
895
-
896
- ```ruby
897
- module Runbook::Statements
898
- class Diagram < Runbook::Statement
899
- attr_reader :alt_text, :url
900
-
901
- def initialize(alt_text, url)
902
- @alt_text = alt_text
903
- @url = url
904
- end
905
- end
906
- end
907
- ```
924
+ DSL extensions can be generated using the dsl_extension generator.
908
925
 
909
- In the above example a keyword `diagram` will be added to the step dsl and its arguments will be used to initialize the Diagram object.
926
+ $ runbook generate dsl_extension aliases --root lib/runbook/extensions
910
927
 
911
- ### Adding Run and View Functionality
928
+ ### Adding Runs and Views
912
929
 
913
- You can add handlers for new statements and entities to your runs and views by prepending the modules with the new desired functionality.
930
+ You can add new run and view types by defining modules under `Runbook:::Runs` and `Runbook::Views` respectively. They will automatically be accessible from the command line or via the `Runner` and `Viewer` classes. See `lib/runbook/runs/ssh_kit.rb` or `lib/runbook/views/markdown.rb` for examples of how to implement runs and views.
914
931
 
915
932
  ```ruby
916
- module MyRunbook::Extensions
917
- module Diagram
918
- def self.runbook__entities__diagram(object, output, metadata)
919
- output << "![#{object.alt_text}](#{object.url})"
933
+ module Runbook::Views
934
+ module Yaml
935
+ include Runbook::View
936
+
937
+ # handler names correspond to the entity or statement class name
938
+ # Everything is underscored and "::" is replaced by "__"
939
+ def self.runbook__entities__book(object, output, metadata)
940
+ output << "---\n"
941
+ output << "book:\n"
942
+ output << " title: #{object.title}\n"
920
943
  end
921
- end
922
944
 
923
- Runbook::Views::Markdown.prepend(Diagram)
945
+ # Add other handlers here
946
+ end
924
947
  end
925
948
  ```
926
949
 
927
- If you are not modifying existing methods, you can simply re-open the module to add new methods.
928
-
929
950
  ### Augmenting Functionality With Hooks
930
951
 
931
952
  You can add `before`, `after`, or `around` hooks to any statement or entity by defining a hook on a `Run` or `View`.
@@ -1024,7 +1045,20 @@ module MyRunbook::Extensions
1024
1045
  end
1025
1046
  ```
1026
1047
 
1027
- This will add a `log_level` attribute to Runbook's configuration with a default value of `:info`.
1048
+ This will add a `log_level` attribute to Runbook's configuration with a default value of `:info`. This configuration value can be accessed via `Runbook.config.log_level`.
1049
+
1050
+ ## Testing
1051
+
1052
+ Runbooks are inherently difficult to test because they are primarily composed of side-effects. That being said, there are a number of strategies you can employ to test your runbooks.
1053
+
1054
+ 1. Push complex logic to stand-alone Ruby objects that can be tested in isolation
1055
+ 2. Use `TEST` or `DEBUG` environment variables to conditionally disable side-effects during execution
1056
+ 3. Execute your runbooks in staging environments
1057
+ 4. Noop your runbooks to understand what they will be executing before executing them
1058
+
1059
+ See Runbook's test suite for more ideas on how to test your runbooks. For example, Runbook uses [aruba](https://github.com/cucumber/aruba) to test Runbook at the CLI level.
1060
+
1061
+ Additionally, runbooks should contain their own assertions, sanity checks, monitoring, and alerting to mitigate errors and alert you if intervention is required.
1028
1062
 
1029
1063
  ## Known Issues
1030
1064
 
@@ -1094,7 +1128,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/braint
1094
1128
 
1095
1129
  ## Feature Requests
1096
1130
 
1097
- Any feature requests are always welcome and will be considered in accordance with time and need. Additionally, existing feature requests are tracked in TODO.md. If you choose to contribute, your contributions will be greatly appreciated.
1131
+ Any feature requests are always welcome and will be considered in accordance with time and need. Additionally, existing feature requests are tracked in TODO.md. If you choose to contribute, your contributions will be greatly appreciated. Please reach out before creating any substantial pull requests. A bit of discussion can save a lot of time and increase the chances that your pull request will be accepted.
1098
1132
 
1099
1133
  ## License
1100
1134
 
data/bin/console CHANGED
@@ -6,6 +6,8 @@ require "runbook"
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
+ Runbook::Configuration.load_config
10
+
9
11
  # (If you use this, don't forget to add pry to your Gemfile!)
10
12
  require "pry"
11
13
  Pry.start
data/lib/runbook/cli.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "thor"
2
2
  require "runbook"
3
3
  require "runbook/cli_base"
4
- require "runbook/installer"
4
+ require "runbook/initializer"
5
5
 
6
6
  # Needed to load custom generators
7
7
  Runbook::Configuration.load_config
@@ -71,15 +71,26 @@ module Runbook
71
71
  LONGDESC
72
72
  subcommand "generate", Runbook::Generator
73
73
 
74
- desc "install", "Install Runbook into an existing project"
74
+ desc "init", "Initialize Runbook in an existing project"
75
75
  long_desc "Set up Runbook directory structure and Runbookfile in an existing project for executing runbooks."
76
- Runbook::Installer.class_options.values.each do |co|
76
+ Runbook::Initializer.class_options.values.each do |co|
77
+ method_option co.name, desc: co.description, required: co.required,
78
+ default: co.default, aliases: co.aliases, type: co.type,
79
+ banner: co.banner, hide: co.hide
80
+ end
81
+ def init
82
+ invoke(Runbook::Initializer)
83
+ end
84
+
85
+ desc "install", "Install Runbook in an existing project", hide: true
86
+ Runbook::Initializer.class_options.values.each do |co|
77
87
  method_option co.name, desc: co.description, required: co.required,
78
88
  default: co.default, aliases: co.aliases, type: co.type,
79
89
  banner: co.banner, hide: co.hide
80
90
  end
81
91
  def install
82
- invoke(Runbook::Installer)
92
+ Runbook.deprecator.deprecation_warning(:install, :init)
93
+ invoke(Runbook::Initializer)
83
94
  end
84
95
 
85
96
  desc "--version", "Print runbook's version"
@@ -1,6 +1,10 @@
1
1
  module Runbook
2
2
  class << self
3
3
  attr_accessor :configuration
4
+
5
+ def config
6
+ @configuration
7
+ end
4
8
  end
5
9
 
6
10
  def self.configure
@@ -1,5 +1,5 @@
1
1
  module Runbook
2
- class Installer < Thor::Group
2
+ class Initializer < Thor::Group
3
3
  include Thor::Actions
4
4
 
5
5
  source_root File.join(
@@ -59,10 +59,10 @@ module Runbook
59
59
  _keep_dir(target)
60
60
  end
61
61
 
62
- def runbook_installation_overview
62
+ def runbook_initialization_overview
63
63
  msg = [
64
64
  "",
65
- "Runbook was successfully installed",
65
+ "Runbook was successfully initialized.",
66
66
  "Add runbooks to the `runbooks` directory.",
67
67
  "Add shared code to `lib/runbook`.",
68
68
  "Execute runbooks using `bundle exec runbook exec <RUNBOOK_PATH>`",
data/lib/runbook/run.rb CHANGED
@@ -42,7 +42,8 @@ module Runbook
42
42
  toolbox.output("Step #{metadata[:position]}:#{title}\n\n")
43
43
  return if metadata[:auto] || metadata[:noop] ||
44
44
  !metadata[:paranoid] || object.title.nil?
45
- continue_result = toolbox.expand("Continue?", _step_choices)
45
+ step_choices = _step_choices(object, metadata)
46
+ continue_result = toolbox.expand("Continue?", step_choices)
46
47
  _handle_continue_result(continue_result, object, metadata)
47
48
  end
48
49
 
@@ -210,7 +211,7 @@ module Runbook
210
211
  object.class.to_s.underscore.gsub("/", "__")
211
212
  end
212
213
 
213
- def _step_choices
214
+ def _step_choices(object, metadata)
214
215
  [
215
216
  {key: "c", name: "Continue to execute this step", value: :continue},
216
217
  {key: "s", name: "Skip this step", value: :skip},
@@ -15,6 +15,16 @@ module Runbook
15
15
  _child_classes(Runbook::Generators)
16
16
  end
17
17
 
18
+ def self.deprecator
19
+ return @deprecator if @deprecator
20
+ major_version = Gem::Version.new(Runbook::VERSION).segments[0]
21
+ next_major_version = major_version + 1
22
+ @deprecator = ActiveSupport::Deprecation.new(
23
+ "#{next_major_version}.0",
24
+ "Runbook"
25
+ )
26
+ end
27
+
18
28
  def self._child_classes(mod)
19
29
  mod.constants.map { |const|
20
30
  "#{mod.to_s}::#{const}".constantize
@@ -1,3 +1,3 @@
1
1
  module Runbook
2
- VERSION = "0.13.0"
2
+ VERSION = "0.14.0"
3
3
  end
data/runbook.gemspec CHANGED
@@ -32,17 +32,17 @@ Gem::Specification.new do |spec|
32
32
  spec.require_paths = ["lib"]
33
33
 
34
34
  spec.add_runtime_dependency 'activesupport', '~> 5.0', '>= 5.0.0.1'
35
- spec.add_runtime_dependency "method_source", "~> 0.9.0"
35
+ spec.add_runtime_dependency "method_source", "~> 0.9"
36
36
  spec.add_runtime_dependency "sshkit", "1.16"
37
- spec.add_runtime_dependency "sshkit-sudo", "~> 0.1.0"
37
+ spec.add_runtime_dependency "sshkit-sudo", "~> 0.1"
38
38
  spec.add_runtime_dependency "airbrussh", "~> 1.3"
39
- spec.add_runtime_dependency "thor", "~> 0.20.0"
40
- spec.add_runtime_dependency "tty-progressbar", "~> 0.14.0"
41
- spec.add_runtime_dependency "tty-prompt", "~> 0.16.0"
39
+ spec.add_runtime_dependency "thor", "~> 0.20"
40
+ spec.add_runtime_dependency "tty-progressbar", "~> 0.14"
41
+ spec.add_runtime_dependency "tty-prompt", "~> 0.16"
42
42
 
43
- spec.add_development_dependency "aruba", "~> 0.14.5"
43
+ spec.add_development_dependency "aruba", "~> 0.14"
44
44
  spec.add_development_dependency "bundler", "~> 1.15"
45
- spec.add_development_dependency "pry", "~> 0.11.3"
45
+ spec.add_development_dependency "pry", "~> 0.11"
46
46
  spec.add_development_dependency "pry-byebug", "~> 3.6"
47
47
  spec.add_development_dependency "rake", "~> 10.0"
48
48
  spec.add_development_dependency "rspec", "~> 3.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pblesi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-07-10 00:00:00.000000000 Z
11
+ date: 2019-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.9.0
39
+ version: '0.9'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.9.0
46
+ version: '0.9'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sshkit
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 0.1.0
67
+ version: '0.1'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.1.0
74
+ version: '0.1'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: airbrussh
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -92,56 +92,56 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: 0.20.0
95
+ version: '0.20'
96
96
  type: :runtime
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: 0.20.0
102
+ version: '0.20'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: tty-progressbar
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: 0.14.0
109
+ version: '0.14'
110
110
  type: :runtime
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: 0.14.0
116
+ version: '0.14'
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: tty-prompt
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: 0.16.0
123
+ version: '0.16'
124
124
  type: :runtime
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: 0.16.0
130
+ version: '0.16'
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: aruba
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: 0.14.5
137
+ version: '0.14'
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: 0.14.5
144
+ version: '0.14'
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: bundler
147
147
  requirement: !ruby/object:Gem::Requirement
@@ -162,14 +162,14 @@ dependencies:
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: 0.11.3
165
+ version: '0.11'
166
166
  type: :development
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: 0.11.3
172
+ version: '0.11'
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: pry-byebug
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -278,7 +278,7 @@ files:
278
278
  - lib/runbook/helpers/ssh_kit_helper.rb
279
279
  - lib/runbook/helpers/tmux_helper.rb
280
280
  - lib/runbook/hooks.rb
281
- - lib/runbook/installer.rb
281
+ - lib/runbook/initializer.rb
282
282
  - lib/runbook/node.rb
283
283
  - lib/runbook/run.rb
284
284
  - lib/runbook/runner.rb