Skip to content

Commit 2aa03be

Browse files
authored
Merge pull request #1798 from appwrite/refactor/cli-go-codebase
Make the CLI target the Go implementation
2 parents c901f45 + 5c7e7cd commit 2aa03be

366 files changed

Lines changed: 2338 additions & 41262 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ jobs:
2121
sdk: [
2222
Android5Java17,
2323
Android16Java17,
24-
CLIBun10,
25-
CLIBun11,
26-
CLIBun13,
27-
GoCLI126,
24+
CLIGo126,
25+
CLIWasm,
26+
CLIWasmBrowser,
2827
DartBeta,
2928
DartStable,
3029
DotNet60,

.github/workflows/validation.yml

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ jobs:
1111
name: ${{ matrix.sdk }} (${{ matrix.platform }})
1212
runs-on: ubuntu-latest
1313
env:
14-
# Bun 1.3.12 has a sig_size calculation bug in macho.zig that truncates the
15-
# LC_CODE_SIGNATURE blob on cross-compiled Darwin binaries (oven-sh/bun#29120).
16-
# Unpin once a Bun release includes the upstream fix (oven-sh/bun#29122).
17-
CLI_BUN_VERSION: '1.3.11'
1814
FLUTTER_VERSION: '3.35.7'
1915
OSV_SCANNER_VERSION: 'v2.3.1'
2016
strategy:
@@ -76,9 +72,6 @@ jobs:
7672
- sdk: cli
7773
platform: console
7874

79-
- sdk: go-cli
80-
platform: console
81-
8275
- sdk: web
8376
platform: console
8477

@@ -101,7 +94,7 @@ jobs:
10194
run: php example.php ${{ matrix.sdk }} ${{ matrix.platform }}
10295

10396
- name: Verify generated SDK is idempotent
104-
if: matrix.sdk == 'zed-extension' || matrix.sdk == 'go-cli'
97+
if: matrix.sdk == 'zed-extension' || matrix.sdk == 'cli'
10598
run: |
10699
checksums() {
107100
find examples/${{ matrix.sdk }} -type f -print0 \
@@ -122,12 +115,6 @@ jobs:
122115
with:
123116
node-version: '22'
124117

125-
- name: Setup Bun
126-
if: matrix.sdk == 'cli'
127-
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
128-
with:
129-
bun-version: ${{ env.CLI_BUN_VERSION }}
130-
131118
- name: Cache Flutter SDK
132119
if: matrix.sdk == 'flutter'
133120
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -180,7 +167,7 @@ jobs:
180167
sdk: 'stable'
181168

182169
- name: Setup Go
183-
if: matrix.sdk == 'go' || matrix.sdk == 'go-cli' || matrix.sdk == 'dart' || matrix.sdk == 'flutter'
170+
if: matrix.sdk == 'go' || matrix.sdk == 'cli' || matrix.sdk == 'dart' || matrix.sdk == 'flutter'
184171
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
185172
with:
186173
go-version: '1.26.6'
@@ -227,18 +214,17 @@ jobs:
227214
npm run build
228215
;;
229216
cli)
230-
bun install
231-
bun run linux-x64
232-
bun run linux-arm64
233-
bun run mac-x64
234-
bun run mac-arm64
235-
bun run windows-x64
236-
bun run windows-arm64
237-
238-
npm ci
239-
npm audit
240-
npm run build
241-
node dist/cli.cjs --help
217+
go mod tidy
218+
unformatted=$(gofmt -l .)
219+
if [ -n "$unformatted" ]; then
220+
echo "Not gofmt-clean:"
221+
echo "$unformatted"
222+
exit 1
223+
fi
224+
go build ./...
225+
go vet ./...
226+
go test -race ./...
227+
go build -o appwrite .
242228
;;
243229
react-native)
244230
npm ci --omit=peer
@@ -298,19 +284,6 @@ jobs:
298284
go build ./...
299285
go test ./...
300286
;;
301-
go-cli)
302-
go mod tidy
303-
unformatted=$(gofmt -l .)
304-
if [ -n "$unformatted" ]; then
305-
echo "Not gofmt-clean:"
306-
echo "$unformatted"
307-
exit 1
308-
fi
309-
go build ./...
310-
go vet ./...
311-
go test -race ./...
312-
go build -o appwrite .
313-
;;
314287
dotnet)
315288
dotnet restore
316289
dotnet build --no-restore
@@ -335,34 +308,53 @@ jobs:
335308
;;
336309
esac
337310
338-
- name: Verify CLI Darwin binaries include valid embedded code signatures
339-
if: matrix.sdk == 'cli'
340-
working-directory: examples/cli
341-
run: python3 ../../scripts/verify-darwin-signatures.py
342-
343311
- name: Test compiled CLI binary
344312
if: matrix.sdk == 'cli'
345313
working-directory: examples/cli
346-
run: |
347-
BIN=build/appwrite-cli-linux-x64
348-
"./$BIN" --version
349-
# The root screen is grouped by intent, so assert a section renders
350-
# rather than just that some help was produced.
351-
"./$BIN" --help | grep -q "USAGE"
352-
"./$BIN" --help | grep -q "GET STARTED"
353-
SHELL=bash "./$BIN" completion bash >/dev/null
354-
355-
- name: Test compiled Go CLI binary
356-
if: matrix.sdk == 'go-cli'
357-
working-directory: examples/go-cli
358314
run: |
359315
./appwrite --version
360316
./appwrite --help | grep -q "USAGE"
361317
./appwrite --help | grep -q "GET STARTED"
362318
SHELL=bash ./appwrite completion bash >/dev/null
363319
320+
# The CLI ships as a native binary behind an npm launcher, so it has no
321+
# dist/ entry points to check -- what can break is the launcher itself
322+
# and the two scripts that assemble the platform packages.
323+
- name: Verify CLI npm launcher
324+
if: matrix.sdk == 'cli'
325+
working-directory: examples/cli
326+
run: |
327+
node --check npm/run.js
328+
node --check scripts/build-npm-packages.mjs
329+
node --check scripts/stage-assets.mjs
330+
331+
node -e "
332+
const pkg = require('./npm/package.json');
333+
const commands = Object.keys(pkg.bin ?? {});
334+
if (commands.length !== 1 || pkg.bin[commands[0]] !== 'run.js') {
335+
console.error('ERROR: npm/package.json must map exactly one bin command to run.js');
336+
process.exit(1);
337+
}
338+
if (!(pkg.files ?? []).includes('run.js')) {
339+
console.error('ERROR: npm/package.json files must include run.js or the launcher is not published');
340+
process.exit(1);
341+
}
342+
if (!pkg.license) {
343+
console.error('ERROR: npm/package.json is missing a license');
344+
process.exit(1);
345+
}
346+
for (const [name, version] of Object.entries(pkg.optionalDependencies ?? {})) {
347+
if (version !== pkg.version) {
348+
console.error(\`ERROR: \${name} is pinned to \${version}, not the launcher version \${pkg.version}\`);
349+
process.exit(1);
350+
}
351+
}
352+
"
353+
354+
(cd npm && npm pack --dry-run)
355+
364356
- name: Verify npm package entry points
365-
if: matrix.sdk == 'web' || matrix.sdk == 'node' || matrix.sdk == 'react-native' || matrix.sdk == 'cli'
357+
if: matrix.sdk == 'web' || matrix.sdk == 'node' || matrix.sdk == 'react-native'
366358
working-directory: examples/${{ matrix.sdk }}
367359
run: |
368360
echo "::group::Checking package.json has files field"

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ templates/swift/example/Example.xcodeproj/xcuserdata
2828
go.sum
2929

3030
# exclude raw lock files in templates (use .twig versions instead)
31-
templates/cli/package-lock.json
32-
templates/cli/bun.lock
3331

3432
# Written by the CLI conformance harness when it runs in place.
3533
tests/e2e/languages/cli/appwrite.config.json

AGENTS.md

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,25 @@ The generator does not auto-discover templates. Every output file must have an e
1616

1717
| Parent | Children affected |
1818
|--------|------------------|
19-
| `Node` | `CLI`, `ReactNative` |
19+
| `Node` | `ReactNative` |
2020
| `Dart` | `Flutter` |
2121
| `Swift` | `Apple` |
2222
| `Kotlin` | `Android` |
23-
| `Go` | `GoCLI` |
23+
| `Go` | `CLI` |
2424

2525
Modifying a parent's template or `getFiles()` affects all children. Regenerate and verify child SDKs too.
2626

27-
**Two couplings are not visible in the hierarchy.**
27+
**One coupling is not visible in the hierarchy.**
2828

29-
`Concern/CliCommandSurface.php` is a trait used by **both** `CLI` and `GoCLI`. It holds
30-
the nine helpers that decide what a generated command looks like — flag syntax, query
31-
flags, promoted root commands, service scopes. It is shared precisely so the two CLIs
32-
cannot drift, which means a change there alters the TypeScript CLI and the Go CLI at once.
29+
`templates/cli/install.sh.twig` and `templates/cli/install.ps1.twig` build every download
30+
URL from `language.params.npmPackage`. The same parameter names release assets produced
31+
by `.goreleaser.yaml` and consumed by the Scoop manifest and npm platform packages.
32+
Change the asset naming in one place and all four must move together.
3333

34-
`templates/cli/install.sh.twig` and `templates/cli/install.ps1.twig` live under
35-
`templates/cli/` but are registered in **both** `CLI::getFiles()` and `GoCLI::getFiles()`.
36-
They build every download URL from `language.params.npmPackage`, which also names every
37-
release asset produced by `.goreleaser.yaml` and consumed by the scoop manifest and the
38-
npm platform packages. Change the asset naming in one place and all four must move
39-
together, for both CLIs.
40-
41-
Either way, regenerate both:
34+
Regenerate the CLI after changing any of them:
4235

4336
```bash
44-
php example.php cli && php example.php go-cli
37+
php example.php cli
4538
```
4639

4740
### Rule 4: `copy` scope = no Twig processing
@@ -57,14 +50,13 @@ The `destination` string in each `getFiles()` entry supports Twig expressions an
5750

5851
### Rule 6: Never modify lock file templates directly
5952

60-
Lock file templates (`package-lock.json.twig`, `bun.lock.twig`) contain Twig expressions that get corrupted if you copy a raw lock file over them. Always use the update script:
53+
Lock file templates (`package-lock.json.twig`) contain Twig expressions that get corrupted if you copy a raw lock file over them. Always use the update script:
6154

6255
```bash
63-
./scripts/update-lockfiles.sh cli # update CLI lock files only
64-
./scripts/update-lockfiles.sh all # update all TS-based SDK lock files
56+
./scripts/update-lockfiles.sh all
6557
```
6658

67-
The script strips Twig expressions before running `npm install`/`bun install`, then restores them automatically. Never run `cp package-lock.json package-lock.json.twig` or edit these files by hand.
59+
The script strips Twig expressions before running `npm install`, then restores them automatically. Never copy a raw lock file over a lock template or edit one by hand.
6860

6961
## Repository at a Glance
7062

@@ -80,7 +72,7 @@ examples/<lang>/ ← Generated SDK output (gitignored; regenerate t
8072
example.php ← Entry point: regenerates all SDKs from specs
8173
```
8274

83-
**Supported SDKs:** PHP, Web, Node, CLI, GoCLI, Ruby, Python, Dart, Flutter, React Native, Go, Swift, Apple, DotNet, Android, Kotlin, Unity, REST, GraphQL, Rust, Skills, CursorPlugin, ClaudePlugin, CodexPlugin
75+
**Supported SDKs:** PHP, Web, Node, CLI, Ruby, Python, Dart, Flutter, React Native, Go, Swift, Apple, DotNet, Android, Kotlin, Unity, REST, GraphQL, Rust, Skills, CursorPlugin, ClaudePlugin, CodexPlugin
8476

8577
## Primary Workflows
8678

@@ -172,7 +164,6 @@ Pass as first argument to generate only that SDK:
172164
| `flutter` | Flutter | `examples/flutter/` |
173165
| `react-native` | ReactNative | `examples/react-native/` |
174166
| `go` | Go | `examples/go/` |
175-
| `go-cli` | GoCLI | `examples/go-cli/` |
176167
| `swift` | Swift | `examples/swift/` |
177168
| `apple` | Apple | `examples/apple/` |
178169
| `dotnet` | DotNet | `examples/dotnet/` |
@@ -242,8 +233,6 @@ Before submitting changes that touch templates or language classes:
242233
- [ ] Rector check passes (`composer refactor:check`)
243234
- [ ] Twig linter passes (`composer lint-twig`)
244235
- [ ] If a parent language was modified, child SDKs were also checked
245-
- [ ] If `Concern/CliCommandSurface.php` was touched, **both** CLIs were regenerated and
246-
their e2e suites run — the trait is shared, so a change there moves the shipping
247-
TypeScript CLI as well as the Go one
248-
- [ ] Go CLI changes compile and pass their tests:
249-
`cd examples/go-cli && go build ./... && go vet ./... && go test ./...`
236+
- [ ] If `Concern/CliCommandSurface.php` was touched, the CLI was regenerated and its e2e suite run
237+
- [ ] CLI changes compile and pass their tests:
238+
`cd examples/cli && go build ./... && go vet ./... && go test ./...`

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ php example.php zed-extension
183183

184184
| Target | Argument | Supported Versions | Coding Standards | Package Manager | Output |
185185
|--------|----------|--------------------|------------------|-----------------|--------|
186-
| CLI | `cli` | Node.js 20 and Bun 1.3.11 in CI | [NPM Coding Style] | NPM, Bun, native binaries | `examples/cli/` |
186+
| CLI | `cli` | Go 1.26.5 | [Effective Go] | Go modules, native binaries, NPM | `examples/cli/` |
187187
| REST examples | `rest` | N/A | Markdown | N/A | `examples/REST/` |
188188
| GraphQL | `graphql` | N/A | GraphQL | N/A | `examples/graphql/` |
189189
| Skills | `skills` | N/A | Markdown | N/A | `examples/skills/` |

example.php

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use Appwrite\SDK\Language\Web;
1010
use Appwrite\SDK\Language\Node;
1111
use Appwrite\SDK\Language\CLI;
12-
use Appwrite\SDK\Language\GoCLI;
1312
use Appwrite\SDK\Language\PHP;
1413
use Appwrite\SDK\Language\Python;
1514
use Appwrite\SDK\Language\Ruby;
@@ -65,6 +64,7 @@ function configureSDK(SDK $sdk, array $overrides = []): SDK {
6564
'shortDescription' => 'Repo short description goes here',
6665
'url' => 'https://example.com',
6766
'coverImage' => Config::COVER_IMAGE,
67+
'license' => 'BSD-3-Clause',
6868
'licenseContent' => 'test test test',
6969
'warning' => '**WORK IN PROGRESS - NOT READY FOR USAGE**',
7070
'changelog' => '**CHANGELOG**',
@@ -97,6 +97,7 @@ function configureSDK(SDK $sdk, array $overrides = []): SDK {
9797
->setShortDescription($config['shortDescription'])
9898
->setURL($config['url'])
9999
->setCoverImage($config['coverImage'])
100+
->setLicense($config['license'])
100101
->setLicenseContent($config['licenseContent'])
101102
->setWarning($config['warning'])
102103
->setChangelog($config['changelog'])
@@ -160,7 +161,6 @@ function buildStaticSpecification(): Specification {
160161
'web',
161162
'node',
162163
'cli',
163-
'go-cli',
164164
'ruby',
165165
'python',
166166
'dart',
@@ -261,7 +261,6 @@ function buildStaticSpecification(): Specification {
261261
\_/ \_/ .__/| .__/ \_/\_/ |_| |_|\__\___| \____/\____/\____/
262262
|_| |_| ";
263263

264-
// Shared by both CLIs: they present the same command surface.
265264
$cliExcludes = [
266265
'services' => [
267266
['name' => 'assistant'],
@@ -308,18 +307,17 @@ function buildStaticSpecification(): Specification {
308307
// Absent from the published Go SDK, which is generated from the server spec:
309308
// three console-only services, plus `migrations`, which has never shipped.
310309
// Generating their commands would import packages that do not exist.
311-
$goCliExcludes = $cliExcludes;
312-
$goCliExcludes['services'] = [
313-
...$goCliExcludes['services'],
310+
$cliExcludes['services'] = [
311+
...$cliExcludes['services'],
314312
['name' => 'affiliates'],
315313
['name' => 'migrations'],
316314
['name' => 'notifications'],
317315
['name' => 'vcs'],
318316
];
319317
// Individual endpoints the same SDK has no function for. Read off the
320318
// compiler, not guessed -- an invented name silently matches nothing.
321-
$goCliExcludes['methods'] = [
322-
...$goCliExcludes['methods'],
319+
$cliExcludes['methods'] = [
320+
...$cliExcludes['methods'],
323321
// Account API keys and push targets, and account deletion.
324322
['service' => 'account', 'name' => 'createKey'],
325323
['service' => 'account', 'name' => 'listKeys'],
@@ -357,37 +355,19 @@ function buildStaticSpecification(): Specification {
357355

358356
// CLI
359357
if (!$requestedSdk || $requestedSdk === 'cli') {
360-
$language = new CLI();
361-
$language->setNPMPackage('appwrite-cli');
362-
$language->setExecutableName('appwrite');
363-
$language->setLogo(json_encode($cliLogo));
364-
$language->setLogoUnescaped($cliLogoUnescaped);
365-
366-
$sdk = new SDK($language, buildSpecification($spec));
367-
$sdk->setTest(false);
368-
configureSDK($sdk, [
369-
'exclude' => $cliExcludes,
370-
]);
371-
372-
$sdk->generate(__DIR__ . '/examples/cli');
373-
}
374-
375-
// Go CLI -- shares the TypeScript CLI's command surface via CliCommandSurface.
376-
if (!$requestedSdk || $requestedSdk === 'go-cli') {
377-
$language = new GoCLI();
358+
$language = new CLI();
378359
$language->setExecutableName('appwrite');
379360
$language->setLogo($cliLogo);
380361
$language->setLogoUnescaped($cliLogoUnescaped);
381-
// Same package as the TypeScript CLI, and it names every release asset.
382362
$language->setNPMPackage('appwrite-cli');
383363

384364
$sdk = new SDK($language, buildSpecification($spec));
385365
$sdk->setTest(false);
386366
configureSDK($sdk, [
387-
'exclude' => $goCliExcludes,
367+
'exclude' => $cliExcludes,
388368
]);
389369

390-
$sdk->generate(__DIR__ . '/examples/go-cli');
370+
$sdk->generate(__DIR__ . '/examples/cli');
391371
}
392372

393373
// Ruby

0 commit comments

Comments
 (0)