From a8e35d5e1dd1eb2aa7ab504a28829c29fcea15b6 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 25 Nov 2024 14:26:25 +0100 Subject: [PATCH 1/4] Update install script with explicit update option (#412) --- docs/_config.yml | 10 +- docs/installation/start-stop.md | 30 ++++ docs/installation/uninstall.md | 9 +- docs/installation/update.md | 22 +++ install.sh | 249 +++++++++++++++++++++++++------- 5 files changed, 266 insertions(+), 54 deletions(-) create mode 100644 docs/installation/start-stop.md create mode 100644 docs/installation/update.md diff --git a/docs/_config.yml b/docs/_config.yml index ed0052bc9..526c621fb 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -29,4 +29,12 @@ gh_edit_link_text: "Edit this page on GitHub." gh_edit_repository: "https://github.com/lanedirt/AliasVault" # the github URL for your repo gh_edit_branch: "main" # the branch that your docs is served from gh_edit_source: docs # the source that your files originate from -gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately \ No newline at end of file +gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately + +callouts: + warning: + title: Warning + color: red + note: + title: Note + color: purple diff --git a/docs/installation/start-stop.md b/docs/installation/start-stop.md new file mode 100644 index 000000000..459eee4d5 --- /dev/null +++ b/docs/installation/start-stop.md @@ -0,0 +1,30 @@ +--- +layout: default +title: Start/stop +parent: Installation Guide +nav_order: 3 +--- + +# Starting and stopping AliasVault +You can start and stop AliasVault easily by using the install script. + +## Stop +To stop AliasVault, run the install script with the `stop` option. This will stop all running AliasVault containers. + +```bash +./install.sh stop +``` + +## Start +To start AliasVault, run the install script with the `start` option. This will start all AliasVault containers. + +```bash +./install.sh start +``` + +## Restart +To restart AliasVault, run the install script with the `restart` option. This will restart all AliasVault containers. + +```bash +./install.sh restart +``` diff --git a/docs/installation/uninstall.md b/docs/installation/uninstall.md index 4f03126d6..2dcd6032e 100644 --- a/docs/installation/uninstall.md +++ b/docs/installation/uninstall.md @@ -2,17 +2,18 @@ layout: default title: Uninstall parent: Installation Guide -nav_order: 3 +nav_order: 5 --- # Uninstall To uninstall AliasVault, run the install script with the `uninstall` option. This will stop and remove the AliasVault containers, remove the Docker images, and delete the .env file. +{: .note } +This will not delete any data stored in the database. If you wish to delete all data, you should manually delete the `database` directory and the other directories created by AliasVault. + ### Steps 1. Run the install script with the `uninstall` option ```bash ./install.sh uninstall -``` - -> **Note:** This will not delete any data stored in the database. If you wish to delete all data, you should manually delete the `database` directory and the other directories created by AliasVault. +``` \ No newline at end of file diff --git a/docs/installation/update.md b/docs/installation/update.md new file mode 100644 index 000000000..41c536f86 --- /dev/null +++ b/docs/installation/update.md @@ -0,0 +1,22 @@ +--- +layout: default +title: Update +parent: Installation Guide +nav_order: 4 +--- + +# Updating AliasVault +To update AliasVault to the latest version, run the install script with the `update` option. This will pull the latest version of AliasVault from GitHub and restart all containers. + +You can see the latest available version of AliasVault on [GitHub](https://github.com/lanedirt/AliasVault/releases). + +{: .warning } +Before updating, it's recommended to backup your database and other important data. You can do this by making +a copy of the `database` and `certificates` directories. + +## Updating to the latest version +To update to the latest version, run the install script with the `update` option. The script will check for the latest version and prompt you to confirm the update. Follow the prompts to complete the update. + +```bash +./install.sh update +``` \ No newline at end of file diff --git a/install.sh b/install.sh index a4dbbaae4..3515cbb8a 100755 --- a/install.sh +++ b/install.sh @@ -37,11 +37,15 @@ show_usage() { printf "Usage: $0 [COMMAND] [OPTIONS]\n" printf "\n" printf "Commands:\n" - printf " install Install AliasVault by pulling pre-built images from GitHub Container Registry (default)\n" - printf " build Build AliasVault from source (takes longer and requires sufficient specs)\n" - printf " reset-password Reset admin password\n" + printf " install Install AliasVault by pulling pre-built images from GitHub Container Registry (recommended)\n" printf " uninstall Uninstall AliasVault\n" + printf " update Update AliasVault to the latest version\n" printf " configure-ssl Configure SSL certificates (Let's Encrypt or self-signed)\n" + printf " start Start AliasVault containers\n" + printf " stop Stop AliasVault containers\n" + printf " restart Restart AliasVault containers\n" + printf " reset-password Reset admin password\n" + printf " build Build AliasVault from source (takes longer and requires sufficient specs)\n" printf "\n" printf "Options:\n" @@ -84,6 +88,22 @@ parse_args() { COMMAND="configure-ssl" shift ;; + start|s) + COMMAND="start" + shift + ;; + stop|st) + COMMAND="stop" + shift + ;; + restart|r) + COMMAND="restart" + shift + ;; + update|up) + COMMAND="update" + shift + ;; --verbose) VERBOSE=true shift @@ -136,6 +156,18 @@ main() { "configure-ssl") handle_ssl_configuration ;; + "start") + handle_start + ;; + "stop") + handle_stop + ;; + "restart") + handle_restart + ;; + "update") + handle_update + ;; esac } @@ -434,58 +466,32 @@ get_docker_compose_command() { echo "$base_command" } -# Function to handle installation +# Function to handle initial installation or reinstallation handle_install() { - printf "${YELLOW}+++ Installing AliasVault +++${NC}\n" - printf "\n" + # Check for existing version + local current_version="" + if grep -q "^ALIASVAULT_VERSION=" "$ENV_FILE"; then + current_version=$(grep "^ALIASVAULT_VERSION=" "$ENV_FILE" | cut -d '=' -f2) + printf "${CYAN}> Current AliasVault version: ${current_version}${NC}\n" + printf "${YELLOW}> AliasVault is already installed.${NC}\n" + printf "1. To reinstall the current version (${current_version}), continue with this script\n" + printf "2. To check for updates and to install the latest version, use: ./install.sh update\n" + printf "\n" - # Initialize workspace which makes sure all required directories and files exist - initialize_workspace - - # Initialize environment - create_env_file || { printf "${RED}> Failed to create .env file${NC}\n"; exit 1; } - populate_hostname || { printf "${RED}> Failed to set hostname${NC}\n"; exit 1; } - populate_jwt_key || { printf "${RED}> Failed to set JWT key${NC}\n"; exit 1; } - populate_data_protection_cert_pass || { printf "${RED}> Failed to set certificate password${NC}\n"; exit 1; } - set_private_email_domains || { printf "${RED}> Failed to set email domains${NC}\n"; exit 1; } - set_smtp_tls_enabled || { printf "${RED}> Failed to set SMTP TLS${NC}\n"; exit 1; } - set_support_email || { printf "${RED}> Failed to set support email${NC}\n"; exit 1; } - - # Only generate admin password if not already set - if ! grep -q "^ADMIN_PASSWORD_HASH=" "$ENV_FILE" || [ -z "$(grep "^ADMIN_PASSWORD_HASH=" "$ENV_FILE" | cut -d '=' -f2)" ]; then - generate_admin_password || { printf "${RED}> Failed to generate admin password${NC}\n"; exit 1; } - fi - - # Pull images from GitHub Container Registry - printf "\n${YELLOW}+++ Pulling Docker images +++${NC}\n" - printf "\n" - - images=( - "${GITHUB_CONTAINER_REGISTRY}-reverse-proxy:latest" - "${GITHUB_CONTAINER_REGISTRY}-api:latest" - "${GITHUB_CONTAINER_REGISTRY}-client:latest" - "${GITHUB_CONTAINER_REGISTRY}-admin:latest" - "${GITHUB_CONTAINER_REGISTRY}-smtp:latest" - ) - - for image in "${images[@]}"; do - printf "${CYAN}> Pulling $image...${NC}\n" - if [ "$VERBOSE" = true ]; then - docker pull $image || { printf "${RED}> Failed to pull image: $image${NC}\n"; exit 1; } - else - docker pull $image > /dev/null 2>&1 || { printf "${RED}> Failed to pull image: $image${NC}\n"; exit 1; } + read -p "Would you like to reinstall the current version? [y/N]: " REPLY + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + printf "${YELLOW}> Installation cancelled.${NC}\n" + exit 0 fi - done - # Start containers - printf "\n${YELLOW}+++ Starting services +++${NC}\n" - printf "\n" - recreate_docker_containers - - # Only show success message if we made it here without errors - print_success_message + handle_install_version "$current_version" + else + # First time installation, use latest + handle_install_version "latest" + fi } + # Function to handle build handle_build() { printf "${YELLOW}+++ Building AliasVault from source +++${NC}\n" @@ -814,4 +820,149 @@ generate_self_signed_cert() { printf "${GREEN}> New self-signed certificate has been generated successfully!${NC}\n" } +# New functions to handle container lifecycle: +handle_start() { + printf "${CYAN}> Starting AliasVault containers...${NC}\n" + $(get_docker_compose_command) up -d + printf "${GREEN}> AliasVault containers started successfully.${NC}\n" +} + +handle_stop() { + printf "${CYAN}> Stopping AliasVault containers...${NC}\n" + if ! docker compose ps --quiet 2>/dev/null | grep -q .; then + printf "${YELLOW}> No containers are currently running.${NC}\n" + exit 0 + fi + + $(get_docker_compose_command) down + printf "${GREEN}> AliasVault containers stopped successfully.${NC}\n" +} + +handle_restart() { + printf "${CYAN}> Restarting AliasVault containers...${NC}\n" + $(get_docker_compose_command) down + $(get_docker_compose_command) up -d + printf "${GREEN}> AliasVault containers restarted successfully.${NC}\n" +} + +# Function to handle updates +handle_update() { + printf "${YELLOW}+++ Checking for AliasVault updates +++${NC}\n" + printf "\n" + + # Check current version + if ! grep -q "^ALIASVAULT_VERSION=" "$ENV_FILE"; then + printf "${YELLOW}> No version information found. Running first-time update check...${NC}\n" + handle_install_version "latest" + return + fi + + current_version=$(grep "^ALIASVAULT_VERSION=" "$ENV_FILE" | cut -d '=' -f2) + latest_version=$(curl -s "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + + if [ -z "$latest_version" ]; then + printf "${RED}> Failed to check for updates. Please try again later.${NC}\n" + exit 1 + fi + + printf "${CYAN}> Current version: ${current_version}${NC}\n" + printf "${CYAN}> Latest version: ${latest_version}${NC}\n" + printf "\n" + + if [ "$current_version" = "$latest_version" ]; then + printf "${GREEN}> You are already running the latest version of AliasVault!${NC}\n" + exit 0 + fi + + printf "${YELLOW}> A new version of AliasVault is available!${NC}\n" + printf "\n" + printf "${MAGENTA}Important:${NC}\n" + printf "1. It's recommended to backup your database before updating\n" + printf "2. The update process will restart all containers\n" + printf "\n" + + read -p "Would you like to update to the latest version? [y/N]: " REPLY + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + printf "${YELLOW}> Update cancelled.${NC}\n" + exit 0 + fi + + printf "${CYAN}> Updating AliasVault...${NC}\n" + handle_install_version "$latest_version" + + printf "${GREEN}> Update completed successfully!${NC}\n" +} + +# Function to perform the actual installation with specific version +handle_install_version() { + local target_version="$1" + + # If latest, get actual version number from GitHub API + if [ "$target_version" = "latest" ]; then + local actual_version=$(curl -s "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + if [ -n "$actual_version" ]; then + target_version="$actual_version" + fi + fi + + printf "${YELLOW}+++ Installing AliasVault ${target_version} +++${NC}\n" + printf "\n" + + # Initialize workspace which makes sure all required directories and files exist + initialize_workspace + + # Initialize environment + create_env_file || { printf "${RED}> Failed to create .env file${NC}\n"; exit 1; } + populate_hostname || { printf "${RED}> Failed to set hostname${NC}\n"; exit 1; } + populate_jwt_key || { printf "${RED}> Failed to set JWT key${NC}\n"; exit 1; } + populate_data_protection_cert_pass || { printf "${RED}> Failed to set certificate password${NC}\n"; exit 1; } + set_private_email_domains || { printf "${RED}> Failed to set email domains${NC}\n"; exit 1; } + set_smtp_tls_enabled || { printf "${RED}> Failed to set SMTP TLS${NC}\n"; exit 1; } + set_support_email || { printf "${RED}> Failed to set support email${NC}\n"; exit 1; } + + # Only generate admin password if not already set + if ! grep -q "^ADMIN_PASSWORD_HASH=" "$ENV_FILE" || [ -z "$(grep "^ADMIN_PASSWORD_HASH=" "$ENV_FILE" | cut -d '=' -f2)" ]; then + generate_admin_password || { printf "${RED}> Failed to generate admin password${NC}\n"; exit 1; } + fi + + # Pull images from GitHub Container Registry + printf "\n${YELLOW}+++ Pulling Docker images +++${NC}\n" + printf "\n" + + printf "${CYAN}> Installing version: ${target_version}${NC}\n" + + local tag="$target_version" + if [ "$target_version" = "latest" ]; then + tag="latest" + fi + + images=( + "${GITHUB_CONTAINER_REGISTRY}-reverse-proxy:${tag}" + "${GITHUB_CONTAINER_REGISTRY}-api:${tag}" + "${GITHUB_CONTAINER_REGISTRY}-client:${tag}" + "${GITHUB_CONTAINER_REGISTRY}-admin:${tag}" + "${GITHUB_CONTAINER_REGISTRY}-smtp:${tag}" + ) + + for image in "${images[@]}"; do + printf "${CYAN}> Pulling $image...${NC}\n" + if [ "$VERBOSE" = true ]; then + docker pull $image || { printf "${RED}> Failed to pull image: $image${NC}\n"; exit 1; } + else + docker pull $image > /dev/null 2>&1 || { printf "${RED}> Failed to pull image: $image${NC}\n"; exit 1; } + fi + done + + # Save version to .env + update_env_var "ALIASVAULT_VERSION" "$target_version" + + # Start containers + printf "\n${YELLOW}+++ Starting services +++${NC}\n" + printf "\n" + recreate_docker_containers + + # Only show success message if we made it here without errors + print_success_message +} + main "$@" From 4c379802fc6fe859982cec18ceefc8ec74cf1506 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 25 Nov 2024 14:38:45 +0100 Subject: [PATCH 2/4] Update docker compose build workflow (#412) --- .github/workflows/docker-compose-build.yml | 80 +++++++++++----------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/.github/workflows/docker-compose-build.yml b/.github/workflows/docker-compose-build.yml index 09ee1acfc..9b932a89e 100644 --- a/.github/workflows/docker-compose-build.yml +++ b/.github/workflows/docker-compose-build.yml @@ -32,14 +32,14 @@ jobs: - name: Wait for services to be up run: | # Wait for a few seconds - sleep 10 - - name: Test if localhost:443 (WASM app) responds - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443) + sleep 15 + - name: Test if localhost:443 (WASM app) responds + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with 200 OK. Check if client app and/or nginx is configured correctly." exit 1 @@ -47,13 +47,13 @@ jobs: echo "Service responded with 200 OK" fi - - name: Test if localhost:443/api (WebApi) responds - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/api) + - name: Test if localhost:443/api (WebApi) responds + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/api) if [ "$http_code" -ne 200 ]; then echo "Service did not respond with expected 200 OK. Check if WebApi and/or nginx is configured correctly." exit 1 @@ -61,32 +61,32 @@ jobs: echo "Service responded with $http_code" fi - - name: Test if localhost:443/admin (Admin) responds - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/admin/user/login) - if [ "$http_code" -ne 200 ]; then - echo "Service did not respond with expected 200 OK. Check if admin app and/or nginx is configured correctly." - exit 1 - else - echo "Service responded with $http_code" - fi + - name: Test if localhost:443/admin (Admin) responds + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/admin/user/login) + if [ "$http_code" -ne 200 ]; then + echo "Service did not respond with expected 200 OK. Check if admin app and/or nginx is configured correctly." + exit 1 + else + echo "Service responded with $http_code" + fi - - name: Test if localhost:2525 (SmtpService) responds - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then - echo "SmtpService did not respond on port 2525. Check if the SmtpService service is running." - exit 1 - else - echo "SmtpService responded on port 2525" - fi + - name: Test if localhost:2525 (SmtpService) responds + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then + echo "SmtpService did not respond on port 2525. Check if the SmtpService service is running." + exit 1 + else + echo "SmtpService responded on port 2525" + fi - name: Test install.sh reset-password output run: | From c1d70fe50481b91118468ed45274e21b69a6079e Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 25 Nov 2024 14:39:04 +0100 Subject: [PATCH 3/4] Add support for installing specific version (#412) --- install.sh | 125 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 44 deletions(-) diff --git a/install.sh b/install.sh index 3515cbb8a..de5c5be5f 100755 --- a/install.sh +++ b/install.sh @@ -56,54 +56,74 @@ show_usage() { # Function to parse command line arguments parse_args() { - COMMAND="" # Remove default command + COMMAND="" VERBOSE=false FORCE_YES=false + COMMAND_ARG="" - # Show usage if no arguments provided if [ $# -eq 0 ]; then show_usage exit 0 fi + # First argument is always the command + case $1 in + install|i) + COMMAND="install" + shift + # Check for version argument + if [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; then + COMMAND_ARG="$1" + shift + fi + ;; + # Other commands remain unchanged + build|b) + COMMAND="build" + shift + ;; + uninstall|u) + COMMAND="uninstall" + shift + ;; + reset-password|reset-admin-password|rp) + COMMAND="reset-password" + shift + ;; + configure-ssl|ssl) + COMMAND="configure-ssl" + shift + ;; + start|s) + COMMAND="start" + shift + ;; + stop|st) + COMMAND="stop" + shift + ;; + restart|r) + COMMAND="restart" + shift + ;; + update|up) + COMMAND="update" + shift + ;; + --help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac + + # Parse remaining flags while [[ $# -gt 0 ]]; do case $1 in - install|i) - COMMAND="install" - shift - ;; - build|b) - COMMAND="build" - shift - ;; - uninstall|u) - COMMAND="uninstall" - shift - ;; - reset-password|reset-admin-password|rp) - COMMAND="reset-password" - shift - ;; - configure-ssl|ssl) - COMMAND="configure-ssl" - shift - ;; - start|s) - COMMAND="start" - shift - ;; - stop|st) - COMMAND="stop" - shift - ;; - restart|r) - COMMAND="restart" - shift - ;; - update|up) - COMMAND="update" - shift - ;; --verbose) VERBOSE=true shift @@ -112,10 +132,6 @@ parse_args() { FORCE_YES=true shift ;; - --help) - show_usage - exit 0 - ;; *) echo "Unknown option: $1" show_usage @@ -138,7 +154,7 @@ main() { print_logo case $COMMAND in "install") - handle_install + handle_install "$COMMAND_ARG" ;; "build") handle_build @@ -397,6 +413,16 @@ update_env_var() { printf " ${GREEN}> $key has been set in $ENV_FILE.${NC}\n" } +# Helper function to delete environment variables +delete_env_var() { + local key=$1 + + if [ -f "$ENV_FILE" ]; then + sed -i.bak "/^${key}=/d" "$ENV_FILE" && rm -f "$ENV_FILE.bak" + printf " ${GREEN}> $key has been removed from $ENV_FILE.${NC}\n" + fi +} + # Function to print success message print_success_message() { printf "\n" @@ -468,6 +494,14 @@ get_docker_compose_command() { # Function to handle initial installation or reinstallation handle_install() { + local specified_version="$1" + + # If version specified, install that version directly + if [ -n "$specified_version" ]; then + handle_install_version "$specified_version" + return + fi + # Check for existing version local current_version="" if grep -q "^ALIASVAULT_VERSION=" "$ENV_FILE"; then @@ -476,6 +510,7 @@ handle_install() { printf "${YELLOW}> AliasVault is already installed.${NC}\n" printf "1. To reinstall the current version (${current_version}), continue with this script\n" printf "2. To check for updates and to install the latest version, use: ./install.sh update\n" + printf "3. To install a specific version, use: ./install.sh install \n" printf "\n" read -p "Would you like to reinstall the current version? [y/N]: " REPLY @@ -491,7 +526,6 @@ handle_install() { fi } - # Function to handle build handle_build() { printf "${YELLOW}+++ Building AliasVault from source +++${NC}\n" @@ -600,6 +634,9 @@ handle_uninstall() { fi printf "${GREEN}> Docker containers stopped and removed.${NC}\n" + # Remove version from .env + delete_env_var "ALIASVAULT_VERSION" "" + printf "${CYAN}> Removing Docker images...${NC}\n" if [ "$VERBOSE" = true ]; then docker compose -f docker-compose.yml down --rmi all || { From 57f6b0961c09aa0060ee8a64533355e49c1da8bb Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 25 Nov 2024 14:40:38 +0100 Subject: [PATCH 4/4] Update docs (#412) --- docs/installation/update.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/installation/update.md b/docs/installation/update.md index 41c536f86..47b05fba5 100644 --- a/docs/installation/update.md +++ b/docs/installation/update.md @@ -14,9 +14,19 @@ You can see the latest available version of AliasVault on [GitHub](https://githu Before updating, it's recommended to backup your database and other important data. You can do this by making a copy of the `database` and `certificates` directories. -## Updating to the latest version +## Updating to the latest available version To update to the latest version, run the install script with the `update` option. The script will check for the latest version and prompt you to confirm the update. Follow the prompts to complete the update. ```bash ./install.sh update -``` \ No newline at end of file +``` + +## Installing a specific version +To install a specific version and skip the automatic version checks, run the install script with the `install` option and specify the version you want to install. + +```bash +./install.sh install + +# Example: +./install.sh install 0.7.0 +```