@dijji/cli 0.1.0
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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/dijji.js +25195 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dijitales
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# dijji CLI (`@dijji/cli`)
|
|
2
|
+
|
|
3
|
+
`dijji` runs the mobile E2E jobs of a Dijji pipeline on a developer machine. The cloud container cannot run an
|
|
4
|
+
Android emulator or an iOS simulator, so a stage with an `ios`/`android` E2E target parks until a subscribed machine
|
|
5
|
+
takes the job, builds the app with the recipe your team wrote, installs it, runs the Maestro flows and reports the
|
|
6
|
+
result back.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -g @dijji/cli
|
|
12
|
+
dijji --version
|
|
13
|
+
dijji doctor
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The package is a single bundled file (`dist/dijji.js`) with no runtime dependencies and no install scripts. Node.js 24
|
|
17
|
+
or later is required. Every release is published from `packages/cli` by a GitHub Actions workflow on a `cli-v*` tag
|
|
18
|
+
(npm trusted publishing with provenance); nothing is published from a laptop.
|
|
19
|
+
|
|
20
|
+
The CLI talks to `https://app.dijji.ai` by default. A self-hosted or non-production control plane is given at
|
|
21
|
+
login and remembered for the device: `dijji login --api <api-url> --frontend <frontend-url>`.
|
|
22
|
+
|
|
23
|
+
### From the repository (development)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm --filter @dijji/cli build
|
|
27
|
+
pnpm --filter @dijji/cli pack --pack-destination packages/cli # writes packages/cli/dijji-cli-<version>.tgz
|
|
28
|
+
npm i -g ./packages/cli/dijji-cli-<version>.tgz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Without `--pack-destination`, `pnpm --filter ... pack` writes the tarball into the directory it is run from.
|
|
32
|
+
|
|
33
|
+
## Requirements
|
|
34
|
+
|
|
35
|
+
| Runner | Needs |
|
|
36
|
+
|---|---|
|
|
37
|
+
| all | git, Java 17+ (`java -version` must work, not just exist), Maestro 2.7.0+ |
|
|
38
|
+
| android | adb and a running emulator or connected device (APK artifacts; AAB is not supported) |
|
|
39
|
+
| ios | macOS with Xcode (`xcode-select -p`, `xcodebuild -version`) and an installed iOS simulator runtime |
|
|
40
|
+
|
|
41
|
+
`dijji doctor` checks all of this from each tool's own version output. Supported platforms: macOS (Android and iOS),
|
|
42
|
+
Linux (Android). Windows is not supported.
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
dijji login [--runners android,ios] [--api <url> --frontend <url>]
|
|
48
|
+
dijji logout
|
|
49
|
+
dijji subscribe [<projectId>] [--runners android,ios] [--yes] [--android-device <serial>] [--ios-device <udid>]
|
|
50
|
+
dijji unsubscribe <projectId> [--purge]
|
|
51
|
+
dijji status
|
|
52
|
+
dijji doctor [--json] [--runners android,ios]
|
|
53
|
+
dijji run [--once] [--yes] [--keep-workdir] [--allow-scripts] [--fresh]
|
|
54
|
+
[--android-device <serial>] [--ios-device <udid>] [--print-service-template launchd|systemd]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **login** opens `/cli/authorize` in the browser and prints a verification code (`XXXX-XXXX`). Approve only if the
|
|
58
|
+
browser shows the same code. The browser returns to a listener on `127.0.0.1`; nothing is sent to the API before
|
|
59
|
+
that. `--api`/`--frontend` point a self-hosted installation (both are required together). A dijji process that is
|
|
60
|
+
already running keeps the session it started with: once `dijji login` writes another session it stops with exit
|
|
61
|
+
code 4 at its next token refresh instead of switching; start it again.
|
|
62
|
+
- **subscribe** checks three gates before it writes anything: every repository is readable with your own git
|
|
63
|
+
credentials (`git ls-remote`, no prompts), the tools for the selected runners pass `doctor`, and every build recipe
|
|
64
|
+
the project's stages use for the selected runners is printed with the stages that use it and approved (a recipe
|
|
65
|
+
whose paths could never run on this machine is refused). A device holds at most 5 subscriptions.
|
|
66
|
+
- **run** polls for jobs, keeps the lease alive with a heartbeat, and runs one job at a time. Each heartbeat also
|
|
67
|
+
renews the artifact upload URL, which is never valid beyond the lease, so a job uploads its evidence however long it
|
|
68
|
+
runs. `--once` handles at most
|
|
69
|
+
one poll. A network or server error while polling does not stop the worker: it retries, backing off from 5 s to
|
|
70
|
+
60 s (`--once` exits with the error instead). If the server can no longer resolve a subscribed project (it was
|
|
71
|
+
deleted, or you are no longer in its team), `dijji run` names the subscription and exits with code 2; remove it with
|
|
72
|
+
`dijji unsubscribe <projectId>`. `--print-service-template` prints a launchd/systemd definition; it does not install
|
|
73
|
+
it.
|
|
74
|
+
- **status** shows local state only and never prints secrets.
|
|
75
|
+
|
|
76
|
+
Exit codes: `0` ok, `1` error, `2` usage, `3` upgrade required (the server rejected this CLI version; the message names
|
|
77
|
+
the minimum version), `4` login required, `5` doctor failed, `130` interrupted.
|
|
78
|
+
|
|
79
|
+
## What a job does
|
|
80
|
+
|
|
81
|
+
1. Validates the job payload (clone URLs are recomputed from the provider, commit SHAs, app id, recipe paths, `E2E_`
|
|
82
|
+
environment names).
|
|
83
|
+
2. Checks the tools and the device, and `git ls-remote` for every repository.
|
|
84
|
+
3. Downloads the flow bundle and opens it safely (no paths outside the bundle, no links, size limits); the bundle's
|
|
85
|
+
`manifest.json` must match the job's runner, app id and case ids.
|
|
86
|
+
4. Rejects flows that use `runScript`/`evalScript` unless `--allow-scripts` is given.
|
|
87
|
+
5. Asks for approval when the build recipe (command, artifact glob, workdir, repository) is new on this machine.
|
|
88
|
+
`--yes` approves recipe changes without asking — anyone who can edit the pipeline can then run commands on this
|
|
89
|
+
machine.
|
|
90
|
+
6. Brings the persistent workspace to the job's commit: `fetch`, `checkout --detach`, `reset --hard`, `clean -dff`.
|
|
91
|
+
Git hooks never run. Gitignored files (`local.properties`, `google-services.json`, `.env`, build caches) are kept.
|
|
92
|
+
7. Deletes the previous artifact, runs the build (40 minute limit), finds exactly one artifact produced by this build.
|
|
93
|
+
The recipe's `workdir` is a folder inside its repository (relative, never above the repository root, symlinks
|
|
94
|
+
included); the artifact glob stays inside the workdir. The pipeline editor refuses a recipe that breaks these rules.
|
|
95
|
+
8. Installs the app and compares the hash of what the device actually runs with the local build.
|
|
96
|
+
9. Runs each flow in its own `maestro --device <id> test` process with one retry; test credentials are passed only in
|
|
97
|
+
that process's environment.
|
|
98
|
+
10. Writes the result to disk first, uploads the artifacts (up to 20 MB) with the latest upload URL the heartbeat
|
|
99
|
+
renewed, and posts the result with a deterministic id, so a crash-and-restart repost is recognised as a duplicate.
|
|
100
|
+
|
|
101
|
+
## Files
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
~/.dijji/ 0700
|
|
105
|
+
credentials.json 0600 refresh token only; access tokens stay in memory
|
|
106
|
+
credentials.lock 0600 lets one dijji process at a time rotate or rewrite the credentials
|
|
107
|
+
projects/<projectId>.json 0600 subscription, selected devices, approved recipe fingerprints
|
|
108
|
+
workspaces/<projectId>/<subfolder>/ persistent clones (kept by unsubscribe unless --purge)
|
|
109
|
+
jobs/<jobRef>.p<publishSeq>/ one job: bundle/, out/ (JUnit, Maestro output, logcat), result.json
|
|
110
|
+
logs/<jobRef>.p<publishSeq>.log redacted job log
|
|
111
|
+
run.lock the running `dijji run`
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
A result the server did not accept stays in its job directory:
|
|
115
|
+
|
|
116
|
+
- `posted: false` (the server could not be reached): the unchanged result is posted again when `dijji run` starts and
|
|
117
|
+
whenever the server hands the same publication back; the job is not run a second time for it.
|
|
118
|
+
- rejected (the job is no longer this device's): the result is kept for inspection.
|
|
119
|
+
- refused outright (for example a contract mismatch after a server upgrade): `dijji run` stops, keeps the result
|
|
120
|
+
unposted and asks you to upgrade the CLI.
|
|
121
|
+
|
|
122
|
+
`dijji status` lists all of them.
|
|
123
|
+
|
|
124
|
+
## Git providers
|
|
125
|
+
|
|
126
|
+
GitHub, GitLab (including self-managed) and Bitbucket Cloud. The CLI recomputes each clone URL from the provider and
|
|
127
|
+
refuses a job whose URL does not match. A provider added on the server later is unknown to older CLIs, so adding one
|
|
128
|
+
must raise the server's minimum supported CLI version. The persistent clone keeps the clone URL in its own git config;
|
|
129
|
+
`url.<base>.insteadOf` rewrites in your git config (for example https to ssh) are applied by git and do not make the
|
|
130
|
+
clone look foreign.
|
|
131
|
+
|
|
132
|
+
## Known limits
|
|
133
|
+
|
|
134
|
+
- Submodules are not fetched; a build that needs them fails.
|
|
135
|
+
- Windows is not supported.
|
|
136
|
+
- Android artifacts must be a single `.apk`; AAB is not supported. iOS artifacts must be a simulator `.app`.
|
|
137
|
+
- The Gradle cache (`~/.gradle`) is shared with your other projects; there is no per-project cache isolation.
|
|
138
|
+
- A polluted workspace is reset with `dijji run --fresh` (it re-clones the project).
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pnpm --filter @dijji/cli typecheck
|
|
144
|
+
pnpm --filter @dijji/cli test
|
|
145
|
+
pnpm --filter @dijji/cli build && node packages/cli/dist/dijji.js --version
|
|
146
|
+
```
|