How to package a distribution

In this guide we will demonstrate how to package an Attini distribution.

Introduction

The Attini distribution is a package that contains all the resources needed to perform a deployment. Almost anything can be included in a distribution, but normally it will contain:

  1. All your Infrastructure as Code.
  2. Any configuration, scripts or code needed.
  3. A deployment plan.
  4. An Attini configuration file.

Why do we want to create a package? By creating a package we freeze the state of our project. We can give it a version, name and other metadata we might need. Attini also includes the deployment instructions in the package, meaning that we can always deploy the package with the same command (attini deploy run) no matter what it contains. This makes sharing resources between teams much easier because only the owner of the distribution needs to know how the resources it contains should be deployed.

How to use

To package a distribution with the Attini CLI, run the command:

attini distribution package <path to project root>

This will create a distribution in the project called attini_dist/{distribution name}.zip. To package a distribution, the project needs to contain an Attini configuration file in its root directory. Both JSON and YAML are supported, and the file has to have one of the following names:

  1. attini-config.yaml
  2. attini-config.yml
  3. attini-config.json

A typical attini-config file could look like this:


distributionName: attini-package-demo
initDeployConfig:
  template: deployment-plan.yaml
  stackName: ${distributionName}-deployment-plan

package:
  prePackage:
    commands:
      - attini configure set-dist-id --random

The only required part in the file is the distributionName, however two more sections are normally included. The initDeployConfig section and the package section. The initDeployConfig section contains the configuration for your init-deploy stack. This is the stack that creates your deployment plan and is covered more extensively in the deploying a distribution guide. In this guide we will focus on the package section.

The package lifecycle

Packaging a distribution has the following lifecycle:

flowchart LR 1[Copy files in to temp directory] --> 2[Run pre-package commands] 2 --> 3[Package archive] 3 --> 4[Run post-package commands] 4 --> 5[Clean up]

It is important to note that in the first phase, all the files are copied to a temporary directory that will be deleted in the last phase. This means that any actions we perform via the package commands will not affect the original files.

Package commands

The prePackage and postPackage sections are good places to specify any actions that should be performed when packaging the distribution. Any shell commands can be executed, making it very flexible. In the example above the command attini configure set-dist-id --random is executed as a prePackage command. This will set a random distributionId to the configuration file before the distribution is packaged. When executed on a build server or as a GitHub action, the git commit id could be used instead of a random value, giving the distribution id a bit more meaning. For example if using GitHub actions, the command could look like this:

   attini configure set-dist-id -id ${GITHUB_SHA}

This would set the distribution id to the commit SHA value.

Container build

To remove dependencies on the machine packaging the distribution, Attini support packaging using docker containers. This makes it easier to build and get predictable results, no matter if the packaging is performed on a build server or a local machine.

What image to use is configured in the Attini configuration file under the package section, together with any container options and login commands needed to access the image repository.

package:
  container:
    image: public.ecr.aws/attini/attini-cli:latest
    options:
      - "-v $HOME/.aws:/root/.aws:rw"
    loginCommands:
      - aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

To package a distribution using a container you can add two additional options to the package commands.

attini distribution package <path to project root> --container-build --container-repository-login

the --container-build option instructs the CLI to perform a container build and the --container-repository-login instructs the CLI to execute the login commands. If no login is required the --container-repository-login option can be omitted. You can also use the short names:

attini distribution package <path to project root> -cd -crl

This makes it easy to use the container when working on your local machine, but skip it when working on a build server that is probably using the correct container image.

Mocking environment configuration

Sometimes when we package a distribution we are dependent on some configuration that is not available on the machine we are using. For example if our package commands read some environment variables that are not present on our local machine. To solve this we can pass an “environment configuration” file to the package command. The environment configuration file is a script file that will be sourced in the shell that executes our package commands. An environment config file could look like this:

export GITHUB_SHA=local-build-id

Because the GITHUB_SHA variable is always available when using GitHub actions we can use the environment configuration file to mock it for our local builds. To use an environment configuration file, use the package commands --environment-config-script option, or its short name -ec. For example:

attini distribution package <path to project root> -ec <path to config file>

Ignore files

A lot of the time we have files in our projects that should not be included in the distribution. By default, the CLI will ignore:

  • Hidden files
  • Files with names that contain special characters that are not supported by S3
  • The node_modules folder if one is present.

The ignores can be overridden or extended by creating a file called “.attini-ignore” in the root of the project. The attini-ignore file uses the globbing pattern, similar to .gitignore.

Read more about the ignores here.

distribution id and version

Every packaged distribution must have a distribution id. The distribution id is a unique identifier of the distribution and it will be set to a random value if it is not specified. The distribution id should not be confused with the distribution version. The distribution version is a semantic version and is normally set when a Distribution is ready for production. A distribution id is set every time the distribution is packaged, even during development.

The distribution version is not required, but it is good practice to set it because it will give the distribution a chronological order. A semantic version is also easier for a person to work with. Making it easier to compare different environments with each other.

To set a version when packaging a distribution, use the --distribution-version option with the package command.

attini distribution package <path to project root> --distribution-version 1.0.0

Unlike the distribution id, the distribution version has to follow the semantic version format.

Next step

For more information about the package command, run it with the --help option or visit the CLI documentation.

For learning about how to deploy a distribution, read the deploying a distribution guide.