Skip to content

Bash Task Authoring

Shebang and Prelude

  • Always start with #!/usr/bin/env bash
  • Include {{ template "prelude.tmpl" . }} — this is the standard prelude (NOT setup.tmpl which is deprecated)
  • prelude.tmpl provides: set -e -o pipefail -o errexit, shopt -s nullglob extglob globstar, DEBIAN_FRONTEND=noninteractive, LC_ALL=C, PS4 debug formatting
  • Environment vars set by prelude: RS_MC_ROLE, RS_MC_TYPE, RS_MC_NAME
  • Debug support: if rs-debug-enable param is true, set -x is activated (unless security/debug-block overrides)
  • Exit helpers: __exit(), exit_incomplete(), exit_reboot(), exit_shutdown(), exit_stop(), exit_incomplete_reboot(), exit_incomplete_shutdown()
  • job_fail() for fatal errors (prints message and exits 1); job_error() for non-fatal error logging
  • xiterr()deprecated, use job_fail instead
  • Architecture detection: $arch (amd64/arm64/ppc64le)
  • OS detection: $OS_TYPE, $OS_VER, $OS_FAMILY, $OS_NAME, $osfamily, $osversion
  • Package management: install(), install_lookup(), clean_pkg() — cross-distro package installation
  • Service management: service() — handles systemd, sysv, upstart
  • Path management: fixup_path()
  • Parameter helpers: get_param "name", set_param "name" "value"
  • jq/drpjq symlinks created automatically

Environment Variables

  • $RS_UUID — Current machine UUID (set by agent)
  • $RS_TOKEN — Authentication token (set by agent)
  • $RS_ENDPOINTDRP API endpoint URL (set by agent)
  • $RS_MC_ROLEMachine role from metadata (set by prelude)
  • $RS_MC_TYPEMachine type parameter (set by prelude)
  • $RS_MC_NAMEMachine name (set by prelude)
  • $RS_WORKORDER_UUID — Work order UUID (when running in work order context)
  • $RS_TASK — Current task name
  • $RS_TASK_DIR — Task working directory
  • $RS_DEBUG_ENABLE — Set to "true" when debugging enabled

Error Handling Patterns

  • prelude.tmpl already sets set -e -o pipefail — any command failure exits
  • For commands that may legitimately fail: command || true
  • For custom error messages: command || job_fail "Failed to do X"
  • job_fatal and xiterr are deprecated — use job_fail instead
  • Trap pattern for cleanup:

    Bash
    cleanup() { rm -f /tmp/tempfile; }
    trap cleanup EXIT
    

Common Patterns

  • Machine update: drpcli machines update $RS_UUID '{"Description": "configured"}'
  • Profile param set: drpcli profiles set $PROFILE param "key" to "value"
  • Runtime param read: get_param "param-name" (simple, no --decode/--compose) or drpcli machines get $RS_UUID param "param-name" (full API; returns JSON — use | jq -r . for raw strings)
  • JSON parsing with jq: VALUE=$(drpcli machines show $RS_UUID | jq -r '.Address')
  • Package install: install curl wget jq (uses prelude's cross-distro installer)
  • Conditional param:

    Bash
    {{ if .ParamExists "my-param" }}
    MY_VAL="{{ .Param "my-param" }}"
    {{ else }}
    MY_VAL="default-value"
    {{ end }}
    
  • Posting events:

    Bash
    drpcli events post - >/dev/null <<EOF
    {"Type": "task", "Action": "completed", "Key": "$RS_UUID"}
    EOF
    

Complete Example

The following shows a full task YAML with all best practices:

YAML
---
Name: "example-configure-service"
Description: "Install and configure an example service"
Documentation: |
  Installs the example-svc package and configures it based on
  DRP parameters. Idempotent: skips installation if already present.

  Requires the `example/config-url` parameter to be set.
RequiredParams:
  - example/config-url
OptionalParams:
  - example/service-port    # Defined with default: 8080
  - rs-debug-enable
Meta:
  icon: "cogs"
  color: "blue"
  title: "Example Content"
  feature-flags: "sane-exit-codes"
Templates:
  - Name: "example-configure-service.sh"
    Meta:
      OS: "linux"
    Contents: |
      #!/usr/bin/env bash
      {{ template "prelude.tmpl" . }}

      CONFIG_URL="{{ .Param "example/config-url" }}"
      # example/service-port has a default of 8080 in its param definition,
      # so no ParamExists check is needed here.
      PORT="{{ .Param "example/service-port" }}"

      # Idempotency check
      if command -v example-svc &>/dev/null; then
          job_info "example-svc already installed, checking config..."
      else
          job_info "Installing example-svc..."
          install example-svc
      fi

      # Configure the service
      job_info "Configuring example-svc with URL=$CONFIG_URL port=$PORT"
      cat > /etc/example-svc/config.yaml <<CONF
      url: $CONFIG_URL
      port: $PORT
      CONF

      # Enable and start
      service example-svc enable
      service example-svc restart

      # job_success prints the message and exits 0
      job_success "example-svc configured and running on port $PORT"