From 39c5daeae477818f6a8e3712c1d3b92cd3d6442e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:37:09 +0000 Subject: [PATCH 1/6] Initial plan From 6c21d284cb4be81fb3e4183e44e7440409b0e50a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:47:24 +0000 Subject: [PATCH 2/6] Add comprehensive GitHub Actions workflows for CI/CD Co-authored-by: cpursley <1223773+cpursley@users.noreply.github.com> --- .github/workflows/ci.yml | 79 ++++++++++++++++++++++++++++++++ .github/workflows/matrix.yml | 79 ++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 82 ++++++++++++++++++++++++++++++++++ .github/workflows/security.yml | 33 ++++++++++++++ mix.exs | 2 +- 5 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/matrix.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/security.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4c3160d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +env: + MIX_ENV: test + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + test: + name: Test & Quality + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + strategy: + matrix: + elixir: ['1.15.7'] + otp: ['26.1'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ matrix.elixir }} + otp-version: ${{ matrix.otp }} + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + deps + _build + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix- + + - name: Install dependencies + run: | + mix local.hex --force + mix local.rebar --force + mix deps.get + + - name: Check formatting + run: mix format --check-formatted + + - name: Run static analysis (Credo) + run: mix credo --strict + + - name: Compile + run: mix compile --warnings-as-errors + + - name: Run tests with coverage + run: mix coveralls.github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run quality checks + run: mix quality \ No newline at end of file diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml new file mode 100644 index 0000000..1a8ce07 --- /dev/null +++ b/.github/workflows/matrix.yml @@ -0,0 +1,79 @@ +name: Matrix Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + # Run every week on Sunday at 6:00 UTC + - cron: '0 6 * * 0' + +env: + MIX_ENV: test + +jobs: + test-matrix: + name: Test (Elixir ${{ matrix.elixir }}, OTP ${{ matrix.otp }}) + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + strategy: + fail-fast: false + matrix: + include: + # Minimum supported versions + - elixir: '1.15.0' + otp: '26.0' + # Current stable versions + - elixir: '1.15.7' + otp: '26.1' + # Latest versions + - elixir: '1.16.0' + otp: '26.2' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ matrix.elixir }} + otp-version: ${{ matrix.otp }} + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + deps + _build + key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}- + + - name: Install dependencies + run: | + mix local.hex --force + mix local.rebar --force + mix deps.get + + - name: Compile + run: mix compile + + - name: Run tests + run: mix test \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bb50c67 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,82 @@ +name: Release + +on: + push: + tags: + - 'v*' + +env: + MIX_ENV: prod + +jobs: + release: + name: Build and Release + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: '1.15.7' + otp-version: '26.1' + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + deps + _build + key: ${{ runner.os }}-mix-prod-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix-prod- + + - name: Install dependencies + run: | + mix local.hex --force + mix local.rebar --force + mix deps.get --only=prod + + - name: Compile + run: mix compile + + - name: Build documentation + run: mix docs + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: | + doc/**/* + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish: + name: Publish to Hex + runs-on: ubuntu-latest + needs: release + if: startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: '1.15.7' + otp-version: '26.1' + + - name: Install dependencies + run: | + mix local.hex --force + mix local.rebar --force + mix deps.get + + - name: Publish to Hex + run: mix hex.publish --yes + env: + HEX_API_KEY: ${{ secrets.HEX_API_KEY }} \ No newline at end of file diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..540291c --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,33 @@ +name: Security + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + # Run every Monday at 10:00 UTC + - cron: '0 10 * * 1' + +jobs: + secrets-scan: + name: Security Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: '.' + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' \ No newline at end of file diff --git a/mix.exs b/mix.exs index 2213d64..ce3975b 100644 --- a/mix.exs +++ b/mix.exs @@ -11,7 +11,7 @@ defmodule Tango.MixProject do elixirc_paths: elixirc_paths(Mix.env()), aliases: aliases(), test_coverage: [tool: ExCoveralls], - preferred_cli_coverages: ["coveralls", "coveralls.detail", "coveralls.html"], + preferred_cli_coverages: ["coveralls", "coveralls.detail", "coveralls.html", "coveralls.github"], dialyzer: [ plt_file: {:no_warn, "priv/plts/dialyzer.plt"} ] From 3b8e64a2b831730a1dbd338c56454e87bfe0169c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:48:23 +0000 Subject: [PATCH 3/6] Add comprehensive GitHub Actions documentation Co-authored-by: cpursley <1223773+cpursley@users.noreply.github.com> --- .github/README.md | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 .github/README.md diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..8c57074 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,118 @@ +# GitHub Actions CI/CD Setup + +This repository includes comprehensive GitHub Actions workflows for continuous integration, security scanning, and automated releases. + +## Workflows + +### ๐Ÿ”„ CI Workflow (`.github/workflows/ci.yml`) + +**Triggers:** Push or Pull Request to `main` or `develop` branches + +**What it does:** +- Sets up PostgreSQL 15 database service +- Installs Elixir 1.15.7 with OTP 26.1 +- Caches dependencies for faster builds +- Runs code formatting checks +- Performs static analysis with Credo +- Compiles with warnings treated as errors +- Executes tests with coverage reporting +- Runs quality checks using `mix quality` + +### ๐Ÿš€ Release Workflow (`.github/workflows/release.yml`) + +**Triggers:** Git tags starting with `v*` (e.g., `v1.0.0`) + +**What it does:** +- Builds production release +- Generates documentation +- Creates GitHub release with auto-generated notes +- Optionally publishes to Hex.pm (requires `HEX_API_KEY` secret) + +### ๐Ÿ”’ Security Workflow (`.github/workflows/security.yml`) + +**Triggers:** +- Push or Pull Request to `main` +- Weekly schedule (Mondays at 10:00 UTC) + +**What it does:** +- Scans for security vulnerabilities using Trivy +- Uploads results to GitHub Security tab +- Runs automatically on a weekly basis + +### ๐Ÿงช Matrix Testing (`.github/workflows/matrix.yml`) + +**Triggers:** +- Push or Pull Request to `main` +- Weekly schedule (Sundays at 6:00 UTC) + +**What it does:** +- Tests across multiple Elixir/OTP version combinations +- Ensures compatibility with minimum and latest supported versions +- Includes Elixir 1.15.0-1.16.0 with OTP 26.0-26.2 + +## Configuration + +### Required Secrets + +For full functionality, add these secrets to your repository: + +- `HEX_API_KEY` - Required for automatic Hex.pm publishing on releases +- `GITHUB_TOKEN` - Automatically provided by GitHub Actions + +### Database Setup + +All workflows use PostgreSQL with these credentials (matching `config/test.exs`): +- **Username:** `postgres` +- **Password:** `postgres` +- **Database:** `postgres` +- **Port:** `5432` + +### Coverage Reporting + +The CI workflow generates coverage reports using ExCoveralls and uploads them to GitHub. The project requires a minimum of 85% test coverage as configured in `.coveralls.exs`. + +## Status Badges + +Add these badges to your README.md: + +```markdown +[![CI](https://github.com/agoodway/tango/actions/workflows/ci.yml/badge.svg)](https://github.com/agoodway/tango/actions/workflows/ci.yml) +[![Security](https://github.com/agoodway/tango/actions/workflows/security.yml/badge.svg)](https://github.com/agoodway/tango/actions/workflows/security.yml) +``` + +## Local Development + +To run the same checks locally: + +```bash +# Install dependencies +mix deps.get + +# Run formatting check +mix format --check-formatted + +# Run static analysis +mix credo --strict + +# Run tests with coverage +mix coveralls + +# Run all quality checks +mix quality +``` + +## Troubleshooting + +### Common Issues + +1. **Database Connection Errors**: Ensure PostgreSQL is running locally with the credentials in `config/test.exs` +2. **Coverage Failures**: Check that tests meet the 85% minimum coverage requirement +3. **Formatting Errors**: Run `mix format` to auto-fix formatting issues +4. **Credo Warnings**: Address static analysis issues highlighted by Credo + +### Workflow Debugging + +- Check the Actions tab for detailed logs +- Failed steps will show specific error messages +- Use the re-run feature to retry failed workflows +- Check the matrix workflow for compatibility issues across Elixir versions \ No newline at end of file From 1bcba735b158010bb457a2df1d5d98222538fc7b Mon Sep 17 00:00:00 2001 From: Chase Pursley Date: Wed, 17 Sep 2025 09:20:25 -0400 Subject: [PATCH 4/6] mix.exs formatting --- mix.exs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index ce3975b..325bff9 100644 --- a/mix.exs +++ b/mix.exs @@ -11,7 +11,12 @@ defmodule Tango.MixProject do elixirc_paths: elixirc_paths(Mix.env()), aliases: aliases(), test_coverage: [tool: ExCoveralls], - preferred_cli_coverages: ["coveralls", "coveralls.detail", "coveralls.html", "coveralls.github"], + preferred_cli_coverages: [ + "coveralls", + "coveralls.detail", + "coveralls.html", + "coveralls.github" + ], dialyzer: [ plt_file: {:no_warn, "priv/plts/dialyzer.plt"} ] From f0db5bd8763210e9af5b8fa7789194ada96a35c2 Mon Sep 17 00:00:00 2001 From: Chase Pursley Date: Wed, 17 Sep 2025 09:26:29 -0400 Subject: [PATCH 5/6] Loosen up credo --- .github/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/README.md b/.github/README.md index 8c57074..cfc91e1 100644 --- a/.github/README.md +++ b/.github/README.md @@ -92,7 +92,7 @@ mix deps.get mix format --check-formatted # Run static analysis -mix credo --strict +mix credo # Run tests with coverage mix coveralls @@ -115,4 +115,4 @@ mix quality - Check the Actions tab for detailed logs - Failed steps will show specific error messages - Use the re-run feature to retry failed workflows -- Check the matrix workflow for compatibility issues across Elixir versions \ No newline at end of file +- Check the matrix workflow for compatibility issues across Elixir versions From d965cfa7ca2b4bbb9ed7343ec5b23c39f8d10e1c Mon Sep 17 00:00:00 2001 From: Chase Pursley Date: Wed, 17 Sep 2025 09:31:27 -0400 Subject: [PATCH 6/6] Remove strict option from Credo analysis --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c3160d..a1bad03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: run: mix format --check-formatted - name: Run static analysis (Credo) - run: mix credo --strict + run: mix credo - name: Compile run: mix compile --warnings-as-errors @@ -76,4 +76,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run quality checks - run: mix quality \ No newline at end of file + run: mix quality