diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 0000000..d71183b --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,91 @@ +name: Dev + +on: + push: + branches: + - dev + +jobs: + release: + runs-on: ubuntu-latest + container: + image: python:3.13-slim + steps: + - name: Install system dependencies + run: | + apt-get update && apt-get install -y --no-install-recommends git ca-certificates + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Configure git + run: | + git config user.name "GitHub Actions" + git config user.email "noreply@github.com" + + - name: Install uv and dependencies + run: | + pip install uv + uv sync --group dev + + - name: Semantic release (rc) + id: semrel + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION=$(uv run semantic-release version --print 2>/dev/null || echo "") + if [ -z "$VERSION" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "No release needed" + else + uv run semantic-release version + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "new_version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "Released v${VERSION}" + fi + + outputs: + new_version: ${{ steps.semrel.outputs.new_version }} + skip: ${{ steps.semrel.outputs.skip }} + + docker: + needs: release + if: needs.release.outputs.skip != 'true' + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: dev + fetch-depth: 0 + fetch-tags: true + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push pre-release image + run: | + VERSION="${{ needs.release.outputs.new_version }}" + IMAGE="ghcr.io/${{ github.repository_owner }}/wiregui" + + echo "Building ${IMAGE}:v${VERSION}" + + docker build --no-cache \ + --build-arg "VERSION=${VERSION}" \ + -t "${IMAGE}:v${VERSION}" \ + -t "${IMAGE}:dev" \ + . + + docker push "${IMAGE}:v${VERSION}" + docker push "${IMAGE}:dev" + + echo "Pushed ${IMAGE}:v${VERSION}, ${IMAGE}:dev" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d5bfb1e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,205 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + 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 + env: + CI: "true" + WG_DATABASE_URL: postgresql+asyncpg://wiregui:wiregui@postgres/wiregui + steps: + - name: Install system dependencies + run: | + apt-get update && apt-get install -y --no-install-recommends \ + git wireguard-tools pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl + + - name: Checkout + uses: actions/checkout@v4 + + - name: Install uv + run: pip install uv + + - name: Install dependencies + run: uv sync + + - name: Run unit tests + run: uv run pytest tests/ --ignore=tests/e2e -v --tb=short + + - name: Run E2E tests + run: | + uv run alembic upgrade head + uv run pytest tests/e2e/ -v --tb=short + + release: + needs: test + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + runs-on: ubuntu-latest + 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 + run: | + apt-get update && apt-get install -y --no-install-recommends bash git python3 ca-certificates + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Configure git + run: | + git config user.name "GitHub Actions" + git config user.email "noreply@github.com" + + - 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: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + NEW_TAG="${{ steps.version.outputs.new_tag }}" + + git tag -a "${NEW_TAG}" -m "Release ${NEW_TAG}" + git push origin "${NEW_TAG}" + + gh release create "${NEW_TAG}" \ + --title "${NEW_TAG}" \ + --notes-file /tmp/changelog.md + + docker: + needs: release + if: needs.release.outputs.skip != 'true' + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + run: | + VERSION="${{ needs.release.outputs.new_version }}" + TAG="${{ needs.release.outputs.new_tag }}" + IMAGE="ghcr.io/${{ github.repository_owner }}/wiregui" + + MAJOR=$(echo "$VERSION" | cut -d. -f1) + MINOR=$(echo "$VERSION" | cut -d. -f2) + + echo "Building ${IMAGE}:${TAG}" + + docker build --no-cache \ + --build-arg "VERSION=${VERSION}" \ + -t "${IMAGE}:${TAG}" \ + -t "${IMAGE}:${MAJOR}.${MINOR}" \ + -t "${IMAGE}:latest" \ + . + + docker push "${IMAGE}:${TAG}" + docker push "${IMAGE}:${MAJOR}.${MINOR}" + docker push "${IMAGE}:latest" + + echo "Pushed ${IMAGE}:${TAG}, ${IMAGE}:${MAJOR}.${MINOR}, ${IMAGE}:latest" \ No newline at end of file