This repository provides shell scripts for the Helmfile hooks. Mainly it is designed to be managed by administrators, DevOps engineers, SREs.
helm, kubectl, yq = version are specified in
the project.yaml
file
of each project of the repository in the tools section.
This repository uses the classic GitFlow workflow, embracing all its advantages and disadvantages.
Stable branches: develop, master
Each merge into the master branch adds a new SemVer2 tag and a GitHub release is created.
-
This set of hook scripts can only be launched from the project repository via RMK, because the entire input set of the variables is formed by RMK at the moment the release commands are launched, e.g.:
rmk release sync
RMK also keeps track of which version of the release hook scripts the project repository will use. The version of the hook scripts artifact is described in the project.yaml file of each project repository in the
inventory.hookssection, e.g.:inventory: # ... hooks: helmfile.hooks.infra: version: <SemVer2> url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} # ...
-
The hook scripts are designed to ensure consistent deployment of Helm releases described in a Helmfile. These scripts should be designed with declarative deployment in mind. They will only execute when there are differences in the state.
For development, navigate to the local .PROJECT/inventory/hooks/helmfile.hooks.infra-<version>/bin directory of a
project repository,
then perform the changes directly in the files and test them. Finally, copy the changed files to a new feature branch
of this repository and create a pull request (PR).
This section defines the standards and best practices for developing hook scripts in this repository. All scripts must adhere to these guidelines to ensure consistency, maintainability, and reliability.
-
Shebang and Error Handling
- Line 1: Must contain
#!/usr/bin/env bash - Line 2: Must be blank
- Line 3: Must contain
set -eto exit immediately if a command exits with a non-zero status - No duplicate
set -estatements elsewhere in the script
- Line 1: Must contain
-
Indentation
- Use exactly 2 spaces for indentation throughout all bash code
- Do not use tabs
-
Quoting Variables
- All variable assignments must use quotes:
VAR="${value}" - All variable expansions must use quotes:
"${VAR}" - Command substitutions must be quoted:
VAR="$(command)" - Exception: Arithmetic expansion does not require quotes:
$(( EXPR ))or(( EXPR ))
- All variable assignments must use quotes:
-
Variable Naming
- Avoid unnecessary prefixes (e.g.,
K8S_,MONGODB_,PG_) - Use descriptive, clear names without redundant prefixes
- Avoid unnecessary prefixes (e.g.,
-
Readonly Variables
- Declare variables as
readonlyif they are assigned once and never modified - Apply
readonlyto initial argument assignments:readonly NAMESPACE="${1}" - Apply
readonlyto constants and configuration values - Default argument values are compatible with
readonly:readonly LIMIT="${3:-180}"
- Declare variables as
-
Local Variables
- Declare variables as
localwithin functions to prevent global scope pollution - Prefer combining
localdeclaration with assignment:local COUNT=0 - All function-scoped variables must be declared as
local
- Declare variables as
- Arithmetic Formatting
- Use spaces around operators in arithmetic expressions:
(( COUNT > LIMIT )) - Use spaces around equality comparisons:
(( COUNT == 0 )) - Use spaces in arithmetic expansion:
$(( SA_DATE - POD_DATE )) - Increment operations:
(( ++COUNT ))(pre-increment with spaces) - Comparison operators:
(( COUNT <= LIMIT )),(( COUNT >= LIMIT ))
- Use spaces around operators in arithmetic expressions:
- Array Usage
- Iterate arrays using
"${ARRAY[@]}":for ITEM in "${ARRAY[@]}"; do - Array slicing:
("${ARRAY[@]:start}")to maintain array structure - Creating arrays from command output: Use
while IFS= read -rloop with here-document for POSIX compatibility:ARRAY=() while IFS= read -r ITEM; do if [[ -n "${ITEM}" ]]; then ARRAY+=("${ITEM}") fi done <<EOF ${COMMAND_OUTPUT} EOF
- Avoid
mapfilefor compatibility with older Bash versions or restricted shell environments
- Iterate arrays using
- YAML/JSON Processing
- Prefer
yqfor most YAML/JSON processing tasks - Exception: Golang templates (
go-template) are currently used in some ready hooks, but may be migrated toyqin the future for unification - Avoid low-level Linux utilities (
sed,awk,grep) unless:- The output is plain text (not YAML/JSON)
yqcannot handle the specific use case
- Prefer
-
Standard Argument Order
- First argument:
NAMESPACE(required, no default value) - Second argument:
RELEASE_NAMEorCLUSTER_NAME(required, no default value) - Subsequent arguments: Other parameters as needed
- Last argument:
LIMIT(if present, must be the final positional argument) - Boolean/enable flags (if present) should come last with default values:
ENABLE_HOOK="${4:-true}"
- First argument:
-
Argument Naming
- Use
RELEASE_NAMEfor Helm release names - Use
CLUSTER_NAMEonly when referring to a Kubernetes cluster resource (e.g., PostgreSQL cluster) - Use
CLUSTER_NAMESPACEwhen the cluster resource exists in a different namespace
- Use
- Exit Code Standards
- Use
exit 0for successful completion - Use
exit 1for errors and failures - Ensure all code paths have explicit exit codes
- Use
- Error Message Format
- Include script name in error messages:
$(basename "${0}"): Wait timeout exceeded. - Use descriptive error messages that explain what failed
- Send error messages to stderr:
>&2 echo "ERROR: message"
- Include script name in error messages:
- Loop Usage
- Prefer
forloops for counting iterations:for (( COUNT=0; COUNT < LIMIT; ++COUNT )); do - Use
while trueloops for polling/waiting scenarios:while true; do ... done - Convert counting
whileloops toforloops where appropriate
- Prefer
- Naming Pattern
- Format:
<event>-<action>.shor<event>-<event>-<action>.shfor multiple events - Supported events (for the full list,
see Helmfile hooks documentation):
presync: Execute before Helm sync operationpostsync: Execute after Helm sync operationpreuninstall: Execute before Helm uninstall operationpostuninstall: Execute after Helm uninstall operation
- Multiple events: When a hook is used for multiple events, list them explicitly following the order above:
<event1>-<event2>-<action>.sh(e.g.,preuninstall-postuninstall-delete-cluster.sh) - Action: Descriptive verb optionally followed by resource or purpose (e.g.,
wait-postgres-ready,create-postgres-user,delete-failed-job,restart-airbyte-worker) - Backward compatibility: If a new event is needed for an existing hook, create a new hook file to maintain backward compatibility
- Examples:
presync-create-postgres-user.shpostsync-wait-postgres-ready.shpreuninstall-postuninstall-delete-cluster.shpostuninstall-wait-persistent-volumes-deleted.sh
- Format:
- Function Best Practices
- Use
functionkeyword orfunction_name()syntax consistently - Declare all function variables as
local - Use descriptive function names that indicate purpose
- Return explicit exit codes:
return 0for success,return 1for failure
- Use
- Process Substitution Compatibility
- Use here-document with command substitution instead of process substitution (
< <(...)) for better compatibility:OUTPUT="$(command)" while IFS= read -r LINE; do # process line done <<EOF ${OUTPUT} EOF
- This ensures compatibility with restricted shell environments
- Use here-document with command substitution instead of process substitution (