Skip to content

Commit e709fa8

Browse files
authored
Merge pull request #53 from semd/library/airgap-bundle
[Library] Publish air-gap catalog bundle to GitHub Releases
2 parents 14cbafc + 3400879 commit e709fa8

3 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Publish air-gap bundle
2+
3+
# Builds the catalog and attaches it to a GitHub Release as a tarball, for
4+
# air-gapped Kibana deployments that cannot reach the CDN.
5+
#
6+
# Unlike the catalog publish (Buildkite, credentialed, on every merge to
7+
# `main`), this needs no secrets beyond the default `GITHUB_TOKEN`: the
8+
# generator resolves Kibana versions over unauthenticated `git ls-remote`.
9+
#
10+
# Three ways in:
11+
# - Weekly check — cuts a bundle only when the set of Kibana versions in the
12+
# catalog has changed: a new minor branch appeared in `elastic/kibana`, or
13+
# the version `main`'s `package.json` declares moved to the next minor
14+
# (both happen when a release branches, not on every commit to main). So
15+
# every bundle carries an exact catalog for every version current at the
16+
# time, and quiet weeks produce nothing.
17+
# - Manual run (`workflow_dispatch`) — cut a bundle on demand, e.g. to pick
18+
# up template content without waiting for a version change.
19+
# - Tag push (`v*`) — cut a bundle for a tag you created yourself.
20+
21+
on:
22+
schedule:
23+
- cron: '0 6 * * 1'
24+
workflow_dispatch:
25+
push:
26+
tags:
27+
- 'v*'
28+
29+
permissions:
30+
contents: write
31+
32+
jobs:
33+
bundle:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
37+
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
38+
with:
39+
node-version: '20'
40+
cache: 'npm'
41+
- run: npm ci --ignore-scripts
42+
- run: npm run build:catalog
43+
44+
- name: Decide whether to cut a bundle
45+
id: decide
46+
env:
47+
GH_TOKEN: ${{ github.token }}
48+
EVENT: ${{ github.event_name }}
49+
TAG: ${{ github.ref_name }}
50+
run: |
51+
set -euo pipefail
52+
53+
# `(id, kibana)` pairs rather than ids alone, so a `main` semver bump
54+
# counts as a change and not just the arrival of a new minor.
55+
current="$(jq -Sc '[.versions[] | {id, kibana}]' dist/v1/kibana-versions.json)"
56+
# Minors the bundle serves, for the release title. Sorted numerically so
57+
# 9.10 lands after 9.6 rather than before 9.5.
58+
minors="$(jq -r '[.versions[].kibana | split(".") | .[0:2] | map(tonumber)] | unique | map(join(".")) | join(", ")' dist/v1/kibana-versions.json)"
59+
echo "versions=${minors}" >> "${GITHUB_OUTPUT}"
60+
echo "Catalog covers Kibana ${minors} — ${current}"
61+
62+
if [ "${EVENT}" = "push" ]; then
63+
echo "Tag ${TAG} was pushed; cutting a bundle for it."
64+
echo "tag=${TAG}" >> "${GITHUB_OUTPUT}"
65+
echo "cut=true" >> "${GITHUB_OUTPUT}"
66+
exit 0
67+
fi
68+
69+
if ! previous_tag="$(gh release view --json tagName --jq .tagName 2>/dev/null)"; then
70+
echo "No previous release; cutting the first bundle."
71+
else
72+
gh release download "${previous_tag}" --pattern '*.tar.gz' --dir previous
73+
tar -xzf previous/*.tar.gz -C previous
74+
previous_versions="$(jq -Sc '[.versions[] | {id, kibana}]' previous/v1/kibana-versions.json)"
75+
echo "Catalog versions in ${previous_tag}: ${previous_versions}"
76+
77+
if [ "${current}" = "${previous_versions}" ] && [ "${EVENT}" = "schedule" ]; then
78+
echo "Unchanged since ${previous_tag}; nothing to cut."
79+
echo "cut=false" >> "${GITHUB_OUTPUT}"
80+
exit 0
81+
fi
82+
fi
83+
84+
echo "tag=v$(date -u +%Y.%m.%d)" >> "${GITHUB_OUTPUT}"
85+
echo "cut=true" >> "${GITHUB_OUTPUT}"
86+
87+
- name: Package the catalog tree
88+
if: steps.decide.outputs.cut == 'true'
89+
env:
90+
TAG: ${{ steps.decide.outputs.tag }}
91+
run: |
92+
asset="workflows-library-${TAG}.tar.gz"
93+
# Archive `v1` (not its contents): extracting yields a `v1/` directory,
94+
# which is one of the two layouts Kibana's `library.bundlePath` accepts.
95+
tar -C dist -czf "${asset}" v1
96+
sha256sum "${asset}" > "${asset}.sha256"
97+
echo "ASSET=${asset}" >> "${GITHUB_ENV}"
98+
99+
- name: Attach the bundle to the release
100+
if: steps.decide.outputs.cut == 'true'
101+
env:
102+
GH_TOKEN: ${{ github.token }}
103+
TAG: ${{ steps.decide.outputs.tag }}
104+
VERSIONS: ${{ steps.decide.outputs.versions }}
105+
run: |
106+
set -euo pipefail
107+
108+
# Re-runs (and tags released by hand) upload onto the existing release
109+
# rather than failing. `--target` creates the tag for scheduled and
110+
# manual runs, where it does not exist yet.
111+
if gh release view "${TAG}" > /dev/null 2>&1; then
112+
gh release upload "${TAG}" "${ASSET}" "${ASSET}.sha256" --clobber
113+
exit 0
114+
fi
115+
116+
# One archive serves every listed version — the bundle reader selects the
117+
# catalog matching the Kibana it runs in — so the versions belong in the
118+
# title, not the tag or the filename.
119+
notes="$(printf 'Catalog bundle for air-gapped Kibana deployments.\n\nCovered Kibana versions: **%s**\n\nSee [Air-gapped deployments](https://github.com/elastic/workflows#air-gapped-deployments) for download, checksum verification, and `bundlePath` setup.\n' "${VERSIONS}")"
120+
121+
gh release create "${TAG}" "${ASSET}" "${ASSET}.sha256" \
122+
--target "${GITHUB_SHA}" \
123+
--title "Workflow Template Library ${TAG} — Kibana ${VERSIONS}" \
124+
--notes "${notes}"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ workflow-metadata.json
2424
# Build output (generated by scripts/build-catalog.mjs; uploaded to the CDN)
2525
dist/
2626

27+
# Air-gap release assets (built in CI by .github/workflows/publish-bundle.yml)
28+
workflows-library-*.tar.gz
29+
workflows-library-*.tar.gz.sha256
30+
2731
# Environment and secrets
2832
.env
2933
.env.*

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@ The catalogue is republished on every merge to `main`.
126126

127127
---
128128

129+
## Air-gapped deployments
130+
131+
Kibana instances that cannot reach the CDN read the same catalogue from disk. Each [release](https://github.com/elastic/workflows/releases) carries a `workflows-library-<tag>.tar.gz` asset — a snapshot of the `/v1` tree published to the CDN — plus a `.sha256` sidecar to verify the download.
132+
133+
```bash
134+
sha256sum -c workflows-library-<tag>.tar.gz.sha256
135+
tar -xzf workflows-library-<tag>.tar.gz -C /path/to/workflows-library
136+
```
137+
138+
Point Kibana at the extracted directory in `kibana.yml`:
139+
140+
```yaml
141+
workflowsManagement.library.bundlePath: /path/to/workflows-library
142+
```
143+
144+
`bundlePath` is mutually exclusive with `registryUrl` — setting both fails config validation. The bundle is read once at startup, so replacing the directory takes effect on the next Kibana restart.
145+
146+
[`publish-bundle.yml`](./.github/workflows/publish-bundle.yml) cuts a bundle whenever the set of Kibana versions in the catalogue changes: a new minor branch appears in `elastic/kibana`, or the version declared in `main`'s `package.json` moves to the next minor. Both happen when a release branches, so every bundle carries an exact catalogue for every version current at the time. A weekly check compares against the previous release and does nothing when the version set is unchanged.
147+
148+
Because that trigger tracks Kibana versions rather than template content, a bundle can lag behind recently merged templates. Maintainers can cut one at any time by running the workflow manually (**Actions → Publish air-gap bundle → Run workflow**) or by pushing a `v*` tag.
149+
150+
---
151+
129152
## Building the catalogue locally
130153

131154
```bash

0 commit comments

Comments
 (0)