Skip to content

Commit

Permalink
Merge pull request #2088 from beechtom/2078/bolt-guides
Browse files Browse the repository at this point in the history
(GH-2078) Add Bolt guide pages
  • Loading branch information
lucywyman committed Aug 21, 2020
2 parents 321c544 + 0234b38 commit 309dbdf
Show file tree
Hide file tree
Showing 16 changed files with 359 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ plans/
pwsh_module/PuppetBolt.psm1
pwsh_module/PuppetBolt.psd1
pwsh_module/autogenerated.tests.ps1
pwsh_module/en-US
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
* @puppetlabs/bolt

/documentation @hestonhoffman @puppetlabs/bolt
/guides @hestonhoffman @puppetlabs/bolt
/lib/bolt_server @puppetlabs/skeletor
/spec/bolt_server @puppetlabs/skeletor
3 changes: 2 additions & 1 deletion bolt.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Gem::Specification.new do |spec|
Dir['modules/*/locales/**/*'] +
Dir['modules/*/plans/**/*.pp'] +
Dir['modules/*/tasks/**/*'] +
Dir['Puppetfile']
Dir['Puppetfile'] +
Dir['guides/*.txt']
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
Expand Down
43 changes: 43 additions & 0 deletions guides/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Topic guides

Topic guides are concise descriptions of Bolt's features and concepts with links
to relevant documentation. They act as a reference for users who are looking to
better understand Bolt's features and concepts and quickly get to the
information they are looking for.

## Adding new topic guides

To add a new topic guide, create a text file with the name `<topic>.txt` in this
directory. Topics should be a single word containing only lowercase letters. The
format for a guide should follow the same format as existing guides.

## Adding guides to Bolt packages

During the packaging process, Bolt will typically include all guides in this
directory automatically. However, an extra step is required when adding new
guides to ensure they are added to the Bolt PowerShell module when building the
Windows package.

