name: CI
on:
pull_request:
branches:
- develop
push:
branches:
- develop
tags:
- "v*"
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
permissions:
contents: read
env:
REGISTRY: ghcr.io
IMAGE_NAME: niklasrosenstein/rise
BUILDER_IMAGE_NAME: niklasrosenstein/rise-builder
CHART_REGISTRY: ghcr.io/niklasrosenstein/rise
RUST_VERSION: "1.91.1"
jobs:
prepare:
name: Prepare CI metadata
runs-on: ubuntu-latest
outputs:
ci_version: ${{ steps.meta.outputs.ci_version }}
image_tag: ${{ steps.meta.outputs.image_tag }}
short_sha: ${{ steps.meta.outputs.short_sha }}
is_trusted_pr: ${{ steps.meta.outputs.is_trusted_pr }}
should_publish: ${{ steps.meta.outputs.should_publish }}
steps:
- name: Compute metadata
id: meta
shell: bash
run: |
set -euo pipefail
short_sha="${GITHUB_SHA::7}"
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
pr_number="${{ github.event.pull_request.number }}"
ci_version="0.0.0-pr.${pr_number}+${short_sha}"
image_tag="pr-${pr_number}-${short_sha}"
is_trusted_pr="false"
if [[ "${{ github.event.pull_request.head.repo.full_name }}" == "${{ github.repository }}" ]]; then
is_trusted_pr="true"
fi
elif [[ "${GITHUB_REF}" == refs/heads/develop ]]; then
ci_version="0.0.0-develop.${GITHUB_RUN_NUMBER}+${short_sha}"
image_tag="develop-${short_sha}"
is_trusted_pr="false"
elif [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
version="${GITHUB_REF#refs/tags/v}"
ci_version="${version}"
image_tag="${version}"
is_trusted_pr="false"
else
ci_version="0.0.0-dev.${GITHUB_RUN_NUMBER}+${short_sha}"
image_tag="dev-${short_sha}"
is_trusted_pr="false"
fi
should_publish="false"
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
should_publish="true"
elif [[ "${GITHUB_EVENT_NAME}" == "pull_request" && "${is_trusted_pr}" == "true" ]]; then
should_publish="true"
fi
echo "ci_version=${ci_version}" >> "$GITHUB_OUTPUT"
echo "image_tag=${image_tag}" >> "$GITHUB_OUTPUT"
echo "short_sha=${short_sha}" >> "$GITHUB_OUTPUT"
echo "is_trusted_pr=${is_trusted_pr}" >> "$GITHUB_OUTPUT"
echo "should_publish=${should_publish}" >> "$GITHUB_OUTPUT"
echo "CI Version: ${ci_version}"
echo "Image Tag: ${image_tag}"
echo "Short SHA: ${short_sha}"
echo "Is Trusted PR: ${is_trusted_pr}"
echo "Should Publish: ${should_publish}"
rust-quality:
name: Rust quality checks
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
components: rustfmt, clippy
- name: Rust cache
uses: Swatinem/rust-cache@v2
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g cargo:sqlx-cli cargo:cargo-all-features
- name: Verify backend config schema is up to date
run: mise config:schema:check
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run Clippy (all feature combinations)
run: cargo all-features clippy --all-targets -- -D warnings
env:
SQLX_OFFLINE: true
- name: Verify SQLX offline query data
run: mise run sqlx:check
unit-tests-linux:
name: Linux tests
runs-on: ubuntu-latest
needs: prepare
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: rise_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Rust cache
uses: Swatinem/rust-cache@v2
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g cargo:sqlx-cli
- name: Run database migrations
run: |
sqlx database create
sqlx migrate run
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/rise_test
- name: Run tests
run: cargo test --all-features
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/rise_test
helm-lint:
name: Helm lint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g helm
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.1
- name: Run chart-testing (lint)
run: ct lint --target-branch ${{ github.event.repository.default_branch }} --charts helm/rise --validate-maintainers=false
- name: Install kubeconform
run: |
set -euo pipefail
curl -sSLo /tmp/kubeconform.tar.gz https://github.com/yannh/kubeconform/releases/download/v0.7.0/kubeconform-linux-amd64.tar.gz
tar -xzf /tmp/kubeconform.tar.gz -C /tmp kubeconform
sudo install -m 0755 /tmp/kubeconform /usr/local/bin/kubeconform
- name: Validate Helm templates with kubeconform
run: |
helm template rise helm/rise | kubeconform -strict -summary
helm template rise helm/rise -f helm/rise/values-ci.yaml | kubeconform -strict -summary
package-image:
name: Build and publish image
runs-on: ubuntu-latest
needs:
- prepare
- rust-quality
- unit-tests-linux
- helm-lint
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
if: needs.prepare.outputs.should_publish == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and optionally push image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: rise
push: ${{ needs.prepare.outputs.should_publish == 'true' }}
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.image_tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ needs.prepare.outputs.short_sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
provenance: false
- name: Build and optionally push builder image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: rise-builder
push: ${{ needs.prepare.outputs.should_publish == 'true' }}
tags: |
${{ env.REGISTRY }}/${{ env.BUILDER_IMAGE_NAME }}:${{ needs.prepare.outputs.image_tag }}
${{ env.REGISTRY }}/${{ env.BUILDER_IMAGE_NAME }}:sha-${{ needs.prepare.outputs.short_sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
provenance: false
package-helm:
name: Package and publish Helm chart
runs-on: ubuntu-latest
needs:
- prepare
- package-image
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g helm
- name: Patch chart version and appVersion for CI package
shell: bash
run: |
set -euo pipefail
ci_version="${{ needs.prepare.outputs.ci_version }}"
image_tag="${{ needs.prepare.outputs.image_tag }}"
sed -i "s/^version: .*/version: ${ci_version}/" helm/rise/Chart.yaml
sed -i "s/^appVersion: .*/appVersion: \"${image_tag}\"/" helm/rise/Chart.yaml
cat helm/rise/Chart.yaml
- name: Package chart
run: |
mkdir -p .helm-packages
helm package helm/rise -d .helm-packages
- name: Upload chart artifact
uses: actions/upload-artifact@v4
with:
name: helm-chart-${{ needs.prepare.outputs.image_tag }}
path: .helm-packages/*.tgz
- name: Login to GHCR for Helm OCI push
if: needs.prepare.outputs.should_publish == 'true'
run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ${{ env.REGISTRY }} -u "${{ github.actor }}" --password-stdin
- name: Push chart to OCI registry
if: needs.prepare.outputs.should_publish == 'true'
run: |
chart_file=$(ls .helm-packages/*.tgz)
helm push "$chart_file" "oci://${{ env.CHART_REGISTRY }}"
publish-crates:
name: Publish to crates.io
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
needs:
- rust-quality
- unit-tests-linux
permissions:
id-token: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Verify version matches tag
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/v}
CARGO_VERSION=$(cargo metadata --format-version=1 --no-deps | jq -r '.packages[0].version')
echo "Tag version: $TAG_VERSION"
echo "Cargo version: $CARGO_VERSION"
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) doesn't match Cargo version ($CARGO_VERSION)"
exit 1
fi
- name: Authenticate with crates.io
uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish to crates.io
run: cargo publish --features cli
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
e2e-build:
name: E2E build tests
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Rust cache
uses: Swatinem/rust-cache@v2
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g pack railpack buildkit
- name: Build Rise CLI
run: cargo build --release --features cli
- name: Run e2e build tests (no proxy)
run: RISE_BIN=./target/release/rise bash tests/e2e-build/run.sh --no-proxy
- name: Run e2e build tests (with proxy)
run: RISE_BIN=./target/release/rise bash tests/e2e-build/run.sh --only-proxy
e2e-minikube:
name: Minikube end-to-end smoke tests
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
needs:
- prepare
- package-image
- package-helm
permissions:
contents: read
packages: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup mise
uses: jdx/mise-action@v2
with:
cache: true
experimental: true
- name: Install mise tools
run: mise use -g minikube kubectl helm
- name: Start Minikube and run smoke tests
env:
RISE_IMAGE_REPOSITORY: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
RISE_IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
run: bash scripts/ci/e2e-minikube.sh