CI: GitHub Actions build and release workflows

- build.yml: runs on push to main, builds Docker image + packs s9pk,
  uploads artifact
- release.yml: runs on version tags (v*.*), builds and creates GitHub
  release with s9pk attached
- Dockerfile: build context is repo root (docker build -f deploy/Dockerfile .)
- Manifest: switched to dockerTag (pre-built image pattern)
- Makefile + s9pk.mk: local build support
This commit is contained in:
Johnny 5
2026-06-07 22:09:05 +00:00
parent 48fef14bc9
commit 84ecbbffa3
7 changed files with 290 additions and 9 deletions
+74
View File
@@ -0,0 +1,74 @@
name: Build Service
on:
workflow_dispatch:
pull_request:
paths-ignore: ['*.md']
branches: ['main']
push:
paths-ignore: ['*.md']
branches: ['main']
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y squashfs-tools-ng
- name: Install start-cli
run: |
curl -fsSL -o start-cli https://github.com/Start9Labs/start-os/releases/download/v0.4.0-alpha.20/start-cli_x86_64-linux
chmod +x start-cli
sudo mv start-cli /usr/local/bin/
start-cli --version
- name: Initialize developer key
env:
DEV_KEY: ${{ secrets.DEV_KEY }}
run: |
if [ -n "$DEV_KEY" ]; then
mkdir -p ~/.startos
echo "$DEV_KEY" > ~/.startos/developer.key.pem
else
start-cli init-key
fi
- name: Build Docker image
run: docker build -f deploy/Dockerfile -t ten31portal:local .
- name: Install StartOS packaging deps
working-directory: deploy
run: npm ci
- name: Build StartOS JavaScript
working-directory: deploy
run: npm run build
- name: Pack s9pk
working-directory: deploy
run: start-cli s9pk pack --arch x86_64 --license ../LICENSE -o ten31portal.s9pk
- name: Upload s9pk artifact
uses: actions/upload-artifact@v4
with:
name: ten31portal-s9pk
path: deploy/ten31portal.s9pk
+61
View File
@@ -0,0 +1,61 @@
name: Release Service
on:
push:
tags:
- 'v*.*'
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y squashfs-tools-ng
- name: Install start-cli
run: |
curl -fsSL -o start-cli https://github.com/Start9Labs/start-os/releases/download/v0.4.0-alpha.20/start-cli_x86_64-linux
chmod +x start-cli
sudo mv start-cli /usr/local/bin/
start-cli --version
- name: Initialize developer key
env:
DEV_KEY: ${{ secrets.DEV_KEY }}
run: |
mkdir -p ~/.startos
echo "$DEV_KEY" > ~/.startos/developer.key.pem
- name: Build Docker image
run: docker build -f deploy/Dockerfile -t ten31portal:local .
- name: Install StartOS packaging deps
working-directory: deploy
run: npm ci
- name: Build StartOS JavaScript
working-directory: deploy
run: npm run build
- name: Pack s9pk
working-directory: deploy
run: start-cli s9pk pack --arch x86_64 --license ../LICENSE -o ten31portal.s9pk
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: deploy/ten31portal.s9pk
generate_release_notes: true
+1
View File
@@ -1,2 +1,3 @@
node_modules/
javascript/
*.s9pk
+6 -7
View File
@@ -1,18 +1,17 @@
# Build context is the repo root (docker build -f deploy/Dockerfile .)
# --- Frontend build stage ---
FROM node:20-slim AS frontend-build
WORKDIR /build/frontend
WORKDIR /build
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci --include=dev
COPY frontend/ ./
RUN npx vite build
# --- Backend stage ---
# --- Backend + runtime stage ---
FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python deps
@@ -23,7 +22,7 @@ COPY backend/alembic.ini ./
RUN pip install --no-cache-dir .
# Copy built frontend
COPY --from=frontend-build /build/frontend/dist ./static/
COPY --from=frontend-build /build/dist ./static/
# Startup script
COPY deploy/start.sh ./start.sh
@@ -33,7 +32,7 @@ RUN chmod +x ./start.sh
RUN mkdir -p /data
ENV TEN31_DB_PATH=/data/portal.db
ENV TEN31_SESSION_SECRET=change-me
ENV TEN31_SESSION_SECRET=***
EXPOSE 8000
+19
View File
@@ -0,0 +1,19 @@
# Ten31Portal StartOS package build
# Docker image must be built separately before packing:
# docker build -f deploy/Dockerfile -t ten31portal:local . (from repo root)
# cd deploy && make x86
LICENSE := ../LICENSE
include s9pk.mk
# Override the license path for s9pk pack
$(BASE_NAME).s9pk: $(INGREDIENTS) .git/HEAD .git/index
@$(MAKE) --no-print-directory ingredients
@echo " Packing '$@'..."
start-cli s9pk pack --license $(LICENSE) -o $@
$(BASE_NAME)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index
@$(MAKE) --no-print-directory ingredients
@echo " Packing '$@'..."
start-cli s9pk pack --arch=$* --license $(LICENSE) -o $@
+127
View File
@@ -0,0 +1,127 @@
# ** Plumbing. DO NOT EDIT **.
# This file is imported by ./Makefile. Make edits there
PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest/index.ts)
INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null)
ARCHES ?= x86 arm riscv
TARGETS ?= arches
ifdef VARIANT
BASE_NAME := $(PACKAGE_ID)_$(VARIANT)
else
BASE_NAME := $(PACKAGE_ID)
endif
.PHONY: all arches aarch64 x86_64 riscv64 arm arm64 x86 riscv arch/* clean install check-deps check-init package ingredients
.DELETE_ON_ERROR:
.SECONDARY:
define SUMMARY
@manifest=$$(start-cli s9pk inspect $(1) manifest); \
size=$$(du -h $(1) | awk '{print $$1}'); \
title=$$(printf '%s' "$$manifest" | jq -r .title); \
version=$$(printf '%s' "$$manifest" | jq -r .version); \
arches=$$(printf '%s' "$$manifest" | jq -r '[.images[].arch // []] | flatten | unique | join(", ")'); \
sdkv=$$(printf '%s' "$$manifest" | jq -r .sdkVersion); \
gitHash=$$(printf '%s' "$$manifest" | jq -r .gitHash | sed -E 's/(.*-modified)$$/\x1b[0;31m\1\x1b[0m/'); \
printf "\n"; \
printf "\033[1;32m✅ Build Complete!\033[0m\n"; \
printf "\n"; \
printf "\033[1;37m📦 $$title\033[0m \033[36mv$$version\033[0m\n"; \
printf "───────────────────────────────\n"; \
printf " \033[1;36mFilename:\033[0m %s\n" "$(1)"; \
printf " \033[1;36mSize:\033[0m %s\n" "$$size"; \
printf " \033[1;36mArch:\033[0m %s\n" "$$arches"; \
printf " \033[1;36mSDK:\033[0m %s\n" "$$sdkv"; \
printf " \033[1;36mGit:\033[0m %s\n" "$$gitHash"; \
echo ""
endef
all: $(TARGETS)
arches: $(ARCHES)
universal: $(BASE_NAME).s9pk
$(call SUMMARY,$<)
arch/%: $(BASE_NAME)_%.s9pk
$(call SUMMARY,$<)
x86 x86_64: arch/x86_64
arm arm64 aarch64: arch/aarch64
riscv riscv64: arch/riscv64
$(BASE_NAME).s9pk: $(INGREDIENTS) .git/HEAD .git/index
@$(MAKE) --no-print-directory ingredients
@echo " Packing '$@'..."
start-cli s9pk pack -o $@
$(BASE_NAME)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index
@$(MAKE) --no-print-directory ingredients
@echo " Packing '$@'..."
start-cli s9pk pack --arch=$* -o $@
ingredients: $(INGREDIENTS)
@echo " Re-evaluating ingredients..."
install: | check-deps check-init
@HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \
if [ -z "$$HOST" ]; then \
echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \
exit 1; \
fi; \
S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \
if [ -z "$$S9PK" ]; then \
echo "Error: No .s9pk file found. Run 'make' first."; \
exit 1; \
fi; \
printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \
start-cli package install -s "$$S9PK"
publish: | all
@REGISTRY=$$(awk -F'/' '/^registry:/ {print $$3}' ~/.startos/config.yaml); \
if [ -z "$$REGISTRY" ]; then \
echo "Error: You must define \"registry: https://my-registry.tld\" in ~/.startos/config.yaml"; \
exit 1; \
fi; \
S3BASE=$$(awk -F'/' '/^s9pk-s3base:/ {print $$3}' ~/.startos/config.yaml); \
if [ -z "$$S3BASE" ]; then \
echo "Error: You must define \"s3base: https://s9pks.my-s3-bucket.tld\" in ~/.startos/config.yaml"; \
exit 1; \
fi; \
command -v s3cmd >/dev/null || \
(echo "Error: s3cmd not found. It must be installed to publish using s3." && exit 1); \
printf "\n🚀 Publishing to %s; indexing on %s ...\n" "$$S3BASE" "$$REGISTRY"; \
for s9pk in *.s9pk; do \
age=$$(( $$(date +%s) - $$(stat -c %Y "$$s9pk") )); \
if [ "$$age" -gt 3600 ]; then \
printf "\033[1;33m⚠️ %s is %d minutes old. Publish anyway? [y/N] \033[0m" "$$s9pk" "$$((age / 60))"; \
read -r ans; \
case "$$ans" in [yY]*) ;; *) echo "Skipping $$s9pk"; continue ;; esac; \
fi; \
start-cli s9pk publish "$$s9pk"; \
done
check-deps:
@command -v start-cli >/dev/null || \
(echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1)
@command -v npm >/dev/null || \
(echo "Error: npm not found. Please install Node.js and npm." && exit 1)
check-init:
@if [ ! -f ~/.startos/developer.key.pem ]; then \
echo "Initializing StartOS developer environment..."; \
start-cli init-key; \
fi
javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules
npm run build
node_modules: package-lock.json
npm ci
package-lock.json: package.json
npm i
clean:
@echo "Cleaning up build artifacts..."
@rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules
+2 -2
View File
@@ -16,8 +16,8 @@ export const manifest = setupManifest({
volumes: ['main'],
images: {
main: {
source: { dockerBuild: { dockerfile: './Dockerfile', workdir: '.' } },
arch: ['x86_64', 'aarch64'],
source: { dockerTag: 'ten31portal:local' },
arch: ['x86_64'],
},
},
alerts: {