Files
vault/.github/workflows/build-artifacts-ce.yml
Ryan Cragun 89c75d3d7c [QT-637] Streamline our build pipeline (#24892)
Context
-------
Building and testing Vault artifacts on pull requests and merges is
responsible for about 1/3rd of our overall spend on Vault CI. Of the
artifacts that we ship as part of a release, we do Enos testing scenarios
on the `linux/amd64` and `linux/arm64` binaries and their derivative
artifacts. The extended build artifacts for non-Linux platforms or less
common machine architectures are not tested at this time. They are built,
notarized, and signed as part of every pull request update and merge. As
we don't actually test these artifacts, the only gain we get from this
rather expensive behavior is that we wont merge a change that would prevent
Vault from building on one of the extended targets. Extended platform or
architecture changes are quite rare, so performing this work as frequently
as we do is costly in both monetary and developer time for little relative
safety benefit.

Goals
-----
Rethink and implement how and when we build binaries and artifacts of Vault
so that we can spend less money on repetitive work and while also reducing
the time it takes for the build and test pipelines to complete.

Solution
--------
Instead of building all release artifacts on every push, we'll opt to build
only our testable (core) artifacts. With this change we are introducing a
bit of risk. We could merge a change that breaks an extended platform and
only find out after the fact when we trigger a complete build for a release.
We'll hedge against that risk by building all of the release targets on a
scheduled cadence to ensure that they are still buildable.

We'll make building all of the targets optional on any pull request by
use of a `build/all` label on the pull request.

Further considerations
----------------------
* We want to reduce the total number of workflows and runners for all of our
  pipelines if possible. As each workflow runner has infrastructure cost and
  runner time penalties, using a single runner over many is often preferred.
* Many of our jobs runners have been optimized for cost and performance. We
  should simplify the choices of which runners to use.
* CRT requires us to use the same build workflow in both CE and Ent.
  Historically that meant that modifying `build.yml` in CE would result in a
  merge conflict with `build.yml` in Ent, and break our merge workflows.
* Workflow flow control in both `build.yml` and `ci.yml` can be quite
  complicated, as each needs to maintain compatibility whether executed as CE
  or Ent, and when triggered with various Github events like pull_request,
  push, and workflow_call, each with their own requirements.
* Many jobs utilize similar patterns of flow control and metadata but are not
  reusable.
* Workflow call depth has a maximum of four, so we need to be quite
  considerate when calling other workflows.
* Called workflows can only have 10 inputs.

Implementation
--------------
* Refactor the `build.yml` workflow to be agnostic to whether or not it is
  executing in CE or Ent. That makes future updates to the build much easier
  as we won't have to worry about merge conflicts when the change is merged
  downstream.
* Extract common steps in workflows into composite actions that we can reuse.
* Fix bugs where some but not all workflows would use different Git
  references when building and testing a pull request.
* We rewrite the application, docs, and UI change helpers as a composite
  action. This allows us to re-use this logic to make consistent behavior
  choices across build and CI.
* We combine several `build.yml` and `ci.yml` jobs into our final job.
  This reduces the number of workflows required for the same behavior while
  saving time overall.
* Update most of our action pins.

Results
-------

| Metric            | Before   | After   | Diff  |
|-------------------|----------|---------|-------|
| Duration:         | ~14-18m  | ~15-18m | ~ =   |
| Workflows:        | 43       | 18      | - 58% |
| Billable time:    | ~1h15m   | 16m     | - 79% |
| Saved artifacts:  | 34       | 12      | - 65% |

Infra costs should map closely to billable time.
Network I/O costs should map closely to the workflow count.
Storage costs should map directly with saved artifacts.

We could probably get parity with duration by getting more clever with
our UBI container build, as that's where we're seeing the increase. I'm
not yet concerned as it takes roughly the same time for this job to
complete as it did before.

While the CI workflow was not the focus on the PR, some shared
refactoring does show some marginal improvements there.

| Metric            | Before   | After    | Diff   |
|-------------------|----------|----------|--------|
| Duration:         | ~24m     | ~12.75m  | - 15%  |
| Workflows:        | 55       | 47       | - 8%   |
| Billable time:    | ~4h20m   | ~3h36m   | - 7%   |

Further focus on streamlining the CI workflows would likely result in a
few more marginal improvements, but nothing on the order like we've seen
with the build workflow.

Signed-off-by: Ryan Cragun <me@ryan.ec>
2024-02-06 21:11:33 +00:00

242 lines
7.6 KiB
YAML

name: ce
# The inputs and outputs for this workflow have been carefully defined as a sort of workflow
# interface as defined in the build.yml workflow. The inputs and outputs here must be consistent
# across the build-artifacts-ce workflow and the build-artifacts-ent workflow.
on:
workflow_dispatch:
inputs:
build-all:
type: boolean
default: false
build-date:
type: string
required: true
checkout-ref:
type: string
default: ""
compute-build:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for build worfkflows
required: true
compute-build-compat:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for build workflows that need older glibc
required: true
compute-small:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for non-resource-intensive workflows
required: true
vault-revision:
type: string
required: true
vault-version:
type: string
required: true
vault-version-package:
type: string
required: true
web-ui-cache-key:
type: string
required: true
workflow_call:
inputs:
build-all:
type: boolean
default: false
build-date:
type: string
required: true
checkout-ref:
type: string
default: ""
compute-build:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for build worfkflows
required: true
compute-build-compat:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for build workflows that need older glibc
required: true
compute-small:
type: string # JSON encoded to support passing arrays
description: A JSON encoded "runs-on" for non-resource-intensive workflows
required: true
vault-revision:
type: string
required: true
vault-version:
type: string
required: true
vault-version-package:
type: string
required: true
web-ui-cache-key:
type: string
required: true
outputs:
testable-containers:
value: ${{ jobs.core.outputs.testable-containers }}
testable-packages:
value: ${{ jobs.core.outputs.testable-packages }}
jobs:
# Core are the Linux builds that are officially supported and tested as part of the normal
# CI/CD pipeline.
core:
strategy:
matrix:
include:
- goos: linux
goarch: amd64
redhat: true
- goos: linux
goarch: arm64
redhat: false
fail-fast: true
runs-on: ${{ fromJSON(inputs.compute-build) }}
name: (${{ matrix.goos }}, ${{ matrix.goarch }})
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ inputs.checkout-ref }}
- uses: ./.github/actions/build-vault
with:
cgo-enabled: 0
create-docker-container: true
create-packages: true
create-redhat-container: ${{ matrix.redhat }}
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
goarch: ${{ matrix.goarch }}
goos: ${{ matrix.goos }}
go-tags: ui
vault-binary-name: vault
vault-edition: ce
vault-version: ${{ inputs.vault-version }}
web-ui-cache-key: ${{ inputs.web-ui-cache-key }}
outputs:
# Outputs are strings so we need to encode our collection outputs as JSON.
testable-containers: |
[
{ "artifact": "${{ github.event.repository.name }}_default_linux_amd64_${{ inputs.vault-version }}_${{ inputs.vault-revision }}.docker.tar" }
]
testable-packages: |
[
{ "sample": "build_ce_linux_amd64_deb",
"artifact": "vault_${{ inputs.vault-version-package }}-1_amd64.deb",
"edition": "ce"
},
{ "sample": "build_ce_linux_arm64_deb",
"artifact": "vault_${{ inputs.vault-version-package }}-1_arm64.deb",
"edition": "ce"
},
{ "sample": "build_ce_linux_amd64_rpm",
"artifact": "vault-${{ inputs.vault-version-package }}-1.x86_64.rpm",
"edition": "ce"
},
{ "sample": "build_ce_linux_arm64_rpm",
"artifact": "vault-${{ inputs.vault-version-package }}-1.aarch64.rpm",
"edition": "ce"
},
{ "sample": "build_ce_linux_amd64_zip",
"artifact": "vault_${{ inputs.vault-version }}_linux_amd64.zip",
"edition": "ce"
},
{ "sample": "build_ce_linux_arm64_zip",
"artifact": "vault_${{ inputs.vault-version }}_linux_arm64.zip",
"edition": "ce"
}
]
# Extended build targets are best-case builds for non-Linux platforms that we create for
# convenience but are not built or tested as part our normal CI pipeline.
extended:
if: inputs.build-all == true
strategy:
matrix:
docker:
- false
packages:
- false
goos:
- freebsd
- netbsd
- openbsd
- solaris
- windows
goarch:
- 386
- amd64
- arm
exclude:
- goos: solaris
goarch: 386
- goos: solaris
goarch: arm
- goos: windows
goarch: arm
include:
- goos: darwin
goarch: amd64
go-tags: ui netcgo
docker: false
packages: false
- goos: darwin
goarch: arm64
go-tags: ui netcgo
docker: false
packages: false
- goos: linux
goarch: 386
docker: true
packages: true
- goos: linux
docker: true
goarch: arm
goarm: 6
packages: true
fail-fast: true
name: (${{ matrix.goos }}, ${{ matrix.goarch }}${{ matrix.goarm && ' ' || '' }}${{ matrix.goarm }})
runs-on: ${{ fromJSON(inputs.compute-build) }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ inputs.checkout-ref }}
- uses: ./.github/actions/build-vault
with:
github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
create-docker-container: ${{ matrix.docker }}
create-packages: ${{ matrix.packages }}
create-redhat-container: false
goarch: ${{ matrix.goarch }}
goos: ${{ matrix.goos }}
goarm: ${{ matrix.goarm }}
go-tags: ${{ matrix.go-tags != '' && matrix.go-tags || 'ui' }}
vault-binary-name: vault
vault-edition: ce
vault-version: ${{ inputs.vault-version }}
web-ui-cache-key: ${{ inputs.web-ui-cache-key }}
status:
if: always()
runs-on: ${{ fromJSON(inputs.compute-small) }}
permissions:
id-token: write
contents: read
needs:
- core
- extended
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ inputs.checkout-ref }}
- name: Determine status
run: |
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
echo "One or more required build workflows failed: ${results}"
exit 1
fi
exit 0