diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index f1e4706..c508dc4 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,4 +1,7 @@ # Runs on pull requests: unit tests plus a compile check. +# Split into separate jobs (Odin, API, Infra) so they can run concurrently, +# and gated by changed paths so each only runs when its code changes. +# Infra always runs regardless of what changed. name: CI @@ -16,21 +19,38 @@ env: ODIN_VERSION: dev-2026-07 jobs: - test: - name: Unit tests + changes: + name: Detect changed paths + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + odin: ${{ steps.filter.outputs.odin }} + api: ${{ steps.filter.outputs.api }} + steps: + - uses: dorny/paths-filter@v4 + id: filter + with: + filters: | + odin: + - 'src/**' + - 'lib/**' + - 'scripts/**' + - 'odinfmt.json' + api: + - 'api/**' + + odin: + name: Odin unit tests and build + needs: changes + if: ${{ needs.changes.outputs.odin == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: recursive - - uses: actions/setup-go@v6 - with: - go-version: '1.26' - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install Odin ${{ env.ODIN_VERSION }} run: scripts/install_odin.sh @@ -54,16 +74,18 @@ jobs: mkdir -p bin odin build src -collection:lib=lib/local -o:speed -out:bin/desi_explorer - - name: Run Go tests - run: | - cd infra - go mod download - go test ./... + api: + name: API unit tests and lint + needs: changes + if: ${{ needs.changes.outputs.api == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive - - name: Vet Go code - run: | - cd infra - go vet ./... + - name: Install Rust + uses: dtolnay/rust-toolchain@stable - name: Test Rust API run: | @@ -79,3 +101,26 @@ jobs: run: | cd api cargo fmt --all --check + + infra: + name: Infra unit tests and vet + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + + - name: Run Go tests + run: | + cd infra + go mod download + go test ./... + + - name: Vet Go code + run: | + cd infra + go vet ./... diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..35e2d12 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,166 @@ +# Runs on version tags (e.g. `v1.0.0`) tagged off `main`: detects which +# sections changed since the previous tag, builds only those, and runs +# `pulumi up` to deploy. Infra always runs (it reconciles the cluster) while +# the Odin web build and the API are only built/deployed when their code +# changed. + +name: Deploy + +on: + push: + tags: + - 'v*' + +concurrency: + group: desi-explorer-deploy-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + # Odin release to use (matches `dev-2026-07`, the version used locally). + ODIN_VERSION: dev-2026-07 + +jobs: + changes: + name: Detect what changed since previous tag + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + odin: ${{ steps.filter.outputs.odin }} + api: ${{ steps.filter.outputs.api }} + infra: ${{ steps.filter.outputs.infra }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Determine previous tag + id: prev + run: | + git fetch --tags --force + PREV="$(git tag --sort=-v:refname --list 'v*' | sed -n 2p)" + if [ -z "$PREV" ]; then PREV="origin/main"; fi + echo "prev=${PREV}" >> "$GITHUB_OUTPUT" + echo "Comparing ${GITHUB_REF_NAME} against ${PREV}" + + - uses: dorny/paths-filter@v4 + id: filter + with: + base: ${{ steps.prev.outputs.prev }} + filters: | + odin: + - 'src/**' + - 'lib/**' + - 'scripts/**' + - 'odinfmt.json' + api: + - 'api/**' + infra: + - 'infra/**' + + build-web: + name: Build web (Odin/WASM) + needs: changes + if: ${{ needs.changes.outputs.odin == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install Odin ${{ env.ODIN_VERSION }} + run: scripts/install_odin.sh + + - name: Install Emscripten + run: | + git clone --depth 1 https://github.com/emscripten-core/emsdk.git /tmp/emsdk + /tmp/emsdk/emsdk install latest + /tmp/emsdk/emsdk activate latest + + - name: Build WebAssembly + run: | + source /tmp/emsdk/emsdk_env.sh + scripts/build_web.sh + + - name: Package + run: | + mkdir -p dist + cd build/web + python3 - <<'EOF' + import glob, zipfile + z = zipfile.ZipFile('../../dist/desi-explorer-web.zip', 'w', zipfile.ZIP_DEFLATED) + for f in glob.glob('*'): + z.write(f) + EOF + + - uses: christopherHX/gitea-upload-artifact@v4 + with: + name: web + path: dist/desi-explorer-web.zip + + build-api: + name: Build API (Rust) + needs: changes + if: ${{ needs.changes.outputs.api == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Build release binary + run: | + cd api + cargo build --release + + - uses: christopherHX/gitea-upload-artifact@v4 + with: + name: api + path: api/target/release/desi-explorer-api + + deploy: + name: Deploy (infra up) + needs: [changes, build-web, build-api] + if: ${{ !failure() && !cancelled() }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + + # Pulumi CLI. State uses the local file backend (infra/Pulumi.yaml -> + # file://~/.pulumi), so no Pulumi Cloud token is needed — but the + # self-hosted runner must persist ~/.pulumi between runs to keep state. + - name: Install Pulumi + run: | + curl -fsSL https://get.pulumi.com | sh + echo "$HOME/.pulumi/bin" >> "$GITHUB_PATH" + + # The runner must reach the on-prem k3s cluster. For a self-hosted runner + # this is the ambient kubeconfig; otherwise populate it from a KUBECONFIG + # secret here: + - name: Configure kubeconfig + run: | + mkdir -p "$HOME/.kube" + if [ -n "$KUBECONFIG_B64" ]; then + printf '%s' "$KUBECONFIG_B64" | base64 -d > "$HOME/.kube/config" + fi + env: + KUBECONFIG_B64: ${{ secrets.KUBECONFIG }} + + - name: Deploy with Pulumi + run: | + go mod download + pulumi up --yes --skip-preview + working-directory: infra