Repository navigation
202 lines (197 loc) · 10.3 KB
/
Copy pathpublish-offline-docs.yml
File metadata and controls
202 lines (197 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
name: Publish offline documentation image
on:
release:
types: [published]
workflow_dispatch:
inputs:
documentation_sha:
description: Reviewed full documentation commit SHA (40 lowercase hexadecimal characters)
required: false
type: string
release_tag:
description: Existing stable docs release tag to retry, e.g. v1.0.0 (leave documentation_sha empty)
required: false
type: string
AIRGAP_VERSION:
description: Zylon manual version, e.g. 1.58; empty uses .env.airgap; unknown builds all versions
required: false
default: ''
type: string
permissions:
contents: read
concurrency:
group: publish-offline-documentation
cancel-in-progress: false
env:
AWS_REGION: us-east-1
AWS_ACCOUNT_ID: '918593742895'
REGISTRY: 918593742895.dkr.ecr.us-east-1.amazonaws.com
ECR_REPOSITORY: zylon-docs
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Select a reviewed snapshot or stable docs release
id: source
env:
DOCS_SHA: ${{ inputs.documentation_sha }}
RELEASE_TAG: ${{ inputs.release_tag || github.event.release.tag_name }}
run: |
if [ -n "$DOCS_SHA" ] && [ -z "$RELEASE_TAG" ]; then
[[ "$DOCS_SHA" =~ ^[0-9a-f]{40}$ ]] || { echo 'Provide a full lowercase 40-character commit SHA.' >&2; exit 1; }
printf 'ref=%s\n' "$DOCS_SHA" >> "$GITHUB_OUTPUT"
elif [ -z "$DOCS_SHA" ] && [[ "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
printf 'ref=refs/tags/%s\n' "$RELEASE_TAG" >> "$GITHUB_OUTPUT"
else
echo 'Provide either a full documentation SHA or an existing stable vX.Y.Z docs release tag, never both.' >&2
exit 1
fi
- uses: actions/checkout@v6
with:
ref: ${{ steps.source.outputs.ref }}
fetch-depth: 0
persist-credentials: false
- name: Resolve immutable image tag from the checked-out commit
id: image
env:
DOCS_SHA: ${{ inputs.documentation_sha }}
RELEASE_TAG: ${{ inputs.release_tag || github.event.release.tag_name }}
GH_TOKEN: ${{ github.token }}
run: |
sha=$(git rev-parse HEAD)
if [ -n "$DOCS_SHA" ]; then
test "$sha" = "$DOCS_SHA"
tag="docs-$sha"
else
test "$(git rev-parse "refs/tags/$RELEASE_TAG^{commit}")" = "$sha"
tag=${RELEASE_TAG#v}
test "$(cat version.txt)" = "$tag"
test "$(python3 -c 'import json; print(json.load(open(".release-please-manifest.json"))["."])')" = "$tag"
# A retry must name an actual published stable release, not an arbitrary Git tag.
gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > "$RUNNER_TEMP/docs-release.json"
python3 -c 'import json,sys; r=json.load(open(sys.argv[1])); assert r["tag_name"] == sys.argv[2] and not r["draft"] and not r["prerelease"]' "$RUNNER_TEMP/docs-release.json" "$RELEASE_TAG"
fi
printf 'tag=%s\nsha=%s\n' "$tag" "$sha" >> "$GITHUB_OUTPUT"
- name: Require publishing credentials
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
test -n "$AWS_ACCESS_KEY_ID" && test -n "$AWS_SECRET_ACCESS_KEY" || {
echo 'Grant this repository access to AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY before publishing.' >&2
exit 1
}
- uses: actions/setup-node@v6
with:
node-version: '24'
- name: Resolve the airgap documentation version and image variant
id: airgap
env:
REQUESTED_AIRGAP_VERSION: ${{ inputs.AIRGAP_VERSION }}
BASE_IMAGE_TAG: ${{ steps.image.outputs.tag }}
run: |
version=""
if [ -f offline/airgap.mjs ]; then
if [ -n "$REQUESTED_AIRGAP_VERSION" ]; then
version=$(AIRGAP_VERSION="$REQUESTED_AIRGAP_VERSION" node offline/airgap.mjs)
else
version=$(env -u AIRGAP_VERSION node offline/airgap.mjs)
fi
elif [ -n "$REQUESTED_AIRGAP_VERSION" ]; then
echo 'This documentation commit does not support AIRGAP_VERSION. Select a commit containing the airgap version selector.' >&2
exit 1
fi
tag="$BASE_IMAGE_TAG"
if [ -n "$REQUESTED_AIRGAP_VERSION" ]; then tag="$tag-airgap-${version:-all}"; fi
printf 'version=%s\ntag=%s\n' "$version" "$tag" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
allowed-account-ids: ${{ env.AWS_ACCOUNT_ID }}
- name: Require the provisioned immutable repository and locate existing tags
id: repository
env:
IMAGE_TAG: ${{ steps.airgap.outputs.tag }}
run: |
test "$(aws sts get-caller-identity --query Account --output text)" = "$AWS_ACCOUNT_ID"
mutability=$(aws ecr describe-repositories --repository-names "$ECR_REPOSITORY" --query 'repositories[0].imageTagMutability' --output text)
test "$mutability" = IMMUTABLE || { echo 'Provision the immutable zylon-docs ECR repository before publishing.' >&2; exit 1; }
existing_tag=$(aws ecr list-images --repository-name "$ECR_REPOSITORY" --filter tagStatus=TAGGED --query "imageIds[?imageTag=='$IMAGE_TAG'].imageTag" --output text)
if [ -n "$existing_tag" ]; then
echo 'exists=true' >> "$GITHUB_OUTPUT"
echo 'Immutable documentation tag already exists; its platforms and revision will be checked.'
else
echo 'exists=false' >> "$GITHUB_OUTPUT"
fi
- name: Compile and validate the actual runtime image
if: steps.repository.outputs.exists == 'false'
env:
AIRGAP_VERSION: ${{ steps.airgap.outputs.version }}
run: |
docker build -f offline/Dockerfile --build-arg "AIRGAP_VERSION=$AIRGAP_VERSION" -t zylon-docs:release .
bash offline/check-container.sh zylon-docs:release "$AIRGAP_VERSION"
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Set up QEMU
if: steps.repository.outputs.exists == 'false'
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Publish the independent documentation image
if: steps.repository.outputs.exists == 'false'
env:
IMAGE_TAG: ${{ steps.airgap.outputs.tag }}
DOCS_SHA: ${{ steps.image.outputs.sha }}
AIRGAP_VERSION: ${{ steps.airgap.outputs.version }}
run: |
docker buildx build -f offline/Dockerfile \
--platform linux/amd64,linux/arm64 --provenance=false \
--build-arg "AIRGAP_VERSION=$AIRGAP_VERSION" \
--label "org.opencontainers.image.revision=$DOCS_SHA" \
--label "org.opencontainers.image.version=$IMAGE_TAG" \
--label "ai.zylon.docs.airgap-version=$AIRGAP_VERSION" \
--tag "$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" --push .
- name: Verify both published platforms and their source revision
env:
IMAGE_TAG: ${{ steps.airgap.outputs.tag }}
DOCS_SHA: ${{ steps.image.outputs.sha }}
AIRGAP_VERSION: ${{ steps.airgap.outputs.version }}
run: |
image="$REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
docker buildx imagetools inspect --raw "$image" > "$RUNNER_TEMP/docs-image.json"
python3 - "$RUNNER_TEMP/docs-image.json" > "$RUNNER_TEMP/docs-platforms.txt" <<'PYTHON'
import json, re, sys
manifests = json.load(open(sys.argv[1]))["manifests"]
for arch in ("amd64", "arm64"):
matches = [item for item in manifests if item["platform"].get("os") == "linux" and item["platform"].get("architecture") == arch]
assert len(matches) == 1, f"Expected one linux/{arch} manifest"
digest = matches[0]["digest"]
assert re.fullmatch(r"sha256:[0-9a-f]{64}", digest), "Invalid platform digest"
print(arch, digest)
PYTHON
while read -r arch digest; do
platform_image="$REGISTRY/$ECR_REPOSITORY@$digest"
docker pull --platform "linux/$arch" "$platform_image" > /dev/null
test "$(docker image inspect "$platform_image" --format '{{ .Os }}/{{ .Architecture }}')" = "linux/$arch"
revision=$(docker image inspect "$platform_image" --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}')
test "$revision" = "$DOCS_SHA" || { echo "Published $arch image does not match the requested documentation commit." >&2; exit 1; }
airgap=$(docker image inspect "$platform_image" --format '{{ with index .Config.Labels "ai.zylon.docs.airgap-version" }}{{ . }}{{ end }}')
test "$airgap" = "$AIRGAP_VERSION" || { echo "Published $arch image has a different airgap documentation selection." >&2; exit 1; }
if [[ "$IMAGE_TAG" != docs-* ]]; then
version=$(docker image inspect "$platform_image" --format '{{ index .Config.Labels "org.opencontainers.image.version" }}')
test "$version" = "$IMAGE_TAG" || { echo "Published $arch image has a different documentation version." >&2; exit 1; }
fi
done < "$RUNNER_TEMP/docs-platforms.txt"
- name: Report the published image digest
env:
IMAGE_TAG: ${{ steps.airgap.outputs.tag }}
AIRGAP_VERSION: ${{ steps.airgap.outputs.version }}
run: |
digest=$(aws ecr describe-images --repository-name "$ECR_REPOSITORY" --image-ids "imageTag=$IMAGE_TAG" --query 'imageDetails[0].imageDigest' --output text)
[[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]]
printf '### Documentation image\n\n`%s/%s:%s`\n\nDigest: `%s`\n' "$REGISTRY" "$ECR_REPOSITORY" "$IMAGE_TAG" "$digest" >> "$GITHUB_STEP_SUMMARY"
printf '\nAirgap manual: `%s`\n' "${AIRGAP_VERSION:-all versions}" >> "$GITHUB_STEP_SUMMARY"