Merge pull request 'Flist CLI Updated for Linux, MacOS, Windows' (#7) from development_mac_2 into main
Reviewed-on: #7
This commit is contained in:
commit
1580602523
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
flist
|
||||
flist.exe
|
||||
test/
|
16
Makefile
16
Makefile
@ -4,12 +4,24 @@ build:
|
||||
sudo ./flist install
|
||||
|
||||
rebuild:
|
||||
sudo rm flist
|
||||
sudo flist uninstall
|
||||
v fmt -w flist.v
|
||||
v -o flist .
|
||||
sudo ./flist install
|
||||
|
||||
delete:
|
||||
sudo rm flist
|
||||
sudo flist uninstall
|
||||
|
||||
build-win:
|
||||
v fmt -w flist.v
|
||||
v -o flist .
|
||||
./flist.exe install
|
||||
|
||||
rebuild-win:
|
||||
./flist.exe uninstall
|
||||
v fmt -w flist.v
|
||||
v -o flist .
|
||||
./flist.exe install
|
||||
|
||||
delete-win:
|
||||
./flist.exe uninstall
|
62
README.md
62
README.md
@ -8,7 +8,6 @@
|
||||
- [Building and Installing](#building-and-installing)
|
||||
- [Rebuild and Uninstall](#rebuild-and-uninstall)
|
||||
- [Usage](#usage)
|
||||
- [Commands](#commands)
|
||||
- [OS-Specific Instructions](#os-specific-instructions)
|
||||
- [Linux](#linux)
|
||||
- [macOS](#macos)
|
||||
@ -29,34 +28,52 @@ Flist CLI is a tool that turns Dockerfiles and Docker images directly into Flist
|
||||
### Prerequisites
|
||||
|
||||
- [V programming language](https://vlang.io/) (latest version) installed on your system
|
||||
- Docker installed and running
|
||||
- Docker Engine installed and running (Linux)
|
||||
- Docker Desktop installed and running (MacOS+Windows)
|
||||
- Docker Hub account
|
||||
- TF Hub account and token
|
||||
|
||||
### Building and Installing
|
||||
|
||||
Clone this repository, build the project, and install the CLI:
|
||||
```
|
||||
git clone https://git.ourworld.tf/tfgrid/flist_cli_v
|
||||
cd flist_cli_v
|
||||
make build
|
||||
```
|
||||
- To clone this repository, build the project, and install the CLI:
|
||||
- MacOS and Linux
|
||||
```
|
||||
git clone https://git.ourworld.tf/tfgrid/flist_cli_v
|
||||
cd flist_cli_v
|
||||
make build
|
||||
```
|
||||
- Windows
|
||||
```
|
||||
git clone https://git.ourworld.tf/tfgrid/flist_cli_v
|
||||
cd flist_cli_v
|
||||
make build-win
|
||||
```
|
||||
|
||||
This will build the `flist` executable and install it to the appropriate system location.
|
||||
This will build the executable and install it to the appropriate system location.
|
||||
|
||||
### Rebuild and Uninstall
|
||||
|
||||
You can use the following Makefile commands:
|
||||
|
||||
- To rebuild and reinstall:
|
||||
- MacOS and Linux
|
||||
```
|
||||
make rebuild
|
||||
```
|
||||
- Windows
|
||||
```
|
||||
make rebuild-win
|
||||
```
|
||||
|
||||
- To uninstall and remove the binary:
|
||||
- MacOS and Linux
|
||||
```
|
||||
make delete
|
||||
```
|
||||
- Windows
|
||||
```
|
||||
make delete-win
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@ -66,24 +83,13 @@ After installation, you can use the `flist` command followed by various subcomma
|
||||
flist <command> [arguments]
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
- `install`: Install the Flist CLI
|
||||
- `uninstall`: Uninstall the Flist CLI
|
||||
- `login`: Log in to Docker Hub and save the Flist Hub token
|
||||
- `logout`: Log out of Docker Hub and remove the Flist Hub token
|
||||
- `push <image>:<tag>`: Build and push a Docker image, then convert and push it as an flist
|
||||
- `delete <flist_name>`: Delete an flist from Flist Hub
|
||||
- `rename <flist_name> <new_flist_name>`: Rename an flist in Flist Hub
|
||||
- `ls`: List all flists of the current user
|
||||
- `ls url`: List all flists of the current user with full URLs
|
||||
- `help`: Display help information
|
||||
Run `flist` or `flist help` to see all available commands for your specific OS.
|
||||
|
||||
## OS-Specific Instructions
|
||||
|
||||
### Linux
|
||||
|
||||
1. Ensure Docker is installed and the Docker daemon is running.
|
||||
1. Ensure Docker Engine is installed and running.
|
||||
2. The `flist` executable will be installed to:
|
||||
```
|
||||
/usr/local/bin/flist
|
||||
@ -99,21 +105,25 @@ flist <command> [arguments]
|
||||
|
||||
### Windows
|
||||
|
||||
For now, we recommend to use WSL and follow the Linux steps.
|
||||
1. Ensure Docker Desktop is installed and running.
|
||||
2. Run the program and installer in an admin PowerShell.
|
||||
3. The `flist.exe` executable will be installed to:
|
||||
```
|
||||
C:\\Program Files\\flist\\flist.exe
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If you encounter permission issues, ensure you're running the command with appropriate privileges (e.g., as administrator on Windows or with `sudo` on Unix-like systems).
|
||||
- Make sure Docker is running before using Flist CLI commands.
|
||||
- If you face issues with Docker commands, try logging out and logging back in to refresh your Docker credentials.
|
||||
- If you encounter compilation errors, ensure you have the latest version of V installed.
|
||||
- If you encounter compilation errors, ensure you have the latest version of V installed. To update v, run `v up`.
|
||||
|
||||
## Development
|
||||
|
||||
To modify the Flist CLI:
|
||||
|
||||
1. Make your changes to the `flist.v` file.
|
||||
2. Rebuild the project using `make rebuild`
|
||||
2. Rebuild the project using using the appropriate Make command.
|
||||
3. Test your changes thoroughly across different operating systems if possible.
|
||||
|
||||
## Contributing
|
||||
|
249
flist.v
249
flist.v
@ -2,15 +2,31 @@ import os
|
||||
import net.http
|
||||
import term
|
||||
import json
|
||||
import x.json2
|
||||
|
||||
const token_file = os.join_path(os.home_dir(), '.config', 'tfhubtoken')
|
||||
const binary_location = if os.user_os() == 'windows' {
|
||||
const docker_username_file = os.join_path(os.home_dir(), '.config', 'dockerusername')
|
||||
const config_dir = os.join_path(os.home_dir(), '.config')
|
||||
|
||||
const binary_location = $if windows {
|
||||
'C:\\Program Files\\flist\\flist.exe'
|
||||
} else {
|
||||
} $else {
|
||||
'/usr/local/bin/flist'
|
||||
}
|
||||
|
||||
const docker_cmd = $if windows {
|
||||
'docker'
|
||||
} $else {
|
||||
'sudo docker'
|
||||
}
|
||||
|
||||
const info_msg = $if windows {
|
||||
'Note: Make sure to use an admin PowerShell. Docker Desktop must be running to use the push function.\n'
|
||||
} $else $if linux {
|
||||
'Note: Docker Engine must be running to use the push function.\n'
|
||||
} $else $if macos {
|
||||
'Note: Docker Desktop must be running to use the push function.\n'
|
||||
}
|
||||
|
||||
struct FlistItem {
|
||||
name string
|
||||
}
|
||||
@ -23,6 +39,64 @@ struct Response {
|
||||
payload Payload
|
||||
}
|
||||
|
||||
fn add_path_windows() {
|
||||
// Define the new directory path to add
|
||||
new_path := r'C:\Program Files\flist'
|
||||
|
||||
// Get the current PATH environment variable
|
||||
current_path := os.getenv('PATH')
|
||||
|
||||
// Check if the new_path is already in the current PATH
|
||||
if current_path.contains(new_path) {
|
||||
println('The directory is already in the PATH.')
|
||||
return
|
||||
}
|
||||
|
||||
// Construct the new PATH by appending the new_path
|
||||
new_env_path := '${current_path};${new_path}'
|
||||
|
||||
// Prepare the command to set the new PATH
|
||||
cmd := 'setx PATH "${new_env_path}"'
|
||||
|
||||
// Execute the command to update the PATH
|
||||
exit_code := os.system(cmd.replace('"$', '${new_env_path}"'))
|
||||
|
||||
if exit_code == 0 {
|
||||
println('PATH updated successfully.')
|
||||
} else {
|
||||
println('Failed to update the PATH. Please try running the script as an administrator.')
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_path_windows() {
|
||||
// Define the directory path to remove
|
||||
path_to_remove := r'C:\Program Files\flist'
|
||||
|
||||
// Get the current PATH environment variable
|
||||
current_path := os.getenv('PATH')
|
||||
|
||||
// Check if the path_to_remove is in the current PATH
|
||||
if !current_path.contains(path_to_remove) {
|
||||
println('The directory is not in the PATH.')
|
||||
return
|
||||
}
|
||||
|
||||
// Remove specified directory from the PATH
|
||||
new_env_path := current_path.split(';').filter(it != path_to_remove).join(';')
|
||||
|
||||
// Prepare the command to set the new PATH
|
||||
cmd := 'setx PATH "${new_env_path}"'
|
||||
|
||||
// Execute the command to update the PATH
|
||||
exit_code := os.system(cmd.replace('"$', '${new_env_path}"'))
|
||||
|
||||
if exit_code == 0 {
|
||||
println('PATH updated successfully.')
|
||||
} else {
|
||||
println('Failed to update the PATH. Please try running the script as an administrator.')
|
||||
}
|
||||
}
|
||||
|
||||
fn error_message(msg string) {
|
||||
println(term.red('\nError: ') + msg)
|
||||
println(term.yellow("Run 'flist help' for usage information.\n"))
|
||||
@ -33,7 +107,7 @@ fn success_message(msg string) {
|
||||
}
|
||||
|
||||
fn info_message(msg string) {
|
||||
println(term.blue('\n' + msg + '\n'))
|
||||
println(term.cyan('\n' + msg + '\n'))
|
||||
}
|
||||
|
||||
fn create_box(content []string, padding int) string {
|
||||
@ -47,17 +121,17 @@ fn create_box(content []string, padding int) string {
|
||||
max_width += padding * 2
|
||||
|
||||
separator := '━'.repeat(max_width + 2) // +2 for left and right borders
|
||||
mut box_content := term.blue('┏${separator}┓') + '\n'
|
||||
mut box_content := term.cyan('┏${separator}┓') + '\n'
|
||||
|
||||
for line in content {
|
||||
clean_line := term.strip_ansi(line)
|
||||
padding_left := ' '.repeat(padding)
|
||||
padding_right := ' '.repeat(max_width - clean_line.len)
|
||||
box_content += term.blue('┃') + padding_left + line + padding_right + term.blue('┃') +
|
||||
box_content += term.cyan('┃') + padding_left + line + padding_right + term.cyan('┃') +
|
||||
'\n'
|
||||
}
|
||||
|
||||
box_content += term.blue('┗${separator}┛')
|
||||
box_content += term.cyan('┗${separator}┛')
|
||||
return box_content
|
||||
}
|
||||
|
||||
@ -68,8 +142,11 @@ fn install() {
|
||||
os.mkdir_all(os.dir(binary_location)) or { panic(err) }
|
||||
os.cp(current_exe, binary_location) or { panic(err) }
|
||||
os.chmod(binary_location, 0o755) or { panic(err) }
|
||||
$if windows {
|
||||
add_path_windows()
|
||||
}
|
||||
success_message('Flist CLI has been installed to ' + binary_location)
|
||||
info_message("You can now use it by running 'flist help'")
|
||||
info_message("Run 'flist help' to see all commands.")
|
||||
} else {
|
||||
error_message('Cannot find the executable file')
|
||||
exit(1)
|
||||
@ -78,26 +155,43 @@ fn install() {
|
||||
|
||||
fn uninstall() {
|
||||
info_message('Uninstalling Flist CLI...')
|
||||
|
||||
if os.exists(binary_location) {
|
||||
// Remove the binary file
|
||||
os.rm(binary_location) or { panic(err) }
|
||||
success_message('Flist CLI has been removed from ' + binary_location)
|
||||
} else {
|
||||
info_message('Flist CLI is not installed at ' + binary_location)
|
||||
}
|
||||
|
||||
// Uncomment the following block if you want to remove from PATH for Windows.
|
||||
// if os.user_os() == 'windows' {
|
||||
// remove_path_windows()
|
||||
// }
|
||||
}
|
||||
|
||||
fn login() {
|
||||
mut token_exists := os.exists(token_file)
|
||||
|
||||
os.mkdir_all(config_dir) or { panic(err) }
|
||||
if !token_exists {
|
||||
tfhub_token := os.input('Please enter your tfhub token: ')
|
||||
tfhub_token := os.input('Please enter your TF Hub token: ')
|
||||
os.write_file(token_file, tfhub_token) or { panic(err) }
|
||||
success_message('Token saved in ' + token_file)
|
||||
success_message('TF Hub token saved in ' + token_file)
|
||||
} else {
|
||||
info_message('Your Flist Hub token is already saved.')
|
||||
info_message('Your TF Hub token is already saved.')
|
||||
}
|
||||
|
||||
result := os.system('sudo docker login')
|
||||
mut result := 0
|
||||
|
||||
mut dockername_exists := os.exists(docker_username_file)
|
||||
|
||||
if !dockername_exists {
|
||||
docker_username := os.input('Please enter your Docker username: ')
|
||||
os.write_file(docker_username_file, docker_username) or { panic(err) }
|
||||
success_message('Docker username saved in ' + docker_username_file)
|
||||
info_message('Enter your Docker password.')
|
||||
result = os.system('${docker_cmd} login -u ${docker_username}')
|
||||
}
|
||||
|
||||
if result == 0 {
|
||||
info_message('\nYou are already logged in to Docker.')
|
||||
@ -109,82 +203,29 @@ fn login() {
|
||||
fn logout() {
|
||||
if os.exists(token_file) {
|
||||
os.rm(token_file) or { panic(err) }
|
||||
success_message('Your Flist Hub Token has been removed')
|
||||
success_message('Your TF Hub token has been removed')
|
||||
} else {
|
||||
info_message('Your Flist Hub Token was already not present.')
|
||||
info_message('Your TF Hub token was already not present.')
|
||||
}
|
||||
|
||||
exit_code := os.system('sudo docker logout')
|
||||
if os.exists(docker_username_file) {
|
||||
os.rm(docker_username_file) or { panic(err) }
|
||||
success_message('Your Docker username has been removed from the config folder.')
|
||||
} else {
|
||||
info_message('Your Docker username was already not present in the config folder.')
|
||||
}
|
||||
|
||||
exit_code := os.system('${docker_cmd} logout')
|
||||
if exit_code != 0 {
|
||||
error_message('Failed to log out from Docker Hub.')
|
||||
}
|
||||
|
||||
success_message('You are now logged out of Docker Hub and your Flist Hub token has been removed.')
|
||||
}
|
||||
|
||||
fn get_docker_credential() !string {
|
||||
// Try to get the Docker credential automatically
|
||||
credential := get_docker_credential_auto() or {
|
||||
// If automatic retrieval fails, prompt the user for input
|
||||
println(term.yellow("\nCouldn't find your Docker username automatically."))
|
||||
username := os.input('Please enter your Docker username and press ENTER: ')
|
||||
if username.trim_space() == '' {
|
||||
return error('No Docker username provided')
|
||||
}
|
||||
return username.trim_space()
|
||||
}
|
||||
return credential
|
||||
}
|
||||
|
||||
fn get_docker_credential_auto() !string {
|
||||
// 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")
|
||||
if system_info_result.exit_code == 0 && system_info_result.output.trim_space() != '' {
|
||||
return system_info_result.output.trim_space()
|
||||
}
|
||||
|
||||
// If the above method fails, proceed with the current method
|
||||
// Read the Docker config file
|
||||
config_path := os.join_path(os.home_dir(), '.docker', 'config.json')
|
||||
config_content := os.read_file(config_path) or {
|
||||
return error('Failed to read Docker config file: ${err}')
|
||||
}
|
||||
|
||||
// Parse the JSON content
|
||||
config := json2.raw_decode(config_content) or {
|
||||
return error('Failed to parse Docker config: ${err}')
|
||||
}
|
||||
|
||||
// Extract the credsStore value
|
||||
creds_store := config.as_map()['credsStore'] or {
|
||||
return error('credsStore not found in Docker config')
|
||||
}.str()
|
||||
|
||||
// Execute the docker-credential command
|
||||
cred_helper := 'docker-credential-${creds_store}'
|
||||
cred_output := os.execute('${cred_helper} list')
|
||||
if cred_output.exit_code != 0 {
|
||||
return error('Failed to execute ${cred_helper}: ${cred_output.output}')
|
||||
}
|
||||
|
||||
// Parse the credential list
|
||||
cred_list := json2.raw_decode(cred_output.output) or {
|
||||
return error('Failed to parse credential list: ${err}')
|
||||
}
|
||||
|
||||
// Find the first docker.io entry
|
||||
for key, value in cred_list.as_map() {
|
||||
if key.contains('docker.io') {
|
||||
return value.str()
|
||||
}
|
||||
}
|
||||
|
||||
return error('No docker.io credential found')
|
||||
success_message('You are now logged out of Docker Hub and your TF Hub token has been removed.')
|
||||
}
|
||||
|
||||
fn push(tag string) {
|
||||
docker_user := get_docker_credential() or {
|
||||
error_message('Failed to get Docker username: ${err}')
|
||||
docker_user := os.read_file(docker_username_file) or {
|
||||
error_message("No Docker username found. Please run 'flist login' first.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@ -193,18 +234,18 @@ fn push(tag string) {
|
||||
full_tag := '${docker_user}/${tag}'
|
||||
|
||||
tfhub_token := os.read_file(token_file) or {
|
||||
error_message("No token found. Please run 'flist login' first.")
|
||||
error_message("No TF Hub token found. Please run 'flist login' first.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
info_message('Starting Docker build')
|
||||
if os.system('sudo docker buildx build -t ${full_tag} .') != 0 {
|
||||
if os.system('${docker_cmd} buildx build -t ${full_tag} .') != 0 {
|
||||
error_message('Docker build failed')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
info_message('Finished local Docker build, now pushing to Docker Hub')
|
||||
if os.system('sudo docker push ${full_tag}') != 0 {
|
||||
if os.system('${docker_cmd} push ${full_tag}') != 0 {
|
||||
error_message('Docker push failed')
|
||||
exit(1)
|
||||
}
|
||||
@ -236,7 +277,7 @@ fn push(tag string) {
|
||||
|
||||
if response.status_code == 200 {
|
||||
hub_user := get_hub_username(tfhub_token) or {
|
||||
error_message('Failed to get hub username')
|
||||
error_message('Failed to get TF Hub username')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@ -254,9 +295,9 @@ fn push(tag string) {
|
||||
'',
|
||||
'You can access your Flist using the URL above.',
|
||||
'To manage your Flists, use the following commands:',
|
||||
term.yellow(' flist ls ') + '- List all your Flists',
|
||||
term.yellow(' flist delete') + '- Delete an Flist',
|
||||
term.yellow(' flist rename') + '- Rename an Flist',
|
||||
term.yellow(' flist ls ') + ' - List all your Flists',
|
||||
term.yellow(' flist delete') + ' - Delete an Flist',
|
||||
term.yellow(' flist rename') + ' - Rename an Flist',
|
||||
]
|
||||
|
||||
println(create_box(success_content, 2))
|
||||
@ -270,7 +311,7 @@ fn push(tag string) {
|
||||
|
||||
fn delete(flist_name string) {
|
||||
tfhub_token := os.read_file(token_file) or {
|
||||
error_message("No token found. Please run 'flist login' first.")
|
||||
error_message("No TF Hub token found. Please run 'flist login' first.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@ -296,7 +337,7 @@ fn delete(flist_name string) {
|
||||
|
||||
fn rename(flist_name string, new_flist_name string) {
|
||||
tfhub_token := os.read_file(token_file) or {
|
||||
error_message("No token found. Please run 'flist login' first.")
|
||||
error_message("No TF Hub token found. Please run 'flist login' first.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@ -357,7 +398,7 @@ fn get_hub_username(tfhub_token string) ?string {
|
||||
|
||||
fn ls(show_url bool) {
|
||||
tfhub_token := os.read_file(token_file) or {
|
||||
error_message("No token found. Please run 'flist login' first.")
|
||||
error_message("No TF Hub token found. Please run 'flist login' first.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@ -409,21 +450,29 @@ fn help() {
|
||||
println(create_box([welcome_msg], 2))
|
||||
|
||||
println('This tool turns Dockerfiles and Docker images directly into Flists on the TF Flist Hub, passing by the Docker Hub.\n')
|
||||
println(term.cyan(info_msg))
|
||||
println(term.bold('Available commands:'))
|
||||
println(term.blue(' install ') + '- Install 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(' 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(' delete ') + '- Delete an Flist from 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 url ') + '- List all Flists of the current user with full URLs')
|
||||
println(term.blue(' help ') + '- Display this help message\n')
|
||||
$if !windows {
|
||||
println(term.cyan(' install ') + ' - Install the Flist CLI')
|
||||
println(term.cyan(' uninstall') + ' - Uninstall the Flist CLI')
|
||||
}
|
||||
println(term.cyan(' login ') + ' - Log in to Docker Hub and save the Flist Hub token')
|
||||
println(term.cyan(' logout ') + ' - Log out of Docker Hub and remove the Flist Hub token')
|
||||
println(term.cyan(' push ') +
|
||||
' - Build and push a Docker image to Docker Hub, then convert and push it as an Flist to Flist Hub')
|
||||
println(term.cyan(' delete ') + ' - Delete an Flist from Flist Hub')
|
||||
println(term.cyan(' rename ') + ' - Rename an Flist in Flist Hub')
|
||||
println(term.cyan(' ls ') + ' - List all Flists of the current user')
|
||||
println(term.cyan(' ls url ') + ' - List all Flists of the current user with full URLs')
|
||||
println(term.cyan(' help ') + ' - Display this help message\n')
|
||||
println(term.bold('Usage:'))
|
||||
$if linux {
|
||||
println(term.yellow(' sudo ./flist install'))
|
||||
println(term.yellow(' sudo flist uninstall'))
|
||||
} $else $if macos {
|
||||
println(term.yellow(' sudo ./flist install'))
|
||||
println(term.yellow(' flist uninstall'))
|
||||
}
|
||||
println(term.yellow(' flist login'))
|
||||
println(term.yellow(' flist logout'))
|
||||
println(term.yellow(' flist push <image>:<tag>'))
|
||||
|
Loading…
Reference in New Issue
Block a user