# https://taskfile.dev

version: "3"
vars:
  GREETING: Hello, World!
env:
  DEFAULT_GROUP: Home
  DEFAULT_HOUSEHOLD: Family
  PRODUCTION: false
  API_PORT: 9000
  API_DOCS: True
  TOKEN_TIME: 256 # hours
  # mailplit SMTP config
  # start dev:services to use mailpit
  SMTP_HOST: localhost
  SMTP_PORT: 1025
  SMTP_FROM_NAME: MealieDev
  SMTP_FROM_EMAIL: mealie@example.com
  SMTP_AUTH_STRATEGY: NONE
  BASE_URL: http://localhost:3000
  LANG: en-US

# loads .env file if it exists
dotenv:
  - .env
  - .dev.env
tasks:
  docs:
    desc: runs the documentation server
    dir: docs
    cmds:
      - uv run python -m mkdocs serve

  setup:ui:
    desc: setup frontend dependencies
    dir: frontend
    run: once
    cmds:
      - yarn install
    sources:
      - package.json
      - yarn.lock

  setup:py:
    desc: setup python dependencies
    run: once
    cmds:
      - uv sync --extra pgsql --group dev
      - uv run pre-commit install
    sources:
      - uv.lock
      - pyproject.toml
      - .pre-commit-config.yaml

  setup:e2e:
    desc: setup e2e test dependencies
    dir: tests/e2e
    run: once
    cmds:
      - yarn install
      - yarn playwright install --with-deps
    sources:
      - package.json
      - playwright.config.ts
      - yarn.lock

  setup:
    desc: setup all dependencies
    deps:
      - setup:ui
      - setup:py

  dev:generate:
    desc: run code generators
    cmds:
      - uv run python dev/code-generation/main.py {{ .CLI_ARGS }}
      - task: py:format

  dev:services:
    desc: starts postgres and mailpit containers
    dir: docker
    cmds:
      - docker compose -f docker-compose.dev.yml up

  dev:clean:
    desc: cleans up dev environment !! removes all data files !!
    vars:
      DEV_DATA: ""
    cmds:
      - rm -r ./dev/data/recipes/
      - rm -r ./dev/data/users/
      - rm -f ./dev/data/mealie*.db
      - rm -f ./dev/data/mealie*.db-shm
      - rm -f ./dev/data/mealie*.db-wal
      - rm -f ./dev/data/mealie.log
      - rm -f ./dev/data/.secret

  py:mypy:
    desc: runs python type checking
    cmds:
      - uv run mypy mealie

  py:test:
    desc: runs python tests (support args after '--')
    cmds:
      - uv run pytest {{ .CLI_ARGS }}

  py:format:
    desc: runs python code formatter
    cmds:
      - uv run ruff format .

  py:lint:
    desc: runs python linter
    cmds:
      - uv run ruff check mealie

  py:check:
    desc: runs all linters, type checkers, and formatters
    deps:
      - py:format
      - py:lint
      - py:mypy
      - py:test

  py:coverage:
    desc: runs python coverage and generates html report
    cmds:
      - uv run pytest
      - uv run coverage report -m
      - uv run coveragepy-lcov
      - uv run coverage html
      - open htmlcov/index.html

  py:package:copy-frontend:
    desc: copy the frontend files into the Python package
    internal: true
    deps:
      - ui:generate
    cmds:
      - rm -rf mealie/frontend
      - cp -a frontend/dist mealie/frontend
    sources:
      - frontend/dist/**
    generates:
      - mealie/frontend/**

  py:package:generate-requirements:
    desc: Generate requirements file to pin all packages, effectively a "pip freeze" before installation begins
    internal: true
    cmds:
      - uv export --no-editable --no-emit-project --extra pgsql --format requirements-txt --output-file dist/requirements.txt
      # Include mealie in the requirements, hashing the package that was just built to ensure it's the one installed
      - echo "mealie[pgsql]=={{.MEALIE_VERSION}} \\" >> dist/requirements.txt
      - pip hash dist/mealie-{{.MEALIE_VERSION}}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt
      - echo " \\" >> dist/requirements.txt
      - pip hash dist/mealie-{{.MEALIE_VERSION}}.tar.gz | tail -n1 >> dist/requirements.txt
    vars:
      MEALIE_VERSION:
         sh: python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])"
    sources:
      - uv.lock
      - pyproject.toml
      - dist/mealie-*.whl
      - dist/mealie-*.tar.gz
    generates:
      - dist/requirements.txt

  py:package:deps-parallel:
    desc: Run py:package dependencies in parallel
    internal: true
    deps:
      - setup:py
      - py:package:copy-frontend

  py:package:deps:
    desc: Dependencies of py:package, skippable by setting SKIP_PACKAGE_DEPS=true
    internal: true
    cmds:
      - task: py:package:deps-parallel
    status:
      - '{{ .SKIP_PACKAGE_DEPS | default "false"}}'

  py:package:build:
    internal: true
    deps:
      - py:package:deps
    cmds:
      - uv build --out-dir dist
    sources:
      - uv.lock
      - pyproject.toml
      - mealie/**

  py:package:
    desc: builds Python packages (sdist and wheel) in top-level dist directory
    cmds:
      - task: py:package:build
      - task: py:package:generate-requirements

  py:
    desc: runs the backend server
    cmds:
      - uv run python mealie/app.py

  py:postgres:
    desc: runs the backend server configured for containerized postgres
    env:
      DB_ENGINE: postgres
      POSTGRES_USER: mealie
      POSTGRES_PASSWORD: mealie
      POSTGRES_SERVER: localhost
      POSTGRES_PORT: 5432
      POSTGRES_DB: mealie
    cmds:
      - uv run python mealie/app.py

  py:migrate:
    desc: generates a new database migration file e.g. task py:migrate -- "add new column"
    cmds:
      - uv run alembic --config mealie/alembic/alembic.ini revision --autogenerate -m "{{ .CLI_ARGS }}"
      - task: py:format

  ui:build:
    desc: builds the frontend in frontend/dist
    dir: frontend
    cmds:
      - yarn build
    sources:
      - "**"
      - exclude: .nuxt/**
      - exclude: .output/**
      - exclude: dist/**
      - exclude: node_modules/.cache/**

  ui:generate:
    desc: generates a static version of the frontend in frontend/dist
    dir: frontend
    deps:
      - setup:ui
    cmds:
      - yarn generate
    sources:
      - "**"
      - exclude: .nuxt/**
      - exclude: .output/**
      - exclude: dist/**
      - exclude: node_modules/.cache/**

  ui:lint:
    desc: runs the frontend linter
    dir: frontend
    cmds:
      - yarn lint --max-warnings=0
    sources:
      - "**"
      - exclude: .nuxt/**
      - exclude: .output/**
      - exclude: dist/**
      - exclude: node_modules/.cache/**

  ui:test:
    desc: runs the frontend tests
    dir: frontend
    cmds:
      - yarn test
    sources:
      - "**"
      - exclude: .nuxt/**
      - exclude: .output/**
      - exclude: dist/**
      - exclude: node_modules/.cache/**

  ui:check:
    desc: runs all frontend checks
    deps:
      - ui:lint
      - ui:test

  ui:
    desc: runs the frontend server
    dir: frontend
    cmds:
      - yarn run dev --no-fork

  docker:build-from-package:
    desc: Builds the Docker image from the existing Python package in dist/
    deps:
       - py:package
    cmds:
       - docker build --tag mealie:dev --file docker/Dockerfile --build-arg COMMIT={{.GIT_COMMIT}} --build-context packages=dist .
    vars:
      GIT_COMMIT:
        sh: git rev-parse HEAD

  docker:prod:
    desc: builds and runs the production docker image locally
    dir: docker
    cmds:
      - docker compose -f docker-compose.yml -p mealie up -d --build

  e2e:build-image:
    desc: builds the e2e test docker image
    deps:
       - py:package
    cmds:
      - docker build --tag mealie:e2e --file docker/Dockerfile --build-context packages=dist .
    sources:
      - docker/Dockerfile
      - dist/**

  e2e:start-server:
    desc: Builds the image and starts the containers for e2e testing
    dir: tests/e2e/docker
    deps:
       - e2e:build-image
    vars:
      WAIT_UNTIL_HEALTHY: '{{if .WAIT_UNTIL_HEALTHY}}--wait{{else}}{{end}}'
    cmds:
      - docker compose up -d {{.WAIT_UNTIL_HEALTHY}}

  e2e:stop-server:
    desc: Shuts down the e2e testing containers
    dir: tests/e2e/docker
    cmds:
      - docker compose down --volumes

  e2e:test:
    desc: runs the e2e tests
    dir: tests/e2e
    deps:
      - setup:e2e
    vars:
      PREVENT_REPORT_OPEN: '{{if .PREVENT_REPORT_OPEN}}PLAYWRIGHT_HTML_OPEN=never{{else}}{{end}}'
    cmds:
      - '{{.PREVENT_REPORT_OPEN}} yarn playwright test'

  e2e:
    desc: runs the full e2e test suite
    cmds:
      - task: e2e:start-server
        vars: { WAIT_UNTIL_HEALTHY: true }
      - defer: { task: e2e:stop-server }
      - task: e2e:test
        vars: { PREVENT_REPORT_OPEN: true }
