This commit is contained in:
mik-tf 2024-10-05 18:31:50 -04:00
parent b7669954d6
commit 3947c93e59

115
flist.v
View File

@ -4,14 +4,12 @@ import term
import json import json
import x.json2 import x.json2
const ( const token_file = os.join_path(os.home_dir(), '.config', 'tfhubtoken')
token_file = os.join_path(os.home_dir(), '.config', 'tfhubtoken') const binary_location = if os.user_os() == 'windows' {
binary_location = if os.user_os() == 'windows' {
'C:\\Program Files\\flist\\flist.exe' 'C:\\Program Files\\flist\\flist.exe'
} else { } else {
'/usr/local/bin/flist' '/usr/local/bin/flist'
} }
)
struct FlistItem { struct FlistItem {
name string name string
@ -27,7 +25,7 @@ struct Response {
fn error_message(msg string) { fn error_message(msg string) {
println(term.red('\nError: ') + msg) println(term.red('\nError: ') + msg)
println(term.yellow('Run \'flist help\' for usage information.\n')) println(term.yellow("Run 'flist help' for usage information.\n"))
} }
fn success_message(msg string) { fn success_message(msg string) {
@ -49,16 +47,17 @@ fn create_box(content []string, padding int) string {
max_width += padding * 2 max_width += padding * 2
separator := '━'.repeat(max_width + 2) // +2 for left and right borders separator := '━'.repeat(max_width + 2) // +2 for left and right borders
mut box_content := term.blue('$separator') + '\n' mut box_content := term.blue('${separator}') + '\n'
for line in content { for line in content {
clean_line := term.strip_ansi(line) clean_line := term.strip_ansi(line)
padding_left := ' '.repeat(padding) padding_left := ' '.repeat(padding)
padding_right := ' '.repeat(max_width - clean_line.len) padding_right := ' '.repeat(max_width - clean_line.len)
box_content += term.blue('┃') + padding_left + line + padding_right + term.blue('┃') + '\n' box_content += term.blue('┃') + padding_left + line + padding_right + term.blue('┃') +
'\n'
} }
box_content += term.blue('$separator') box_content += term.blue('${separator}')
return box_content return box_content
} }
@ -70,7 +69,7 @@ fn install() {
os.cp(current_exe, binary_location) or { panic(err) } os.cp(current_exe, binary_location) or { panic(err) }
os.chmod(binary_location, 0o755) or { panic(err) } os.chmod(binary_location, 0o755) or { panic(err) }
success_message('Flist CLI has been installed to ' + binary_location) success_message('Flist CLI has been installed to ' + binary_location)
info_message('You can now use it by running \'flist help\'') info_message("You can now use it by running 'flist help'")
} else { } else {
error_message('Cannot find the executable file') error_message('Cannot find the executable file')
exit(1) exit(1)
@ -109,7 +108,7 @@ fn login() {
fn logout() { fn logout() {
if os.exists(token_file) { if os.exists(token_file) {
os.rm(token_file) or {panic(err)} os.rm(token_file) or { panic(err) }
success_message('Your Flist Hub Token has been removed') success_message('Your Flist Hub Token has been removed')
} else { } else {
info_message('Your Flist Hub Token was already not present.') info_message('Your Flist Hub Token was already not present.')
@ -127,7 +126,7 @@ fn get_docker_credential() !string {
// Try to get the Docker credential automatically // Try to get the Docker credential automatically
credential := get_docker_credential_auto() or { credential := get_docker_credential_auto() or {
// If automatic retrieval fails, prompt the user for input // If automatic retrieval fails, prompt the user for input
println(term.yellow('\nCouldn\'t find your Docker username automatically.')) println(term.yellow("\nCouldn't find your Docker username automatically."))
username := os.input('Please enter your Docker username and press ENTER: ') username := os.input('Please enter your Docker username and press ENTER: ')
if username.trim_space() == '' { if username.trim_space() == '' {
return error('No Docker username provided') return error('No Docker username provided')
@ -139,7 +138,7 @@ fn get_docker_credential() !string {
fn get_docker_credential_auto() !string { fn get_docker_credential_auto() !string {
// First, try to get the Docker username using the system info command // First, try to get the Docker username using the system info command
system_info_result := os.execute('sudo docker system info | grep \'Username\' | cut -d \' \' -f 3') system_info_result := os.execute("sudo docker system info | grep 'Username' | cut -d ' ' -f 3")
if system_info_result.exit_code == 0 && system_info_result.output.trim_space() != '' { if system_info_result.exit_code == 0 && system_info_result.output.trim_space() != '' {
return system_info_result.output.trim_space() return system_info_result.output.trim_space()
} }
@ -148,12 +147,12 @@ fn get_docker_credential_auto() !string {
// Read the Docker config file // Read the Docker config file
config_path := os.join_path(os.home_dir(), '.docker', 'config.json') config_path := os.join_path(os.home_dir(), '.docker', 'config.json')
config_content := os.read_file(config_path) or { config_content := os.read_file(config_path) or {
return error('Failed to read Docker config file: $err') return error('Failed to read Docker config file: ${err}')
} }
// Parse the JSON content // Parse the JSON content
config := json2.raw_decode(config_content) or { config := json2.raw_decode(config_content) or {
return error('Failed to parse Docker config: $err') return error('Failed to parse Docker config: ${err}')
} }
// Extract the credsStore value // Extract the credsStore value
@ -162,15 +161,15 @@ fn get_docker_credential_auto() !string {
}.str() }.str()
// Execute the docker-credential command // Execute the docker-credential command
cred_helper := 'docker-credential-$creds_store' cred_helper := 'docker-credential-${creds_store}'
cred_output := os.execute('$cred_helper list') cred_output := os.execute('${cred_helper} list')
if cred_output.exit_code != 0 { if cred_output.exit_code != 0 {
return error('Failed to execute $cred_helper: ${cred_output.output}') return error('Failed to execute ${cred_helper}: ${cred_output.output}')
} }
// Parse the credential list // Parse the credential list
cred_list := json2.raw_decode(cred_output.output) or { cred_list := json2.raw_decode(cred_output.output) or {
return error('Failed to parse credential list: $err') return error('Failed to parse credential list: ${err}')
} }
// Find the first docker.io entry // Find the first docker.io entry
@ -185,16 +184,16 @@ fn get_docker_credential_auto() !string {
fn push(tag string) { fn push(tag string) {
docker_user := get_docker_credential() or { docker_user := get_docker_credential() or {
error_message('Failed to get Docker username: $err') error_message('Failed to get Docker username: ${err}')
exit(1) exit(1)
} }
info_message('Docker username: $docker_user') info_message('Docker username: ${docker_user}')
full_tag := '${docker_user}/${tag}' full_tag := '${docker_user}/${tag}'
tfhub_token := os.read_file(token_file) or { tfhub_token := os.read_file(token_file) or {
error_message('No token found. Please run \'flist login\' first.') error_message("No token found. Please run 'flist login' first.")
exit(1) exit(1)
} }
@ -213,7 +212,7 @@ fn push(tag string) {
info_message('Converting Docker image to Flist now...') info_message('Converting Docker image to Flist now...')
url := 'https://hub.grid.tf/api/flist/me/docker' url := 'https://hub.grid.tf/api/flist/me/docker'
data := 'image=$full_tag' data := 'image=${full_tag}'
mut config := http.FetchConfig{ mut config := http.FetchConfig{
url: url url: url
@ -221,17 +220,17 @@ fn push(tag string) {
data: data data: data
header: http.new_header( header: http.new_header(
key: .authorization key: .authorization
value: 'bearer $tfhub_token' value: 'bearer ${tfhub_token}'
) )
} }
config.header.add_custom('Content-Type', 'application/x-www-form-urlencoded') or { config.header.add_custom('Content-Type', 'application/x-www-form-urlencoded') or {
error_message('Add custom failed: $err') error_message('Add custom failed: ${err}')
exit(1) exit(1)
} }
response := http.fetch(config) or { response := http.fetch(config) or {
error_message('HTTP POST request failed: $err') error_message('HTTP POST request failed: ${err}')
exit(1) exit(1)
} }
@ -242,10 +241,11 @@ fn push(tag string) {
} }
flist_name := tag.replace(':', '-') + '.flist' flist_name := tag.replace(':', '-') + '.flist'
flist_url := 'https://hub.grid.tf/$hub_user/$flist_name' flist_url := 'https://hub.grid.tf/${hub_user}/${flist_name}'
success_content := [ success_content := [
term.bold(term.green('Success!') + ' Your Flist has been created and pushed to the TF Hub.'), term.bold(term.green('Success!') +
' Your Flist has been created and pushed to the TF Hub.'),
'', '',
term.bold('Flist Details:'), term.bold('Flist Details:'),
term.yellow('Name: ') + flist_name, term.yellow('Name: ') + flist_name,
@ -256,7 +256,7 @@ fn push(tag string) {
'To manage your Flists, use the following commands:', 'To manage your Flists, use the following commands:',
term.yellow(' flist ls ') + '- List all your Flists', term.yellow(' flist ls ') + '- List all your Flists',
term.yellow(' flist delete') + '- Delete an Flist', term.yellow(' flist delete') + '- Delete an Flist',
term.yellow(' flist rename') + '- Rename an Flist' term.yellow(' flist rename') + '- Rename an Flist',
] ]
println(create_box(success_content, 2)) println(create_box(success_content, 2))
@ -270,7 +270,7 @@ fn push(tag string) {
fn delete(flist_name string) { fn delete(flist_name string) {
tfhub_token := os.read_file(token_file) or { tfhub_token := os.read_file(token_file) or {
error_message('No token found. Please run \'flist login\' first.') error_message("No token found. Please run 'flist login' first.")
exit(1) exit(1)
} }
@ -296,7 +296,7 @@ fn delete(flist_name string) {
fn rename(flist_name string, new_flist_name string) { fn rename(flist_name string, new_flist_name string) {
tfhub_token := os.read_file(token_file) or { tfhub_token := os.read_file(token_file) or {
error_message('No token found. Please run \'flist login\' first.') error_message("No token found. Please run 'flist login' first.")
exit(1) exit(1)
} }
@ -328,22 +328,22 @@ fn get_hub_username(tfhub_token string) ?string {
method: .get method: .get
header: http.new_header( header: http.new_header(
key: .authorization key: .authorization
value: 'bearer $tfhub_token' value: 'bearer ${tfhub_token}'
) )
} }
response := http.fetch(config) or { response := http.fetch(config) or {
error_message('Failed to fetch hub username: $err') error_message('Failed to fetch hub username: ${err}')
return none return none
} }
if response.status_code != 200 { if response.status_code != 200 {
error_message('Failed to fetch hub username. Status code: $response.status_code') error_message('Failed to fetch hub username. Status code: ${response.status_code}')
return none return none
} }
parsed_response := json.decode(Response, response.body) or { parsed_response := json.decode(Response, response.body) or {
error_message('Failed to parse JSON response: $err') error_message('Failed to parse JSON response: ${err}')
return none return none
} }
@ -357,7 +357,7 @@ fn get_hub_username(tfhub_token string) ?string {
fn ls(show_url bool) { fn ls(show_url bool) {
tfhub_token := os.read_file(token_file) or { tfhub_token := os.read_file(token_file) or {
error_message('No token found. Please run \'flist login\' first.') error_message("No token found. Please run 'flist login' first.")
exit(1) exit(1)
} }
@ -366,29 +366,29 @@ fn ls(show_url bool) {
exit(1) exit(1)
} }
url := 'https://hub.grid.tf/api/flist/$hub_user' url := 'https://hub.grid.tf/api/flist/${hub_user}'
config := http.FetchConfig{ config := http.FetchConfig{
url: url url: url
method: .get method: .get
header: http.new_header( header: http.new_header(
key: .authorization key: .authorization
value: 'bearer $tfhub_token' value: 'bearer ${tfhub_token}'
) )
} }
response := http.fetch(config) or { response := http.fetch(config) or {
error_message('Failed to fetch data: $err') error_message('Failed to fetch data: ${err}')
exit(1) exit(1)
} }
if response.status_code != 200 { if response.status_code != 200 {
error_message('Failed to fetch data. Status code: $response.status_code') error_message('Failed to fetch data. Status code: ${response.status_code}')
exit(1) exit(1)
} }
data := json.decode([]FlistItem, response.body) or { data := json.decode([]FlistItem, response.body) or {
error_message('Failed to parse JSON: $err') error_message('Failed to parse JSON: ${err}')
exit(1) exit(1)
} }
@ -414,7 +414,8 @@ fn help() {
println(term.blue(' uninstall ') + '- Uninstall the Flist CLI') println(term.blue(' uninstall ') + '- Uninstall the Flist CLI')
println(term.blue(' login ') + '- Log in to Docker Hub and save the Flist Hub token') println(term.blue(' login ') + '- Log in to Docker Hub and save the Flist Hub token')
println(term.blue(' logout ') + '- Log out of Docker Hub and remove the Flist Hub token') println(term.blue(' logout ') + '- Log out of Docker Hub and remove the Flist Hub token')
println(term.blue(' push ') + '- Build and push a Docker image to Docker Hub, then convert and push it as an Flist to Flist Hub') println(term.blue(' push ') +
'- Build and push a Docker image to Docker Hub, then convert and push it as an Flist to Flist Hub')
println(term.blue(' delete ') + '- Delete an Flist from Flist Hub') println(term.blue(' delete ') + '- Delete an Flist from Flist Hub')
println(term.blue(' rename ') + '- Rename an Flist in Flist Hub') println(term.blue(' rename ') + '- Rename an Flist in Flist Hub')
println(term.blue(' ls ') + '- List all Flists of the current user') println(term.blue(' ls ') + '- List all Flists of the current user')
@ -440,23 +441,31 @@ fn main() {
} }
match os.args[1] { match os.args[1] {
'install' { install() } 'install' {
'uninstall' { uninstall() } install()
}
'uninstall' {
uninstall()
}
'push' { 'push' {
if os.args.len == 3 { if os.args.len == 3 {
push(os.args[2]) push(os.args[2])
} else { } else {
error_message('Incorrect number of arguments for \'push\'.') error_message("Incorrect number of arguments for 'push'.")
exit(1) exit(1)
} }
} }
'login' { login() } 'login' {
'logout' { logout() } login()
}
'logout' {
logout()
}
'delete' { 'delete' {
if os.args.len == 3 { if os.args.len == 3 {
delete(os.args[2]) delete(os.args[2])
} else { } else {
error_message('Incorrect number of arguments for \'delete\'.') error_message("Incorrect number of arguments for 'delete'.")
exit(1) exit(1)
} }
} }
@ -464,7 +473,7 @@ fn main() {
if os.args.len == 4 { if os.args.len == 4 {
rename(os.args[2], os.args[3]) rename(os.args[2], os.args[3])
} else { } else {
error_message('Incorrect number of arguments for \'rename\'.') error_message("Incorrect number of arguments for 'rename'.")
exit(1) exit(1)
} }
} }
@ -474,11 +483,13 @@ fn main() {
} else if os.args.len == 3 && os.args[2] == 'url' { } else if os.args.len == 3 && os.args[2] == 'url' {
ls(true) ls(true)
} else { } else {
error_message('Incorrect usage of \'ls\'. Use \'ls\' or \'ls url\'.') error_message("Incorrect usage of 'ls'. Use 'ls' or 'ls url'.")
exit(1) exit(1)
} }
} }
'help' { help() } 'help' {
help()
}
else { else {
error_message('Unknown command: ' + os.args[1]) error_message('Unknown command: ' + os.args[1])
exit(1) exit(1)