Release User Guide

Use a channel to deliver your application, infrastructure and scripts to the cloud.

Concepts

Channel

A channel delivers your application to development, test and production targets. Conceptually a channel is similar to the deploy/release portion of a pipeline.

The recommended naming convention for channels is <repository>-<branch> (Example: munchii.com-master)

Release

A standard unit for delivering an application. It is typically produced as an output of a build process and may include infrastructure code, configuration and scripts.

Target

A destination like AWS, Azure, Google Cloud, an app store or even a database (for backfills and database upgrade scripts). A target unpacks a release and invokes the main script or executable in an ubuntu Docker Image.

Topology

A directed graph that expresses dependencies between targets.

Getting Started

You can follow these steps to create a channel named HelloWorld and release to a target named test.

Note: The curl commands must be executed with an authorization header (not shown).

  1. Create a channel

    curl -X PUT \
      -H "Content-Type: application/json" \
      -d '{ "data": { "name": "HelloWorld" } }' \
      https://api.munchii.com/channels/HelloWorld
  2. Add a target

    curl -X PUT \
      -H "Content-Type: application/json" \
      -d '{ "data": { "name": "test" } }' \
      https://api.munchii.com/channels/HelloWorld/targets/test
  3. Create a release file (typically an output of your build process)

    cat << EOF > /var/tmp/main
      #!/bin/sh
      echo "Hello World!"
    EOF
    chmod +x /var/tmp/main
    tar --create --gzip --file /var/tmp/helloWorld.tar.gz \
      --directory /var/tmp main
  4. Start a release

    curl -X GET --silent --show-error \
      https://api.munchii.com/channels/HelloWorld/releases/tail \
      | jq --raw-output ".links.put" \
      | xargs curl -X PUT \
          --upload-file /var/tmp/helloWorld.tar.gz
  5. View the release log

    curl -X GET \
      https://api.munchii.com/channels/HelloWorld/targets/test/logs

Next steps:

  1. Update your build process to create and upload a release file
  2. Add targets and (if desired) rearrange the topology

Cleanup (Optional):

  1. Delete target

    curl -X GET --include --silent --show-error \
      https://api.munchii.com/channels/HelloWorld/targets/test \
      | sed -n "s/ETag/If-Match/ip" \
      | tr -d "\r" \
      | xargs --delimiter "\n" -I '{}' curl -X DELETE -H '{}' \
          https://api.munchii.com/channels/HelloWorld/targets/test
  2. Delete channel

    curl -X GET --include --silent --show-error \
      https://api.munchii.com/channels/HelloWorld \
      | sed -n "s/ETag/If-Match/ip" \
      | tr -d "\r" \
      | xargs --delimiter "\n" -I '{}' curl -X DELETE -H '{}' \
          https://api.munchii.com/channels/HelloWorld

Scheduling

A channel behaves like a queue that accepts and delivers releases to targets. By default targets are scheduled sequentially in the order they were added to a channel. Targets can be reordered and more complex dependencies can be expressed as a directed graph (referred to as a topology). Releases always occur one-by-one and in-order for any given target - even if targets are reordered.

Release File Format

The release file format is designed to be simple and flexible. A minimal release file is a tar.gz with an executable main (script or binary). The main script or binary is freeform. For example: you can write a script that uses the AWS CLI, the Serverless Framework or execute your own compiled program.

The tar.gz file may include other artifacts like templates, scripts, config and data. The entire tar.gz will be unpacked and available alongside the main script or executable. For example: you might include a compiled ZIP file for your application to deploy to AWS Lambda.

Reserved: release.yaml in the tar.gz is reserved for future use (like specifying a Docker image or different main script or executable name).

Local Development & Testing

You can develop and test release files locally. You can test a release file in a Docker container like:

docker run --mount type=bind,src=`pwd`/task,dst=/var/task \
  --env CHANNEL_NAME=HelloWorld \
  --env TARGET_NAME=local \
  --env RELEASE_ORDINAL=1 \
  --workdir /var/task ubuntu /var/task/main

Note: This command assumes `pwd`/task exists and contains an executable main.

Best Practice: Don't store secrets in source code and don't test a release file against a production target.

Release File Script Patterns

If you choose to use a main script in your release file, these patterns may be helpful:

  • Load Configuration

    Source target-specific environment variables/configuration that are needed by the release process.

  • . configuration/$TARGET_NAME
  • Reduce Log Verbosity

    Buffers the stdout and stderr output of a command to a file and only display the output if the command fails. We like to use this for commands that set up the release environment - like installing new packages.

    
    suppress () {
      /bin/rm --force /tmp/suppress.out 2> /dev/null;
      ${1+"$@"} > /tmp/suppress.out 2>&1 || cat /tmp/suppress.out;
      /bin/rm /tmp/suppress.out;
    }

    Credit: https://serverfault.com/a/607903

  • Organize into Smaller Scripts

    Version sort and execute scripts. We like to use this to organize releases into phases like 1_setup, 2_install, 3_test and 4_cleanup.

    COMMANDS=$(find "smaller-scripts" -type f -print | sort -V)
    
    for COMMAND in $COMMANDS
    do
      . $COMMAND
    done

Secrets

Secrets (like AWS credentials) can be added to a target. Secrets are encrypted and stored at rest using one AES 256 key per target. Secrets cannot be retrieved programtically, but they are decrypted and made available as environment variables for a release.

Best Practice: Rotate secrets periodically. Any time a secret is created, updated or deleted, the AES 256 encryption key for the affected target is rotated.

Environment Variables

The following environment variables are available (in addition to secrets) and may be useful for conditional release logic or configuring an application:

  • CHANNEL_NAME
  • TARGET_NAME
  • RELEASE_ORDINAL

Manual Approvals

A simple way to manually approve changes between targets (e.g. between a test and production environment) is to model each target as a separate source code branch and create a corresponding channel for each source code branch. This is similar to Gitflow Workflows.

Caution: Running a build process at different times for an identical commit may not produce an identical release file. Dependencies are a common example of input to a build process that may change over time. If you prefer to promote an identical release file between targets, skip creating separate source code branches and, instead, release the same release file to the next channel and target after manually verifying the prior channel and target.