@studiometa/forge-cli 0.5.1 → 0.5.2

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/README.md CHANGED
@@ -45,7 +45,7 @@ forge <command> [subcommand] [options]
45
45
  | ------------------- | ------- | ---------------------------------------------------- |
46
46
  | `config` | | Manage CLI configuration (`set`, `get`, `delete`) |
47
47
  | `servers` | `s` | List, get, reboot servers |
48
- | `sites` | | List, get sites on a server |
48
+ | `sites` | | List, get sites; apply a site file |
49
49
  | `deployments` | `d` | List deployments, trigger deploys, deploy script |
50
50
  | `databases` | `db` | List, get databases on a server |
51
51
  | `database-users` | | List, get, create, delete database users on a server |
@@ -131,6 +131,63 @@ forge servers create --name <name> --ip-address <ip> [--php-version php84 --data
131
131
  ```bash
132
132
  forge sites list --server <id> # List sites on a server
133
133
  forge sites get <site_id> --server <id> # Get site details
134
+ forge sites apply --file site.json --check # Print the plan, change nothing
135
+ forge sites apply --file site.json # Make the site match the file
136
+ ```
137
+
138
+ #### Declare a site in a file with `sites apply`
139
+
140
+ `sites apply` makes a Forge site match a JSON file kept in the project, for example `config/forge/production/site.json`. Only `server` and `domain` are required. A key that is absent is not managed: `apply` does not read it, compare it or change it.
141
+
142
+ ```json
143
+ {
144
+ "$schema": "https://unpkg.com/@studiometa/forge-cli/schemas/site.schema.json",
145
+ "server": "web-1",
146
+ "domain": "www.example.com",
147
+ "aliases": ["example.com"],
148
+ "type": "php",
149
+ "php": "php83",
150
+ "webDirectory": "/public",
151
+ "repository": {
152
+ "provider": "custom",
153
+ "url": "https://git.example.com/group/project.git",
154
+ "branch": "main"
155
+ },
156
+ "database": { "name": "example", "user": "example" },
157
+ "env": { "APP_ENV": "production" },
158
+ "workers": [{ "command": "php artisan queue:work database --queue=default", "processes": 4 }],
159
+ "schedule": [{ "command": "php artisan schedule:run", "frequency": "minutely" }]
160
+ }
161
+ ```
162
+
163
+ | Key | What `apply` does |
164
+ | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
165
+ | `server`, `domain` | Find the server and the site by exact name (never a partial match, with or without `--exact`). `server` can also be an ID |
166
+ | `type`, `php`, `webDirectory`, `repository` | Create the site when it is missing (`type` is then required) and wait until Forge installs it. On an existing site, update `php`, `webDirectory` and the branch |
167
+ | `aliases` | Add the missing aliases. Never remove one |
168
+ | `database` | Create the database and the user when they are missing. A new user gets a random password, written only into the site `.env` (`DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`) |
169
+ | `env` | Add the missing keys to the site `.env`. A key with another value on Forge is reported, never overwritten |
170
+ | `workers` | Forge background processes of the site. Create the missing ones, delete the ones of the site that the file does not declare. The directory defaults to the site root |
171
+ | `schedule` | Forge scheduled jobs of the site (`minutely`, `hourly`, `nightly`, `weekly`, `monthly`, `reboot`, or `custom` with `cron`). A relative `artisan` becomes the absolute path of the site |
172
+
173
+ A worker or a scheduled job matches the file when its command, directory, processes (or frequency) and user are the same. Commands are compared after normalization: quotes (`'database'` and `database`), repeated spaces and a relative `artisan` (`php artisan` and `php /home/forge/www.example.com/artisan`) do not count as differences. Forge cannot update a worker: a changed worker is created again, then the old one is deleted.
174
+
175
+ `workers` and `schedule` delete what the file does not declare, so `apply` deletes only what surely belongs to the site, because a server often hosts several sites:
176
+
177
+ - A scheduled job belongs to the site when Forge lists it on the site, or when its command uses the site path and no other site path.
178
+ - A worker belongs to the site when its directory is the site path or inside it. Without a directory, its command must use the site path and no other site path, and Forge must associate it with the site.
179
+ - Paths are compared on whole segments: `/home/forge/example.com` does not own `/home/forge/example.com-staging` or `/home/forge/example.com.old`.
180
+ - Workers and scheduled jobs that run as `root` are never deleted, and neither are the ones whose owner is not certain: they are reported as kept.
181
+ - Without the `workers` key, no worker is read or deleted. With `"workers": []`, all the workers of the site are deleted. The same applies to `schedule`.
182
+ - The site, its aliases, its database and its `.env` keys are never deleted.
183
+
184
+ With `--check`, `apply` prints the plan and changes nothing. It exits `0` when Forge matches the file, `1` when it differs, and another code on an error (`3` for an invalid file). Every deletion shows its ID and full command. The plan gives `.env` key names only, never values, and shows a generated password as `<generated>`. With `--format json`, it prints `{ "inSync": false, "changes": [...], "warnings": [...] }`.
185
+
186
+ Run `--check` in merge request pipelines and `apply` on the main branch:
187
+
188
+ ```bash
189
+ forge sites apply --file config/forge/production/site.json --check
190
+ forge sites apply --file config/forge/production/site.json
134
191
  ```
135
192
 
136
193
  ### `deployments` (`d`) — Manage deployments
@@ -340,11 +397,23 @@ All write operations (`deploy`, `create`, `delete`, `reboot`, `restart`, `update
340
397
  | `--org <slug>` | | Organization slug (overrides config and env) |
341
398
  | `--server <id>` | | Server ID or name (auto-resolved if non-numeric) |
342
399
  | `--site <id>` | | Site ID or domain (auto-resolved if non-numeric) |
400
+ | `--exact` | | Match `--server` and `--site` names exactly |
343
401
  | `--format <fmt>` | `-f` | Output format: `json`, `human`, `table` |
344
402
  | `--no-color` | | Disable colored output |
345
403
  | `--help` | `-h` | Show help |
346
404
  | `--version` | `-v` | Show version |
347
405
 
406
+ ### Exact name matching
407
+
408
+ `--server` and `--site` accept a numeric ID or a name. A name resolves to the server or site with that exact name first, then to a single partial match: `--site example.com` resolves to `shop.example.com` when no site is named `example.com`.
409
+
410
+ In CI and scripts, turn on exact matching with the `--exact` flag or the `FORGE_EXACT_MATCH=1` environment variable. A name must then match exactly (case-insensitive); otherwise the command fails and lists the near matches.
411
+
412
+ ```bash
413
+ FORGE_EXACT_MATCH=1 forge deployments deploy --server 123 --site example.com
414
+ forge deployments deploy --server 123 --site example.com --exact
415
+ ```
416
+
348
417
  ## AI Agent Integration
349
418
 
350
419
  The CLI is designed for programmatic use by AI agents: