pinchflat/AGENTS.md
2026-03-04 14:17:36 +01:00

5.1 KiB

Agent Guidelines for Pinchflat

This document provides context and guidelines for AI agents working on this codebase.

Project Overview

Pinchflat is an Elixir/Phoenix application for self-hosted media management. It uses:

  • Backend: Elixir 1.17+, Phoenix 1.7, Ecto with SQLite
  • Frontend: Phoenix LiveView, Tailwind CSS, esbuild
  • Background Jobs: Oban
  • Containerization: Docker with multi-arch support (amd64/arm64)

Development Environment

This project uses Nix flakes for reproducible development environments.

# Enter dev shell
nix develop

# Or with specific shell
nix develop . --command fish

Available Tools (via flake.nix)

  • lefthook - Git hooks manager
  • actionlint - GitHub Actions linter
  • typos - Spell checker
  • prettier - Code formatter (JS/CSS/YAML/JSON)
  • cocogitto - Conventional commits
  • just - Command runner
  • Docker tooling (docker, docker-buildx, docker-compose)

Git Hooks (lefthook)

Pre-commit hooks are configured in lefthook.yml:

Hook Files Purpose
prettier *.{css,html,js,json,md,mjs,ts,yaml,yml} Format check
mix format *.{ex,exs} Elixir formatting
typos All staged files Spell checking
actionlint .github/workflows/*.{yml,yaml} Validate GH Actions

Bypassing Hooks

If hooks fail due to environment issues (e.g., permission errors):

git commit --no-verify -m "message"

GitHub Actions Workflows

lint_and_test.yml

Runs on PRs and pushes to master. Uses Docker Compose for testing.

Key features:

  • Concurrency control (cancels in-progress runs on same PR)
  • 30-minute timeout
  • Pinned action versions (SHA-based for security)

docker_release.yml

Builds and pushes Docker images to GHCR.

Architecture:

prepare job (determines platform matrix)
    |
    v
build job (parallel)
├── linux/amd64 on ubuntu-latest (native)
└── linux/arm64 on ubuntu-24.04-arm (native, NOT QEMU)
    |
    v
merge job (creates multi-arch manifest)

Trigger behavior:

  • Push to master: Builds amd64 only (fast dev iteration)
  • Release published: Builds both amd64 and arm64
  • workflow_dispatch: User chooses platforms

Important: GHCR requires lowercase repository names. The workflow converts $GITHUB_REPOSITORY to lowercase using ${GITHUB_REPOSITORY,,}.

Releasing a New Version

  1. Update version in mix.exs:

    version: "YYYY.M.D",
    
  2. Commit and tag:

    git add mix.exs
    git commit -m "chore: bump version to vYYYY.M.D"
    git tag vYYYY.M.D
    
  3. Push to your fork:

    git push origin master && git push origin vYYYY.M.D
    
  4. Create GitHub release:

    gh release create vYYYY.M.D --title "vYYYY.M.D" --generate-notes --repo <owner>/pinchflat
    

Cleaning Up Tags

If you need to delete and recreate tags:

# Delete locally
git tag -d vX.Y.Z

# Delete on remote
git push origin --delete vX.Y.Z

# Recreate on specific commit
git tag vX.Y.Z <commit-sha>

# Force push to overwrite remote
git push origin vX.Y.Z --force

Validating GitHub Actions Locally

Always validate workflow changes before pushing:

# Check all workflows
actionlint

# Check specific file
actionlint .github/workflows/docker_release.yml

Common Issues actionlint Catches

  • Invalid YAML syntax
  • Unknown/misspelled action inputs
  • Type errors in expressions (${{ }})
  • Invalid runs-on values
  • Shell script issues (via shellcheck)
  • Deprecated features

Shellcheck Directives

For intentional word splitting in workflows, add the directive inside the run: block:

run: |
  # shellcheck disable=SC2046
  docker buildx imagetools create $(jq -cr '...' <<< "$JSON") \
    $(printf 'image@sha256:%s ' *)  

CI Best Practices

  1. Pin action versions to SHAs for security:

    uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
    
  2. Quote shell variables to satisfy shellcheck:

    run: echo "value" >> "$GITHUB_OUTPUT"
    
  3. Use native ARM runners instead of QEMU for faster builds:

    runs-on: ubuntu-24.04-arm # Native ARM64
    
  4. Scope caches by platform to avoid conflicts:

    cache-from: type=gha,scope=build-linux-amd64
    
  5. Add concurrency controls to save CI minutes:

    concurrency:
      group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
      cancel-in-progress: true
    

Running Tests

# Via Docker Compose (as CI does)
docker compose -f docker-compose.ci.yml up -d
docker compose exec phx mix deps.get
docker compose exec phx mix ecto.create
docker compose exec phx mix ecto.migrate
docker compose exec phx mix check --no-fix --no-retry

Code Style

  • Elixir: Follow mix format (configured in .formatter.exs)
  • JavaScript/CSS: Follow Prettier (configured in .prettierrc.js)
  • Commits: Conventional commits recommended (cocogitto installed)