Skip to content

Commit

Permalink
CLI for creating config.yaml (#413)
Browse files Browse the repository at this point in the history
Signed-off-by: Nate Koenig <[email protected]>
  • Loading branch information
nkoenig authored Apr 25, 2024
1 parent 6880aec commit d14ffcd
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 21 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ Downloading model:
Download succeeded.
```

**Private access tokens**
Private models and worlds can be downloaded using access tokens.
Access tokens are generated on `app.gazebosim.org`. After logging in,
go to `Settings->Access Tokens`.

An access token can be used with CLI commands via the `--header` option:

```bash
$ gz fuel download -u https://fuel.gazebosim.org/1.0/openrobotics/models/ambulance --header 'Private-Token: <access_token>'
```

Or, an access token can be stored in a `~/.gz/fuel/config.yaml` file. The token is then
automatically used by the command line tool and API calls. Use the `configure` helper
tool create your `~/.gz/fuel/config.yaml` file.

```bash
$ gz fuel configure
```

**C++ Get List models**
```
// Create a client (uses https://fuel.gazebosim.org by default)
Expand Down
4 changes: 0 additions & 4 deletions conf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,3 @@ configure_file(

# Install the yaml configuration files in an unversioned location.
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fuel${PROJECT_VERSION_MAJOR}.yaml DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/gz/)

# Install config.yaml
install (FILES config.yaml DESTINATION
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/gz/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}/)
14 changes: 0 additions & 14 deletions conf/config.yaml

This file was deleted.

150 changes: 149 additions & 1 deletion src/cmd/cmdfuel.rb.in
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ else
include Fiddle
end

require 'fileutils'
require 'optparse'
require 'uri'
require 'yaml'

# Constants.
LIBRARY_NAME = '@library_location@'
Expand Down Expand Up @@ -61,6 +64,7 @@ COMMANDS = { 'fuel' =>
" gz fuel [action] [options] \n"\
" \n"\
"Available Actions: \n"\
" configure Create config.yaml configuration file \n"\
" delete Delete resources \n"\
" download Download resources \n"\
" edit Edit a resource \n"\
Expand All @@ -80,6 +84,20 @@ COMMANDS = { 'fuel' =>
}

SUBCOMMANDS = {
'configure' =>
"Create `~/.gz/fuel/config.yaml` to hold Fuel server configurations. \n"\
" \n"\
" gz fuel configure [options] \n"\
" \n"\
" --defaults Use all the defaults and save. \n"\
" This will overwrite ~/.gz/fuel/config.yaml. \n"\
" --console Output to the console instead of to a file. \n"\
" -h [--help] Print this help message. \n"\
" \n"\
" --force-version <VERSION> Use a specific library version. \n"\
" \n"\
" --versions Show the available versions. \n",

'delete' =>
"Delete simulation resources \n"\
" \n"\
Expand Down Expand Up @@ -208,7 +226,9 @@ class Cmd
'pbtxt2config' => '',
'private' => '',
'onlymodels' => '0',
'onlyworlds' => '0'
'onlyworlds' => '0',
'defaults' => false,
'console' => false
}

usage = COMMANDS[args[0]]
Expand Down Expand Up @@ -276,6 +296,12 @@ class Cmd
opts.on('--onlyworlds', 'Only update worlds') do
options['onlyworlds'] = '1'
end
opts.on('--defaults', 'Use default values') do
options['defaults'] = true
end
opts.on('--console', 'Output to console') do
options['console'] = true
end
end # opt_parser do

opt_parser.parse!(args)
Expand Down Expand Up @@ -347,6 +373,12 @@ class Cmd
end # parse()

def execute(args)
# Graceful exit on ctrl-c
Signal.trap("SIGINT") do
puts "\nExiting"
exit
end

options = parse(args)

# Read the plugin that handles the command.
Expand Down Expand Up @@ -394,6 +426,8 @@ class Cmd
end

case options['subcommand']
when 'configure'
configure(options['defaults'], options['console'])
when 'delete'
Importer.extern 'int deleteUrl(const char *, const char *)'
if not Importer.deleteUrl(options['url'], options['header'])
Expand Down Expand Up @@ -463,4 +497,118 @@ class Cmd
"from #{plugin}."
end # begin
end # execute

# Runs `gz fuel configure`
def configure(defaults, console)
# Default fuel directory and configuration path
local_fuel_dir = File.join(Dir.home, '.gz', 'fuel')
config_path = File.join(local_fuel_dir, 'config.yaml')

# The default Fuel server URL
default_url = 'https://fuel.gazebosim.org'

# A lambda function that prompts the user to enter Fuel server information
get_server_info = lambda {
server_url = default_url
default_name = 'Fuel'

# Prompt the user for a server name with a default value
# Repeat until the URL is valid, or the user hits ctrl-c
until defaults
print "Fuel server URL [#{default_url}]: "
server_url = STDIN.gets.chomp
server_url = default_url if server_url.empty?
begin
uri = URI.parse(server_url)
default_name = !uri.host || uri.host.empty? ? uri.to_s : uri.host
break
rescue URI::InvalidURIError
puts 'Invalid URL.\n'
end
end

# Prompt the user for an access token with a default value of an
# empty string
access_token = ''
unless defaults
print 'Optional access token [None]: '
access_token = STDIN.gets.chomp
end

# Get the cache location
cache = local_fuel_dir
unless defaults
print "Local cache path [#{local_fuel_dir}]: "
cache = STDIN.gets.chomp
cache = local_fuel_dir if cache.empty?
end

# Get a name to associate with this server
server_name = default_name
unless defaults
print "Name this server [#{default_name}]: "
server_name = STDIN.gets.chomp
server_name = default_name if server_name.empty?
end

[server_name, server_url, access_token, cache]
}

unless console
puts '# Set Fuel server configurations.'
puts "# This will create or replace `#{config_path}`.\n\n"
end

servers = []
confirmation = 'n'
# Allow the user to enter multiple Fuel servers
begin
server_name, server_url, access_token, cache = get_server_info.call
servers << { 'name' => server_name,
'url' => server_url,
'private-token' => access_token,
'cache' => { 'path' => cache } }
unless defaults
print "\nAdd another Fuel server? [y/N]:"
confirmation = STDIN.gets.chomp.downcase
confirmation = 'n' if confirmation.empty?
end
end while confirmation == 'y'

unless defaults || console
puts "\nReview:\n"
servers.each do |server|
puts " Name: #{server['name']}"
puts " URL: #{server['url']}"
puts " Cache: #{server['cache']['path']}"
puts " Access token: #{server['private-token']}"
puts "\n" if servers.size > 1
end
end

confirmation = 'y'
unless defaults || console
print 'Save? [Y/n]:'
confirmation = STDIN.gets.chomp.downcase
confirmation = 'y' if confirmation.empty?
end

# Save settings
if confirmation == 'y'
config = {'servers' => servers}

if console
puts config.to_yaml
else
# Make sure the ~/.gz/fuel directory exists.
FileUtils.mkdir_p(local_fuel_dir)

# Write to the config.yaml file
File.open(config_path, 'w') { |file| file.write(config.to_yaml) }
puts 'Settings saved to ~/.gz/fuel/config.yaml.'
end
else
puts 'Settings not saved.'
end
end
end # class
19 changes: 18 additions & 1 deletion src/gz_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ TEST(CmdLine, GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ListFail))
TEST(CmdLine,
GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ModelListConfigServerUgly))
{
auto output = custom_exec_str(g_listCmd + " -t model --raw");
auto output = custom_exec_str(g_listCmd +
" -t model --raw -u 'https://fuel.gazebosim.org/1.0' -o openrobotics");
EXPECT_NE(output.find("https://fuel.gazebosim.org/1.0/"),
std::string::npos) << output;
EXPECT_EQ(output.find("owners"), std::string::npos) << output;
Expand Down Expand Up @@ -151,3 +152,19 @@ TEST(CmdLine,
EXPECT_NE(output.find("owners"), std::string::npos) << output;
EXPECT_NE(output.find("worlds"), std::string::npos) << output;
}

TEST(CmdLine,
GZ_UTILS_TEST_ENABLED_ONLY_ON_LINUX(ConfigureDefaultsConsole))
{
std::string output = custom_exec_str(
g_exec + " fuel configure --console --defaults");

std::string expected =
"---\n"
"servers:\n"
"- name: Fuel\n"
" url: https://fuel.gazebosim.org\n"
" private-token: ''\n";

EXPECT_EQ(output.find(expected), 0);
}
3 changes: 2 additions & 1 deletion src/gz_src_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ TEST_F(CmdLine, ModelListFail)
// https://github.com/gazebosim/gz-fuel-tools/issues/105
TEST_F(CmdLine, ModelListConfigServerUgly)
{
EXPECT_TRUE(listModels("", "openroboticstest", "true"));
EXPECT_TRUE(listModels("https://fuel.gazebosim.org",
"openroboticstest", "true"));

EXPECT_NE(this->stdOutBuffer.str().find("https://fuel.gazebosim.org"),
std::string::npos) << this->stdOutBuffer.str();
Expand Down
11 changes: 11 additions & 0 deletions tutorials/02_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ The `cache` section captures options related with the local storage of the
assets. `path` specifies the local directory where all assets will be
downloaded. If not used, all assets are stored under `$HOME/.gz/fuel`.

## Guided Configuration

The `gz fuel configure` CLI will walk you through the process of creating a
`~/.gz/fuel/config.yaml` file. Just run the following command, and answer
the prompts. Note that this command will replace your existing `~/.gz/fuel/config.yaml`
if you choose to save on the last step.

```bash
gz fuel configure
```

## Custom configuration file path

Gazebo Fuel's default configuration file is stored under
Expand Down

0 comments on commit d14ffcd

Please sign in to comment.