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.
- package/AGENTS.md +111 -0
- package/README.md +485 -0
- package/action-entry.js +42 -0
- package/action.yml +55 -0
- package/defaults/amxbuild.defaults.yml +40 -0
- package/index.js +4 -0
- package/mcp/dep-resolver.js +75 -0
- package/mcp/handlers.js +862 -0
- package/mcp/mcp-server.js +112 -0
- package/mcp/registry.js +863 -0
- package/mcp/symbol-index.js +171 -0
- package/package.json +68 -0
- package/src/archiver.js +140 -0
- package/src/asset-fetcher.js +277 -0
- package/src/build-plan.js +101 -0
- package/src/build-service.js +188 -0
- package/src/cache-dir.js +21 -0
- package/src/cache-info.js +104 -0
- package/src/cli.js +302 -0
- package/src/collector.js +89 -0
- package/src/commands/build.js +49 -0
- package/src/commands/cache.js +77 -0
- package/src/commands/clean.js +33 -0
- package/src/commands/compile-renderer.js +38 -0
- package/src/commands/deploy.js +40 -0
- package/src/commands/deps-tree.js +92 -0
- package/src/commands/doctor.js +77 -0
- package/src/commands/dry-run.js +64 -0
- package/src/commands/init.js +228 -0
- package/src/commands/mcp.js +13 -0
- package/src/commands/releases.js +45 -0
- package/src/commands/resolve-manifest.js +27 -0
- package/src/commands/serve.js +489 -0
- package/src/commands/shared.js +24 -0
- package/src/commands/validate.js +34 -0
- package/src/commands/watch.js +209 -0
- package/src/compile-utils.js +65 -0
- package/src/compiler-fetcher.js +327 -0
- package/src/compiler.js +228 -0
- package/src/dep-graph.js +92 -0
- package/src/deployer.js +197 -0
- package/src/deps-resolver.js +127 -0
- package/src/deps-tree.js +202 -0
- package/src/env.js +18 -0
- package/src/events.js +29 -0
- package/src/format.js +23 -0
- package/src/fs-utils.js +87 -0
- package/src/include-tree.js +845 -0
- package/src/ini-builder.js +44 -0
- package/src/jsonrpc-transport.js +195 -0
- package/src/logger.js +50 -0
- package/src/manifest-path.js +34 -0
- package/src/manifest.js +373 -0
- package/src/progress.js +66 -0
- package/src/rcon.js +103 -0
- package/src/release-fetcher.js +206 -0
- package/src/release-lister.js +79 -0
- package/src/repo-fetcher.js +273 -0
- package/src/retry.js +50 -0
- package/src/schema.js +54 -0
- package/src/update-check.js +114 -0
- package/src/validate.js +69 -0
- package/src/watcher.js +135 -0
- package/templates/init-build.bat +11 -0
- package/templates/init-build.sh +7 -0
- package/templates/init-deploy.env +14 -0
- package/templates/init-manifest.yml +6 -0
- 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
|
+
```
|
package/action-entry.js
ADDED
|
@@ -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');
|