Skip to content

Instantly share code, notes, and snippets.

@DinoChiesa
Created May 18, 2025 16:57
Show Gist options
  • Save DinoChiesa/edcea80136139dc6d4a9aa0724fd3447 to your computer and use it in GitHub Desktop.
Save DinoChiesa/edcea80136139dc6d4a9aa0724fd3447 to your computer and use it in GitHub Desktop.
Deploy Apigee bundle with sha-256 checksum in the Description
#!/bin/bash
# -*- mode:shell-script; coding:utf-8; -*-
#
# Copyright © 2025 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Deploy a bundle for an APIProxy or SharedFlowBundle, from an exploded directory,
# updating the description in the bundle file to include a SHA256 sum of
# the directory. This will represent a hash of the bundle configuration, and
# will serve as a uniquifier to allow comparison of API proxy revisions
# across environments.
#
# Usage:
# ENV=eval ORG=my-gcp-apigee-project deploy-with-shasum.sh ./my-bundle-dir/apiproxy
#
# This utility relies on the following commands:
# cp rm mktemp tar shasum xsltproc apigeecli
scriptid="deploy-with-shasum"
need_wait=0
# Array of environment variable names to check
env_vars_to_check=(
"ENV"
"ORG"
)
# ====================================================================
shasum_for_dir() {
local dir_of_interest SHASUM_FILE value
dir_of_interest=$1
if ! [[ -d "$dir_of_interest" ]]; then
printf "Directory does not exist. Cannot continue.\n"
exit 1
fi
SHASUM_FILE=$(mktemp /tmp/${scriptid}.out.XXXXXX)
# https://stackoverflow.com/a/5431932
tar -cf - --exclude='*.*~' --exclude='*~' $dir_of_interest | shasum -a 256 >"$SHASUM_FILE"
value=$(<"$SHASUM_FILE")
value="${value//[$'\t\r\n '-]/}"
rm "$SHASUM_FILE"
printf "%s" "$value"
}
update_description_in_bundle() {
local bundle_manifest_file shasum asset_type description_value element_name tmp_file
bundle_manifest_file="$1"
shasum="$2"
asset_type="$3"
description_value="shasum: $shasum"
# The source document looks like this:
#
# <APIProxy revision="1" name="quota-for-apikey-or-token">
# <ConfigurationVersion minorVersion="0" majorVersion="4"/>
# <CreatedBy>dino</CreatedBy>
# <Description/>
# <LastModifiedBy>orgAdmin</LastModifiedBy>
# <TargetEndpoints/>
# </APIProxy>
#
# OR
#
# <SharedFlowBundle revision="1" name="apigeesample-unified-auth">
# <ConfigurationVersion minorVersion="0" majorVersion="4"/>
# <CreatedAt>1433453156684</CreatedAt>
# <CreatedBy>[email protected]</CreatedBy>
# <Description/>
# <DisplayName>apigeesample-unified-auth</DisplayName>
# ...
# </SharedFlowBundle>
if [[ "${asset_type}" = "sharedflowbundle" ]]; then
element_name="SharedFlowBundle"
elif [[ "${asset_type}" = "apiproxy" ]]; then
element_name="APIProxy"
else
# should never happen
printf "unhandled asset type\n"
exit 1
fi
tmp_file=$(mktemp /tmp/${scriptid}.out.XXXXXX)
xsltproc --stringparam newDescriptionValue "" - "$bundle_manifest_file" >"${tmp_file}" <<EOF_XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="newDescriptionValue"/>
<xsl:template match="/${element_name}/Description">
<Description>${description_value}</Description>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
EOF_XSLT
if [[ $? -eq 0 ]]; then
mv "${tmp_file}" "$bundle_manifest_file"
#printf "Successfully updated the Description element in '%s'\n" "$bundle_manifest_file"
true
else
echo "Error: xsltproc failed to update the XML file."
rm -f "${tmp_file}.tmp" # Clean up temporary file
false
fi
}
import_and_deploy_with_shasum() {
local dirpath asset_type object files bundle_manifest_file name TMP_PROXY_DIR shasum_value
dirpath=$1
asset_type=$(basename $dirpath)
if [[ "${asset_type}" = "sharedflowbundle" ]]; then
object="sharedflows"
elif [[ "${asset_type}" = "apiproxy" ]]; then
object="apis"
else
printf "Specify a directory ending in 'sharedflowbundle' or 'apiproxy'\n"
exit 1
fi
files=(${dirpath}/*.xml)
if [[ ${#files[@]} -eq 1 ]]; then
bundle_manifest_file="${files[0]}"
name=$(basename "${bundle_manifest_file%.*}")
# 1. copy the directory to a temp location
TMP_PROXY_DIR=$(mktemp -d /tmp/${scriptid}.out.XXXXXX)
cp -r "$dirpath" "$TMP_PROXY_DIR"
# 2. Compute the checksum for the proxy configuration
shasum_value=$(shasum_for_dir "$dirpath")
# 3. Update the bundle description to contain the checksum
files=(${TMP_PROXY_DIR}/${asset_type}/*.xml)
bundle_manifest_file="${files[0]}"
if update_description_in_bundle "$bundle_manifest_file" "$shasum_value" "$asset_type"; then
# 4. actually import the modified thing
printf "Importing a new revision of %s [%s]...\n" "$asset_type" "$name"
apigeecli "$object" create bundle -f "${TMP_PROXY_DIR}/${asset_type}" --name "${name}" -o "$ORG" --token "$TOKEN"
printf "Deploying...\n"
apigeecli "$object" deploy --wait --name "$name" --ovr --org "$ORG" --env "$ENV" --token "$TOKEN" &
need_wait=1
else
printf "could not update, not importing...\n"
fi
rm -fr "$TMP_PROXY_DIR"
else
printf "could not determine name of thing to import\n"
fi
}
check_required_commands() {
local missing
missing=()
for cmd in "$@"; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [[ -n "$missing" ]]; then
printf -v joined '%s,' "${missing[@]}"
printf "\n\nThese commands are missing; they must be available on path: %s\nExiting.\n" "${joined%,}"
exit 1
fi
}
check_shell_variables() {
local MISSING_ENV_VARS
MISSING_ENV_VARS=()
for var_name in "$@"; do
if [[ -z "${!var_name}" ]]; then
MISSING_ENV_VARS+=("$var_name")
fi
done
[[ ${#MISSING_ENV_VARS[@]} -ne 0 ]] && {
printf -v joined '%s,' "${MISSING_ENV_VARS[@]}"
printf "You must set these environment variables: %s\n" "${joined%,}"
exit 1
}
printf "Settings in use:\n"
for var_name in "${env_vars_to_check[@]}"; do
printf " %s=%s\n" "$var_name" "${!var_name}"
done
}
# ====================================================================
PUTATIVE_DIR=$1
if ! [[ -d "$PUTATIVE_DIR" ]]; then
printf "specify a directory containing the bundle as the first argument\n"
exit 1
fi
check_shell_variables "${env_vars_to_check[@]}"
check_required_commands cp rm mktemp tar shasum xsltproc apigeecli
TOKEN=$(gcloud auth print-access-token)
# import the bundle (Sharedflow or apiproxy)
import_and_deploy_with_shasum "$PUTATIVE_DIR"
if [[ $need_wait -eq 1 ]]; then
printf "Waiting...\n"
wait
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment