diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 0000000..330189e --- /dev/null +++ b/.credo.exs @@ -0,0 +1,20 @@ +%{ + configs: [ + %{ + name: "default", + files: %{ + included: ["lib/", "test/", "config/", "mix.exs"], + excluded: ["_build/", "deps/", "dist/"] + }, + strict: true, + color: true, + checks: [ + {Credo.Check.Design.TagTODO, false}, + {Credo.Check.Readability.ModuleDoc, false}, + {Credo.Check.Readability.Specs, false}, + {Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 15}, + {Credo.Check.Refactor.Nesting, max_nesting: 3} + ] + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..deec8ad --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + lint-test-build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Elixir/OTP + uses: erlef/setup-beam@v1 + with: + elixir-version: '1.19.4' + otp-version: '28.3' + + - name: Restore Mix cache + uses: actions/cache@v4 + with: + path: | + deps + _build + key: ${{ runner.os }}-mix-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix- + + - name: Install dependencies + run: mix deps.get + + - name: Check formatting + run: mix format --check-formatted + + - name: Compile with warnings as errors + run: mix compile --warnings-as-errors + + - name: Run Credo + run: mix credo --strict + + - name: Run tests + run: mix test + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build container image (amd64 smoke build) + run: docker buildx build --platform linux/amd64 --load -t codeline:ci -f docker/Dockerfile . diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..64ff855 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,106 @@ +name: Deploy + +on: + workflow_dispatch: + workflow_run: + workflows: + - CI + types: + - completed + branches: + - main + +permissions: + contents: read + packages: write + +env: + IMAGE_REPO: ghcr.io/carverauto/codeline + +jobs: + build-and-push: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + outputs: + image: ${{ steps.meta.outputs.image }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Compute image tag + id: meta + run: | + IMAGE_TAG="sha-$(git rev-parse --short=12 HEAD)" + echo "image=${IMAGE_REPO}:${IMAGE_TAG}" >> "$GITHUB_OUTPUT" + + - name: Build and push amd64 image + run: | + docker buildx build \ + --platform linux/amd64 \ + --push \ + -t "${{ steps.meta.outputs.image }}" \ + -f docker/Dockerfile \ + . + + deploy-prod: + runs-on: ubuntu-latest + needs: build-and-push + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.workflow_run.head_sha || github.sha }} + + - name: Setup kubectl + uses: azure/setup-kubectl@v4 + + - name: Setup kustomize + uses: imranismail/setup-kustomize@v2 + with: + kustomize-version: '5.4.3' + + - name: Write kubeconfig + env: + KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} + run: | + mkdir -p "$HOME/.kube" + printf '%s' "$KUBE_CONFIG" > "$HOME/.kube/config" + chmod 600 "$HOME/.kube/config" + + - name: Set image in prod overlay + working-directory: k8s/prod + run: | + kustomize edit set image ghcr.io/carverauto/codeline=${{ needs.build-and-push.outputs.image }} + + - name: Ensure namespace exists + run: | + kubectl create namespace codeline --dry-run=client -o yaml | kubectl apply -f - + + - name: Apply admin secret + env: + CODELINE_ADMIN_CODE: ${{ secrets.CODELINE_ADMIN_CODE }} + run: | + kubectl -n codeline create secret generic codeline-secret-prod \ + --from-literal=CODELINE_ADMIN_CODE="$CODELINE_ADMIN_CODE" \ + --dry-run=client -o yaml | kubectl apply -f - + + - name: Apply manifests + run: kubectl apply -k k8s/prod + + - name: Wait for rollout + run: kubectl -n codeline rollout status deployment/codeline-prod --timeout=300s diff --git a/mix.exs b/mix.exs index d2f1644..1728d09 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule Codeline.MixProject do version: "0.1.0", elixir: "~> 1.16", start_permanent: Mix.env() == :prod, - deps: [] + deps: deps() ] end @@ -17,4 +17,10 @@ defmodule Codeline.MixProject do mod: {Codeline.Application, []} ] end + + defp deps do + [ + {:credo, "~> 1.7", only: [:dev, :test], runtime: false} + ] + end end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..2657658 --- /dev/null +++ b/mix.lock @@ -0,0 +1,6 @@ +%{ + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, +}