diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..7a14f0e --- /dev/null +++ b/.github/README.md @@ -0,0 +1,264 @@ +# GitHub Workflows Documentation + +This directory contains GitHub Actions workflows for automating the build, test, and release process of the UniFi Network Operator. + +## Workflows Overview + +### 1. Docker Build and Push (`docker-build-push.yaml`) + +**Triggers:** +- Push to `main` branch +- Push to `feature/**` branches +- Push of tags starting with `v*` +- Pull requests to `main` +- Manual dispatch + +**What it does:** +- Runs Go tests with coverage +- Builds multi-architecture Docker images (amd64, arm64) +- Pushes images to GitHub Container Registry (ghcr.io) +- Creates tags based on branch/tag names + +**Image naming:** +- `ghcr.io/vegardengen/unifi-network-operator:main` - Latest from main branch +- `ghcr.io/vegardengen/unifi-network-operator:feature-xyz` - Feature branch builds +- `ghcr.io/vegardengen/unifi-network-operator:v1.0.0` - Version tags +- `ghcr.io/vegardengen/unifi-network-operator:latest` - Latest stable release + +### 2. Helm Chart Release (`helm-release.yaml`) + +**Triggers:** +- Push to `main` branch with changes in `helm/` directory +- Manual dispatch + +**What it does:** +- Packages the Helm chart +- Creates GitHub releases for chart versions +- Publishes chart to GitHub Pages +- Updates the Helm repository index + +**Chart repository:** https://vegardengen.github.io/unifi-network-operator + +### 3. Full Release (`release.yaml`) + +**Triggers:** +- Push of version tags (e.g., `v1.0.0`, `v1.2.3`) +- Manual dispatch with version input + +**What it does:** +1. Builds and pushes multi-arch Docker images +2. Updates Chart.yaml and values.yaml with release version +3. Packages and releases Helm chart +4. Creates GitHub release with release notes +5. Attaches Helm chart package to release + +**Pre-release detection:** Automatically marks releases as pre-release if tag contains `alpha`, `beta`, or `rc` + +### 4. PR Validation (`pr-validation.yaml`) + +**Triggers:** +- Pull requests to `main` branch +- Manual dispatch + +**What it does:** +- Validates Go code formatting +- Runs `go vet` +- Executes tests with race detection +- Uploads coverage to Codecov +- Lints Helm chart +- Validates rendered Kubernetes manifests +- Test builds Docker image + +## Setup Requirements + +### 1. Enable GitHub Pages + +1. Go to repository Settings → Pages +2. Source: Deploy from a branch +3. Branch: `gh-pages` / `/ (root)` +4. Save + +The Helm chart will be available at: https://vegardengen.github.io/unifi-network-operator + +### 2. Enable GitHub Packages + +GitHub Container Registry is enabled by default. Images are automatically pushed to: +`ghcr.io/vegardengen/unifi-network-operator` + +To pull images: +```bash +docker pull ghcr.io/vegardengen/unifi-network-operator:latest +``` + +### 3. Configure Secrets (Optional) + +The workflows use `GITHUB_TOKEN` which is automatically provided. Additional secrets you might want to add: + +- `CODECOV_TOKEN` - For uploading coverage reports (optional) + +### 4. Branch Protection (Recommended) + +Configure branch protection for `main`: +1. Go to Settings → Branches → Branch protection rules +2. Add rule for `main` +3. Enable: + - Require pull request reviews + - Require status checks to pass (select PR Validation workflow) + - Require branches to be up to date + +## Usage + +### Creating a Release + +#### Method 1: Using Git Tags (Recommended) + +```bash +# Create and push a version tag +git tag -a v1.0.0 -m "Release v1.0.0" +git push github v1.0.0 +``` + +This automatically: +1. Builds Docker images for `v1.0.0` and `latest` +2. Packages Helm chart with version `1.0.0` +3. Creates GitHub release +4. Publishes to Helm repository + +#### Method 2: Manual Dispatch + +1. Go to Actions → Release workflow +2. Click "Run workflow" +3. Enter the version tag (e.g., `v1.0.0`) +4. Click "Run workflow" + +### Installing from the Helm Repository + +Once the release workflow completes: + +```bash +# Add the Helm repository +helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator + +# Update repository cache +helm repo update + +# Install the operator +helm install unifi-network-operator unifi-network-operator/unifi-network-operator \ + --namespace unifi-network-operator-system \ + --create-namespace \ + --set unifi.url="https://your-unifi-controller:8443" \ + --set unifi.password="your-password" +``` + +### Using Development Builds + +Feature branch builds are automatically pushed: + +```bash +# Use a specific feature branch build +helm install unifi-network-operator ./helm/unifi-network-operator \ + --set image.repository=ghcr.io/vegardengen/unifi-network-operator \ + --set image.tag=feature-xyz +``` + +## Workflow Files + +- [`docker-build-push.yaml`](workflows/docker-build-push.yaml) - Docker image CI/CD +- [`helm-release.yaml`](workflows/helm-release.yaml) - Helm chart publishing +- [`release.yaml`](workflows/release.yaml) - Complete release process +- [`pr-validation.yaml`](workflows/pr-validation.yaml) - PR checks +- [`cr.yaml`](cr.yaml) - Chart Releaser configuration + +## Versioning Strategy + +### Docker Images + +- `latest` - Latest stable release from main branch +- `vX.Y.Z` - Specific version tag +- `X.Y.Z` - Version without 'v' prefix +- `X.Y` - Major.minor version +- `X` - Major version only +- `main` - Latest commit on main branch +- `feature-name` - Feature branch builds + +### Helm Charts + +- Chart version follows semantic versioning (X.Y.Z) +- AppVersion matches Docker image tag +- Both are automatically updated during release + +## Troubleshooting + +### Docker Build Fails + +Check: +- Dockerfile syntax +- Go dependencies in go.mod +- Build context includes all necessary files + +### Helm Release Fails + +Check: +- Chart.yaml is valid +- All template files are valid YAML +- No syntax errors in templates +- Version in Chart.yaml is unique + +### GitHub Pages Not Updating + +1. Check workflow completed successfully +2. Verify `gh-pages` branch exists +3. Check Pages is enabled in repository settings +4. Wait a few minutes for CDN propagation + +### Images Not Accessible + +Public images require: +1. Repository → Settings → Packages +2. Find the package +3. Package settings → Change visibility → Public + +## Best Practices + +1. **Always test locally first:** + ```bash + make helm-lint + make helm-template + docker build -t test . + ``` + +2. **Use semantic versioning:** + - Major (X): Breaking changes + - Minor (Y): New features, backward compatible + - Patch (Z): Bug fixes + +3. **Create release notes:** + - Describe what changed + - Highlight breaking changes + - Document upgrade path + +4. **Test releases in a dev environment:** + - Use pre-release tags (`v1.0.0-beta1`) + - Validate before promoting to stable + +## Monitoring + +### Check Workflow Status + +- Go to repository → Actions +- View workflow runs +- Check logs for failures + +### View Published Artifacts + +- **Docker images:** https://github.com/vegardengen/unifi-network-operator/pkgs/container/unifi-network-operator +- **Helm charts:** https://github.com/vegardengen/unifi-network-operator/releases +- **Chart repository:** https://vegardengen.github.io/unifi-network-operator + +## Support + +For issues with workflows: +1. Check the Actions tab for detailed logs +2. Review the workflow YAML files +3. Consult GitHub Actions documentation +4. Open an issue in the repository diff --git a/.github/SETUP.md b/.github/SETUP.md new file mode 100644 index 0000000..cce72bf --- /dev/null +++ b/.github/SETUP.md @@ -0,0 +1,244 @@ +# GitHub Actions Setup Guide + +Quick guide to get the CI/CD workflows running for the UniFi Network Operator. + +## Prerequisites + +- Repository pushed to GitHub +- Admin access to the repository + +## Step-by-Step Setup + +### 1. Enable GitHub Container Registry + +Images will be pushed to `ghcr.io/vegardengen/unifi-network-operator` + +**Make the package public (after first push):** + +1. Go to your GitHub profile → Packages +2. Find `unifi-network-operator` +3. Package settings → Change visibility → Public +4. Confirm by typing the package name + +### 2. Enable GitHub Pages + +**Set up GitHub Pages for Helm chart hosting:** + +1. Go to repository **Settings** → **Pages** +2. Under "Build and deployment": + - **Source:** Deploy from a branch + - **Branch:** `gh-pages` / `/ (root)` + - Click **Save** + +3. Wait for initial deployment (workflow will create the branch) + +Your Helm repository will be available at: +``` +https://vegardengen.github.io/unifi-network-operator +``` + +### 3. Configure Repository Permissions + +**Allow workflows to create releases:** + +1. Go to **Settings** → **Actions** → **General** +2. Scroll to "Workflow permissions" +3. Select **Read and write permissions** +4. Check **Allow GitHub Actions to create and approve pull requests** +5. Click **Save** + +### 4. Set Up Branch Protection (Optional but Recommended) + +**Protect the main branch:** + +1. Go to **Settings** → **Branches** +2. Click **Add branch protection rule** +3. Branch name pattern: `main` +4. Enable: + - ☑ Require a pull request before merging + - ☑ Require status checks to pass before merging + - Search and select: `lint-and-test`, `helm-lint`, `docker-build` + - ☑ Require branches to be up to date before merging +5. Click **Create** or **Save changes** + +### 5. Test the Workflows + +**Test PR validation:** + +```bash +# Create a test branch +git checkout -b test-workflows + +# Make a small change +echo "# Test" >> README.md + +# Commit and push +git add README.md +git commit -m "Test workflows" +git push github test-workflows + +# Create a PR on GitHub +# Check Actions tab to see PR validation running +``` + +**Test Docker build:** + +```bash +# Push to main branch (after PR is merged) +git checkout main +git pull github main +git push github main + +# Check Actions → Docker Build and Push workflow +``` + +**Test full release:** + +```bash +# Create and push a version tag +git tag -a v0.1.0 -m "First release" +git push github v0.1.0 + +# Check Actions → Release workflow +# This will: +# 1. Build Docker images +# 2. Package Helm chart +# 3. Create GitHub release +# 4. Publish to Helm repository +``` + +### 6. Verify Everything Works + +**Check Docker image:** + +```bash +# Pull the image +docker pull ghcr.io/vegardengen/unifi-network-operator:v0.1.0 + +# Verify it works +docker run --rm ghcr.io/vegardengen/unifi-network-operator:v0.1.0 --version +``` + +**Check Helm repository:** + +```bash +# Add the Helm repo +helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator + +# Update +helm repo update + +# Search for charts +helm search repo unifi-network-operator + +# Show chart info +helm show chart unifi-network-operator/unifi-network-operator +``` + +## Optional: Add Codecov Integration + +**For test coverage reports:** + +1. Go to https://codecov.io +2. Sign in with GitHub +3. Add your repository +4. Copy the token +5. Go to repository **Settings** → **Secrets and variables** → **Actions** +6. Click **New repository secret** + - Name: `CODECOV_TOKEN` + - Value: [paste token] +7. Click **Add secret** + +## Troubleshooting + +### Workflow Fails with "Resource not accessible by integration" + +**Fix:** Enable read and write permissions (see Step 3 above) + +### Docker Image Push Fails with "Permission denied" + +**Fix:** +1. Go to package settings +2. Add repository access +3. Or change package visibility to public + +### Helm Chart Not Appearing on GitHub Pages + +**Check:** +1. `gh-pages` branch was created +2. Pages is enabled in settings +3. Workflow completed successfully +4. Wait 5-10 minutes for CDN + +**Manually create gh-pages branch if needed:** +```bash +git checkout --orphan gh-pages +git rm -rf . +echo "# Helm Charts" > README.md +git add README.md +git commit -m "Initialize gh-pages" +git push github gh-pages +``` + +### Release Workflow Fails + +**Common issues:** +- Chart version already exists → Bump version in Chart.yaml +- Invalid YAML → Run `make helm-lint` locally first +- Missing permissions → Check Step 3 + +## Next Steps + +Once everything is working: + +1. **Update README.md** with installation instructions: + ```markdown + ## Installation + + ### Using Helm + + ```bash + helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator + helm repo update + helm install unifi-network-operator unifi-network-operator/unifi-network-operator \ + --namespace unifi-network-operator-system \ + --create-namespace \ + --set unifi.url="https://your-controller:8443" \ + --set unifi.password="your-password" + ``` + ``` + +2. **Add badges to README.md:** + ```markdown + ![Build Status](https://github.com/vegardengen/unifi-network-operator/workflows/Build%20and%20Push%20Docker%20Image/badge.svg) + ![Helm Release](https://github.com/vegardengen/unifi-network-operator/workflows/Release%20Helm%20Chart/badge.svg) + ``` + +3. **Create your first official release:** + ```bash + git tag -a v0.1.0 -m "Initial release" + git push github v0.1.0 + ``` + +4. **Monitor the Actions tab** to ensure everything completes successfully + +## Workflow Files Summary + +| File | Purpose | Trigger | +|------|---------|---------| +| `docker-build-push.yaml` | Build and push Docker images | Push to main, tags, PRs | +| `helm-release.yaml` | Publish Helm chart to GitHub Pages | Push to main (helm changes) | +| `release.yaml` | Complete release process | Version tags (v*) | +| `pr-validation.yaml` | Validate PRs | Pull requests to main | + +## Getting Help + +- **GitHub Actions Docs:** https://docs.github.com/en/actions +- **Helm Chart Releaser:** https://github.com/helm/chart-releaser-action +- **GitHub Container Registry:** https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry + +## Configuration Files + +- [`.github/cr.yaml`](cr.yaml) - Chart Releaser configuration +- [`.github/README.md`](README.md) - Detailed workflow documentation +- [`workflows/`](workflows/) - All workflow definitions diff --git a/.github/cr.yaml b/.github/cr.yaml new file mode 100644 index 0000000..9b8e527 --- /dev/null +++ b/.github/cr.yaml @@ -0,0 +1,6 @@ +# Chart Releaser configuration +owner: vegardengen +git-repo: unifi-network-operator +charts-repo-url: https://vegardengen.github.io/unifi-network-operator +# Skip packaging if the chart version already exists +skip-existing: true diff --git a/.github/workflows/docker-build-push.yaml b/.github/workflows/docker-build-push.yaml new file mode 100644 index 0000000..5ed8409 --- /dev/null +++ b/.github/workflows/docker-build-push.yaml @@ -0,0 +1,92 @@ +name: Build and Push Docker Image + +on: + push: + branches: + - main + - 'feature/**' + tags: + - 'v*' + pull_request: + branches: + - main + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run tests + run: | + go test -v ./... -coverprofile=coverage.out + go tool cover -func=coverage.out + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + VERSION=${{ github.ref_name }} + COMMIT=${{ github.sha }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} + + - name: Upload coverage reports + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.out + retention-days: 7 diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml new file mode 100644 index 0000000..bec89ea --- /dev/null +++ b/.github/workflows/helm-release.yaml @@ -0,0 +1,65 @@ +name: Release Helm Chart + +on: + push: + branches: + - main + paths: + - 'helm/unifi-network-operator/**' + - '.github/workflows/helm-release.yaml' + workflow_dispatch: + +permissions: + contents: write + pages: write + id-token: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.0 + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.6.0 + with: + charts_dir: helm + config: .github/cr.yaml + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_SKIP_EXISTING: true + + publish-pages: + needs: release + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./gh-pages + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/pr-validation.yaml b/.github/workflows/pr-validation.yaml new file mode 100644 index 0000000..1344ad2 --- /dev/null +++ b/.github/workflows/pr-validation.yaml @@ -0,0 +1,90 @@ +name: PR Validation + +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run go fmt + run: | + if [ -n "$(gofmt -s -l .)" ]; then + echo "Go code is not formatted:" + gofmt -s -d . + exit 1 + fi + + - name: Run go vet + run: go vet ./... + + - name: Run tests + run: go test -v -race -coverprofile=coverage.out ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + file: ./coverage.out + fail_ci_if_error: false + + helm-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.0 + + - name: Lint Helm chart + run: | + helm lint helm/unifi-network-operator \ + --set unifi.url="https://test.local" \ + --set unifi.password="test" + + - name: Template Helm chart + run: | + helm template test-release helm/unifi-network-operator \ + --namespace test \ + --set unifi.url="https://test.local" \ + --set unifi.password="test" \ + > /tmp/rendered.yaml + + - name: Validate rendered manifests + run: | + # Check that the rendered output is valid YAML + kubectl --dry-run=client apply -f /tmp/rendered.yaml + + docker-build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image (test only) + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64 + push: false + tags: test:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..1fcf020 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,222 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + workflow_dispatch: + inputs: + tag: + description: 'Tag to release (e.g., v1.0.0)' + required: true + type: string + +permissions: + contents: write + packages: write + pages: write + id-token: write + +jobs: + # Build and push multi-arch Docker images + build-images: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Run tests + run: go test -v ./... + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.tag }}" + else + VERSION="${GITHUB_REF#refs/tags/}" + fi + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "version_no_v=${VERSION#v}" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }} + ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version_no_v }} + ghcr.io/${{ github.repository }}:latest + labels: | + org.opencontainers.image.source=${{ github.event.repository.html_url }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.version=${{ steps.version.outputs.version }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Package and release Helm chart + release-chart: + needs: build-images + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.0 + + - name: Extract version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.tag }}" + else + VERSION="${GITHUB_REF#refs/tags/}" + fi + VERSION_NO_V="${VERSION#v}" + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "version_no_v=${VERSION_NO_V}" >> $GITHUB_OUTPUT + + - name: Update Chart.yaml version and appVersion + run: | + sed -i "s/^version:.*/version: ${{ steps.version.outputs.version_no_v }}/" helm/unifi-network-operator/Chart.yaml + sed -i "s/^appVersion:.*/appVersion: \"${{ steps.version.outputs.version }}\"/" helm/unifi-network-operator/Chart.yaml + + - name: Update values.yaml image tag + run: | + sed -i "s/tag: \".*\"/tag: \"${{ steps.version.outputs.version }}\"/" helm/unifi-network-operator/values.yaml + sed -i "s|repository: .*|repository: ghcr.io/${{ github.repository }}|" helm/unifi-network-operator/values.yaml + + - name: Package Helm chart + run: | + mkdir -p .cr-release-packages + helm package helm/unifi-network-operator -d .cr-release-packages + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.6.0 + with: + charts_dir: helm + skip_packaging: true + config: .github/cr.yaml + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + + - name: Upload Helm chart as artifact + uses: actions/upload-artifact@v4 + with: + name: helm-chart + path: .cr-release-packages/*.tgz + retention-days: 90 + + # Create GitHub Release + create-release: + needs: [build-images, release-chart] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.tag }}" + else + VERSION="${GITHUB_REF#refs/tags/}" + fi + echo "version=${VERSION}" >> $GITHUB_OUTPUT + + - name: Download Helm chart artifact + uses: actions/download-artifact@v4 + with: + name: helm-chart + path: ./artifacts + + - name: Generate release notes + id: notes + run: | + cat > release_notes.md << 'EOF' + ## UniFi Network Operator ${{ steps.version.outputs.version }} + + ### Installation + + #### Using Helm + + ```bash + helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator + helm repo update + helm install unifi-network-operator unifi-network-operator/unifi-network-operator \ + --namespace unifi-network-operator-system \ + --create-namespace \ + --set unifi.url="https://your-unifi-controller:8443" \ + --set unifi.password="your-password" + ``` + + #### Using Docker + + ```bash + docker pull ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }} + ``` + + ### What's Changed + + See the full changelog for details of changes in this release. + + ### Container Images + + - `ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}` + - Available for `linux/amd64` and `linux/arm64` + + ### Helm Chart + + - Chart version: Automatically generated + - App version: ${{ steps.version.outputs.version }} + - Repository: https://vegardengen.github.io/unifi-network-operator + EOF + cat release_notes.md + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ steps.version.outputs.version }} + name: Release ${{ steps.version.outputs.version }} + body_path: release_notes.md + draft: false + prerelease: ${{ contains(steps.version.outputs.version, 'alpha') || contains(steps.version.outputs.version, 'beta') || contains(steps.version.outputs.version, 'rc') }} + files: | + ./artifacts/*.tgz + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CICD.md b/CICD.md new file mode 100644 index 0000000..e826cc8 --- /dev/null +++ b/CICD.md @@ -0,0 +1,399 @@ +# CI/CD Pipeline Documentation + +This document describes the continuous integration and deployment pipeline for the UniFi Network Operator. + +## Overview + +The CI/CD pipeline is built using GitHub Actions and provides: + +- **Automated testing** on every pull request +- **Multi-architecture Docker image builds** (amd64, arm64) +- **Automated Helm chart releases** to GitHub Pages +- **Complete release automation** with version tagging +- **Public Docker images** via GitHub Container Registry + +## Quick Start + +### For Users + +**Install the operator using Helm:** + +```bash +# Add the Helm repository +helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator +helm repo update + +# Install +helm install unifi-network-operator unifi-network-operator/unifi-network-operator \ + --namespace unifi-network-operator-system \ + --create-namespace \ + --set unifi.url="https://your-unifi-controller:8443" \ + --set unifi.password="your-password" +``` + +**Or use Docker directly:** + +```bash +docker pull ghcr.io/vegardengen/unifi-network-operator:latest +``` + +### For Contributors + +**Run checks locally before pushing:** + +```bash +# Format code +make fmt + +# Run linters +make vet + +# Run tests +make test + +# Lint Helm chart +make helm-lint + +# Test Docker build +docker build -t test . +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ GitHub Repository │ +│ vegardengen/unifi-network-operator │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌────────────────┼────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ + │ Pull Request │ │ Push to Main │ │ Tag Push │ + │ Validation │ │ │ │ (v*.*) │ + └──────────────┘ └──────────────┘ └─────────────┘ + │ │ │ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ + │ • Go Fmt │ │ Docker Build │ │ Release │ + │ • Go Vet │ │ & Push │ │ Workflow │ + │ • Tests │ │ │ │ │ + │ • Helm │ │ Helm Chart │ │ • Docker │ + │ Lint │ │ Release │ │ • Helm │ + │ • Docker │ │ │ │ • GitHub │ + │ Build │ │ │ │ Release │ + └──────────────┘ └──────────────┘ └─────────────┘ + │ │ + ▼ ▼ + ┌───────────────────────────┐ + │ GitHub Container Registry │ + │ ghcr.io/vegardengen/... │ + └───────────────────────────┘ + │ + ▼ + ┌───────────────────────────┐ + │ GitHub Pages (Helm) │ + │ vegardengen.github.io/ │ + └───────────────────────────┘ +``` + +## Workflows + +### 1. PR Validation (`pr-validation.yaml`) + +**Triggers:** Pull requests to `main` + +**Steps:** +1. Code formatting check (`go fmt`) +2. Static analysis (`go vet`) +3. Unit tests with race detection +4. Coverage upload to Codecov +5. Helm chart linting +6. Template rendering validation +7. Test Docker build + +**Purpose:** Ensure code quality before merging + +### 2. Docker Build & Push (`docker-build-push.yaml`) + +**Triggers:** +- Push to `main` or `feature/**` branches +- Push of tags starting with `v*` +- Pull requests (build only, no push) + +**Steps:** +1. Run tests +2. Set up QEMU for cross-compilation +3. Set up Docker Buildx +4. Extract metadata for tags +5. Build for multiple architectures +6. Push to GitHub Container Registry + +**Image Tags Created:** +- Branch builds: `main`, `feature-xyz` +- Version tags: `v1.0.0`, `1.0.0`, `1.0`, `1` +- Latest: `latest` (from main branch) +- Commit SHA: `main-abc1234` + +### 3. Helm Chart Release (`helm-release.yaml`) + +**Triggers:** +- Push to `main` with changes in `helm/` directory +- Manual workflow dispatch + +**Steps:** +1. Package Helm chart +2. Create GitHub release for chart +3. Update Helm repository index +4. Publish to GitHub Pages + +**Output:** Helm repository at `https://vegardengen.github.io/unifi-network-operator` + +### 4. Complete Release (`release.yaml`) + +**Triggers:** +- Push of version tags (e.g., `v1.0.0`) +- Manual workflow dispatch with version input + +**Steps:** +1. **Build Phase:** + - Run tests + - Build multi-arch Docker images + - Push with version tags and `latest` + +2. **Chart Phase:** + - Update Chart.yaml with version + - Update values.yaml with image tag + - Package Helm chart + - Release chart to repository + +3. **Release Phase:** + - Generate release notes + - Create GitHub release + - Attach Helm chart package + - Mark as pre-release if needed + +**Pre-release Detection:** Tags containing `alpha`, `beta`, or `rc` are marked as pre-releases + +## Release Process + +### Creating a New Release + +1. **Prepare the release:** + ```bash + # Ensure you're on main and up to date + git checkout main + git pull github main + + # Update CHANGELOG.md with release notes + # Commit any final changes + ``` + +2. **Create and push the tag:** + ```bash + # Create an annotated tag + git tag -a v1.0.0 -m "Release v1.0.0: Description of changes" + + # Push the tag + git push github v1.0.0 + ``` + +3. **Monitor the release:** + - Go to Actions tab on GitHub + - Watch the "Release" workflow + - Check for any errors + +4. **Verify the release:** + ```bash + # Check Docker image + docker pull ghcr.io/vegardengen/unifi-network-operator:v1.0.0 + + # Check Helm chart + helm repo update + helm search repo unifi-network-operator + + # Check GitHub release page + # https://github.com/vegardengen/unifi-network-operator/releases + ``` + +### Version Numbering + +Follow [Semantic Versioning](https://semver.org/): + +- **MAJOR** (X.0.0): Breaking changes +- **MINOR** (0.X.0): New features, backward compatible +- **PATCH** (0.0.X): Bug fixes, backward compatible + +**Pre-release tags:** +- `v1.0.0-alpha.1` - Early preview +- `v1.0.0-beta.1` - Feature complete, testing +- `v1.0.0-rc.1` - Release candidate + +## Artifact Locations + +### Docker Images + +**Repository:** `ghcr.io/vegardengen/unifi-network-operator` + +**Access:** +```bash +# Pull latest +docker pull ghcr.io/vegardengen/unifi-network-operator:latest + +# Pull specific version +docker pull ghcr.io/vegardengen/unifi-network-operator:v1.0.0 +``` + +**Platforms:** `linux/amd64`, `linux/arm64` + +### Helm Charts + +**Repository:** `https://vegardengen.github.io/unifi-network-operator` + +**Access:** +```bash +# Add repository +helm repo add unifi-network-operator https://vegardengen.github.io/unifi-network-operator + +# Search charts +helm search repo unifi-network-operator + +# Install +helm install my-release unifi-network-operator/unifi-network-operator +``` + +**Also available:** As attachments on GitHub Releases + +## Configuration + +### Repository Settings Required + +1. **Actions Permissions:** + - Settings → Actions → General + - Workflow permissions: Read and write + - Allow GitHub Actions to create releases: ✓ + +2. **GitHub Pages:** + - Settings → Pages + - Source: Deploy from branch + - Branch: `gh-pages` / `/ (root)` + +3. **Package Visibility (After First Push):** + - Profile → Packages → unifi-network-operator + - Package settings → Change visibility → Public + +### Branch Protection (Recommended) + +- Settings → Branches → Add rule for `main` +- Require pull request reviews +- Require status checks: `lint-and-test`, `helm-lint`, `docker-build` +- Require branches to be up to date + +## Troubleshooting + +### Common Issues + +**"Resource not accessible by integration"** +- Fix: Enable read/write permissions in repository settings + +**Docker push fails** +- Check package visibility settings +- Verify GITHUB_TOKEN permissions + +**Helm chart not updating** +- Ensure gh-pages branch exists +- Check GitHub Pages is enabled +- Wait 5-10 minutes for CDN propagation + +**Release workflow fails** +- Check Chart.yaml version is unique +- Verify all templates are valid YAML +- Review workflow logs for specific error + +### Debug Locally + +```bash +# Test Helm rendering +make helm-template + +# Lint Helm chart +make helm-lint + +# Build Docker image +docker build -t test:latest . + +# Run tests +make test + +# Check formatting +make fmt +make vet +``` + +## Monitoring + +### Workflow Status + +Check at: https://github.com/vegardengen/unifi-network-operator/actions + +### Artifacts + +- **Docker Images:** https://github.com/vegardengen/unifi-network-operator/pkgs/container/unifi-network-operator +- **Releases:** https://github.com/vegardengen/unifi-network-operator/releases +- **Helm Repository:** https://vegardengen.github.io/unifi-network-operator + +### Metrics + +- Build success rate +- Test coverage (via Codecov) +- Release frequency +- Download statistics (via GitHub Insights) + +## Security + +### Container Scanning + +Consider adding: +- Trivy vulnerability scanning +- Dependabot alerts +- SBOM generation + +### Secrets Management + +- Use GitHub Secrets for sensitive data +- Never commit credentials +- Rotate tokens regularly + +### Supply Chain Security + +- Images built from source +- Signed releases (future enhancement) +- SBOM attached to releases (future enhancement) + +## Future Enhancements + +- [ ] Automated vulnerability scanning +- [ ] Signed container images (cosign) +- [ ] SBOM generation +- [ ] Automated changelog generation +- [ ] Release drafter for draft releases +- [ ] Automated version bumping +- [ ] Integration tests in CI +- [ ] Performance benchmarking +- [ ] Automated security scanning + +## References + +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Helm Chart Best Practices](https://helm.sh/docs/chart_best_practices/) +- [Container Best Practices](https://docs.docker.com/develop/dev-best-practices/) +- [Semantic Versioning](https://semver.org/) + +## Support + +For CI/CD issues: +1. Check workflow logs in Actions tab +2. Review [.github/README.md](.github/README.md) for detailed docs +3. See [.github/SETUP.md](.github/SETUP.md) for setup instructions +4. Open an issue with workflow logs attached diff --git a/helm/unifi-network-operator/Chart.yaml b/helm/unifi-network-operator/Chart.yaml index 51e040e..4a1ce63 100644 --- a/helm/unifi-network-operator/Chart.yaml +++ b/helm/unifi-network-operator/Chart.yaml @@ -4,13 +4,15 @@ description: A Kubernetes operator for managing UniFi network configurations type: application version: 0.1.0 appVersion: "latest" -home: https://github.com/yourusername/unifi-network-operator +home: https://github.com/vegardengen/unifi-network-operator maintainers: - name: Vegar Dengen + url: https://github.com/vegardengen keywords: - unifi - network - operator - firewall + - ubiquiti sources: - - https://github.com/yourusername/unifi-network-operator + - https://github.com/vegardengen/unifi-network-operator diff --git a/helm/unifi-network-operator/values.yaml b/helm/unifi-network-operator/values.yaml index beb1660..170e55a 100644 --- a/helm/unifi-network-operator/values.yaml +++ b/helm/unifi-network-operator/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: # -- Container image repository - repository: gitea.engen.priv.no/klauvsteinen/unifi-network-operator-controller + repository: ghcr.io/vegardengen/unifi-network-operator # -- Image pull policy pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion