Skip to content

Commit e00514b

Browse files
greatgitsbympurnell1
authored andcommitted
ci: publish images to vamos-images repo (#104)
* ci: publish images to vamos-images repo, manifest as release Images are committed to {owner}/vamos-images with a version tag, so forks can publish to their own vamos-images repo. The manifest is published as a GitHub Release on vamOS itself. * build: restore chunking for large images in package_ota Files over 50 MB are split into chunks to stay under raw.githubusercontent.com's 100 MB limit. * build: derive default IMAGES_URL from VERSION file * ci: skip publish gracefully when deploy key is missing Shows setup instructions as workflow warnings instead of failing. * ci: fix profile workflow race with build-system check Upgrade wait-on-check-action to v1.6.0 and add checks-discovery-timeout so it waits up to 5 minutes for build-system to appear instead of immediately failing when the check doesn't exist yet. * Revert "ci: fix profile workflow race with build-system check" This reverts commit b1d74fd. * ci: fix ambiguous refspec when pushing images tag Use an unrelated orphan branch name and push with refs/tags/ prefix to avoid ambiguity between branch and tag with the same name. * ci: include chunked image files in vamos-images push The glob *.img missed chunk files (*.img.00, *.img.01, etc.) produced by the chunking logic for large images like system. * ci: include manifest in vamos-images, link from release Manifest needs CORS headers, which raw.githubusercontent.com provides but release-assets.githubusercontent.com does not. * ci: fix invalid YAML in release notes multiline string
1 parent ba7b198 commit e00514b

File tree

2 files changed

+80
-14
lines changed

2 files changed

+80
-14
lines changed

.github/workflows/build.yml

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,48 @@ jobs:
8787
name: system.erofs.img
8888
path: build/
8989

90+
- name: check deploy key
91+
id: check-key
92+
run: |
93+
if [ -n "${{ secrets.VAMOS_IMAGES_DEPLOY_KEY }}" ]; then
94+
echo "has_key=true" >> $GITHUB_OUTPUT
95+
else
96+
echo "has_key=false" >> $GITHUB_OUTPUT
97+
echo "::warning::VAMOS_IMAGES_DEPLOY_KEY secret is not set — skipping image publish and release."
98+
fi
99+
90100
- name: generate manifest
91-
env:
92-
RELEASE_URL: https://github.com/${{ github.repository }}/releases/download/v${{ env.VERSION }}
101+
if: steps.check-key.outputs.has_key == 'true'
93102
run: |
94103
VERSION=$(cat userspace/root/VERSION)
95104
echo "VERSION=$VERSION" >> $GITHUB_ENV
96-
RELEASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}"
97-
RELEASE_URL=$RELEASE_URL python3 tools/build/package_ota.py
105+
IMAGES_URL="https://github.com/${{ github.repository_owner }}/vamos-images/raw/v${VERSION}"
106+
IMAGES_URL=$IMAGES_URL python3 tools/build/package_ota.py
107+
108+
- name: push images to vamos-images
109+
if: steps.check-key.outputs.has_key == 'true'
110+
uses: actions/checkout@v4
111+
with:
112+
repository: ${{ github.repository_owner }}/vamos-images
113+
ssh-key: ${{ secrets.VAMOS_IMAGES_DEPLOY_KEY }}
114+
path: vamos-images
115+
116+
- name: commit and tag images
117+
if: steps.check-key.outputs.has_key == 'true'
118+
run: |
119+
TAG="v${VERSION}"
120+
cd vamos-images
121+
git checkout --orphan release
122+
cp ../build/ota/*.img* .
123+
cp ../build/ota/manifest.json .
124+
git add .
125+
git -c user.name="github-actions" -c user.email="actions@github.com" \
126+
commit -m "release images for $TAG"
127+
git tag "$TAG"
128+
git push origin "refs/tags/$TAG" --force
98129
99130
- name: create release
131+
if: steps.check-key.outputs.has_key == 'true'
100132
env:
101133
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102134
run: |
@@ -107,10 +139,22 @@ jobs:
107139
git tag -d "$TAG" 2>/dev/null || true
108140
git push origin ":refs/tags/$TAG" 2>/dev/null || true
109141
110-
# Create release with all images and manifest
142+
MANIFEST_URL="https://github.com/${{ github.repository_owner }}/vamos-images/raw/${TAG}/manifest.json"
143+
144+
# Create release linking to manifest
145+
NOTES="Automated release from commit ${{ github.sha }}
146+
147+
Manifest: ${MANIFEST_URL}"
111148
gh release create "$TAG" \
112149
--title "vamOS $TAG" \
113150
--target "${{ github.sha }}" \
114-
--notes "Automated release from commit ${{ github.sha }}" \
115-
build/ota/*.img \
116-
build/ota/manifest.json
151+
--notes "$NOTES"
152+
153+
- name: post setup instructions
154+
if: steps.check-key.outputs.has_key == 'false'
155+
run: |
156+
echo "::warning::VAMOS_IMAGES_DEPLOY_KEY is not configured. To enable image publishing:"
157+
echo "::warning::1. Create a ${{ github.repository_owner }}/vamos-images repo"
158+
echo "::warning::2. Generate an SSH deploy key: ssh-keygen -t ed25519 -f vamos-images-deploy-key -N \"\""
159+
echo "::warning::3. Add the public key to ${{ github.repository_owner }}/vamos-images as a deploy key with write access"
160+
echo "::warning::4. Add the private key as VAMOS_IMAGES_DEPLOY_KEY secret in ${{ github.repository }}"

tools/build/package_ota.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
OTA_OUTPUT_DIR = OUTPUT_DIR / "ota"
1313

1414
SECTOR_SIZE = 4096
15+
CHUNK_SIZE = 52_428_800 # 50 MB - must be under raw.githubusercontent.com's 100 MB limit
1516

16-
RELEASE_URL = os.environ.get("RELEASE_URL", "https://github.com/commaai/vamos/releases/download/untagged")
17+
VERSION = open(ROOT / "userspace" / "root" / "VERSION").read().strip()
18+
IMAGES_URL = os.environ.get("IMAGES_URL", f"https://github.com/commaai/vamos-images/raw/v{VERSION}")
1719

1820
GPT = namedtuple('GPT', ['lun', 'name', 'path', 'start_sector', 'num_sectors', 'has_ab', 'full_check'])
1921
GPTS = [
@@ -73,14 +75,30 @@ def process_file(entry):
7375
sha256.update(b'\x00' * ((SECTOR_SIZE - (size % SECTOR_SIZE)) % SECTOR_SIZE))
7476
ondevice_hash = sha256.hexdigest()
7577

76-
# Copy to output directory
77-
out_fn = OTA_OUTPUT_DIR / f"{entry.name}-{hash_raw}.img"
78-
print(f" copying to {out_fn.name}")
79-
shutil.copy(entry.path, out_fn)
78+
base_name = f"{entry.name}-{hash_raw}.img"
79+
80+
# Write file(s) to output directory, splitting into chunks if needed
81+
chunks = None
82+
if size > CHUNK_SIZE:
83+
chunks = []
84+
chunk_idx = 0
85+
with open(entry.path, 'rb') as f:
86+
while True:
87+
data = f.read(CHUNK_SIZE)
88+
if not data:
89+
break
90+
chunk_name = f"{base_name}.{chunk_idx:02d}"
91+
(OTA_OUTPUT_DIR / chunk_name).write_bytes(data)
92+
chunks.append({"url": f"{IMAGES_URL}/{chunk_name}", "size": len(data)})
93+
print(f" chunk {chunk_idx}: {chunk_name} ({len(data)} bytes)")
94+
chunk_idx += 1
95+
else:
96+
print(f" copying to {base_name}")
97+
shutil.copy(entry.path, OTA_OUTPUT_DIR / base_name)
8098

8199
ret = {
82100
"name": entry.name,
83-
"url": f"{RELEASE_URL}/{out_fn.name}",
101+
"url": f"{IMAGES_URL}/{base_name}",
84102
"hash": hash,
85103
"hash_raw": hash_raw,
86104
"size": size,
@@ -90,6 +108,10 @@ def process_file(entry):
90108
"ondevice_hash": ondevice_hash,
91109
}
92110

111+
if chunks:
112+
ret["url"] = ""
113+
ret["chunks"] = chunks
114+
93115
if isinstance(entry, GPT):
94116
ret["gpt"] = {
95117
"lun": entry.lun,

0 commit comments

Comments
 (0)