11 KiB
title |
---|
Packaging handbook |
Create a new recipe
You can run abra recipe new <recipe>
to generate a new ~/.abra/recipes/<recipe>
repository. See the maintainers tutorial for help on getting started with packaging.
How are recipes are versioned?
We'll use an example to work through this. Let's use Gitea.
The Gitea project maintains a version, e.g. 1.14.3
. This version uses the semver strategy for communicating what type of changes are included in each version, i.e., if there is a breaking change, Gitea will release a new version as 2.0.0
.
However, there are other types of changes that can happen for a recipe. Perhaps the database image gets a breaking update or the actual recipe configuration changes some environment variable. This can mean that end-users of the recipe need to do some work to make sure their updates will deploy successfully.
Therefore, we maintain an additional version part, in front of the project version. So, the first release of the Gitea recipe in the Co-op Cloud project has the version of 1.0.0+1.14.3
. This x.y.z+
is the version part that the recipe maintainer manages. If a new available Gitea version comes out as 1.15
then the recipe maintainer will publish 1.1.0+1.15
as this is a backwards compatible update, following semantic versioning.
In all cases, we follow the semver semantics. So, if we upgrade the Gitea recipe from 1.14.3
to 1.15.3
, we still publish 1.1.0+1.15.3
as our recipe version. In this case, we skipped a few patch releases but it was all backwards compatible, so we only increment the minor version part.
How do I release a new recipe version?
The commands uses for dealing with recipe versioning in abra
are:
abra recipe upgrade
: upgrade the image tags in the compose configs of a recipeabra recipe sync
: upgrade the deploy labels to match the new recipe versionabra recipe release
: publish a git tag for the recipe repo
The abra
recipe publishing commands have been designed to complement a semi-automatic workflow. If abra
breaks or doesn't understand what is going on, you can always finish the process manually with a few Git commands and a bit of luck. We designed abra
to support this way due to the chaotic nature of container publishing versioning schemes. This handbook entry will show you everything you need to know to understand.
Let's take a practical example, publishing a new version of Wordpress.
If we run abra recipe upgrade wordpress
(at time of running), we end up with a prompt to upgrade Wordpress to 5.9.0
. We can skip the database upgrade for now. Here is what that looks like:
➜ ~ abra recipe upgrade wordpress
? upgrade to which tag? (service: app, image: wordpress, tag: 5.8.3) 5.9.0
? upgrade to which tag? (service: db, image: mariadb, tag: 10.6) skip
WARN[0004] not upgrading mariadb, skipping as requested
Now, what happened? abra
queried the upstream container repositories of all the images listed in the Wordpress recipe configuration and checked if there are new tags available. Once you make some choices on the prompt, abra
will update the recipe configurations. Let's take a look by running cd ~/.abra/recipes/wordpress && git diff
:
diff --git a/compose.yml b/compose.yml
index 1618ef5..6cd754d 100644
--- a/compose.yml
+++ b/compose.yml
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
- image: "wordpress:5.8.3"
+ image: "wordpress:5.9.0"
volumes:
- "wordpress_content:/var/www/html/wp-content/"
networks:
!!! warning "Here be versioning dragons"
`abra` doesn't understand all image tags unfortunately. There are limitations which we're still running into. You can pass `-a` to have `abra` list all available image tags from the upstream repository and then make a choice manually. See [`tagcmp`](https://git.coopcloud.tech/coop-cloud/tagcmp) for more info on how we implement image parsing.
Next, we need to update the label in the recipe, we can do that with abra recipe sync wordpress
. You'll be prompted by a question asking what kind of upgrade this is. Take a moment to read the output and if it still doesn't make sense, read this. Since we're upgrading from 5.8.3
-> 5.9.0
, it is a minor release, so we choose minor
:
➜ wordpress (master) ✗ abra recipe sync wordpress
...
INFO[0088] synced label coop-cloud.${STACK_NAME}.version=1.1.0+5.9.0 to service app
Once again, we can run cd ~/.abra/recipes/wordpress && git diff
to see what abra
has done for us:
diff --git a/compose.yml b/compose.yml
index 1618ef5..4a08db6 100644
--- a/compose.yml
+++ b/compose.yml
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
- image: "wordpress:5.8.3"
+ image: "wordpress:5.9.0"
volumes:
- "wordpress_content:/var/www/html/wp-content/"
networks:
@@ -48,7 +48,7 @@ services:
#- "traefik.http.routers.${STACK_NAME}.rule=HostRegexp(`{subdomain:.+}.${DOMAIN}`, `${DOMAIN}`)"
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
- - "coop-cloud.${STACK_NAME}.version=1.0.2+5.8.3"
+ - "coop-cloud.${STACK_NAME}.version=1.1.0+5.9.0"
- "backupbot.backup=true"
- "backupbot.backup.path=/var/www/html"
You'll notice that abra
figured out how to upgrade the Co-op Cloud version label according to our choice, 1.0.2
-> 1.1.0
is a minor update.
At this point, we're all set, we can run abra recipe release --publish wordpress
. This will do the following:
- run
git commit
the new changes - run
git tag
to create a new git tag named1.1.0+5.9.0
- run
git push
to publish changes to the Wordpress repository
Here is the output:
WARN[0000] discovered 1.1.0+5.9.0 as currently synced recipe label
WARN[0000] previous git tags detected, assuming this is a new semver release
? current: 1.0.2+5.8.3, new: 1.1.0+5.9.0, correct? Yes
new release published: https://git.coopcloud.tech/coop-cloud/wordpress/src/tag/1.1.0+5.9.0
And once more, we can valdate this tag has been created with cd ~/.abra/recipes/wordpress && git tag -l
.
How are new recipe versions tested?
This is currently a manual process. Our best estimates are to do a backup and run a test deployment and see how things go.
Following the entry above, before running abra recipe release --publish <recipe>
, you can deploy the new version of the recipe. You find an app that relies on this recipe and pass -C/--chaos
to ugrade
so that it accepts the locally unstaged changes.
It is good practice to take note of all the issues you ran into and share them with other operators. See this entry for more.
If you don't have time or are not an operator, reach out on our communication channels for an operator willing to do some testing.
How do I write version release notes?
In the root of your recipe repository, run the following (if the folder doesn't already exist):
mkdir -p releases
And then simply create a text file which corresponds to the version release, e.g. 1.1.0+5.9.0
and write some notes. abra
will show these when another operator runs abra app deploy
/ abra app upgrade
.
Generate the recipe catalogue
To generate an entire new copy of the catalogue:
abra catalogue generate
You will most likely want to pass --user/--username
/ --pass/--password
with container regsitry credentials to avoid rate limiting.
If you just want to generate a catalogue entry for a single recipe:
abra catalogue generate <recipe>
The changes are generated and added to ~/.abra/catalogue
, you can validate what is done by running:
cd ~/.abra/catalogue
git diff
You can pass --publish
to have abra
automatically publish those changes. You'll need to have write access to the git.coopcloud.tech repository.
How is a recipe structured?
compose.yml
This is a compose specification compliant file that contains a list of:
- services
- secrets
- networks
- volumes
- configs
It describe what is needed to run an app. Whenever you deploy an app, abra
reads this file.
.env.sample
This file is a skeleton for environmental variables that should be adjusted by the user. Examples include: domain or php extention list. Whenever you create a new app with abra app new
this file gets copied to the ~/.abra/servers/<server-domain>/<app-domain>.env
and when you run abra app config <app-domain>
you're editing this file.
abra.sh
The abra.sh
provides versions for configs that are vendored by the recipe maintainer.
entrypoint.sh
After docker creates the filesystem and copies files into a new container it runs what's called an entrypoint. This is usually a shell script that exports some variables and runs the application. Sometimes the vendor entrypoint doesn't do everything that we need it to do. In that case you can write your own entrypoint, do whatever you need to do and then run the vendor entrypoint.
For a simple example check the entrypoint.sh for croc
. In this case, croc
needs the password to be exported as an environmental variable called CROC_PASS
, and that is exactly what the entrypoint does before running vendor entrypoint.
If you write your own entrypoint, it needs to be specified in the config
section of compose.yml.
Optional compose files
I.e. compose.smtp.yml
. These are used to provide non-essential functionality such as (registration) e-mails or single sign on. These are typically loaded by specifying COMPOSE_FILE="compose.yml:compose.smtp.yml"
in your app .env
configuration. Then abra
learns to include these optional files at deploy time. abra
uses the usual docker-compose
configuration merging technique when merging all the compose.**.yml
files together at deploy time.
Docker configs
If you look at a compose.yml
file and see a configs
section, that means this compose file is putting files in the container. This might be used for changing default (vendor) configuration, such as this fpm-tune.ini file used to adjust php-fpm.
Enable healthchecks
TODO.
Tuning deploy configs
TODO.
Tuning resource limits
TODO.
Enable A+ SSL ratings
TODO.
Thread environment variables
TODO.
Entrypoint hacks
TODO.
Exposing secrets
TODO.
/bin/bash is missing?
TODO.
Referening service names in configs?
When referencing an app
service in a config file, you should prefix with the STACK_NAME
to avoid namespace conflicts (because all these containers sit on the traefik overlay network). You might want to do something like this {{ env "STACK_NAME" }}_app
(using the often obscure dark magic of the Golang templating language). You can find examples of this approach used in the Peertube recipe.