To add a guide to the Bolt PowerShell module, you will need to add the file as a
WiX component in `bolt-vanagon`, the tool used to build Bolt packages. To add a
component, modify the following XML and add it to [this
file](https://github.com/puppetlabs/bolt-vanagon/blob/main/resources/windows/wix/powershell.wxs.erb):

```xml
<Component
Id="about_bolt_<TOPIC>.help.txt"
Directory="PowerShellBoltModuleHelpDir"
Guid="<GUID>">
<File
Id="about_bolt_<TOPIC>.help.txt"
Source="$(var.AppSourcePath)\share\PowerShell\Modules\PuppetBolt\en-US\about_bolt_<TOPIC>.help.txt"
KeyPath="yes" />
</Component>
```

> **Note:** Replace `<TOPIC>` with the name of the new topic and `<GUID>` with a
> Globally Unique Identifier (GUID). You can generate a GUID in PowerShell using
> the `Get-Guid` cmdlet.
Once you have modified this file, open a [pull request against
`bolt-vanagon`](https://github.com/puppetlabs/bolt-vanagon/pulls).
19 changes: 19 additions & 0 deletions guides/inventory.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
TOPIC
inventory

DESCRIPTION
The inventory describes the targets that you run Bolt commands on, along
with any data and configuration for the targets. Targets in an inventory can
belong to one or more groups, allowing you to share data and configuration
across multiple targets and to specify multiple targets for your Bolt
commands without the need to list each target individually.

In most cases, Bolt loads the inventory from an inventory file in your Bolt
project. The inventory file is a YAML file named 'inventory.yaml'. Because
Bolt loads the inventory file from a Bolt project, you must have an existing
project configuration file named 'bolt-project.yaml' alongside the inventory
file.

DOCUMENTATION
https://pup.pt/bolt-inventory
https://pup.pt/bolt-inventory-reference
22 changes: 22 additions & 0 deletions guides/project.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
TOPIC
project

DESCRIPTION
A Bolt project is a directory that serves as the launching point for Bolt
and allows you to create a shareable orchestration application. Projects
typically include a project configuration file, an inventory file, and any
content you use in your project workflow, such as tasks and plans.

When you run Bolt, it runs in the context of a project. If the directory you
run Bolt from is not a project, Bolt attempts to find a project by
traversing the parent directories. If Bolt is unable to find a project, it
runs from the default project, located at '~/.puppetlabs/bolt'.

A directory is only considered a Bolt project when it has a project
configuration file named 'bolt-project.yaml'. Bolt doesn't load project data
and content, including inventory files, unless the data and content are part
of a project.

DOCUMENTATION
https://pup.pt/bolt-projects
https://pup.pt/bolt-project-reference
27 changes: 27 additions & 0 deletions lib/bolt/bolt_option_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ def get_help_text(subcommand, action = nil)
{ flags: OPTIONS[:global],
banner: GROUP_HELP }
end
when 'guide'
{ flags: OPTIONS[:global] + %w[format],
banner: GUIDE_HELP }
when 'plan'
case action
when 'convert'
Expand Down Expand Up @@ -164,13 +167,17 @@ def get_help_text(subcommand, action = nil)
command Run a command remotely
file Copy files between the controller and targets
group Show the list of groups in the inventory
guide View guides for Bolt concepts and features
inventory Show the list of targets an action would run on
plan Convert, create, show, and run Bolt plans
project Create and migrate Bolt projects
puppetfile Install and list modules and generate type references
script Upload a local script and run it remotely
secret Create encryption keys and encrypt and decrypt values
task Show and run Bolt tasks
GUIDES
For a list of guides on Bolt's concepts and features, run 'bolt guide'.
HELP

APPLY_HELP = <<~HELP
Expand Down Expand Up @@ -289,6 +296,26 @@ def get_help_text(subcommand, action = nil)
Show the list of groups in the inventory.
HELP

GUIDE_HELP = <<~HELP
NAME
guide
USAGE
bolt guide [topic] [options]
DESCRIPTION
View guides for Bolt's concepts and features.
Omitting a topic will display a list of available guides,
while providing a topic will display the relevant guide.
EXAMPLES
View a list of available guides
bolt guide
View the 'project' guide page
bolt guide project
HELP

INVENTORY_HELP = <<~HELP
NAME
inventory
Expand Down
53 changes: 51 additions & 2 deletions lib/bolt/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class CLI
'inventory' => %w[show],
'group' => %w[show],
'project' => %w[init migrate],
'apply' => %w[] }.freeze
'apply' => %w[],
'guide' => %w[] }.freeze

attr_reader :config, :options

Expand Down Expand Up @@ -354,7 +355,7 @@ def execute(options)
# Initialize inventory and targets. Errors here are better to catch early.
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
# options[:targets] will contain a resolved set of Target objects
unless %w[project puppetfile secret].include?(options[:subcommand]) ||
unless %w[project puppetfile secret guide].include?(options[:subcommand]) ||
%w[convert new show].include?(options[:action])
update_targets(options)
end
Expand Down Expand Up @@ -422,6 +423,12 @@ def execute(options)
end

case options[:subcommand]
when 'guide'
code = if options[:object]
show_guide(options[:object])
else
list_topics
end
when 'project'
case options[:action]
when 'init'
Expand Down Expand Up @@ -992,6 +999,48 @@ def convert_plan(plan)
pal.convert_plan(plan)
end

# Collects the list of Bolt guides and maps them to their topics.
def guides
@guides ||= begin
root_path = File.expand_path(File.join(__dir__, '..', '..', 'guides'))
files = Dir.children(root_path).sort

files.each_with_object({}) do |file, guides|
next if file !~ /\.txt\z/
topic = File.basename(file, '.txt')
guides[topic] = File.join(root_path, file)
end
rescue SystemCallError => e
raise Bolt::FileError.new("#{e.message}: unable to load guides directory", root_path)
end
end

# Display the list of available Bolt guides.
def list_topics
outputter.print_topics(guides.keys)
0
end

# Display a specific Bolt guide.
def show_guide(topic)
if guides[topic]
analytics.event('Guide', 'known_topic', label: topic)

begin
guide = File.read(guides[topic])
rescue SystemCallError => e
raise Bolt::FileError("#{e.message}: unable to load guide page", filepath)
end

outputter.print_guide(guide, topic)
else
analytics.event('Guide', 'unknown_topic', label: topic)
outputter.print_message("Did not find guide for topic '#{topic}'.\n\n")
list_topics
end
0
end

def validate_file(type, path, allow_dir = false)
if path.nil?
raise Bolt::CLIError, "A #{type} must be specified"
Expand Down
10 changes: 10 additions & 0 deletions lib/bolt/outputter/human.rb
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,16 @@ def print_plans(plans, modulepath)
"details and parameters for a specific plan.")
end

def print_topics(topics)
print_message("Available topics are:")
print_message(topics.join("\n"))
print_message("\nUse `bolt guide <topic>` to view a specific guide.")
end

def print_guide(guide, _topic)
@stream.puts(guide)
end

def print_module_list(module_list)
module_list.each do |path, modules|
if (mod = modules.find { |m| m[:internal_module_group] })
Expand Down
11 changes: 11 additions & 0 deletions lib/bolt/outputter/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ def print_plan_result(result)
@stream.puts result.to_json
end

def print_topics(topics)
print_table('topics' => topics)
end

def print_guide(guide, topic)
@stream.puts({
'topic' => topic,
'guide' => guide
}.to_json)
end

def print_puppetfile_result(success, puppetfile, moduledir)
@stream.puts({ "success": success,
"puppetfile": puppetfile,
Expand Down
15 changes: 15 additions & 0 deletions lib/bolt/outputter/rainbow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ def print_summary(results, elapsed_time = nil)
total_msg << " in #{duration_to_string(elapsed_time)}" unless elapsed_time.nil?
@stream.puts colorize(:rainbow, total_msg)
end

def print_guide(guide, _topic)
@stream.puts colorize(:rainbow, guide)
end

def print_topics(topics)
content = String.new("Available topics are:\n")
content += topics.join("\n")
content += "\n\nUse `bolt guide <topic>` to view a specific guide."
@stream.puts colorize(:rainbow, content)
end

def print_message(message)
@stream.puts colorize(:rainbow, message)
end
end
end
end
18 changes: 18 additions & 0 deletions rakelib/pwsh.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'bolt/cli'
require 'erb'
require 'fileutils'

namespace :pwsh do
desc "Generate pwsh from Bolt's command line options"
Expand Down Expand Up @@ -44,6 +45,10 @@ namespace :pwsh do
@mapped_options = {}

Bolt::CLI::COMMANDS.each do |subcommand, actions|
# The 'bolt guide' command is handled by PowerShell's help system, so
# don't create a cmdlet for it.
next if subcommand == 'guide'

actions << nil if actions.empty?
actions.each do |action|
help_text = parser.get_help_text(subcommand, action)
Expand Down Expand Up @@ -312,6 +317,19 @@ namespace :pwsh do
end
end

# Move 'guides' to 'en-US' directory in module, renaming the text files
# so they are recognized by the PowerShell help system
source = File.expand_path(File.join(__dir__, '..', 'guides'))
dest = File.expand_path(File.join(__dir__, '..', 'pwsh_module', 'en-US'))

FileUtils.mkdir(dest)

Dir.children(source).each do |file|
next if file !~ /\.txt\z/
topic = File.basename(file, '.txt')
FileUtils.cp(File.join(source, file), File.join(dest, "about_bolt_#{topic}.help.txt"))
end

# pwsh_module.psm1 ==> PuppetBolt.psm1
content = File.read('pwsh_module/pwsh_bolt_internal.ps1') +
File.read('pwsh_module/pwsh_bolt.psm1.erb')
Expand Down
Loading

0 comments on commit 309dbdf

Please sign in to comment.