feat(deploy): implements multiple apps and sdl templating #10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy me | ||
on: | ||
workflow_call: | ||
inputs: | ||
project-path: | ||
description: 'Path for the project to deploy' | ||
required: false | ||
default: '.' | ||
type: string | ||
project-name: | ||
description: 'Project name (will be used as prefix for cache keys)' | ||
required: false | ||
default: 'app' | ||
type: string | ||
image: | ||
description: 'Change the image tag to deploy' | ||
required: true | ||
type: string | ||
provider: | ||
description: 'Provider address to deploy the project' | ||
type: string | ||
github-message: | ||
type: string | ||
description: 'Commit message for state changes' | ||
required: false | ||
default: 'chore(deploy): update deployment state' | ||
github-author-email: | ||
type: string | ||
description: 'Git commit author email' | ||
required: false | ||
github-author-name: | ||
type: string | ||
description: 'Git commit author name' | ||
required: false | ||
secrets: | ||
seed: | ||
description: 'Seed phrase for the wallet' | ||
required: true | ||
password: | ||
description: 'Password to decrypt the wallet' | ||
required: true | ||
github-token: | ||
description: 'GITHUB_TOKEN' | ||
required: true | ||
env: | ||
ORG: akash-network | ||
REPO: provider | ||
CLIENT: provider-services | ||
CLIENT_VERSION: 0.6.4 | ||
ARCH: linux_amd64 | ||
# Akash Network | ||
NET: mainnet | ||
#BLOCK_TIME: 6s | ||
BLOCK_TIME: 1s | ||
# Quit if chain is running 30 seconds behind | ||
CHAIN_LATENCY: 30 | ||
# Akash Client parameters | ||
AKASH_KEYRING_BACKEND: file | ||
AKASH_BROADCAST_MODE: block | ||
#AKASH_BROADCAST_MODE: async | ||
AKASH_YES: 1 | ||
AKASH_GAS_PRICES: 0.025uakt | ||
AKASH_GAS: auto | ||
AKASH_GAS_ADJUSTMENT: 1.5 | ||
AKASH_HOME: /home/runner/.akash | ||
AKASH_FROM: default | ||
AKASH_OUTPUT: json | ||
# Minimum balance on the wallet in AKT | ||
MIN_BALANCE: 10 | ||
AKASH_GSEQ: 1 | ||
AKASH_OSEQ: 1 | ||
SDL: deploy.yaml | ||
PARSED_SDL: ${{ inputs.project-path }}/deploy-parsed.yaml | ||
PROVIDER: ${{inputs.provider}} | ||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
- name: Call Initialize Akash client | ||
uses: akash-network/akash-deploy-action/.github/actions/init-akash@main | ||
with: | ||
seed: ${{ secrets.seed }} | ||
password: ${{ secrets.password }} | ||
project-name: ${{ inputs.project-name }} | ||
- name: Call Cleanup stale order requests | ||
uses: akash-network/akash-deploy-action/.github/actions/cleanup-stale-orders@main | ||
with: | ||
password: ${{ secrets.password }} | ||
- name: Call Restore state | ||
id: call-restore-state | ||
uses: akash-network/akash-deploy-action/.github/actions/restore-state@main | ||
with: | ||
project-path: ${{ inputs.project-path }} | ||
project-name: ${{ inputs.project-name }} | ||
- name: Call Get deployment status | ||
id: call-get-deployment-status | ||
if: | | ||
steps.call-restore-state.outcome == 'success' && | ||
( env.AKASH_DSEQ != '' && env.AKASH_PROVIDER != '' ) | ||
uses: akash-network/akash-deploy-action/.github/actions/get-deployment-status@main | ||
- name: Call Cleanup stale state | ||
id: call-cleanup-stale-state | ||
uses: akash-network/akash-deploy-action/.github/actions/cleanup-stale-state@main | ||
if: steps.call-get-deployment-status.outputs.is-deployment-active == 'false' | ||
with: | ||
password: ${{ secrets.password }} | ||
project-path: ${{ inputs.project-path }} | ||
project-name: ${{ inputs.project-name }} | ||
- name: Interpolate SDL vars | ||
id: interpolate-sdl-vars | ||
env: | ||
DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }} | ||
SENTRY_SERVER_NAME: ${{ secrets.SENTRY_SERVER_NAME }} | ||
IMAGE_NAME: ${{ inputs.image }} | ||
CLOUD_SQL_TOKEN: ${{ secrets.CLOUD_SQL_TOKEN }} | ||
run: | | ||
# sudo apt-get update && sudo apt-get install -y gettext | ||
VARS=$(sed -n 's/.*{{\s*\([A-Za-z_][A-Za-z0-9_]*\)\s*}}.*/\1/p' ${{ inputs.project-path }}/${SDL} | sort | uniq | sed 's/[^ ]* */$&/g' | tr '\n' ' ') | ||
cat ${{ inputs.project-path }}/${SDL} | ||
sed 's/{{\([A-Za-z_][A-Za-z0-9_]*\)}}/\$\1/g' ${{ inputs.project-path }}/${SDL} | envsubst "$VARS" > ${PARSED_SDL} | ||
echo "--------------- PARSED SDL FILE ---------------" | ||
cat ${PARSED_SDL} | ||
echo "--------------- END OF PARSED SDL FILE ---------------" | ||
- name: Update existing deployment | ||
id: update-deployment | ||
if: steps.call-get-deployment-status.outputs.is-deployment-active == 'true' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
#echo "AKASH_DSEQ: $AKASH_DSEQ" | ||
# extract image name without version from image input | ||
IMAGE_NAME=$(echo "${{ inputs.image }}" | cut -d':' -f1) | ||
echo "IMAGE_NAME: $IMAGE_NAME" | ||
echo "Changing SDL to use the new image: ${{ inputs.image }}" | ||
# Change the image IMAGE_NAME version from the SDL file | ||
sed -i "s|image: $IMAGE_NAME:.*|image: ${{ inputs.image }}|g" ${PARSED_SDL} | ||
## === broadcast tx === ## | ||
TX=$(echo "${password}" | ${CLIENT} tx deployment update ${PARSED_SDL} | jq -r '.txhash') | ||
if test -z $TX; then | ||
echo "No TX broadcasted!" | ||
echo "If you see \"Invalid: deployment version\" error above, it is likely because the SDL file was not changed." | ||
exit 1 | ||
fi | ||
echo "TX: $TX" | ||
sleep ${BLOCK_TIME} | ||
RC=$(${CLIENT} query tx $TX | jq -r '.code') | ||
case $RC in | ||
0) | ||
echo "TX successful" | ||
;; | ||
11) | ||
echo "Out of gas! Consider raising AKASH_GAS_ADJUSTMENT and trying again." | ||
exit 1 | ||
;; | ||
*) | ||
echo "Transaction $TX failed with code: '$RC'" | ||
exit 1 | ||
;; | ||
esac | ||
## === broadcast tx === ## | ||
- name: Broadcast the deployment request | ||
id: create-deployment | ||
if: | | ||
( steps.call-get-deployment-status.outputs.is-deployment-active != 'true' || steps.call-cleanup-stale-state.outcome == 'success' ) || | ||
steps.call-get-deployment-status.outcome == 'skipped' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
if test ! -z "$AKASH_DSEQ"; then | ||
echo "Looks like there was an issue closing stale deployment: $AKASH_DSEQ/$AKASH_GSEQ/$AKASH_OSEQ" | ||
exit 1 | ||
fi | ||
## === broadcast tx === ## | ||
TX=$(echo "${password}" | ${CLIENT} tx deployment create ${PARSED_SDL} | jq -r '.txhash') | ||
if test -z $TX; then | ||
echo "No TX broadcasted!" | ||
exit 1 | ||
fi | ||
echo "TX: $TX" | ||
sleep ${BLOCK_TIME} | ||
RC=$(${CLIENT} query tx $TX | jq -r '.code') | ||
case $RC in | ||
0) | ||
echo "TX successful" | ||
;; | ||
11) | ||
echo "Out of gas! Consider raising AKASH_GAS_ADJUSTMENT and trying again." | ||
exit 1 | ||
;; | ||
*) | ||
echo "Transaction $TX failed with code: '$RC'" | ||
exit 1 | ||
;; | ||
esac | ||
## === broadcast tx === ## | ||
AKASH_DSEQ=$(${CLIENT} query tx $TX | jq -r '.tx.body.messages[].id.dseq') | ||
if test -z "$AKASH_DSEQ"; then | ||
echo "Could not get AKASH_DSEQ, something is really wrong." | ||
exit 1 | ||
fi | ||
echo "Broadcasted AKASH_DSEQ: $AKASH_DSEQ to the Akash network." | ||
echo "AKASH_DSEQ=$AKASH_DSEQ" >> $GITHUB_ENV | ||
- name: Look for the bids on the Akash network | ||
id: find-bids | ||
if: steps.create-deployment.outcome == 'success' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
test ! -z "$AKASH_DSEQ" | ||
echo "Giving providers 12 seconds to bid ..." | ||
sleep 12 | ||
# add "--gseq 0 --oseq 0" -- to catch all (for future placement groups support) | ||
BID_LIST="$(${CLIENT} query market bid list --state open)" | ||
#BID_LENGTH=$(echo "$BID_LIST" | jq -r '.bids | length') | ||
# Remove blocklisted providers from the BID_LIST | ||
if [[ -f ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/BLOCKLIST ]]; then | ||
while read address; do | ||
echo "Warning! Skipping provider $address based on the ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/BLOCKLIST!" | ||
TMP_BID_LIST="$(echo "$BID_LIST" | jq --arg address "$address" -c '.bids[] | del(select(.bid.bid_id.provider == $address))' | jq -s '{"bids": del(.. | nulls)}')" | ||
BID_LIST="$TMP_BID_LIST" | ||
done < <(cat ${{ inputs.project-path }}/.akash/${{inputs.project-name}}BLOCKLIST) # use process substitution to avoid subshell creation (cat file | while read ...) where variables are not preserved | ||
fi | ||
echo "$BID_LIST" | jq -r '.bids[].bid | {"price":.price.amount, "provider":.bid_id.provider}' | jq -s -r 'sort_by(.price | tonumber) | .[] | flatten | @tsv' | ||
if test ! -z $PROVIDER; then | ||
echo "PROVIDER parameter is set, locating the bid from provider: $PROVIDER ..." | ||
AKASH_PROVIDER=$(echo "$BID_LIST" | jq -r '.bids[].bid.bid_id.provider' | grep -w "$PROVIDER") | ||
else | ||
echo "PROVIDER parameter was not set, locating the cheapest bid ..." | ||
AKASH_PROVIDER=$(echo "$BID_LIST" | jq -r '.bids[].bid | {"price":.price.amount, "provider":.bid_id.provider}' | jq -s -r 'sort_by(.price | tonumber) | .[0].provider') | ||
fi | ||
if test -z "$AKASH_PROVIDER" || [[ "$AKASH_PROVIDER" == "null" ]]; then | ||
echo "Could not find bids for $AKASH_DSEQ/$AKASH_GSEQ/$AKASH_OSEQ." | ||
echo "Try to update your SDL manifest file, probably you are requesting too much of resources or too limiting attributes." | ||
echo "Closing the deployment ..." | ||
## === broadcast tx === ## | ||
TX=$(echo "${password}" | ${CLIENT} tx deployment close | jq -r '.txhash') | ||
if test -z $TX; then | ||
echo "No TX broadcasted!" | ||
exit 1 | ||
fi | ||
echo "TX: $TX" | ||
sleep ${BLOCK_TIME} | ||
RC=$(${CLIENT} query tx $TX | jq -r '.code') | ||
case $RC in | ||
0) | ||
echo "TX successful" | ||
;; | ||
11) | ||
echo "Out of gas! Consider raising AKASH_GAS_ADJUSTMENT and trying again." | ||
exit 1 | ||
;; | ||
*) | ||
echo "Transaction $TX failed with code: '$RC'" | ||
exit 1 | ||
;; | ||
esac | ||
## === broadcast tx === ## | ||
exit 1 | ||
fi | ||
echo "AKASH_PROVIDER=$AKASH_PROVIDER" >> $GITHUB_ENV | ||
echo "Selected AKASH_PROVIDER: $AKASH_PROVIDER" | ||
- name: Accept the lease | ||
id: create-lease | ||
if: | | ||
steps.create-deployment.outcome == 'success' && | ||
steps.find-bids.outcome == 'success' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
if test -z "$AKASH_DSEQ"; then | ||
echo "AKASH_DSEQ is not set. Possible causes:" | ||
echo "The client did not succeed broadcasting the deployment request. (Usually can happend due out of gas.)" | ||
exit 1 | ||
fi | ||
if test -z "$AKASH_PROVIDER"; then | ||
echo "AKASH_PROVIDER is not set." | ||
exit 1 | ||
fi | ||
echo "Going to accept the lease for: $AKASH_DSEQ/$AKASH_GSEQ/$AKASH_OSEQ on $AKASH_PROVIDER provider" | ||
## === broadcast tx === ## | ||
TX=$(echo "${password}" | ${CLIENT} tx market lease create | jq -r '.txhash') | ||
if test -z $TX; then | ||
echo "No TX broadcasted!" | ||
exit 1 | ||
fi | ||
echo "TX: $TX" | ||
sleep ${BLOCK_TIME} | ||
RC=$(${CLIENT} query tx $TX | jq -r '.code') | ||
case $RC in | ||
0) | ||
echo "TX successful" | ||
;; | ||
11) | ||
echo "Out of gas! Consider raising AKASH_GAS_ADJUSTMENT and trying again." | ||
exit 1 | ||
;; | ||
*) | ||
echo "Transaction $TX failed with code: '$RC'" | ||
exit 1 | ||
;; | ||
esac | ||
## === broadcast tx === ## | ||
- name: Deposit additional AKT tokens to the deployment | ||
id: deposit-deployment | ||
env: | ||
password: ${{ secrets.password }} | ||
if: steps.create-lease.outcome == 'success' || steps.update-deployment.outcome == 'success' | ||
run: | | ||
test ! -z "$AKASH_DSEQ" | ||
DAYS_LEFT=$(${CLIENT} query escrow blocks-remaining | jq -r '.estimated_time_remaining/pow(10;9)/60/60/24|round') | ||
DEPOSIT=$(${CLIENT} query deployment get | jq -r '.escrow_account.balance.amount|tonumber / pow(10;6)') | ||
echo "${AKASH_DSEQ} has ${DAYS_LEFT} days left with ${DEPOSIT} AKT currently deposited." | ||
echo "Consider depositing more AKT for it to last longer." | ||
# TODO: create a dedicated job for this task? | ||
# TODO: see to automate the minimum deposit (in days) / or rather add a crontab job which will take care of that. | ||
#echo "${password}" | ${CLIENT} tx deployment deposit 10000000uakt | ||
#sleep ${BLOCK_TIME} | ||
- name: Send manifest to the provider | ||
id: send-manifest | ||
if: steps.create-lease.outcome == 'success' || steps.update-deployment.outcome == 'success' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
test ! -z "$AKASH_DSEQ" | ||
test ! -z "$AKASH_PROVIDER" | ||
echo "${password}" | ${CLIENT} send-manifest ${PARSED_SDL} | ||
- name: Lease status | ||
id: lease-status | ||
if: steps.create-lease.outcome == 'success' || steps.update-deployment.outcome == 'success' | ||
env: | ||
password: ${{ secrets.password }} | ||
run: | | ||
test ! -z "$AKASH_DSEQ" | ||
mkdir -p ${{ inputs.project-path }}/.akash/${{inputs.project-name}} 2>/dev/null || : | ||
echo "${password}" | ${CLIENT} lease-status | jq -r . > ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/STATUS | ||
cat ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/STATUS | ||
- name: Save DSEQ and PROVIDER | ||
if: steps.create-lease.outcome == 'success' || steps.update-deployment.outcome == 'success' | ||
run: | | ||
test ! -z "$AKASH_DSEQ" | ||
test ! -z "$AKASH_PROVIDER" | ||
mkdir -p ${{ inputs.project-path }}/.akash/${{inputs.project-name}} 2>/dev/null || : | ||
echo "$AKASH_DSEQ" > ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/DSEQ | ||
echo "$AKASH_PROVIDER" > ${{ inputs.project-path }}/.akash/${{inputs.project-name}}/PROVIDER | ||
- name: Remove the parsed SDL file | ||
run: rm -f ${PARSED_SDL} | ||
- name: Commit & Push changes | ||
uses: actions-js/push@master | ||
with: | ||
github_token: ${{ secrets.ghcr-token }} | ||
author_email: ${{ inputs.github-author-email }} | ||
author_name: ${{ inputs.github-author-name }} | ||
message: ${{ inputs.github-message }} |