amxx-builder 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/AGENTS.md +111 -0
  2. package/README.md +485 -0
  3. package/action-entry.js +42 -0
  4. package/action.yml +55 -0
  5. package/defaults/amxbuild.defaults.yml +40 -0
  6. package/index.js +4 -0
  7. package/mcp/dep-resolver.js +75 -0
  8. package/mcp/handlers.js +862 -0
  9. package/mcp/mcp-server.js +112 -0
  10. package/mcp/registry.js +863 -0
  11. package/mcp/symbol-index.js +171 -0
  12. package/package.json +68 -0
  13. package/src/archiver.js +140 -0
  14. package/src/asset-fetcher.js +277 -0
  15. package/src/build-plan.js +101 -0
  16. package/src/build-service.js +188 -0
  17. package/src/cache-dir.js +21 -0
  18. package/src/cache-info.js +104 -0
  19. package/src/cli.js +302 -0
  20. package/src/collector.js +89 -0
  21. package/src/commands/build.js +49 -0
  22. package/src/commands/cache.js +77 -0
  23. package/src/commands/clean.js +33 -0
  24. package/src/commands/compile-renderer.js +38 -0
  25. package/src/commands/deploy.js +40 -0
  26. package/src/commands/deps-tree.js +92 -0
  27. package/src/commands/doctor.js +77 -0
  28. package/src/commands/dry-run.js +64 -0
  29. package/src/commands/init.js +228 -0
  30. package/src/commands/mcp.js +13 -0
  31. package/src/commands/releases.js +45 -0
  32. package/src/commands/resolve-manifest.js +27 -0
  33. package/src/commands/serve.js +489 -0
  34. package/src/commands/shared.js +24 -0
  35. package/src/commands/validate.js +34 -0
  36. package/src/commands/watch.js +209 -0
  37. package/src/compile-utils.js +65 -0
  38. package/src/compiler-fetcher.js +327 -0
  39. package/src/compiler.js +228 -0
  40. package/src/dep-graph.js +92 -0
  41. package/src/deployer.js +197 -0
  42. package/src/deps-resolver.js +127 -0
  43. package/src/deps-tree.js +202 -0
  44. package/src/env.js +18 -0
  45. package/src/events.js +29 -0
  46. package/src/format.js +23 -0
  47. package/src/fs-utils.js +87 -0
  48. package/src/include-tree.js +845 -0
  49. package/src/ini-builder.js +44 -0
  50. package/src/jsonrpc-transport.js +195 -0
  51. package/src/logger.js +50 -0
  52. package/src/manifest-path.js +34 -0
  53. package/src/manifest.js +373 -0
  54. package/src/progress.js +66 -0
  55. package/src/rcon.js +103 -0
  56. package/src/release-fetcher.js +206 -0
  57. package/src/release-lister.js +79 -0
  58. package/src/repo-fetcher.js +273 -0
  59. package/src/retry.js +50 -0
  60. package/src/schema.js +54 -0
  61. package/src/update-check.js +114 -0
  62. package/src/validate.js +69 -0
  63. package/src/watcher.js +135 -0
  64. package/templates/init-build.bat +11 -0
  65. package/templates/init-build.sh +7 -0
  66. package/templates/init-deploy.env +14 -0
  67. package/templates/init-manifest.yml +6 -0
  68. package/templates/init-workflow.yml +59 -0
package/AGENTS.md ADDED
@@ -0,0 +1,111 @@
1
+ # amxx-builder — AGENTS.md
2
+
3
+ ## Overview
4
+ CLI + GitHub Action for building/packaging AMX Mod X servers from an `amxbuild.yml` manifest.
5
+ Entry: `index.js` (CLI via `commander`). Action: `action-entry.js` → synthesises `process.argv` → requires `index.js`.
6
+
7
+ ## Tech
8
+ - Node.js 18+, pure **CommonJS** (`require`), no ESM. Exception: `action-entry.js` uses `import * as core from '@actions/core'` because `@actions/core@3.x` is ESM-only — esbuild transpiles it to CJS in the bundle. Do not "fix" that import back to `require`, it fails to resolve.
9
+ - **Node 18+ is a deliberate, genuinely minimal floor.** Write code that works on Node 18 — do not use newer-version-only APIs (stable `node:test` features, `fetch`, `AbortSignal.timeout`, …) unless they exist in 18. If a feature truly requires a newer Node, propose raising the minimum in the PR/discussion first; right now there is no practical benefit in raising it (no feature pressure, no dependency forcing it), and a raised floor would only cut off users stuck on 18. CI tests the matrix 18/24 (floor + newest) to keep this honest.
10
+ - Only dev dep: `esbuild` for bundling the GitHub Action.
11
+ - Tests: `node --test` (built-in node:test, zero deps; discovers `test/*.test.js` automatically — keep fixtures out of `test/`). No linter or type checker configured.
12
+
13
+ ## Architecture: logic lives once in the core
14
+ - All business logic MUST live in `src/` (the core) and be interface-agnostic: no `process.argv`, no `process.stdout` writes, no `commander`, no JSON-RPC/MCP schemas, no CLI rendering.
15
+ - Interfaces are thin adapters that map params → core calls and render core events → their output format.
16
+ - Current interfaces: CLI (`src/commands/`), MCP (`mcp/`), serve (JSON-RPC over stdio).
17
+ - **serve** — started via `amxb serve`; uses `src/jsonrpc-transport.js` (generic JSON-RPC 2.0 over stdio, lives in core); methods are thin wrappers over core calls (`manifest.validate`, `build.start`, `include.resolve`, …); build progress is pushed as notifications (`build.stage` / `build.compiled` / `build.done` / `build.error`).
18
+ - Repo fetching (`source: git`) downloads GitHub tarballs (codeload) instead of `git clone`; system git is only required when `github.ssh: true`.
19
+ - **Never copy a core function into an interface layer.** If two interfaces need the same behavior, it belongs in `src/` — refactor it there instead of duplicating.
20
+ - Interfaces must not do their own resolution/parsing: reuse the core single-source-of-truth helpers instead of reimplementing them. Known ones:
21
+ - Include-path candidate lists (`['scripting/include', 'amxmodx/scripting/include', 'include', '.']`) — single source: `src/deps-resolver.js` (`resolveIncludePath`)
22
+ - Dep string parsing (`owner/repo@ref[:include_path]`) — single source: `src/manifest.js` (`parseDepsLines`)
23
+ - GitHub token resolution — `src/manifest.js` (`resolveGithubToken`); repo key / normalization — `src/deps-resolver.js` (`repoKey`, `normalize`)
24
+ - When touching an interface layer, check whether the logic already exists in `src/` before writing new code. New core exports are cheap; new duplication is debt.
25
+
26
+ ## Commands
27
+ | Command | Description |
28
+ |---------|-------------|
29
+ | `amxb build` | Full build from `amxbuild.yml` |
30
+ | `amxb build --dry-run` | Show plan without executing |
31
+ | `amxb build --set key=value` | Override manifest fields (dot notation for nested, e.g. `output.archive_name=...`) |
32
+ | `amxb build --define DEBUG` | Add compiler define (appends to `amxmodx.defines`) |
33
+ | `amxb build --verbose` | Detailed per-file output |
34
+ | `amxb deploy` | Deploy `build/` to server path |
35
+ | `amxb deploy --build` | Build then deploy |
36
+ | `amxb watch` | Watch local files, incremental build+deploy |
37
+ | `amxb init` | Scaffold manifest and optional files |
38
+ | `amxb init --script` | Also create `build.bat` / `build.sh` quick-build scripts |
39
+ | `amxb clean` | Clean build/ and clone cache |
40
+ | `amxb clean --all` | Also clean compiler cache |
41
+ | `amxb cache info` | Show cache contents |
42
+ | `amxb serve` | Start JSON-RPC server for editor integration (stdio transport) |
43
+ | `npm start` | Alias for `node index.js` |
44
+
45
+ ## Build order (matters)
46
+ 1. Parse manifest (deep-merge with `defaults/amxbuild.defaults.yml`)
47
+ 2. Fetch compiler (`amxxpc`, auto-resolves latest version)
48
+ 3. Resolve refs + clone repos (deduped by `repo@resolved_ref`)
49
+ 4. Resolve deps (git or release), collect `.inc` files
50
+ 5. **Collect** — copy files from repos + local `amxmodx/` + local `assets/` into `build/`
51
+ 6. Fetch remote assets (URLs, GitHub releases)
52
+ 7. **Compile** — all `.sma` → `.amxx` in parallel (overwrites pre-built plugins in `build/`)
53
+ 8. Generate `plugins-*.ini` into `build/amxmodx/configs/`
54
+ 9. Archive → `.zip` or copy to output dir
55
+
56
+ ## Manifest quirks
57
+ - **Arrays are replaced entirely** (repos, deps, assets.sources) — not merged with defaults.
58
+ - `version` **must be a quoted string** in YAML or parsing fails.
59
+ - `ref: latest` resolves to the latest GitHub release tag automatically.
60
+ - Plugin rules (`plugins:`) apply **only to local** `.sma` files, not repo plugins.
61
+ - Local `amxmodx/` always wins over repo files (intentional override layer, no conflict warning).
62
+ - `.sma` files ARE copied during collect (like any other file) and are also compiled; exclude them per-repo via `exclude_files` if sources should not ship.
63
+
64
+ ## GitHub Action release flow
65
+ ```bash
66
+ npm ci
67
+ npm run bundle # esbuild action-entry.js → dist/index.js + scripts/gen-licenses.js → dist/licenses.txt
68
+ node scripts/smoke-test.js # runs the bundled action with INPUT_* env, asserts GITHUB_OUTPUT name output
69
+ # Commit dist/, update package.json version, push tags, publish to npm (idempotent: re-runs are no-ops)
70
+ ```
71
+ This is automated in `.github/workflows/release.yml` on `v*.*.*` tags.
72
+
73
+ ## MCP dep-resolver server
74
+ - Source: `mcp/dep-resolver.js` (reuses `src/repo-fetcher.js`, `src/release-fetcher.js`, `src/cache-dir.js`)
75
+ - Runs directly from source — no bundling needed (installed alongside main package)
76
+ - Started via `amxb mcp` (registered as subcommand in `src/cli.js` → `src/commands/mcp.js`)
77
+ - Uses a custom lightweight `McpServer` from `mcp/mcp-server.js` (no external SDK dependency)
78
+ - Register in any project's `.opencode/opencode.json` via `"command": ["amxb", "mcp"]`
79
+ - Exposes tools: `get_dep_interface`, `list_dep_incs`, `get_dep_tree`, `resolve_manifest`, `validate_manifest`, `get_cache_info`, `list_amxmodx_incs`, `get_amxmodx_include`, `resolve_include`, `list_releases`
80
+
81
+ ## Serve (JSON-RPC)
82
+ - Source: `src/commands/serve.js` — thin adapter; transport: `src/jsonrpc-transport.js` (generic JSON-RPC 2.0 over stdio, in core, no external deps)
83
+ - Started via `amxb serve` (registered as subcommand in `src/cli.js` → `src/commands/serve.js`); stdout stays pure JSON-RPC, logs go to stderr, progress bars disabled
84
+ - Method categories (all thin wrappers over core calls — no domain logic in the adapter):
85
+ - manifest: `manifest.validate`, `manifest.resolve`
86
+ - include: `include.resolve`, `include.list`
87
+ - deps: `deps.tree`
88
+ - releases: `releases.list`
89
+ - cache: `cache.info`
90
+ - build: `build.plan`, `build.start`, `build.cancel`
91
+ - compile: `compile.single`
92
+ - watch: `watch.start`, `watch.stop`
93
+ - Lifecycle events are pushed as server→client notifications: `build.stage` / `build.compiled` / `build.progress` / `build.done` / `build.error`, plus `watch.changed`
94
+ - Full API reference (methods, params, results, error codes): `docs/serve/INDEX.md`
95
+
96
+ ## Cache
97
+ - Win: `%LOCALAPPDATA%\amxx-builder`, Unix: `~/.cache/amxx-builder`
98
+ - Override: `AMXX_BUILDER_CACHE`
99
+ - Local per-manifest asset cache: `.amxb-cache/` next to `amxbuild.yml`
100
+ - Separate dirs: `repos/`, `release-deps/`, `amxxpc/` (compiler binaries)
101
+
102
+ ## Watch mode
103
+ - Uses `chokidar` + dep-graph (`src/dep-graph.js`).
104
+ - `.inc` change → recompile only plugins that `#include` it.
105
+ - `.sma` change → recompile that plugin, deploy + RCON.
106
+ - Manifest change → full rebuild.
107
+ - Non-sma/non-inc files → deploy directly if deploy path set.
108
+
109
+ ## DEPS_LIST files
110
+ Repos can contain a `DEPS_LIST` file (one dep per line, `owner/repo@ref[:include_path]`).
111
+ Overridden by `deps_override` on that repo. Global `deps` in manifest win over everything.
package/README.md ADDED
@@ -0,0 +1,485 @@
1
+ # amxx-builder
2
+
3
+ CLI-инструмент для сборки AMX Mod X серверов. Читает `amxbuild.yml`, клонирует плагины с GitHub, компилирует `.sma → .amxx` и упаковывает всё в готовый `.zip`.
4
+
5
+ ## Установка
6
+
7
+ **Windows** (PowerShell):
8
+
9
+ ```powershell
10
+ irm https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.ps1 | iex
11
+ ```
12
+
13
+ **Linux / macOS**:
14
+
15
+ ```bash
16
+ curl -fsSL https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.sh | bash
17
+ ```
18
+
19
+ Для приватных репозиториев передайте GitHub PAT:
20
+
21
+ ```powershell
22
+ $env:GITHUB_TOKEN="ghp_xxx"; irm .../install.ps1 | iex
23
+ ```
24
+
25
+ Конкретная версия (тэг, ветка, коммит):
26
+
27
+ ```bash
28
+ # По умолчанию — последний релиз
29
+ curl -fsSL https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.sh | bash
30
+
31
+ # Конкретная версия
32
+ AMXB_VERSION=v1.2.3 curl -fsSL https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.sh | bash
33
+ ```
34
+
35
+ ```powershell
36
+ # По умолчанию — последний релиз
37
+ irm https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.ps1 | iex
38
+
39
+ # Конкретная версия
40
+ $env:AMXB_VERSION="v1.2.3"; irm https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.ps1 | iex
41
+ ```
42
+
43
+ Требования: **Node.js 18+**. git не требуется для установки и сборки — нужен только если манифест использует `github.ssh: true` (приватные репозитории по SSH-ключам).
44
+
45
+ ## Использование
46
+
47
+ ```bash
48
+ amxb build # amxbuild.yml в текущей папке
49
+ amxb build --manifest path/to.yml # явный путь
50
+ amxb build --dry-run # показать план без выполнения
51
+ amxb build --no-fetch # использовать кэш, без клонирования
52
+ amxb build --no-archive # только скомпилировать, без .zip
53
+
54
+ amxb deploy # задеплоить build/ на сервер
55
+ amxb deploy --build # сначала собрать, потом задеплоить
56
+
57
+ amxb watch # следить за изменениями и деплоить
58
+ amxb watch --no-deploy # только пересобирать, без деплоя
59
+
60
+ amxb init # создать amxbuild.yml в текущей папке
61
+ amxb init --deploy # + создать .env с заготовками для деплоя
62
+ amxb init --plugin <name> # + создать amxmodx/scripting/<name>.sma
63
+ amxb init --workflow # + создать .github/workflows/ci.yml
64
+ amxb init --script # + создать build.bat / build.sh для быстрого запуска amxb build
65
+
66
+ amxb clean # очистить build/ и кэш клонов
67
+ amxb clean --all # + кэш компилятора
68
+ amxb cache info # показать содержимое кэша
69
+ ```
70
+
71
+ Кэш хранится в `%LOCALAPPDATA%\amxx-builder` (Windows) или `~/.cache/amxx-builder` (Unix).
72
+ Переопределить: `AMXX_BUILDER_CACHE=/path amxb build`.
73
+
74
+ ## Манифест
75
+
76
+ Минимальный — только имя и список репо:
77
+
78
+ ```yaml
79
+ name: MyServer
80
+ repos:
81
+ - AmxxModularEcosystem/VipModular
82
+ - AmxxModularEcosystem/CustomWeaponsAPI
83
+ ```
84
+
85
+ Это автоматически:
86
+
87
+ - берёт последнюю версию компилятора
88
+ - клонирует default branch каждого репо
89
+ - берёт всё содержимое папки `amxmodx/` из каждого репо
90
+ - компилирует все `.sma` из `amxmodx/scripting/`
91
+ - упаковывает в `{name}/addons/amxmodx/` внутри архива
92
+
93
+ ## Структура репо плагина
94
+
95
+ Инструмент ожидает папку `amxmodx/` в корне каждого репо:
96
+
97
+ ```text
98
+ amxmodx/
99
+ scripting/
100
+ my_plugin.sma ← компилируется в plugins/my_plugin.amxx
101
+ SubDir/
102
+ other.sma ← компилируется в plugins/SubDir/other.amxx
103
+ include/ ← используется компилятором
104
+ configs/
105
+ my_plugin.cfg ← копируется как есть
106
+ lang/
107
+ my_plugin.txt ← копируется как есть
108
+ ```
109
+
110
+ Имя папки переопределяется через `amxmodx.dir` (глобально) или `amxmodx_dir` (на репо).
111
+
112
+ ## Локальные файлы
113
+
114
+ Рядом с `amxbuild.yml` можно положить:
115
+
116
+ ```text
117
+ my-server/
118
+ amxbuild.yml
119
+ amxmodx/ ← мержится в addons/amxmodx/ (конфиги, доп. файлы)
120
+ configs/
121
+ server.cfg
122
+ assets/ ← включается по умолчанию (source: local)
123
+ models/
124
+ weapon.mdl
125
+ sound/
126
+ weapon.wav
127
+ ```
128
+
129
+ ## Управление локальными плагинами
130
+
131
+ Поле `plugins:` позволяет фильтровать и распределять плагины из `amxmodx/scripting/` по INI-файлам. Применяется **только к локальным** плагинам; плагины из репо используют `plugins_ini_postfix` своего репо. Первое совпадение побеждает.
132
+
133
+ ```yaml
134
+ plugins_ini_postfix: myserver # глобальный постфикс → plugins-myserver.ini
135
+
136
+ plugins:
137
+ - match: "VipM/*.sma"
138
+ ini: vipm # → plugins-vipm.ini
139
+ - match: "utils/*.sma"
140
+ ini: false # компилировать, но не включать ни в один INI
141
+ - match: "wip/*.sma"
142
+ enabled: false # полностью пропустить (не компилировать, не деплоить)
143
+ ```
144
+
145
+ | Поле | По умолчанию | Описание |
146
+ | --- | --- | --- |
147
+ | `match` | — | Glob-паттерн относительно `scripting/` |
148
+ | `enabled` | `true` | `false` — пропустить компиляцию и деплой |
149
+ | `ini` | `plugins_ini_postfix` | Постфикс INI, `false` — не включать в INI |
150
+
151
+ ## Удалённые ассеты
152
+
153
+ Поле `assets.sources` позволяет добавлять файлы из разных источников. По умолчанию источником является локальная папка `assets/` (`source: local`).
154
+
155
+ При явном указании `sources:` нужно включить `source: local` явно, если нужны локальные ассеты:
156
+
157
+ ```yaml
158
+ assets:
159
+ # on_conflict: last_wins # last_wins (default) / first_wins
160
+
161
+ sources:
162
+ - source: local # assets/ рядом с манифестом
163
+
164
+ # Базовый amxmodx (modules, plugins и т.д.)
165
+ - source: amxmodx
166
+ map:
167
+ - from: addons/amxmodx/modules/
168
+ to: addons/amxmodx/modules/
169
+
170
+ # Архив — всё содержимое в корень ассетов
171
+ - url: https://cdn.example.com/pack.zip
172
+ cache: local # none (default) / local (.amxb-cache/) / global (~/.cache/)
173
+
174
+ # Архив — несколько правил из одного источника
175
+ - url: https://cdn.example.com/full-pack.zip
176
+ map:
177
+ - from: resource/models/ # содержимое папки → models/
178
+ to: models/
179
+ - from: resource/sound/
180
+ to: sound/
181
+
182
+ # Одиночный файл
183
+ - url: https://cdn.example.com/weapon.wav
184
+ to: sound/weapons/
185
+
186
+ # Одиночный файл с переименованием (to без trailing slash)
187
+ - url: https://cdn.example.com/pistol_v2.mdl
188
+ to: models/v_pistol.mdl
189
+
190
+ # GitHub release asset — использует тот же кэш, что и deps
191
+ - source: release
192
+ repo: org/weapon-pack
193
+ ref: v2.0.0
194
+ asset: "weapon-models.zip"
195
+ map:
196
+ - from: models/
197
+ to: models/
198
+ - from: sound/
199
+ to: sound/
200
+ ```
201
+
202
+ **Семантика `from` / `to` (trailing slash = содержимое папки):**
203
+
204
+ | `from` | `to` | Результат |
205
+ | --- | --- | --- |
206
+ | *(нет)* | *(нет)* | весь архив / файл → корень ассетов |
207
+ | `models/` | `models/` | содержимое `models/` → `assets/models/` |
208
+ | `models` | `models/` | папка целиком → `assets/models/models/` |
209
+ | `sound/gun.wav` | `sound/` | файл → `assets/sound/gun.wav` |
210
+ | `sound/gun.wav` | `sound/pistol.wav` | файл с переименованием |
211
+
212
+ ## Деплой и watch
213
+
214
+ Создайте `.env` рядом с манифестом (`amxb init --deploy`):
215
+
216
+ ```env
217
+ AMXB_DEPLOY_PATH=/home/user/hlds/cstrike
218
+ AMXB_DEPLOY_RCON_HOST=127.0.0.1
219
+ AMXB_DEPLOY_RCON_PORT=27015
220
+ AMXB_DEPLOY_RCON_PASSWORD=secret
221
+ AMXB_DEPLOY_RCON_CMD=amxx load {plugin}
222
+ ```
223
+
224
+ Или задайте прямо в манифесте (поддерживается `${VAR}` интерполяция):
225
+
226
+ ```yaml
227
+ deploy:
228
+ path: /home/user/hlds/cstrike # корень сервера (где лежат addons/, models/)
229
+ amxmodx_path: addons/amxmodx # default: addons/amxmodx
230
+ watch_debounce_ms: 500 # мс стабильности файла перед ребилдом (default: 500)
231
+ exclude: # пути от deploy.path, которые не перезаписываются
232
+ - addons/amxmodx/configs/ # сохранить конфиги сервера
233
+ - addons/amxmodx/configs/amxx.cfg
234
+ rcon:
235
+ host: 127.0.0.1
236
+ port: 27015
237
+ password: ${RCON_PASSWORD}
238
+ command: "amxx load {plugin}" # {plugin} = имя без .amxx; пусто = не слать
239
+ ```
240
+
241
+ `amxb watch` отслеживает изменения в `amxmodx/` и `assets/`:
242
+
243
+ - `.sma` → пересобрать плагин, задеплоить `.amxx`, послать RCON
244
+ - `.inc` → пересобрать только плагины, зависящие от этого инклюда (по `#include`/`#tryinclude`)
245
+ - остальные файлы → задеплоить напрямую
246
+ - манифест → полная пересборка
247
+
248
+ ## Несколько GitHub токенов
249
+
250
+ Если репозитории, зависимости или release-ассеты разнесены по разным организациям, а один fine-grained PAT не может охватывать несколько организаций — укажи мапу `github.tokens` (владелец → имя env-переменной с токеном этой организации):
251
+
252
+ ```env
253
+ # .env рядом с amxbuild.yml
254
+ GITHUB_TOKEN=ghp_fallback_xxx # fallback для всех остальных
255
+ GITHUB_TOKEN_ORGA=github_pat_111_...
256
+ GITHUB_TOKEN_ORGB=github_pat_222_...
257
+ ```
258
+
259
+ ```yaml
260
+ github:
261
+ token_env: GITHUB_TOKEN # необязательно, по умолчанию GITHUB_TOKEN
262
+ tokens: # необязательно — owner → env-переменная
263
+ AmxxModularEcosystem: GITHUB_TOKEN_ORGA
264
+ Next21Team: GITHUB_TOKEN_ORGB
265
+ ```
266
+
267
+ Резолвер для каждого `owner/repo`:
268
+
269
+ 1. `github.tokens[owner]` — токен организации (если owner есть в мапе);
270
+ 2. `github.token_env` (по умолчанию `GITHUB_TOKEN`) — глобальный токен;
271
+ 3. иначе — анонимный доступ (публичные репо).
272
+
273
+ Мапа применяется ко всему: репозитории из `repos:`, зависимости (`deps`/`DEPS_LIST`/`deps_override`), GitHub release-ассеты в `assets.sources` и команда `amxb deps-tree`. Для транзитивных зависимостей токен подбирается по владельцу автоматически.
274
+
275
+ ## GitHub Actions
276
+
277
+ ```yaml
278
+ uses: AmxxModularEcosystem/amxx-builder@v1
279
+ ```
280
+
281
+ ### Инпуты
282
+
283
+ | Инпут | По умолчанию | Описание |
284
+ | --- | --- | --- |
285
+ | `manifest` | `./amxbuild.yml` | Путь к манифесту |
286
+ | `build-dir` | `./build` | Директория сборки |
287
+ | `version` | — | Переопределяет `manifest.version` |
288
+ | `archive-name` | — | Переопределяет `output.archive_name` |
289
+ | `set` | — | Переопределить любое поле манифеста (multiline, `key=value`) |
290
+ | `no-fetch` | `false` | Пропустить клонирование (использовать кэш раннера) |
291
+ | `no-archive` | `false` | Только компиляция, без упаковки |
292
+ | `github-token` | `${{ github.token }}` | GitHub токен для приватных репо |
293
+
294
+ ### Выходы
295
+
296
+ | Выход | Описание |
297
+ | --- | --- |
298
+ | `name` | Имя проекта из манифеста (`manifest.name`) |
299
+
300
+ ### Полный пример воркфлоу
301
+
302
+ Манифест плагина (`amxbuild.yml`):
303
+
304
+ ```yaml
305
+ name: MyPlugin
306
+
307
+ amxmodx:
308
+ version: "1.10.5428"
309
+
310
+ deps:
311
+ - AmxxModularEcosystem/ParamsController@1.4.2
312
+ ```
313
+
314
+ Воркфлоу (`.github/workflows/ci.yml`):
315
+
316
+ ```yaml
317
+ name: CI
318
+
319
+ on:
320
+ push:
321
+ branches: [master, feature/**, fix/**]
322
+ paths-ignore:
323
+ - "**.md"
324
+ pull_request:
325
+ types: [opened, reopened, synchronize]
326
+ release:
327
+ types: [published]
328
+
329
+ jobs:
330
+ build:
331
+ name: Build
332
+ runs-on: ubuntu-latest
333
+ outputs:
334
+ sha: ${{ steps.sha.outputs.SHORT }}
335
+ name: ${{ steps.build.outputs.name }}
336
+ steps:
337
+ - uses: actions/checkout@v5
338
+
339
+ - id: sha
340
+ run: echo "SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
341
+
342
+ - id: build
343
+ uses: AmxxModularEcosystem/amxx-builder@v1
344
+ with:
345
+ set: |
346
+ output.pack=false
347
+ output.dir=./artifact
348
+
349
+ - uses: actions/upload-artifact@v5
350
+ with:
351
+ name: ${{ steps.build.outputs.name }}-${{ steps.sha.outputs.SHORT }}-dev
352
+ path: artifact/
353
+
354
+ publish:
355
+ name: Publish release
356
+ runs-on: ubuntu-latest
357
+ needs: [build]
358
+ if: |
359
+ github.event_name == 'release' &&
360
+ github.event.action == 'published' &&
361
+ startsWith(github.ref, 'refs/tags/')
362
+ steps:
363
+ - uses: actions/download-artifact@v5
364
+ with:
365
+ name: ${{ needs.build.outputs.name }}-${{ needs.build.outputs.sha }}-dev
366
+ path: artifact/
367
+
368
+ - name: Package for release
369
+ run: |
370
+ cd artifact
371
+ zip -r "../${{ needs.build.outputs.name }}-${{ github.ref_name }}.zip" .
372
+
373
+ - uses: softprops/action-gh-release@v2
374
+ with:
375
+ files: "${{ needs.build.outputs.name }}-*.zip"
376
+ ```
377
+
378
+ Для приватных репо и зависимостей передай PAT:
379
+
380
+ ```yaml
381
+ - id: build
382
+ uses: AmxxModularEcosystem/amxx-builder@v1
383
+ with:
384
+ github-token: ${{ secrets.MY_PAT }}
385
+ ```
386
+
387
+ Несколько организаций — передай секреты через `env:` и пропиши мапу через инпут `set`:
388
+
389
+ ```yaml
390
+ - id: build
391
+ uses: AmxxModularEcosystem/amxx-builder@v1
392
+ env:
393
+ GITHUB_TOKEN_ORGA: ${{ secrets.TOKEN_ORGA }}
394
+ GITHUB_TOKEN_ORGB: ${{ secrets.TOKEN_ORGB }}
395
+ with:
396
+ set: |
397
+ github.tokens.AmxxModularEcosystem=GITHUB_TOKEN_ORGA
398
+ github.tokens.Next21Team=GITHUB_TOKEN_ORGB
399
+ ```
400
+
401
+ ## Локальная сборка (замена build.bat)
402
+
403
+ `repos:` не обязателен. Если не указан — инструмент работает только с локальными файлами.
404
+ Чтобы архив начинался с имени пакета (как при дистрибуции плагина), используй шаблон `{name}` в путях — это уже поведение по умолчанию:
405
+
406
+ ```yaml
407
+ name: VipModular
408
+ version: "5.0.0"
409
+ ```
410
+
411
+ Результат:
412
+
413
+ ```text
414
+ VipModular.zip
415
+ VipModular/
416
+ addons/amxmodx/
417
+ plugins/vip_core.amxx
418
+ configs/...
419
+ lang/...
420
+ models/...
421
+ README.md
422
+ ```
423
+
424
+ Полный пример: [`example/amxbuild.local.yml`](example/amxbuild.local.yml).
425
+
426
+ ## ref: latest
427
+
428
+ ```yaml
429
+ repos:
430
+ - repo: AmxxModularEcosystem/VipModular
431
+ ref: latest # автоматически берёт тег последнего GitHub release
432
+ ```
433
+
434
+ ## Полный пример
435
+
436
+ Все доступные опции: [`example/amxbuild.yml`](example/amxbuild.yml).
437
+
438
+ ## MCP сервер
439
+
440
+ MCP сервер предоставляет агенту opencode информацию о публичном интерфейсе зависимостей AMX Mod X и стандартной библиотеки. Подробная документация — в [`docs/mcp/INDEX.md`](docs/mcp/INDEX.md).
441
+
442
+ Подключение в opencode:
443
+
444
+ ```json
445
+ {
446
+ "mcp": {
447
+ "amxx-dep-resolver": {
448
+ "type": "local",
449
+ "command": ["amxb", "mcp"],
450
+ "enabled": true
451
+ }
452
+ }
453
+ }
454
+ ```
455
+
456
+ **Все 11 инструментов** — от просмотра `.inc` файлов до построения дерева зависимостей —
457
+ описаны в [`docs/mcp/INDEX.md`](docs/mcp/INDEX.md) с таблицей и ссылками на полную документацию каждого инструмента.
458
+
459
+ ## Приоритеты
460
+
461
+ | Что | Порядок (↑ выше) |
462
+ | --- | --- |
463
+ | плагины `plugins:` | правила применяются по порядку, первое совпадение побеждает |
464
+ | `plugins_ini_postfix` | правило `plugins:` → репо → глобальный |
465
+ | зависимости | `manifest.deps` → `deps_override` → `DEPS_LIST` файл в репо |
466
+ | ассеты | порядок в `sources:` + `on_conflict` |
467
+ | версия компилятора | `amxmodx.version` → последний релиз |
468
+ | значения манифеста | `--set` → манифест проекта → `defaults/amxbuild.defaults.yml` |
469
+
470
+ ## Устранение неполадок
471
+
472
+ ### `no such file or directory: ./amxxpc`
473
+
474
+ Если файл `./amxxpc` существует и имеет права на исполнение (`chmod +x amxxpc`), но вы всё равно получаете ошибку **"No such file or directory"** при его запуске, скорее всего, в вашей 64-битной системе Linux отсутствуют 32-битные библиотеки.
475
+ `amxxpc` — это 32-битный исполняемый файл, которому требуется поддержка 32-битной архитектуры (`i386`).
476
+
477
+ #### Решение: Установите поддержку 32-битной архитектуры
478
+
479
+ **Ubuntu / Debian / WSL:**
480
+
481
+ ```bash
482
+ sudo dpkg --add-architecture i386
483
+ sudo apt update
484
+ sudo apt install libc6:i386 libstdc++6:i386
485
+ ```
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ import * as core from '@actions/core';
4
+ const fs = require('fs');
5
+ const yaml = require('js-yaml');
6
+
7
+ const manifest = core.getInput('manifest') || './amxbuild.yml';
8
+ const buildDir = core.getInput('build-dir') || './build';
9
+
10
+ // Expose manifest name as output before the build runs
11
+ try {
12
+ const raw = yaml.load(fs.readFileSync(manifest, 'utf8'));
13
+ if (raw && raw.name) core.setOutput('name', raw.name);
14
+ } catch (_) {}
15
+ const version = core.getInput('version');
16
+ const archiveName = core.getInput('archive-name');
17
+ const setRaw = core.getInput('set');
18
+ const noFetch = core.getInput('no-fetch') === 'true';
19
+ const noArchive = core.getInput('no-archive') === 'true';
20
+ const githubToken = core.getInput('github-token');
21
+
22
+ if (githubToken) process.env.GITHUB_TOKEN = githubToken;
23
+
24
+ // Collect all --set pairs: shorthands first, then raw multiline block
25
+ const setPairs = [];
26
+ if (version) setPairs.push(`version=${version}`);
27
+ if (archiveName) setPairs.push(`output.archive_name=${archiveName}`);
28
+ if (setRaw) setPairs.push(...setRaw.split(/\r?\n/).map(s => s.trim()).filter(Boolean));
29
+
30
+ // Synthesise argv so Commander in index.js parses our inputs
31
+ process.argv = [
32
+ process.execPath,
33
+ 'amxx-builder',
34
+ 'build',
35
+ '--manifest', manifest,
36
+ '--build-dir', buildDir,
37
+ ...setPairs.flatMap(p => ['--set', p]),
38
+ ...(noFetch ? ['--no-fetch'] : []),
39
+ ...(noArchive ? ['--no-archive'] : []),
40
+ ];
41
+
42
+ require('./index.js');