open-vault/packages-oss.lock/README.md

127 lines
5.8 KiB
Markdown
Raw Normal View History

ci/packagespec (#9653) * add packagespec build system - The majority of changes in this commit are files generated by packagespec (everything in the packages-oss.lock directory). * add .yamllint * update to packagespec@fd54aea4 * ci: bust packagespec cache - Change to packagespec results in package IDs that can use git tag refs, not just commit refs. * update to packagepsec@5fc121d0 - This busts all caches, because of a change to the way we no longer traverse from tag refs to commit refs, due to the potential confusion this can cause. - See https://github.com/hashicorp/packagespec/commit/fd54aea4827bb6cfd637c78a2ab6274111605330 for the original change to packagespec necessitating this. * update to packagespec@5e6c87b6 - This completes the change to allowing git tag refs to be used for source IDs, begun in f130b940a8fbe3e9398225b08ea1d63420bef7b6 * update to packagespec@4d3c9e8b - This busts cache, needed to apply previous change. * remove RELEASE_BUILD_WORKFLOW_NAME * update packagespec, add watch-ci target * fix package names (do not refer to EDITION) * remove EDITION input from packages-oss.yml * bump package cache, update packagespec * update packagespec, add 'aliases' target * update packagespec; less output noise * ci: give release bundle file a better name - When performing a release build, this will include the build ID as part of the name, making it easier to distinguish from other builds. * ci: create aliases tarball artifact * ci: cache package metadata files * ci: add metadata artifact * ci: bust circleci package cache * Revert "ci: bust circleci package cache" This reverts commit 1320d182613466f0999d63f5742db12ac17f8e92. * ci: remove aliases artifact * ci: use buildID not workflowName to id artifacts * packages: add BUNDLE_NAME metadata * do not cache package metadata with binaries * ci: bump package cache * ci: debugging * ci: fix package cache; update packagespec * ci: update packagespec to 10e7beb2 * ci: write package metadata and aliases * ci: switch to .zip artifacts * switch package bundle back to tar.gz (from zip) - Because of the way zip works, the zip archive was over 2GB rather than under 750MB as with tar.gz. * bump packagespec, adds list-staged-builds * update packagespec * add publish stub + general tidy up * bump packagespec * bump packagespec; add make publish-config * Makefile: tidy up packagespec targets * pass PRODUCT_REPO_ROOT to packagespec * bump go to 1.14.6 * packages-oss.yml: use more explicit base image * bump packagespec to b899b7c1 * bump packagespec to f040ce8f * packages-oss.yml: pin base image to digest - This digest is pointed to by debian:buster-20200720 - Using a specific digest ensures that builds use the same base image in all contexts * add release-repo; bump packagespec * remove BUILD_TAGS and -tags flag * bump packagespec to e444f742 * bump to go1.14.7 * ci: bump to go1.14.7
2020-08-11 09:00:59 +00:00
# WARNING: Do not EDIT or MERGE this file, it is generated by 'packagespec lock'.
This directory contains a build system for producing multiple different packages
from the same source code (i.e. a single git commit).
The build system allows the definition of *packages* which are near complete
descriptions of the build environment and build command used to produce each package.
This set of packages is used to generate CI configuration, and drives the building
of builder images (via layer.mk) and final packages (build.mk).
Packages include a set of *layers* which are individually cacheable stages of the
build, expressed as Dockerfile templates and source definitions, and cached as Docker
images.
## Why?
HashiCorp ship software written in Go, which must be compiled for multiple
different platforms, and with different build tags and compiler flags etc.
For some of our software there are many tens of variations that must be
built for a single commit. Managing these by hand is too onerous a task
and one that may easily result in error. The usual solution is to write
iterative programs that directly output the various binaries, however this
itself can be difficult to understand, and difficult to observe clearly.
By separating the workflows of _defining_ packages and then _building_ them
we end up with an easy to understand intermediate representation of
each package (packages.lock). Not only is it easy to understand, but also to
consume for other purposes, such as generating CI pipelines, or
programattically editing to further automation efforts.
packages.lock essentially contains all the non-source inputs to each package
along with pointers to the source itself. We cache all build layers/packages
and intermediate files according to their spec + source (all the inputs).
That is, each item in the cache, and the eventual packages we build are
input-addressable, meaning we can fairly aggressively cache everything,
with a simple invalidation rule: if any input changes, then invalidate the
cache.
## Workflow
The workflow is to edit `packages.yml` which is the human-editable description
of all the packages and build layers, and then to run `make packages` which
translates that definition into the `packages.lock` file tree.
Note: at present, only HashiCorp employees have access to the `packagespec` tool
used by the Makefile to generate packages.lock, this may or may not change in future.
The convenience command `make build` selects the first of these packages that
matches your local GOOS and GOARCH and builds that one.
## Implementation
There are two separate workflows: defining packages (in packages.yml)
and building packages (build.mk). For conveninience the main Makefile exposes targets
that invoke those files on your behalf, which you should always use unless debugging
the system. In CI there is another useful workflow: building the builder images
themselves and saving them to an archive file for caching. This is implemented
in layer.mk.
### config.mk
config.mk is included by all the other make files, and contains global configuration
as well as some utility macros and lists of required tools to install.
### build.mk
build.mk produces package files for distribution by running the build command
inside the relevant build container for that package.
### layer.mk
layer.mk contains all the code for building and caching the build layers,
it is included by build.mk so that you don't need to separately build layers
locally, but in CI you can invoke it directly to pre-cache layers.
### packages.yml
packages.yml is the human-editable defition of package specs.
### packages.lock
packages.lock contains the fully expanded version of packages.yml, including
the rendered dockerfiles in packages.lock/layers. When packages.lock changes
after running 'make packages' you should also re-generate CI config by running
`make ci-config` in the root of the repo, and commit them both.
## Build internals
### 1. Build each builder image layer
#### a. Build source archives, assign SOURCE_ID
The build process uses the definitions in packages.lock to generate source
archives (using the layers' source-include and source-exclude fields)
from either the local filesystem, or direct from git if PRODUCT_REVISION is
set. It assigns each set of source code a SOURCE_ID which is either the
SHA of the latest git commit to affect any of that code, or else if the code
is dirty (contains uncommitted changes), a SHA1 sum of the latest commit plus
the output of 'git diff' in order to make it unique
#### b. Build the docker image
Using the Dockerfile from packages.lock, the source code from the source
archive, and a reference to the base image (the one from the layer below)
build a Docker image. Each layer in packagespec.lock is addressed (has an
ID) which is a hash of its content, its source definition, and the ID of
its parent layer. This layer ID is combined with the SOURCE_ID to produce
the name of the Docker image (LAYER_ID:SOURCE_ID-PARENTHASH). We also append
'PARENTHASH' Which is a cumulative hash of each build layer's ID up to that
point. This ensures that if any base layer changes input-wise in any way,
then all subsequent layers are invalidated.
### 2. Build the package
Using the top-most buider image, which must by now contain all the source code,
execute the build command (passing in some externally calculated paths) to
compile and package the software.
Use `docker cp` to copy out the built artefact. We do this instead of using
mounts, because that's more compatible with various remote Docker scenarios.
### 3. Address the package.
Each package is addressed by all its inputs (including source code), but this
is not a human-readable name. We also add package aliases, in the form of
symlinks in .buildcache/packages/by-alias which are human-readable names
and names required by other systems involved in distribution.