name: CI on: push: branches: - main pull_request: jobs: test: runs-on: docker container: image: python:3.13-slim services: postgres: image: postgres:17 env: POSTGRES_USER: wiregui POSTGRES_PASSWORD: wiregui POSTGRES_DB: wiregui options: >- --health-cmd "pg_isready -U wiregui" --health-interval 5s --health-timeout 5s --health-retries 5 valkey: image: valkey/valkey:8 options: >- --health-cmd "valkey-cli ping" --health-interval 5s --health-timeout 5s --health-retries 5 mock-oidc: image: ghcr.io/navikt/mock-oauth2-server:2.1.10 env: SERVER_PORT: "9000" JSON_CONFIG: '{"interactiveLogin":true,"httpServer":"NettyWrapper","tokenCallbacks":[{"issuerId":"test-idp","tokenExpiry":3600,"requestMappings":[{"requestParam":"scope","match":"*","claims":{"sub":"$${claim:sub}","email":"$${claim:sub}@test.local","name":"Test User"}}]}]}' mock-saml: image: kenchan0130/simplesamlphp env: SIMPLESAMLPHP_SP_ENTITY_ID: http://localhost:13003/auth/saml/test-saml/metadata SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: http://localhost:13003/auth/saml/test-saml/callback SIMPLESAMLPHP_IDP_BASE_URL: http://mock-saml:8080/simplesaml/ env: CI: "true" WG_DATABASE_URL: postgresql+asyncpg://wiregui:wiregui@postgres/wiregui WG_REDIS_URL: redis://valkey:6379/0 MOCK_OIDC_HOST: mock-oidc MOCK_SAML_HOST: mock-saml steps: - name: Install system dependencies and checkout run: | apt-get update && apt-get install -y --no-install-recommends \ git wireguard-tools pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl git clone --depth=1 -b "${GITHUB_REF_NAME}" ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Install uv run: pip install uv - name: Install dependencies run: uv sync - name: Install Playwright browsers run: uv run playwright install --with-deps chromium - name: Run migrations run: uv run alembic upgrade head - name: Run unit tests run: uv run pytest tests/ --ignore=tests/e2e --ignore=tests/integration -v --tb=short - name: Run E2E tests run: uv run pytest tests/e2e/ -v --tb=short release: needs: test if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: docker container: image: node:20-slim outputs: new_tag: ${{ steps.version.outputs.new_tag }} new_version: ${{ steps.version.outputs.new_version }} skip: ${{ steps.version.outputs.skip }} steps: - name: Install dependencies and checkout run: | apt-get update && apt-get install -y --no-install-recommends bash git python3 ca-certificates git clone ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} git config --local http.${GITHUB_SERVER_URL}/.extraheader "AUTHORIZATION: basic $(echo -n "x-access-token:${GITHUB_TOKEN}" | base64 -w0)" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Determine version bump id: version shell: bash run: | LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT" CURRENT="${LATEST_TAG#v}" IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" COMMITS=$(git log "${LATEST_TAG}..HEAD" --pretty=format:"%s" 2>/dev/null || git log --pretty=format:"%s") BUMP="none" while IFS= read -r msg; do case "$msg" in *"BREAKING CHANGE"*|*"!:"*) BUMP="major" break ;; feat:*|feat\(*) [ "$BUMP" != "major" ] && BUMP="minor" ;; fix:*|fix\(*|perf:*|perf\(*|refactor:*|refactor\(*) [ "$BUMP" = "none" ] && BUMP="patch" ;; esac done <<< "$COMMITS" if [ "$BUMP" = "none" ]; then echo "No version-relevant commits since ${LATEST_TAG}, skipping release" echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fi case "$BUMP" in major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; minor) MINOR=$((MINOR + 1)); PATCH=0 ;; patch) PATCH=$((PATCH + 1)) ;; esac NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" echo "new_tag=v${NEW_VERSION}" >> "$GITHUB_OUTPUT" echo "bump=${BUMP}" >> "$GITHUB_OUTPUT" echo "skip=false" >> "$GITHUB_OUTPUT" echo "Version bump: ${BUMP} -> v${NEW_VERSION}" - name: Generate changelog id: changelog if: steps.version.outputs.skip != 'true' shell: bash run: | LATEST_TAG="${{ steps.version.outputs.latest_tag }}" NEW_TAG="${{ steps.version.outputs.new_tag }}" BODY="## ${NEW_TAG}"$'\n\n' for type_label in "feat:Features" "fix:Bug Fixes" "refactor:Refactoring" "perf:Performance" "docs:Documentation" "chore:Maintenance"; do prefix="${type_label%%:*}" label="${type_label#*:}" MATCHES=$(git log "${LATEST_TAG}..HEAD" --pretty=format:"%s" 2>/dev/null | grep -E "^${prefix}[:(]" || true) if [ -n "$MATCHES" ]; then BODY="${BODY}### ${label}"$'\n\n' while IFS= read -r line; do CLEAN=$(echo "$line" | sed -E "s/^${prefix}(\([^)]*\))?:\s*//") BODY="${BODY}- ${CLEAN}"$'\n' done <<< "$MATCHES" BODY="${BODY}"$'\n' fi done echo "${BODY}" > /tmp/changelog.md echo "Generated changelog for ${NEW_TAG}" - name: Create tag and release if: steps.version.outputs.skip != 'true' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | NEW_TAG="${{ steps.version.outputs.new_tag }}" git config user.name "Forgejo Actions" git config user.email "noreply@forge.provvedo.com" git tag -a "${NEW_TAG}" -m "Release ${NEW_TAG}" git push origin "${NEW_TAG}" FORGEJO_URL="${GITHUB_SERVER_URL}" REPO="${GITHUB_REPOSITORY}" python3 -c " import json, urllib.request, os body = open('/tmp/changelog.md').read() tag = '${NEW_TAG}' data = json.dumps({ 'tag_name': tag, 'name': tag, 'body': body, 'draft': False, 'prerelease': False }).encode() req = urllib.request.Request( '${FORGEJO_URL}/api/v1/repos/${REPO}/releases', data=data, headers={ 'Authorization': 'token ' + os.environ['GITHUB_TOKEN'], 'Content-Type': 'application/json' }, method='POST' ) resp = urllib.request.urlopen(req) print(f'Created release {tag} (HTTP {resp.status})') " docker: needs: release if: needs.release.outputs.skip != 'true' runs-on: docker container: image: catthehacker/ubuntu:act-latest options: --privileged steps: - name: Checkout repository run: | git clone --depth=1 -b "${GITHUB_REF_NAME}" ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Build and push image shell: bash env: REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | VERSION="${{ needs.release.outputs.new_version }}" TAG="${{ needs.release.outputs.new_tag }}" REGISTRY=$(echo "${{ github.server_url }}" | sed 's|https://||; s|http://||') IMAGE="${REGISTRY}/${{ github.repository_owner }}/wiregui" MAJOR=$(echo "$VERSION" | cut -d. -f1) MINOR=$(echo "$VERSION" | cut -d. -f2) echo "Building ${IMAGE}:${TAG}" # Log in to Forgejo container registry echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ -u "${{ github.repository_owner }}" --password-stdin # Build the image docker build --no-cache \ --build-arg "VERSION=${VERSION}" \ -t "${IMAGE}:${TAG}" \ -t "${IMAGE}:${MAJOR}.${MINOR}" \ -t "${IMAGE}:latest" \ . # Push all tags docker push "${IMAGE}:${TAG}" docker push "${IMAGE}:${MAJOR}.${MINOR}" docker push "${IMAGE}:latest" echo "Pushed ${IMAGE}:${TAG}, ${IMAGE}:${MAJOR}.${MINOR}, ${IMAGE}:latest"