Compare commits
10 commits
154aa0359d
...
ed7e173799
Author | SHA1 | Date | |
---|---|---|---|
ed7e173799 | |||
e85f6a4d7b | |||
55a2f7e14c | |||
418647a9f0 | |||
5ecc2ba202 | |||
7aaf3ea742 | |||
a2830587c9 | |||
4c67f06599 | |||
f42607b489 | |||
fe0755dd78 |
3 changed files with 252 additions and 250 deletions
|
@ -23,8 +23,8 @@ fi
|
||||||
|
|
||||||
# find the latest release of the requested version and download it
|
# find the latest release of the requested version and download it
|
||||||
echo "downloading minecraft $VERSION..."
|
echo "downloading minecraft $VERSION..."
|
||||||
RELEASE=`curl -s https://papermc.io/api/v2/projects/paper/versions/$VERSION/ | jq -r .builds[-1]`
|
RELEASE=`curl -s https://api.papermc.io/v2/projects/paper/versions/$VERSION/ | jq -r .builds[-1]`
|
||||||
curl https://papermc.io/api/v2/projects/paper/versions/$VERSION/builds/$RELEASE/downloads/paper-$VERSION-$RELEASE.jar -o paperclip.jar
|
curl https://api.papermc.io/v2/projects/paper/versions/$VERSION/builds/$RELEASE/downloads/paper-$VERSION-$RELEASE.jar -o paperclip.jar
|
||||||
|
|
||||||
echo "preparing..." # making sure things have the right owners
|
echo "preparing..." # making sure things have the right owners
|
||||||
adduser --disabled-password --gecos "" mc
|
adduser --disabled-password --gecos "" mc
|
||||||
|
|
250
deploy.sh
250
deploy.sh
|
@ -1,259 +1,13 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# required for the complex pipes in the machine function
|
# required for the complex pipes in the machine function
|
||||||
shopt -s lastpipe
|
shopt -s lastpipe
|
||||||
|
install_dir="$(dirname "$(readlink -f "$0")")" # find the install directory
|
||||||
|
source "$install_dir/functions.sh"
|
||||||
|
|
||||||
#TODO: add these to config create, probably with a select menu or something
|
#TODO: add these to config create, probably with a select menu or something
|
||||||
size="s-2vcpu-4gb"
|
size="s-2vcpu-4gb"
|
||||||
reigon="sfo3"
|
reigon="sfo3"
|
||||||
|
|
||||||
config() {
|
|
||||||
local command="$1"; shift
|
|
||||||
|
|
||||||
case $command in
|
|
||||||
get)
|
|
||||||
echo 'Checking config...'
|
|
||||||
if [ -a "${XDG_CONFIG_HOME}/auto-mc-redux/conf" ]; then
|
|
||||||
source "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
|
||||||
elif [ -a "$HOME/.config/auto-mc-redux/conf" ]; then
|
|
||||||
source "${HOME}/.config/auto-mc-redux/conf"
|
|
||||||
else
|
|
||||||
echo 'No config found!'
|
|
||||||
config create
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
create)
|
|
||||||
read -p "Enter your DigialOcean API token: " TOKEN
|
|
||||||
# this parameter expands to nothing if unset, or x if it is.
|
|
||||||
if [ -z ${XDG_CONFIG_HOME+x} ]; then
|
|
||||||
mkdir -p "${HOME}/.config/auto-mc-redux"
|
|
||||||
echo export TOKEN="${TOKEN}" > "${HOME}/.config/auto-mc-redux/conf"
|
|
||||||
chmod 600 "${HOME}/.config/auto-mc-redux/conf"
|
|
||||||
else
|
|
||||||
mkdir -p "${XDG_CONFIG_HOME}/auto-mc-redux"
|
|
||||||
echo export TOKEN="${TOKEN}" > "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
|
||||||
chmod 600 "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
firewall(){
|
|
||||||
local command="$1"; shift
|
|
||||||
|
|
||||||
case $command in
|
|
||||||
# exits 0 if firewall with tag auto-mc exists, 1 if it does not
|
|
||||||
check)
|
|
||||||
echo 'Querying firewall rules...'
|
|
||||||
curl -s -X GET \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
https://api.digitalocean.com/v2/firewalls |\
|
|
||||||
jq -r .firewalls[].name | grep -q "^auto-mc$"
|
|
||||||
;;
|
|
||||||
create)
|
|
||||||
echo 'Creating firewall rule'
|
|
||||||
curl -s -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
-d @- \
|
|
||||||
"https://api.digitalocean.com/v2/firewalls" <<-json
|
|
||||||
{
|
|
||||||
"name": "auto-mc",
|
|
||||||
"tags":["auto-mc"],
|
|
||||||
"inbound_rules": [
|
|
||||||
{
|
|
||||||
"protocol": "icmp",
|
|
||||||
"ports": "0",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"protocol": "tcp",
|
|
||||||
"ports": "25565",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"protocol": "udp",
|
|
||||||
"ports": "25565",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"outbound_rules": [
|
|
||||||
{
|
|
||||||
"protocol": "icmp",
|
|
||||||
"ports": "0",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"protocol": "tcp",
|
|
||||||
"ports": "0",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"protocol": "udp",
|
|
||||||
"ports": "0",
|
|
||||||
"sources": {
|
|
||||||
"addresses": [
|
|
||||||
"0.0.0.0/0",
|
|
||||||
"::/0"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
json
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
keys(){
|
|
||||||
local command="$1"; shift
|
|
||||||
|
|
||||||
case $command in
|
|
||||||
check)
|
|
||||||
echo "Checking DigitalOcean's keystore..."
|
|
||||||
curl -s -X GET \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/account/keys" |\
|
|
||||||
jq -r .ssh_keys[].public_key |\
|
|
||||||
while read remote_key; do
|
|
||||||
if grep -q "^$remote_key$" "$HOME/.ssh/"*.pub; then
|
|
||||||
authed_key="$remote_key"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$authed_key" ]; then
|
|
||||||
echo "Local key already installed..."
|
|
||||||
ssh-keygen -l -E md5 -f <(echo "$authed_key") |\
|
|
||||||
cut -d':' -f 2- | cut -d' ' -f 1 | read fingerprint
|
|
||||||
export fingerprint
|
|
||||||
else
|
|
||||||
keys copy
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
copy)
|
|
||||||
key_file="$(find "$HOME/.ssh/"*.pub | sort | head -1)"
|
|
||||||
cat "$key_file" | read key
|
|
||||||
key_name=$(echo "$key" | cut --complement -d'@' -f 1)
|
|
||||||
echo "Copying key..."
|
|
||||||
curl -s -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
-d '{"name":"'"$key_name"'","public_key":"'"$key"'"}' \
|
|
||||||
"https://api.digitalocean.com/v2/account/keys" |
|
|
||||||
jq -r .ssh_key.fingerprint | read fingerprint
|
|
||||||
export fingerprint
|
|
||||||
echo "Key copied!"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
machine(){
|
|
||||||
local command="$1"; shift
|
|
||||||
local arg="$1"; shift
|
|
||||||
|
|
||||||
case $command in
|
|
||||||
create)
|
|
||||||
# read isn't used in these pipelines because of job control shinanigans
|
|
||||||
echo 'Creating Droplet...'
|
|
||||||
curl -s -X POST \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
-d '{"name":"auto-mc",
|
|
||||||
"size":"'$size'",
|
|
||||||
"region":"'$reigon'",
|
|
||||||
"image":"ubuntu-24-10-x64",
|
|
||||||
"ssh_keys":["'$fingerprint'"],
|
|
||||||
"tags":["auto-mc"]}' \
|
|
||||||
"https://api.digitalocean.com/v2/droplets" |\
|
|
||||||
jq .droplet.id | read droplet
|
|
||||||
|
|
||||||
export droplet
|
|
||||||
echo 'Waiting for droplet to be active...'
|
|
||||||
while sleep 5; do
|
|
||||||
machine show status
|
|
||||||
if [ "$status" = 'active' ]; then
|
|
||||||
echo 'Droplet ready!'
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
destroy)
|
|
||||||
curl -s -X DELETE \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/droplets?tag_name=auto-mc"
|
|
||||||
;;
|
|
||||||
show)
|
|
||||||
case $arg in
|
|
||||||
ipv4)
|
|
||||||
curl -s -X GET \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/droplets/$droplet" |\
|
|
||||||
jq '.droplet.networks.v4[] | select(.type == "public")'
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
curl -s -X GET \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer $TOKEN" \
|
|
||||||
"https://api.digitalocean.com/v2/droplets/$droplet" |\
|
|
||||||
jq -r .droplet.status | read status
|
|
||||||
export status
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
versionprompt() {
|
|
||||||
# ask the user which version of minecraft they would like to install
|
|
||||||
echo "Which version of minecraft would you like to serve?"
|
|
||||||
echo -n "Version [$latest]:"
|
|
||||||
# have a timeout for user input handy for unattented installs
|
|
||||||
if ! read -t 30 version; then
|
|
||||||
version=$latest
|
|
||||||
echo "Timed out, defaulting to $latest..."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if [ -n "$version" ]; then # check if the user wrote anything
|
|
||||||
echo "Probing..."
|
|
||||||
#validate user input
|
|
||||||
if ! curl -s https://api.papermc.io/v2/projects/paper/ |\
|
|
||||||
jq -r .versions | grep -q \"$version\"; then
|
|
||||||
echo "We cant find that version!"
|
|
||||||
echo "Either its not supported yet or you made an error, trying again..."
|
|
||||||
versionprompt
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
version=$latest
|
|
||||||
echo "Defaulting to $latest..."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
config get
|
config get
|
||||||
keys check
|
keys check
|
||||||
|
|
248
functions.sh
Normal file
248
functions.sh
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
config() {
|
||||||
|
local command="$1"; shift
|
||||||
|
|
||||||
|
case $command in
|
||||||
|
get)
|
||||||
|
echo 'Checking config...'
|
||||||
|
if [ -a "${XDG_CONFIG_HOME}/auto-mc-redux/conf" ]; then
|
||||||
|
source "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
||||||
|
elif [ -a "$HOME/.config/auto-mc-redux/conf" ]; then
|
||||||
|
source "${HOME}/.config/auto-mc-redux/conf"
|
||||||
|
else
|
||||||
|
echo 'No config found!'
|
||||||
|
config create
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
create)
|
||||||
|
read -p "Enter your DigialOcean API token: " TOKEN
|
||||||
|
# this parameter expands to nothing if unset, or x if it is.
|
||||||
|
if [ -z ${XDG_CONFIG_HOME+x} ]; then
|
||||||
|
mkdir -p "${HOME}/.config/auto-mc-redux"
|
||||||
|
echo export TOKEN="${TOKEN}" > "${HOME}/.config/auto-mc-redux/conf"
|
||||||
|
chmod 600 "${HOME}/.config/auto-mc-redux/conf"
|
||||||
|
else
|
||||||
|
mkdir -p "${XDG_CONFIG_HOME}/auto-mc-redux"
|
||||||
|
echo export TOKEN="${TOKEN}" > "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
||||||
|
chmod 600 "${XDG_CONFIG_HOME}/auto-mc-redux/conf"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
firewall(){
|
||||||
|
local command="$1"; shift
|
||||||
|
|
||||||
|
case $command in
|
||||||
|
# exits 0 if firewall with tag auto-mc exists, 1 if it does not
|
||||||
|
check)
|
||||||
|
echo 'Querying firewall rules...'
|
||||||
|
curl -s -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
https://api.digitalocean.com/v2/firewalls |\
|
||||||
|
jq -r .firewalls[].name | grep -q "^auto-mc$"
|
||||||
|
;;
|
||||||
|
create)
|
||||||
|
echo 'Creating firewall rule'
|
||||||
|
curl -s -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-d @- \
|
||||||
|
"https://api.digitalocean.com/v2/firewalls" <<-json
|
||||||
|
{
|
||||||
|
"name": "auto-mc",
|
||||||
|
"tags":["auto-mc"],
|
||||||
|
"inbound_rules": [
|
||||||
|
{
|
||||||
|
"protocol": "icmp",
|
||||||
|
"ports": "0",
|
||||||
|
"sources": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "tcp",
|
||||||
|
"ports": "25565",
|
||||||
|
"sources": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "udp",
|
||||||
|
"ports": "25565",
|
||||||
|
"sources": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbound_rules": [
|
||||||
|
{
|
||||||
|
"protocol": "icmp",
|
||||||
|
"ports": "0",
|
||||||
|
"destinations": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "tcp",
|
||||||
|
"ports": "0",
|
||||||
|
"destinations": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "udp",
|
||||||
|
"ports": "0",
|
||||||
|
"destinations": {
|
||||||
|
"addresses": [
|
||||||
|
"0.0.0.0/0",
|
||||||
|
"::/0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
json
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
keys(){
|
||||||
|
local command="$1"; shift
|
||||||
|
|
||||||
|
case $command in
|
||||||
|
check)
|
||||||
|
echo "Checking DigitalOcean's keystore..."
|
||||||
|
curl -s -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/account/keys" |\
|
||||||
|
jq -r .ssh_keys[].public_key |\
|
||||||
|
while read remote_key; do
|
||||||
|
if grep -q "^$remote_key$" "$HOME/.ssh/"*.pub; then
|
||||||
|
authed_key="$remote_key"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$authed_key" ]; then
|
||||||
|
echo "Local key already installed..."
|
||||||
|
ssh-keygen -l -E md5 -f <(echo "$authed_key") |\
|
||||||
|
cut -d':' -f 2- | cut -d' ' -f 1 | read fingerprint
|
||||||
|
export fingerprint
|
||||||
|
else
|
||||||
|
keys copy
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
copy)
|
||||||
|
key_file="$(find "$HOME/.ssh/"*.pub | sort | head -1)"
|
||||||
|
cat "$key_file" | read key
|
||||||
|
key_name=$(echo "$key" | cut --complement -d'@' -f 1)
|
||||||
|
echo "Copying key..."
|
||||||
|
curl -s -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-d '{"name":"'"$key_name"'","public_key":"'"$key"'"}' \
|
||||||
|
"https://api.digitalocean.com/v2/account/keys" |
|
||||||
|
jq -r .ssh_key.fingerprint | read fingerprint
|
||||||
|
export fingerprint
|
||||||
|
echo "Key copied!"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
machine(){
|
||||||
|
local command="$1"; shift
|
||||||
|
local arg="$1"; shift
|
||||||
|
|
||||||
|
case $command in
|
||||||
|
create)
|
||||||
|
# read isn't used in these pipelines because of job control shinanigans
|
||||||
|
echo 'Creating Droplet...'
|
||||||
|
curl -s -X POST \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-d '{"name":"auto-mc",
|
||||||
|
"size":"'$size'",
|
||||||
|
"region":"'$reigon'",
|
||||||
|
"image":"ubuntu-24-10-x64",
|
||||||
|
"ssh_keys":["'$fingerprint'"],
|
||||||
|
"tags":["auto-mc"]}' \
|
||||||
|
"https://api.digitalocean.com/v2/droplets" |\
|
||||||
|
jq .droplet.id | read droplet
|
||||||
|
|
||||||
|
export droplet
|
||||||
|
echo 'Waiting for droplet to be active...'
|
||||||
|
while sleep 5; do
|
||||||
|
machine show status
|
||||||
|
if [ "$status" = 'active' ]; then
|
||||||
|
echo 'Droplet ready!'
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
destroy)
|
||||||
|
curl -s -X DELETE \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/droplets?tag_name=auto-mc"
|
||||||
|
;;
|
||||||
|
show)
|
||||||
|
case $arg in
|
||||||
|
ipv4)
|
||||||
|
curl -s -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/droplets/$droplet" |\
|
||||||
|
jq '.droplet.networks.v4[] | select(.type == "public")'
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
curl -s -X GET \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
"https://api.digitalocean.com/v2/droplets/$droplet" |\
|
||||||
|
jq -r .droplet.status | read status
|
||||||
|
export status
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
versionprompt() {
|
||||||
|
# ask the user which version of minecraft they would like to install
|
||||||
|
echo "Which version of minecraft would you like to serve?"
|
||||||
|
echo -n "Version [$latest]:"
|
||||||
|
# have a timeout for user input handy for unattented installs
|
||||||
|
if ! read -t 30 version; then
|
||||||
|
version=$latest
|
||||||
|
echo "Timed out, defaulting to $latest..."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -n "$version" ]; then # check if the user wrote anything
|
||||||
|
echo "Probing..."
|
||||||
|
#validate user input
|
||||||
|
if ! curl -s https://api.papermc.io/v2/projects/paper/ |\
|
||||||
|
jq -r .versions | grep -q \"$version\"; then
|
||||||
|
echo "We cant find that version!"
|
||||||
|
echo "Either its not supported yet or you made an error, trying again..."
|
||||||
|
versionprompt
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
version=$latest
|
||||||
|
echo "Defaulting to $latest..."
|
||||||
|
fi
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue