diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..188e2a0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 + - package-ecosystem: "npm" + directory: "/config" + schedule: + interval: weekly + open-pull-requests-limit: 5 + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 diff --git a/.github/workflows/adr-check.yml b/.github/workflows/adr-check.yml new file mode 100644 index 0000000..e8a114c --- /dev/null +++ b/.github/workflows/adr-check.yml @@ -0,0 +1,33 @@ +name: ADR References Check + +on: + pull_request: + paths: + - 'docs/**' + - '.opencode/**' + - '**/*.md' + - '.github/workflows/adr-check.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - id: check + run: | + if [ -f .opencode/scripts/check-adr-refs.py ]; then + echo "has_script=true" >> $GITHUB_OUTPUT + else + echo "has_script=false" >> $GITHUB_OUTPUT + fi + - uses: actions/setup-python@v5 + if: steps.check.outputs.has_script == 'true' + with: + python-version: "3.12" + - run: python3 .opencode/scripts/check-adr-refs.py + if: steps.check.outputs.has_script == 'true' \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ddf1f27 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,93 @@ +name: CI + +on: + pull_request: + branches: [main] + paths-ignore: ['**/*.md', 'docs/**', 'LICENSE'] + push: + branches: [main] + paths-ignore: ['**/*.md', 'docs/**', 'LICENSE'] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + bootstrap: + runs-on: ubuntu-latest + outputs: + has_src: ${{ steps.check.outputs.has_src }} + steps: + - uses: actions/checkout@v4 + - id: check + run: | + if [ -n "$(find src/ -name '*.py' -print -quit 2>/dev/null)" ]; then + echo "has_src=true" >> $GITHUB_OUTPUT + else + echo "has_src=false" >> $GITHUB_OUTPUT + fi + - run: echo "CI bootstrap OK" + + lint: + needs: bootstrap + if: needs.bootstrap.outputs.has_src == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - uses: astral-sh/setup-uv@v3 + - run: uv sync --extra dev + - run: uv run ruff check src/ tests/ .opencode/scripts/ + - run: uv run ruff format --check src/ tests/ .opencode/scripts/ + + typecheck: + needs: bootstrap + if: needs.bootstrap.outputs.has_src == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - uses: astral-sh/setup-uv@v3 + - run: uv sync --extra dev + - run: uv run mypy src/ + + test: + needs: bootstrap + if: needs.bootstrap.outputs.has_src == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + python: ["3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - uses: actions/setup-node@v4 + with: + node-version: '22' + - uses: astral-sh/setup-uv@v3 + - run: uv sync --extra dev --python ${{ matrix.python }} + - run: uv run --python ${{ matrix.python }} pytest + + complexity: + needs: bootstrap + if: needs.bootstrap.outputs.has_src == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - uses: astral-sh/setup-uv@v3 + - run: uv sync --extra dev + - run: uv run xenon --max-absolute B --max-modules A --max-average A src/ diff --git a/.github/workflows/permissions-check.yml b/.github/workflows/permissions-check.yml new file mode 100644 index 0000000..c3358d4 --- /dev/null +++ b/.github/workflows/permissions-check.yml @@ -0,0 +1,33 @@ +name: Permission Security Check + +on: + pull_request: + paths: + - '.opencode/opencode.json' + - '.opencode/agents/**' + - '.opencode/scripts/check-permissions.py' + - '.github/workflows/permissions-check.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - id: check + run: | + if [ -f .opencode/scripts/check-permissions.py ]; then + echo "has_script=true" >> $GITHUB_OUTPUT + else + echo "has_script=false" >> $GITHUB_OUTPUT + fi + - uses: actions/setup-python@v5 + if: steps.check.outputs.has_script == 'true' + with: + python-version: "3.12" + - run: python3 .opencode/scripts/check-permissions.py + if: steps.check.outputs.has_script == 'true' diff --git a/docs/decisions/001-pr-18-ci-bootstrap.md b/docs/decisions/001-pr-18-ci-bootstrap.md new file mode 100644 index 0000000..ef9ee83 --- /dev/null +++ b/docs/decisions/001-pr-18-ci-bootstrap.md @@ -0,0 +1,22 @@ +# ADR-001: CI bootstrap job + output-based skip conditions + +## Статус + +Accepted + +## Контекст + +Chicken-and-egg: pipeline-driver требует CI ✅ для merge, но CI не может работать без #7 (scripts) и #5 (src/tests). Нужен способ иметь CI runs на каждом PR даже когда src/ и scripts/ ещё не мигрированы. + +## Решение + +1. ci.yml: bootstrap job (checkout + file check + always passes) — гарантирует CI run → pipeline_status CI phase = DONE +2. lint/test/typecheck/complexity jobs: `if: needs.bootstrap.outputs.has_src == 'true'` — skip без src/*.py +3. permissions-check/adr-check: step-level file checks (checkout → check script exists → conditional run) + +## Альтернативы + +- `hashFiles()` в job-level `if` — отклонено: GitHub Actions не распознаёт `hashFiles` как функцию в job-level `if` (ошибка "Unrecognized function: 'hashFiles'"). Заменено на output-based conditions. +- Объединить #5+#6+#7 в один PR — отклонено (пользователь хочет атомарные PR) +- Merge без pipeline-driver (admin override) — отклонено (нарушает pipeline протокол) +- ci.yml без modifications (перенести как есть) — отклонено (CI падает без src/tests, нет runs → AMBIGUOUS → pipeline STOP) diff --git a/docs/handoff/pr-18-ci-ubuntu-latest-workflows.md b/docs/handoff/pr-18-ci-ubuntu-latest-workflows.md new file mode 100644 index 0000000..9c2db5f --- /dev/null +++ b/docs/handoff/pr-18-ci-ubuntu-latest-workflows.md @@ -0,0 +1,36 @@ +# PR #18: CI workflows (ubuntu-latest) + dependabot + +## Что сделано + +- Перенесены .github/workflows/ (ci.yml, permissions-check.yml, adr-check.yml) + dependabot.yml +- runs-on: [self-hosted, linux] → ubuntu-latest (security risk для public repo) +- config/scripts/ → .opencode/scripts/ (пути в run steps + trigger paths) +- ci.yml: branches [master] → [main] (новый репо использует main) +- ci.yml: matrix 3.14 добавлен (ubuntu-latest поддерживает, self-hosted комментарий убран) +- ci.yml: добавлен bootstrap job (checkout + file check + always passes) + output-based skip conditions на lint/test/typecheck/complexity (skip без src/*.py) +- permissions-check.yml, adr-check.yml: step-level file checks (skip script execution без .opencode/scripts/) + +## Почему + +Bootstrapping: CI нужен до #7 (.opencode/ scripts) и #5 (src/tests). bootstrap job гарантирует CI run на каждом PR. Output-based conditions skip jobs которые не могут работать без src/ или scripts/. + +## Отклонение от spec + +- hashFiles() не распознан GitHub Actions в job-level `if` — заменён на output-based conditions (bootstrap job checks files, sets `has_src` output, jobs use `if: needs.bootstrap.outputs.has_src == 'true'`) +- permissions-check/adr-check: job-level `if` заменён на step-level file checks (checkout → check → conditional run) +- Дополнительно: branches [master] → [main], trigger paths config/ → .opencode/ (spec не упоминал) + +## Pending + +- После #7: permissions-check/adr-check jobs активируются (scripts доступны) +- После #5 (merge PR#17): lint/test/typecheck/complexity jobs активируются (src/ доступен) +- Matrix 3.14 — проверить совместимость с ubuntu-latest + +## Watch out + +- ci.yml НЕ перенесён "как есть" — добавлен bootstrap job + output-based conditions (адаптация для bootstrapping) +- hashFiles() не работает в GitHub Actions job-level `if` — это известное ограничение, см. ADR-001 +- branches: [master] → [main] — старый репо использовал master, новый main +- trigger paths в permissions-check.yml/adr-check.yml: config/ → .opencode/ (config/ не существует в новом репо) +- permissions-check.yml, adr-check.yml — skip до #7 (нет .opencode/scripts/) +- dependabot.yml — без изменений (directory: "/config" для npm безвреден — директория не существует, dependabot просто пропустит) diff --git a/docs/project-map/README.md b/docs/project-map/README.md new file mode 100644 index 0000000..66cbaa1 --- /dev/null +++ b/docs/project-map/README.md @@ -0,0 +1,37 @@ +# Project Map + +opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). + +## Structure + +``` +opencode-config/ +├── .github/ +│ ├── workflows/ +│ │ ├── ci.yml # Lint, test, typecheck, complexity (bootstrap + output-based skip) +│ │ ├── permissions-check.yml # .opencode/scripts/permissions.py validator (step-level skip) +│ │ └── adr-check.yml # ADR cross-reference validator (step-level skip) +│ └── dependabot.yml # pip + github-actions ecosystem updates +├── docs/ +│ ├── handoff/ # PR handoffs (pr--.md) +│ ├── decisions/ # ADRs (NNN-pr--.md) +│ └── project-map/ # This file — structure snapshot +├── app_data/ +│ ├── workspaces/ # Agent working directory (.gitkeep) +│ └── ssh/ # SSH keys, not in git (.gitkeep) +├── .editorconfig +├── .gitignore +├── .python-version +├── LICENSE +└── README.md +``` + +## Pending (future PRs) + +- `.opencode/` — global opencode config (agents, skills, tools, scripts) — after #7 +- `src/` — Python RAG CLI (second-brain) — after #5 (PR#17) +- `tests/` — pytest test suite — after #5 + +## Update Protocol + +Updated by docs-reviewer subagent on each PR. Reflects tracked files only (`git ls-files`).