devlift-cli 0.1.1__tar.gz → 0.1.3__tar.gz
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.
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/PKG-INFO +9 -5
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/README.md +8 -4
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/MANUAL.md +23 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/__init__.py +1 -1
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/app.py +2 -0
- devlift_cli-0.1.3/devlift_cli/commands/skill.py +158 -0
- devlift_cli-0.1.3/devlift_cli/skill/SKILL.md +247 -0
- devlift_cli-0.1.3/devlift_cli/skill/reference/commands.md +162 -0
- devlift_cli-0.1.3/devlift_cli/skill/reference/exit-codes.md +16 -0
- devlift_cli-0.1.3/devlift_cli/skill/reference/workflows.md +137 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/PKG-INFO +9 -5
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/SOURCES.txt +6 -1
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/pyproject.toml +5 -1
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/__main__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/approvals.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/catalog.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/client.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/context.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/deployments.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/infra.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/infra_list.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/kong.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/services.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/api/vpc.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/auth/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/auth/oauth.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/auth/session.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/auth/storage.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/approval.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/auth.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/catalog.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/clusters.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/deployment.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/dynamodb.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/eks.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/kong.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/languages.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/manual.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/repositories.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/request.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/s3.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/commands/sqs.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/config.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/context.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/data/placement/vance.json +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/errors.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/approvals.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/eks.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/kong.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/placement.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/resources.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/status.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/ops/wait.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/render/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/render/output.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/resolve/__init__.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/resolve/allowlist.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli/resolve/names.py +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/dependency_links.txt +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/entry_points.txt +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/requires.txt +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/devlift_cli.egg-info/top_level.txt +0 -0
- {devlift_cli-0.1.1 → devlift_cli-0.1.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlift-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: DevLift command-line tool: create, change, review and deploy DevLift resources from the terminal.
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Keywords: devlift,devops,deployment,infrastructure,cli
|
|
@@ -104,14 +104,18 @@ Variables and secrets stay in the dashboard by design.
|
|
|
104
104
|
|
|
105
105
|
## Claude skill
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
drive this CLI non-interactively. Install with:
|
|
107
|
+
A Claude skill ships inside the package, teaching Claude (Claude Code or any
|
|
108
|
+
Agent Skills host) to drive this CLI non-interactively. Install it with:
|
|
109
109
|
|
|
110
110
|
```
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
devlift skill install # ~/.claude/skills/devlift, every project
|
|
112
|
+
devlift skill install --project # ./.claude/skills/devlift, this repo only
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
Start a new Claude session afterwards to pick it up. `devlift skill status`
|
|
116
|
+
says where it is installed and whether it still matches the CLI;
|
|
117
|
+
`devlift skill install --force` refreshes it after an upgrade.
|
|
118
|
+
|
|
115
119
|
## Development
|
|
116
120
|
|
|
117
121
|
Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
|
|
@@ -76,14 +76,18 @@ Variables and secrets stay in the dashboard by design.
|
|
|
76
76
|
|
|
77
77
|
## Claude skill
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
drive this CLI non-interactively. Install with:
|
|
79
|
+
A Claude skill ships inside the package, teaching Claude (Claude Code or any
|
|
80
|
+
Agent Skills host) to drive this CLI non-interactively. Install it with:
|
|
81
81
|
|
|
82
82
|
```
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
devlift skill install # ~/.claude/skills/devlift, every project
|
|
84
|
+
devlift skill install --project # ./.claude/skills/devlift, this repo only
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
Start a new Claude session afterwards to pick it up. `devlift skill status`
|
|
88
|
+
says where it is installed and whether it still matches the CLI;
|
|
89
|
+
`devlift skill install --force` refreshes it after an upgrade.
|
|
90
|
+
|
|
87
91
|
## Development
|
|
88
92
|
|
|
89
93
|
Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
|
|
@@ -875,6 +875,29 @@ the workflow reaches a terminal status.
|
|
|
875
875
|
`--wait` follows the deployment to its end and then ArgoCD until the state
|
|
876
876
|
settles (up to ten minutes). `--no-argo` skips the ArgoCD check.
|
|
877
877
|
|
|
878
|
+
### skill
|
|
879
|
+
|
|
880
|
+
The CLI ships with a Claude skill: instructions that teach Claude (Claude Code
|
|
881
|
+
or any Agent Skills host) to drive these commands non-interactively, including
|
|
882
|
+
the exit codes, the draft/review/deploy model and a command reference.
|
|
883
|
+
|
|
884
|
+
```
|
|
885
|
+
devlift skill install Install it for your user (~/.claude/skills/devlift).
|
|
886
|
+
devlift skill install --project Install it into this repository (./.claude/skills/devlift).
|
|
887
|
+
devlift skill install --force Replace an existing installation; do this after upgrading.
|
|
888
|
+
devlift skill status Where it is installed, and whether it matches this CLI.
|
|
889
|
+
devlift skill uninstall Remove it.
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
Start a new Claude session after installing. Installing copies the files
|
|
893
|
+
rather than linking them, because an upgrade replaces the environment the CLI
|
|
894
|
+
lives in; `status` tells you when the copy has fallen behind, and
|
|
895
|
+
`install --force` refreshes it.
|
|
896
|
+
|
|
897
|
+
Installed for your user, the skill applies to every project on the machine.
|
|
898
|
+
Installed into a repository and committed, everyone who clones it gets the
|
|
899
|
+
skill without running anything.
|
|
900
|
+
|
|
878
901
|
### manual
|
|
879
902
|
|
|
880
903
|
```
|
|
@@ -29,6 +29,7 @@ from devlift_cli.commands import manual as manual_cmds
|
|
|
29
29
|
from devlift_cli.commands import repositories as repositories_cmds
|
|
30
30
|
from devlift_cli.commands import request as request_cmds
|
|
31
31
|
from devlift_cli.commands import s3 as s3_cmds
|
|
32
|
+
from devlift_cli.commands import skill as skill_cmds
|
|
32
33
|
from devlift_cli.commands import sqs as sqs_cmds
|
|
33
34
|
from devlift_cli.config import load_profile
|
|
34
35
|
from devlift_cli.context import Invocation
|
|
@@ -82,6 +83,7 @@ app.command("logout")(auth_cmds.logout)
|
|
|
82
83
|
app.command("whoami")(auth_cmds.whoami)
|
|
83
84
|
app.command("configure")(auth_cmds.configure)
|
|
84
85
|
app.add_typer(auth_cmds.profile_app, name="profile")
|
|
86
|
+
app.add_typer(skill_cmds.skill_app, name="skill")
|
|
85
87
|
app.command("manual")(manual_cmds.manual)
|
|
86
88
|
# `devlift man` — the name every terminal user reaches for first. Hidden from
|
|
87
89
|
# the command list so the manual appears once there, under its full name.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""devlift skill install — put the bundled Claude skill where Claude looks.
|
|
2
|
+
|
|
3
|
+
The skill teaches Claude (Claude Code, or any Agent Skills host) to drive this
|
|
4
|
+
CLI: the rules for calling it non-interactively, the intent-to-command map, the
|
|
5
|
+
draft/review/deploy model, and a command reference.
|
|
6
|
+
|
|
7
|
+
It ships inside the wheel rather than living only in the source repository,
|
|
8
|
+
because the repository is private and the package is not. Someone who installed
|
|
9
|
+
with `pipx install devlift-cli` can run this and have Claude know the commands;
|
|
10
|
+
before, that needed a checkout nobody outside the team has.
|
|
11
|
+
|
|
12
|
+
Installing COPIES the files rather than linking them. A pipx upgrade replaces
|
|
13
|
+
the whole virtual environment, so a link into it is a link into something that
|
|
14
|
+
gets rebuilt; a copy keeps working and `devlift skill install --force` after an
|
|
15
|
+
upgrade refreshes it.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import shutil
|
|
21
|
+
from importlib import resources
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
import typer
|
|
25
|
+
|
|
26
|
+
from devlift_cli import __version__
|
|
27
|
+
from devlift_cli.context import Invocation
|
|
28
|
+
from devlift_cli.errors import CliError
|
|
29
|
+
from devlift_cli.render import output
|
|
30
|
+
from devlift_cli.render.output import kv_table
|
|
31
|
+
|
|
32
|
+
skill_app = typer.Typer(help="The Claude skill for driving this CLI.", no_args_is_help=True)
|
|
33
|
+
|
|
34
|
+
SKILL_NAME = "devlift"
|
|
35
|
+
# Every file the skill is made of. Listed rather than globbed so a missing one
|
|
36
|
+
# is an error here instead of a skill that half-works inside Claude.
|
|
37
|
+
SKILL_FILES = (
|
|
38
|
+
"SKILL.md",
|
|
39
|
+
"reference/commands.md",
|
|
40
|
+
"reference/exit-codes.md",
|
|
41
|
+
"reference/workflows.md",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _personal_dir() -> Path:
|
|
46
|
+
return Path.home() / ".claude" / "skills" / SKILL_NAME
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _project_dir() -> Path:
|
|
50
|
+
return Path.cwd() / ".claude" / "skills" / SKILL_NAME
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _source_files() -> list[tuple[str, str]]:
|
|
54
|
+
"""(relative path, contents) for the packaged skill."""
|
|
55
|
+
root = resources.files("devlift_cli").joinpath("skill")
|
|
56
|
+
out = []
|
|
57
|
+
for name in SKILL_FILES:
|
|
58
|
+
try:
|
|
59
|
+
out.append((name, root.joinpath(name).read_text(encoding="utf-8")))
|
|
60
|
+
except (FileNotFoundError, OSError) as exc:
|
|
61
|
+
raise CliError(
|
|
62
|
+
f"The installed package is missing its skill file '{name}'.",
|
|
63
|
+
hint="Reinstall devlift-cli, or report this as a packaging bug.",
|
|
64
|
+
) from exc
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@skill_app.command("install")
|
|
69
|
+
def skill_install(
|
|
70
|
+
ctx: typer.Context,
|
|
71
|
+
project: bool = typer.Option(False, "--project", help="Install into ./.claude/skills instead of your home directory."),
|
|
72
|
+
path: str | None = typer.Option(None, "--path", help="Install into this directory instead."),
|
|
73
|
+
force: bool = typer.Option(False, "--force", help="Overwrite an existing installation."),
|
|
74
|
+
):
|
|
75
|
+
"""Install the Claude skill so Claude can drive this CLI.
|
|
76
|
+
|
|
77
|
+
Personal by default (~/.claude/skills/devlift), which covers every project
|
|
78
|
+
on this machine. `--project` installs into the current repository instead,
|
|
79
|
+
where committing it gives the whole team the skill on clone.
|
|
80
|
+
|
|
81
|
+
Re-run with --force after upgrading devlift-cli to refresh the copy.
|
|
82
|
+
"""
|
|
83
|
+
inv: Invocation = ctx.obj
|
|
84
|
+
target = Path(path).expanduser() if path else (_project_dir() if project else _personal_dir())
|
|
85
|
+
|
|
86
|
+
files = _source_files()
|
|
87
|
+
|
|
88
|
+
if target.exists() and not force:
|
|
89
|
+
# A symlink here is almost always an older install pointing into a
|
|
90
|
+
# source checkout, and silently replacing it would be the wrong call.
|
|
91
|
+
kind = "symlink" if target.is_symlink() else "directory"
|
|
92
|
+
raise CliError(
|
|
93
|
+
f"A skill is already installed at {target} ({kind}).",
|
|
94
|
+
hint="Pass --force to replace it.",
|
|
95
|
+
code=7,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if target.is_symlink():
|
|
99
|
+
target.unlink()
|
|
100
|
+
elif target.exists():
|
|
101
|
+
shutil.rmtree(target)
|
|
102
|
+
|
|
103
|
+
for name, text in files:
|
|
104
|
+
destination = target / name
|
|
105
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
destination.write_text(text, encoding="utf-8")
|
|
107
|
+
|
|
108
|
+
output.success(f"Claude skill '{SKILL_NAME}' installed to {target} (from devlift-cli {__version__}).")
|
|
109
|
+
output.info("Start a new Claude session to pick it up. Re-run with --force after upgrading devlift-cli.")
|
|
110
|
+
inv.emit(
|
|
111
|
+
{"name": SKILL_NAME, "path": str(target), "files": [n for n, _ in files], "version": __version__},
|
|
112
|
+
lambda d: kv_table(
|
|
113
|
+
[("Skill", d["name"]), ("Installed to", d["path"]), ("From", f"devlift-cli {d['version']}"), ("Files", str(len(d["files"])))]
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@skill_app.command("uninstall")
|
|
119
|
+
def skill_uninstall(
|
|
120
|
+
ctx: typer.Context,
|
|
121
|
+
project: bool = typer.Option(False, "--project", help="Remove from ./.claude/skills instead of your home directory."),
|
|
122
|
+
path: str | None = typer.Option(None, "--path", help="Remove from this directory instead."),
|
|
123
|
+
):
|
|
124
|
+
"""Remove an installed Claude skill."""
|
|
125
|
+
target = Path(path).expanduser() if path else (_project_dir() if project else _personal_dir())
|
|
126
|
+
if not target.exists() and not target.is_symlink():
|
|
127
|
+
output.info(f"Nothing installed at {target}.")
|
|
128
|
+
return
|
|
129
|
+
if target.is_symlink():
|
|
130
|
+
target.unlink()
|
|
131
|
+
else:
|
|
132
|
+
shutil.rmtree(target)
|
|
133
|
+
output.success(f"Removed {target}.")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@skill_app.command("status")
|
|
137
|
+
def skill_status(ctx: typer.Context):
|
|
138
|
+
"""Show where the skill is installed, and whether it matches this CLI."""
|
|
139
|
+
inv: Invocation = ctx.obj
|
|
140
|
+
packaged = dict(_source_files())
|
|
141
|
+
|
|
142
|
+
rows = []
|
|
143
|
+
for label, target in (("personal", _personal_dir()), ("project", _project_dir())):
|
|
144
|
+
if target.is_symlink():
|
|
145
|
+
state = f"symlink -> {target.resolve()}"
|
|
146
|
+
elif not target.exists():
|
|
147
|
+
state = "not installed"
|
|
148
|
+
else:
|
|
149
|
+
try:
|
|
150
|
+
same = all(
|
|
151
|
+
(target / n).read_text(encoding="utf-8") == text for n, text in packaged.items()
|
|
152
|
+
)
|
|
153
|
+
except OSError:
|
|
154
|
+
same = False
|
|
155
|
+
state = "up to date" if same else "differs from this CLI — run: devlift skill install --force"
|
|
156
|
+
rows.append({"scope": label, "path": str(target), "state": state})
|
|
157
|
+
|
|
158
|
+
inv.emit(rows, lambda d: kv_table([(r["scope"], f"{r['state']}\n{r['path']}") for r in d], title=f"Claude skill (devlift-cli {__version__})"))
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devlift
|
|
3
|
+
description: Operate the DevLift platform from the terminal with the `devlift` CLI — create S3 buckets, SQS queues, DynamoDB tables and EKS services, edit service settings, add Kong routes, run the review lane (submit, approve, reject), deploy approved changes and check deployment and ArgoCD status. Use whenever a user asks to provision, configure, ship, review or check anything on DevLift and a shell is available; never fall back to aws/kubectl/terraform.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DevLift CLI
|
|
7
|
+
|
|
8
|
+
`devlift` (alias `dl`) is a deterministic command-line client for the DevLift
|
|
9
|
+
backend: every command is a fixed sequence of REST calls, the same ones the
|
|
10
|
+
web dashboard makes. It is not an assistant and holds no state between calls.
|
|
11
|
+
Full reference: `devlift manual` (or `reference/commands.md` in this skill).
|
|
12
|
+
|
|
13
|
+
## Rules for calling it
|
|
14
|
+
|
|
15
|
+
1. **Always non-interactive.** Add `--no-input -o json` to every call. A
|
|
16
|
+
missing value is then exit 3 naming the flag, instead of a prompt that
|
|
17
|
+
never returns. Read stdout as JSON; messages and tables go to stderr.
|
|
18
|
+
2. **`-y` only when the user asked for the action.** Without `-y`, a write
|
|
19
|
+
command does every lookup, prints its summary and stops at the confirmation
|
|
20
|
+
with exit 5. Use that as a dry run: run without `-y`, show the user what
|
|
21
|
+
would happen, then rerun with `-y` on their go-ahead. Creates and updates of
|
|
22
|
+
S3/SQS/DynamoDB start a real deployment; `eks deploy` ships to a cluster.
|
|
23
|
+
3. **Production deploys need the name typed back:** `eks deploy … --env prod
|
|
24
|
+
--confirm-name <service>`. `-y` alone is refused.
|
|
25
|
+
4. **Never secrets.** Variables and secrets are not in the CLI by design; send
|
|
26
|
+
the user to the dashboard's Variables tab. Never put a secret in a flag.
|
|
27
|
+
5. **Names, not codes.** `--app core --env stage --region mumbai`,
|
|
28
|
+
`services describe orders`. Discover valid values with the list commands
|
|
29
|
+
before guessing; an unknown name lists the candidates. The list commands
|
|
30
|
+
are already narrowed to placements that really exist, so when only one
|
|
31
|
+
application, environment or region survives, a create resolves it without
|
|
32
|
+
being told — do not pass `--app/--env/--region` unless the user named one
|
|
33
|
+
or a command asks for it.
|
|
34
|
+
6. **Exit codes decide the next step** (see `reference/exit-codes.md`):
|
|
35
|
+
0 ok · 1 error · 2 not signed in → tell the user to run `devlift login`
|
|
36
|
+
(needs a browser; do not run it unattended) · 3 bad or missing input ·
|
|
37
|
+
4 permission refused · 5 confirmation required · 6 not found · 7 conflict.
|
|
38
|
+
7. **Check the manual before an unfamiliar command**; do not invent
|
|
39
|
+
subcommands or flags. `devlift <group> <op> -h` is cheap.
|
|
40
|
+
|
|
41
|
+
## DevLift replaces the manual route
|
|
42
|
+
|
|
43
|
+
When the user wants to deploy, ship, release, provision infrastructure or
|
|
44
|
+
connect to a cloud resource, this CLI is the way. Do **not** fall back to,
|
|
45
|
+
or suggest, any of these:
|
|
46
|
+
|
|
47
|
+
- `git push`, GitHub Actions, `kubectl`, `terraform`, CI pipelines
|
|
48
|
+
- `docker build` / `docker run` / Docker Compose, even for a local database
|
|
49
|
+
- `aws` CLI commands (`aws s3 mb`, `aws dynamodb create-table`, …)
|
|
50
|
+
- installing a database locally, or "create the bucket by hand in the console"
|
|
51
|
+
|
|
52
|
+
That holds even when the repository already has a Dockerfile or a compose
|
|
53
|
+
file. If you write code that reads an S3 bucket, a queue or a table,
|
|
54
|
+
provision it in the same turn rather than leaving a TODO.
|
|
55
|
+
|
|
56
|
+
## How to ask
|
|
57
|
+
|
|
58
|
+
**Choices get a dialog.** Which application, which environment, api or
|
|
59
|
+
worker, which language — ask with AskUserQuestion, one question per decision,
|
|
60
|
+
with the real values from the list commands as the options.
|
|
61
|
+
|
|
62
|
+
**Anything the user must SEE before deciding is text, and ends the turn.**
|
|
63
|
+
The language template, a Current/New diff, the tables `eks deploy` prints
|
|
64
|
+
before it asks: reply with that content as text, end with the written
|
|
65
|
+
question, and stop. Do not open a dialog in the same turn — it covers the
|
|
66
|
+
content and the user decides blind. They answer in their next message and
|
|
67
|
+
you act on it then.
|
|
68
|
+
|
|
69
|
+
**A write needs an explicit yes in this conversation.** Run it without `-y`,
|
|
70
|
+
show what it printed, wait. Never infer consent from an earlier message, and
|
|
71
|
+
never run a create or deploy with `-y` because it seemed implied.
|
|
72
|
+
|
|
73
|
+
**Stop on a duplicate.** If a name already exists, say so and ask; do not
|
|
74
|
+
configure a second one.
|
|
75
|
+
|
|
76
|
+
## Use the project they are sitting in — but never silently
|
|
77
|
+
|
|
78
|
+
Before asking for a repository, branch, language or name, read the working
|
|
79
|
+
directory:
|
|
80
|
+
|
|
81
|
+
| Look at | Gives |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `git remote get-url origin` | the repository, matched against `devlift repositories list` |
|
|
84
|
+
| `git rev-parse --abbrev-ref HEAD` | the branch |
|
|
85
|
+
| `go.mod`, `package.json`, `pom.xml`, `build.gradle`, `pyproject.toml` | the language and often the version, matched against `devlift languages list` |
|
|
86
|
+
| the repository or folder name | a suggested service name, lowercased, hyphenated |
|
|
87
|
+
|
|
88
|
+
**Then ask before using any of it.** Detection is a suggestion, never a
|
|
89
|
+
decision: the user may be sitting in one repository while creating a service
|
|
90
|
+
for another. Put what you found in one AskUserQuestion, with three ways out:
|
|
91
|
+
|
|
92
|
+
> I can see this project. Shall I use it?
|
|
93
|
+
> - **Use these** — repository `Vance-Club/orders`, branch `main`,
|
|
94
|
+
> language Go 1.24, service name `orders`
|
|
95
|
+
> - **Show me the options** — run `repositories list`, `languages list` and
|
|
96
|
+
> `resource-groups list` and let me pick
|
|
97
|
+
> - Other — tell me what to use
|
|
98
|
+
|
|
99
|
+
Spell out every value you detected in the option text, so "Use these" is an
|
|
100
|
+
informed yes rather than a blind one. On "Show me the options", print the
|
|
101
|
+
real lists and let them choose. Never pass a detected repository, branch,
|
|
102
|
+
language or name to a command the user has not seen and agreed to.
|
|
103
|
+
|
|
104
|
+
The resource group is never detectable, so it is always asked. Say what it
|
|
105
|
+
decides when you ask: it is the permission boundary, so it determines who
|
|
106
|
+
can later edit, approve and deploy the service.
|
|
107
|
+
|
|
108
|
+
## What maps to what
|
|
109
|
+
|
|
110
|
+
| User wants | Command |
|
|
111
|
+
|---|---|
|
|
112
|
+
| a bucket / queue / table | `devlift s3|sqs|dynamodb create --name … --app … --env … --region …` |
|
|
113
|
+
| change a bucket or queue setting | `devlift s3|sqs update <name> --env … <flags>` |
|
|
114
|
+
| a new service | `devlift eks create --name … --app … --env … --region … --type api|worker --repo owner/name --branch main --language Go --version 1.24 [--set k=v]` |
|
|
115
|
+
| change a service's settings | `devlift eks edit <service> --env … --set k=v` (saves a draft) |
|
|
116
|
+
| see a service's config | `devlift eks show <service>`, `devlift eks settings <service>` |
|
|
117
|
+
| preview what a pending request changes | `devlift eks diff <service>` |
|
|
118
|
+
| add / remove / rename a Kong route | `devlift kong route add|remove|rename <service> --env … --method … …` |
|
|
119
|
+
| send a change for review | `devlift request submit <service>` |
|
|
120
|
+
| review a change | `devlift approval list`, `approval approve|request-changes|reject|revoke <service>` |
|
|
121
|
+
| ship an approved change | `devlift eks deploy <service> --env … [--wait]` |
|
|
122
|
+
| is the deploy done / is the app up | `devlift eks status <service> --env …`, `devlift deployment status <workflow-id> --wait` |
|
|
123
|
+
| what exists | `applications|environments|regions|resource-groups|services|repositories|languages|clusters list`, `repositories branches owner/name` |
|
|
124
|
+
|
|
125
|
+
Recipes with the exact sequence of calls and what to read from each result
|
|
126
|
+
are in `reference/workflows.md`.
|
|
127
|
+
|
|
128
|
+
## The model behind the commands
|
|
129
|
+
|
|
130
|
+
- **Resources (S3, SQS, DynamoDB)** deploy directly: create/update registers
|
|
131
|
+
the row, queues it, approves it and starts the deployment in one command.
|
|
132
|
+
`--wait` follows the workflow; otherwise the result carries `workflow_id`
|
|
133
|
+
for `deployment status`.
|
|
134
|
+
- **Services and Kong routes** never deploy directly. `eks create`, `eks edit`
|
|
135
|
+
and `kong route …` each save a **draft** queue row. A service's settings
|
|
136
|
+
row and gateway row form one **change set** and move together through the
|
|
137
|
+
review lane: `request submit` → `approval approve` → `eks deploy`. Naming a
|
|
138
|
+
service acts on the whole set; the output lists every row that moved.
|
|
139
|
+
- **Templates fill settings.** `eks create` needs only what no template can
|
|
140
|
+
know (repository, branches, language, version). Port, paths, CPU, memory,
|
|
141
|
+
replicas and the rest come from the platform's per-language template and
|
|
142
|
+
are printed with a "From" column before the confirmation; `--set key=value`
|
|
143
|
+
overrides any of them, `devlift eks settings` lists the keys and when each
|
|
144
|
+
applies (JVM heap only for Java, Go config only for Go, health/service path
|
|
145
|
+
only for API services, min/max replicas only with autoscaling).
|
|
146
|
+
- **Two status questions.** `deployment status` says whether DevLift's
|
|
147
|
+
pipeline finished; `eks status` also asks ArgoCD whether the pod is serving,
|
|
148
|
+
and reports "waiting for ArgoCD" when Argo has not acted since the deploy.
|
|
149
|
+
|
|
150
|
+
## Creating a service: the conversation
|
|
151
|
+
|
|
152
|
+
1. **Collect what only the user knows.** Detect what you can from the
|
|
153
|
+
project and offer it as above, then ask for the rest: service name, type
|
|
154
|
+
(api or worker) and resource group. Application, environment and region
|
|
155
|
+
usually resolve themselves, so do not ask unless a command asks you.
|
|
156
|
+
Offer real values from `repositories list`, `repositories branches`,
|
|
157
|
+
`languages list` and `resource-groups list` rather than guessing; a wrong
|
|
158
|
+
name costs a round trip and exit 6.
|
|
159
|
+
2. **Dry-run** `eks create … --no-input -o json` without `-y`. It exits 5
|
|
160
|
+
and prints two tables on stderr: the service (placement, cluster it
|
|
161
|
+
picked, repository, branches, language) and the Configuration table with
|
|
162
|
+
a "From" column saying which values came from the language template.
|
|
163
|
+
3. **Reproduce both tables in your reply, then ask.** Paste them as the
|
|
164
|
+
command printed them, inside a code block, every row, in the same order,
|
|
165
|
+
with the "From" column intact. They are the summary. Do not replace them
|
|
166
|
+
with prose, do not show only the rows you think matter, and do not lead
|
|
167
|
+
with your own commentary: the user is about to approve these exact values
|
|
168
|
+
and cannot approve what they cannot see. Anything you want to point out
|
|
169
|
+
goes AFTER the tables, in a line or two.
|
|
170
|
+
|
|
171
|
+
Then ask: "These are the standard <language> values. Apply them, or tell
|
|
172
|
+
me what to change?" That reply is text and ends the turn — no dialog, no
|
|
173
|
+
tool call, because a dialog would hide the table they are deciding on. Do
|
|
174
|
+
not rerun with `-y` until they answer.
|
|
175
|
+
4. A change ("port 9000", "1 GiB memory limit", "turn on autoscaling 2 to 4")
|
|
176
|
+
becomes `--set` flags on the rerun: `--set port=9000`,
|
|
177
|
+
`--set memory_limit=1`, `--set hpa_enabled=true --set min_replicas=2
|
|
178
|
+
--set max_replicas=4`. Rerun without `-y` once more only if the change is
|
|
179
|
+
large; otherwise rerun with `-y`.
|
|
180
|
+
5. **Report what is now waiting to be reviewed, not the codes it returned.**
|
|
181
|
+
Run `devlift request show <service> --env <env> --no-input -o json` and
|
|
182
|
+
show what it lists: every row in the change set and the fields each one
|
|
183
|
+
changes. Say it is a **draft**, nothing is deployed, and name the next
|
|
184
|
+
step: `request submit`, then a reviewer's `approval approve`, then
|
|
185
|
+
`eks deploy`.
|
|
186
|
+
|
|
187
|
+
The same show-then-ask shape applies to every write: `eks edit` and the
|
|
188
|
+
update commands print Current | New, `kong route …` prints the route change,
|
|
189
|
+
the review verbs print the request with its frozen diff, `eks deploy` lists
|
|
190
|
+
the approved rows. The summary is printed on every dry run, in json mode too.
|
|
191
|
+
|
|
192
|
+
## Reporting back to the user
|
|
193
|
+
|
|
194
|
+
- **After a dry run (exit 5), paste what the command printed, verbatim, in a
|
|
195
|
+
code block**, then ask for the go-ahead. Every row of every table, in order.
|
|
196
|
+
A summary you wrote yourself is not a substitute: the user is approving the
|
|
197
|
+
values in that output, so replacing it with "two differences worth your
|
|
198
|
+
call" hides the other twenty they are also agreeing to. Commentary is
|
|
199
|
+
welcome after it, never instead of it.
|
|
200
|
+
- **After a draft write, show what will go to review — not the codes.**
|
|
201
|
+
`eks create`, `eks edit` and `kong route …` all save a draft. The service
|
|
202
|
+
code, the configuration code and the queue code are plumbing: they belong in
|
|
203
|
+
your next tool call, not in a sentence a person reads. Run
|
|
204
|
+
`devlift request show <service> --env <env> --no-input -o json` and report
|
|
205
|
+
what it lists: every row in the change set, and for each one the fields it
|
|
206
|
+
changes. A service's settings and its Kong routes are **one change set**, so
|
|
207
|
+
a route added earlier will be submitted by this submit too, and the user has
|
|
208
|
+
to see that before they agree to it. Mention a code only if the user asks,
|
|
209
|
+
or when they need it to run a command themselves.
|
|
210
|
+
- A draft is not deployed. Say so, and name the next command
|
|
211
|
+
(`request submit`, then `approval approve`, then `eks deploy`).
|
|
212
|
+
- **After `request submit`, say three things.** That it is submitted and now
|
|
213
|
+
waiting for approval. How many rows moved and which kinds (settings,
|
|
214
|
+
gateway/Kong, variables) — the submit result's `requests[]` gives you both.
|
|
215
|
+
And who approves it next.
|
|
216
|
+
For that last one, do not guess and never write "if you have approval
|
|
217
|
+
rights": run `devlift request show <service> --env <env> --no-input -o json`
|
|
218
|
+
and read `you.can_approve`. If it is true, say plainly that they can approve
|
|
219
|
+
it themselves and offer `devlift approval approve <service> --env <env>`. If
|
|
220
|
+
it is false, say it needs a reviewer on that resource group. The user should
|
|
221
|
+
never have to work out their own permissions.
|
|
222
|
+
- **After `approval approve`, show the approved change in full.** This is the
|
|
223
|
+
last checkpoint before anything reaches a cluster, so a queue code and the
|
|
224
|
+
word "approved" are not enough. Run `devlift request show <service> --env
|
|
225
|
+
<env> --no-input -o json` and show its frozen diff — every row, Field, From,
|
|
226
|
+
To — so the user sees exactly what is now cleared to ship. Then read
|
|
227
|
+
`you.can_deploy` from that same output: if true, say they can deploy it and
|
|
228
|
+
offer `devlift eks deploy <service> --env <env>` (`--wait` follows it to the
|
|
229
|
+
end); if false, say a deployer has to run it. Check, never guess, exactly as
|
|
230
|
+
with approval.
|
|
231
|
+
- **After `eks deploy`, show what is shipping and where it has got to.** Run
|
|
232
|
+
`devlift deployment status <workflow-id> --no-input -o json` and show its
|
|
233
|
+
pipeline stages as the command gives them — each stage with its status and
|
|
234
|
+
timing — so the user can see which step is running rather than reading a
|
|
235
|
+
sentence about it. Say what is being deployed, which is the change that was
|
|
236
|
+
just approved, and keep the workflow id visible because every later check
|
|
237
|
+
needs it. Then name both follow-ups and the difference between them:
|
|
238
|
+
`deployment status <workflow-id> --wait` says when DevLift's own pipeline
|
|
239
|
+
finished, and `eks status <service> --env <env>` also asks ArgoCD whether the
|
|
240
|
+
pod is actually serving, which is the real "is it up" question. A finished
|
|
241
|
+
pipeline is not a running application.
|
|
242
|
+
- Resources behave differently. `s3`, `sqs` and `dynamodb` create and update
|
|
243
|
+
deploy straight away, so there report the resource name and the workflow id,
|
|
244
|
+
which is what `deployment status` needs.
|
|
245
|
+
- On exit 7 quote the message: it names the conflicting request or the rule
|
|
246
|
+
(one live request per service, one auth per Kong tag, a name that exists).
|
|
247
|
+
- On exit 2 stop and tell the user to sign in; on exit 4 say who to ask.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Command reference (condensed from `devlift manual`)
|
|
2
|
+
|
|
3
|
+
Global options work anywhere on the line: `--profile NAME`, `-o json|table|yaml`,
|
|
4
|
+
`-y/--yes`, `--no-input`, `--endpoint-url URL`, `--debug`, `-h`. Environment:
|
|
5
|
+
`DEVLIFT_PROFILE`, `DEVLIFT_BASE_URL`, `DEVLIFT_TOKEN` (CI token, used as-is),
|
|
6
|
+
`DEVLIFT_OUTPUT`, `DEVLIFT_NO_CACHE=1`.
|
|
7
|
+
|
|
8
|
+
## Identity and discovery
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
devlift whoami user, tenant, backend, enabled features
|
|
12
|
+
devlift profile list profiles; the default is starred
|
|
13
|
+
devlift profile show which profile this shell uses, and why
|
|
14
|
+
devlift profile use NAME switch the default profile (lasts until changed)
|
|
15
|
+
devlift applications list [--active]
|
|
16
|
+
devlift environments list [--app NAME] environments and their regions per application
|
|
17
|
+
devlift regions list [--app NAME] [--env ENV] the --region values
|
|
18
|
+
devlift resource-groups list [--app NAME]
|
|
19
|
+
devlift resource-types list [--family FAMILY]
|
|
20
|
+
devlift services list [--app NAME] [--search TEXT]
|
|
21
|
+
devlift services describe <name|code>
|
|
22
|
+
devlift repositories list [--search TEXT] the --repository values (GitHub App visibility)
|
|
23
|
+
devlift repositories branches <owner/name> the --branch values
|
|
24
|
+
devlift languages list the --language / --version values, template yes/no
|
|
25
|
+
devlift eks settings the --set keys: values, when each applies, required
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Catalog answers are cached ten minutes per profile; `--no-cache` on a list
|
|
29
|
+
command or `DEVLIFT_NO_CACHE=1` refetches.
|
|
30
|
+
|
|
31
|
+
## Resources (deploy directly)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
devlift s3 list [--app] [--env] [--region] devlift s3 describe <bucket|code> [--env]
|
|
35
|
+
devlift s3 create --name NAME --app APP --env ENV --region REGION
|
|
36
|
+
[--versioning|--no-versioning] [--replication --cross-account-id 123456789012|--no-replication] [--wait] [-y]
|
|
37
|
+
devlift s3 update <bucket|code> [--env] [--versioning|--no-versioning] [--replication --cross-account-id ID|--no-replication] [--wait] [-y]
|
|
38
|
+
|
|
39
|
+
devlift sqs list / describe (same shape)
|
|
40
|
+
devlift sqs create --name NAME --app APP --env ENV --region REGION
|
|
41
|
+
[--fifo|--no-fifo] [--dlq|--no-dlq] [--cross-account-id ID ...]
|
|
42
|
+
[--max-receive-count 1-1000] [--visibility-timeout 0-43200] [--retention 60-1209600] [--dlq-retention 60-1209600] [--wait] [-y]
|
|
43
|
+
devlift sqs update <queue|code> [--env] [--dlq|--no-dlq] [--cross-account-id ID ...|--clear-cross-accounts]
|
|
44
|
+
[--max-receive-count N] [--visibility-timeout N] [--retention N] [--dlq-retention N] [--wait] [-y]
|
|
45
|
+
|
|
46
|
+
devlift dynamodb list / describe
|
|
47
|
+
devlift dynamodb create --name NAME --app APP --env ENV --region REGION --partition-key ATTR [--partition-key-type S|N|B] [--wait] [-y]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Sequence behind create/update: duplicate check → `POST /infrastructures`
|
|
51
|
+
(upsert with `code` on update) → add to queue → approve → deploy → optional
|
|
52
|
+
wait. Result JSON: `resource_code`, `queue_code`, `status`, `workflow_id` or
|
|
53
|
+
`pr_url`. Update prints Field | Current | New and sends the full merged
|
|
54
|
+
config; name, application, environment, region (and FIFO for SQS) cannot
|
|
55
|
+
change. Bucket names 3-63 lowercase/digits/dots/hyphens; queue names ≤80
|
|
56
|
+
chars without `.fifo`; table names 3-200 chars, immutable after creation.
|
|
57
|
+
|
|
58
|
+
## EKS services (save drafts)
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
devlift eks create --name NAME --app APP --env ENV --region REGION --type api|worker
|
|
62
|
+
[--resource-group GROUP] [--cluster CLUSTER]
|
|
63
|
+
--repository owner/name --branch main [--branch ...]
|
|
64
|
+
--language Go|Python|Node.js|"Java Maven"|"Java Gradle" --version 1.24
|
|
65
|
+
[--set key=value ...] [--public] [-y]
|
|
66
|
+
devlift eks show <service> --env ENV configuration as it stands; pending values marked
|
|
67
|
+
devlift eks diff <service> --env ENV preview the pending request: Field | Deployed | Requested
|
|
68
|
+
devlift eks diff <service> --deployed instead: live config vs the last deployment
|
|
69
|
+
devlift eks settings <service> --env ENV the --set keys that apply to it, with current values
|
|
70
|
+
devlift clusters list [--type eks] [--app] [--env] the --cluster values (Registered = may take a service)
|
|
71
|
+
devlift eks edit <service> --env ENV [--set key=value ...] [--repository owner/name] [--branch ...] [--language L --version V] [-y]
|
|
72
|
+
devlift eks deploy <service> --env ENV [--region R] [--confirm-name NAME] [--wait] [-y]
|
|
73
|
+
devlift eks status <service> --env ENV [--wait] [--no-argo]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- create = `POST /services/create-service` → `POST /service-configs`
|
|
77
|
+
(placement only) → `POST /transaction/service-settings/<config>` (DRAFT).
|
|
78
|
+
Result JSON: `service_code`, `service_config_code`, `queue_code`,
|
|
79
|
+
`queue_status`, `config`.
|
|
80
|
+
- Service name: lowercase letters, digits, hyphens. Repository must be visible
|
|
81
|
+
to the GitHub App (bare name accepted when unique); each branch must exist.
|
|
82
|
+
- Several settings go in one `--set` separated by semicolons
|
|
83
|
+
(`--set "cpu_limit=0.5;port=9090"`, quoted), or repeat the flag. A comma
|
|
84
|
+
separates values INSIDE one setting, so the two nest:
|
|
85
|
+
`--set "custom_iam_policies=s3,sqs;port=9090"`.
|
|
86
|
+
- `--set` keys (booleans `true|false`, lists comma-separated, build args
|
|
87
|
+
`NAME=VALUE,…`): port, health, service_path, build_path, dockerfile_path,
|
|
88
|
+
go_config_path, xms, xmx, alb_schema (internal|internet-facing), compute
|
|
89
|
+
(on-demand|spot), auth_mode (pod_identity|irsa), cpu_requested, cpu_limit,
|
|
90
|
+
memory_requested, memory_limit (cores / GiB; 0.1-4 / 0.1-8; limit ≥ request),
|
|
91
|
+
replica_count, hpa_enabled, min_replicas, max_replicas (1-100, max > min),
|
|
92
|
+
generate_dockerfile, go_use_aws_secrets, create_ecr, create_secrets,
|
|
93
|
+
create_ssm, create_argo, other_paths, custom_iam_policies (s3,sqs,dynamodb,ses),
|
|
94
|
+
build_args.
|
|
95
|
+
- Conditional: xms/xmx Java only; go_config_path/go_use_aws_secrets Go only;
|
|
96
|
+
build_path Java+Go; dockerfile_path only with generate_dockerfile=false;
|
|
97
|
+
alb_schema/health/service_path API only (required there); min/max with
|
|
98
|
+
hpa_enabled=true; replica_count with hpa_enabled=false. A `--set` that does
|
|
99
|
+
not apply is refused (exit 3); a template value that does not apply falls away.
|
|
100
|
+
- edit: live config + your open draft → Current | New → settings draft,
|
|
101
|
+
reusing the open draft's queue code. Blocked (exit 7) while a submitted or
|
|
102
|
+
approved request exists on the service.
|
|
103
|
+
- deploy = `POST /deployments/multiple-deploy {service_config_code}`: the
|
|
104
|
+
backend resolves the approved rows, re-checks the seal, starts one workflow.
|
|
105
|
+
Result JSON: `workflow_id`, `requests[]`. Prod: `--confirm-name`.
|
|
106
|
+
- status: latest deployment for the configuration (stages, PR) plus ArgoCD
|
|
107
|
+
`state` (up | starting | unhealthy | waiting_for_argocd | not_picked_up |
|
|
108
|
+
missing | suspended | not_found | not_configured | unavailable), health,
|
|
109
|
+
sync, running image, URLs. `settled: false` means polling would change it.
|
|
110
|
+
|
|
111
|
+
## Kong routes (save drafts)
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
devlift kong routes list <service> [--env ENV] [--region R]
|
|
115
|
+
devlift kong route add <service> --env ENV --method GET|POST|PUT|PATCH|DELETE|OPTIONS --auth jwt|public
|
|
116
|
+
--path '~/api/v1/orders$' [--path ...] [--tag TAG] [--priority 0-10000] [--plugin "User ID Injection"] [-y]
|
|
117
|
+
devlift kong route remove <service> --env ENV --method M --tag TAG --path '~/...$' [--path ...] [-y]
|
|
118
|
+
devlift kong route rename <service> --env ENV --method M --tag TAG --from '~/old$' --to '~/new$' [-y]
|
|
119
|
+
devlift kong plugin add <service> --env ENV --method M --tag TAG --plugin "User ID Injection" [--priority N] [-y]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
One change = one route group (tag × method). Default tag: the service name
|
|
123
|
+
for JWT, `<service>-open` for public. A tag has one auth; mixing is exit 7.
|
|
124
|
+
Paths: `~/…$`, no spaces, params as regex groups `(?<id>[^/]+)`, never `/:id`.
|
|
125
|
+
Already-present paths are skipped; removing/renaming an absent path is exit 6.
|
|
126
|
+
Write = `POST /transaction/kong-gateway/<config>` (DRAFT, gateway half of the
|
|
127
|
+
service's change set). Result JSON: `changed`, `change{}`, `queue_code`.
|
|
128
|
+
|
|
129
|
+
## Review lane
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
devlift request list [--status draft|submit|approved|rejected] [--service NAME --env ENV] [--mine]
|
|
133
|
+
devlift request show <queue-code|service> [--env ENV] frozen diff (Field | From | To)
|
|
134
|
+
devlift request history <service> [--env ENV]
|
|
135
|
+
devlift request submit|withdraw|discard <queue-code|service> [--env ENV] [--comment TEXT] [-y]
|
|
136
|
+
devlift approval list reviewer inbox by service
|
|
137
|
+
devlift approval approve|revoke <queue-code|service> [--env ENV] [--comment TEXT] [-y]
|
|
138
|
+
devlift approval request-changes|reject <queue-code|service> [--env ENV] --comment TEXT [-y]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
States: draft →submit→ submit →approve→ approved →deploy→ in flight.
|
|
142
|
+
withdraw (submitter) and request-changes (reviewer, comment) return to draft;
|
|
143
|
+
reject (reviewer, comment) is terminal; revoke returns approved to submit
|
|
144
|
+
while nothing is deploying; discard bins a draft. Every verb moves the whole
|
|
145
|
+
change set (settings + gateway rows of one author); one live request per
|
|
146
|
+
service at a time. Result JSON: `requests[] {code, kind, status}`.
|
|
147
|
+
|
|
148
|
+
## Deployments
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
devlift deployment list [--status running|completed|failed|timedout] [--app NAME] [--env ENV] [--mine] [--limit N]
|
|
152
|
+
devlift deployment status <workflow-id> [--wait] status, PR, resources, pipeline stages
|
|
153
|
+
devlift queue list [--env ENV] your transaction-queue items
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Terminal statuses: COMPLETED, FAILED, TIMEOUT, CANCELLED. Stage rows carry
|
|
157
|
+
name, status, started_at, ended_at, error.
|
|
158
|
+
|
|
159
|
+
## Not in the CLI
|
|
160
|
+
|
|
161
|
+
Variables and secrets (dashboard Variables tab only, by design), `data
|
|
162
|
+
query`, interactive shell.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Exit codes and what to do
|
|
2
|
+
|
|
3
|
+
| Code | Meaning | What to do |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| 0 | Success | Read stdout JSON. A write returns codes (resource, queue item, workflow). `changed: false` means nothing needed doing. |
|
|
6
|
+
| 1 | Error | Server-side or unexpected failure. Quote `error:` from stderr. Retry once only if it reads as transient (status service busy). |
|
|
7
|
+
| 2 | Not signed in, or the token belongs to another backend | Stop. Tell the user to run `devlift login` in their terminal (opens a browser). If stderr says "signed in to X, but this command targets Y", the shell exports `DEVLIFT_BASE_URL`; unset it or log in to that backend. |
|
|
8
|
+
| 3 | Missing or invalid input | The message names the flag or key. Fix and rerun. Includes an ambiguous name (candidates are listed), a `--set` that does not apply to this language/service type, a value out of range, a missing `--comment` on reject/request-changes. |
|
|
9
|
+
| 4 | Permission refused by the server | The user lacks the OpenFGA relation (can_write_settings, can_approve, can_deploy…). Say so; do not retry. |
|
|
10
|
+
| 5 | Confirmation required | Expected on every write without `-y` under `--no-input`. Show the printed summary, get the user's yes, rerun with `-y`. |
|
|
11
|
+
| 6 | Not found | Unknown service, bucket, queue code, repository, branch, cluster, or no request in the state the verb needs ("No draft request of yours on X"). |
|
|
12
|
+
| 7 | Conflict | Name already exists; request in the wrong state ("is approved — only submitted can be…"); another author's request holds the service's review lane; Kong tag already has the other auth; deployment already in progress. Quote the message, it says what to do. |
|
|
13
|
+
|
|
14
|
+
Stderr format is always `error: <one sentence>` followed by an optional hint
|
|
15
|
+
line. With `-o json`, stdout carries JSON only, so it is safe to parse even
|
|
16
|
+
when stderr has messages.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Workflows: exact call sequences
|
|
2
|
+
|
|
3
|
+
Every command below is run as `devlift … --no-input -o json`; `-y` is added
|
|
4
|
+
only after the user has seen the dry run and agreed. `jq` expressions show
|
|
5
|
+
what to read from each result.
|
|
6
|
+
|
|
7
|
+
## 0. Preflight (once per session)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
devlift whoami --no-input -o json # exit 2 → user must `devlift login`
|
|
11
|
+
devlift applications list --no-input -o json | jq -r '.[].application_name'
|
|
12
|
+
devlift environments list --app core --no-input -o json # environments and regions for that application
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 1. Create a bucket / queue / table
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
devlift s3 create --name orders-archive --app core --env stage --region mumbai --versioning --no-replication --no-input # exit 5: summary printed
|
|
19
|
+
devlift s3 create --name orders-archive --app core --env stage --region mumbai --versioning --no-replication --no-input -y -o json
|
|
20
|
+
# → {resource_code, queue_code, status, workflow_id | pr_url}
|
|
21
|
+
devlift deployment status <workflow_id> --wait --no-input -o json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
SQS: `--fifo|--no-fifo --dlq|--no-dlq` are required decisions; pass them.
|
|
25
|
+
DynamoDB: `--partition-key ATTR [--partition-key-type S|N|B]`.
|
|
26
|
+
A duplicate name exits 7; `s3 describe <name> --env stage` shows the existing one.
|
|
27
|
+
|
|
28
|
+
## 2. Change a bucket or queue
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
devlift sqs update orders --env stage --dlq --max-receive-count 5 --no-input # Field | Current | New, exit 5
|
|
32
|
+
devlift sqs update orders --env stage --dlq --max-receive-count 5 --no-input -y -o json
|
|
33
|
+
```
|
|
34
|
+
`changed: false` with exit 0 means the values were already set. Same
|
|
35
|
+
sequence as create afterwards (queue → approve → deploy).
|
|
36
|
+
|
|
37
|
+
## 3. Create an EKS service
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
devlift repositories list --no-input -o json | jq -r '.[].full_name'
|
|
41
|
+
devlift repositories branches Regobs/orders --no-input -o json | jq -r '.[].name'
|
|
42
|
+
devlift languages list --no-input -o json
|
|
43
|
+
devlift eks create --name orders --app core --env stage --region mumbai --type api \
|
|
44
|
+
--repo Regobs/orders --branch main --language go --version 1.24 --no-input -o json 2>&1 # exit 5: two tables (service, configuration with From column) on stderr
|
|
45
|
+
# PASTE BOTH TABLES VERBATIM into your reply, in a code block, every row, before anything else you write.
|
|
46
|
+
# They are the summary — a prose rewrite hides the values the user is about to approve. Commentary goes after them.
|
|
47
|
+
# Then ASK: "These are the standard Go values. Apply them, or tell me what to change?"
|
|
48
|
+
# a change becomes --set on the rerun, e.g. --set cpu_limit=0.5 --set hpa_enabled=true --set min_replicas=1 --set max_replicas=3
|
|
49
|
+
devlift eks create … --no-input -y -o json
|
|
50
|
+
# → {service_code, service_config_code, queue_code, queue_status: "draft", config}
|
|
51
|
+
devlift request show orders --env stage --no-input -o json # what now waits for review
|
|
52
|
+
```
|
|
53
|
+
Report what `request show` lists — the rows in the change set and the fields
|
|
54
|
+
each changes. Do NOT report service_code / service_config_code / queue_code:
|
|
55
|
+
they are plumbing for your next call, not something a person reads. Then tell
|
|
56
|
+
the user it is a draft, nothing is deployed, and continue with workflow 6.
|
|
57
|
+
`--type worker` drops health/service_path/ALB fields (no load balancer).
|
|
58
|
+
|
|
59
|
+
## 4. Change a running service's settings
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
devlift eks show orders --env stage --no-input -o json # current values; pending_request + pending_keys if a draft exists
|
|
63
|
+
devlift eks edit orders --env stage --set memory_limit=1 --no-input # Current | New, exit 5
|
|
64
|
+
devlift eks edit orders --env stage --set memory_limit=1 --no-input -y -o json
|
|
65
|
+
# → {changed, changes{field:{from,to}}, queue_code, queue_status}
|
|
66
|
+
```
|
|
67
|
+
Exit 7 "has a submit/approved settings request": the change set is under
|
|
68
|
+
review; use `request withdraw` (own) or wait for deploy/revoke.
|
|
69
|
+
|
|
70
|
+
## 5. Kong routes
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
devlift kong routes list orders --env stage --no-input -o json # groups: method, auth, tag, paths, pending
|
|
74
|
+
devlift kong route add orders --env stage --method GET --auth jwt --path '~/orders/api/v1/orders$' --no-input # exit 5
|
|
75
|
+
devlift kong route add … --no-input -y -o json # → {changed, change{tag,method,added,…}, queue_code}
|
|
76
|
+
```
|
|
77
|
+
Public routes: `--auth public` (default tag `orders-open`). Existing tag with
|
|
78
|
+
the other auth → exit 7: pick another `--tag`.
|
|
79
|
+
|
|
80
|
+
## 6. Review lane (settings and routes move together)
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
devlift request show orders --env stage --no-input -o json # EVERYTHING that submit will send: settings AND Kong routes
|
|
84
|
+
devlift request list --service orders --env stage --no-input -o json # what exists, statuses, you.can_approve / can_deploy
|
|
85
|
+
devlift request submit orders --no-input -y -o json # → requests[] all "submit"; exit 7 if another author holds the lane
|
|
86
|
+
devlift request show orders --env stage --no-input -o json # after submit: you.can_approve says whether THIS user may approve it
|
|
87
|
+
devlift approval list --no-input -o json # reviewer inbox
|
|
88
|
+
devlift request show orders --no-input -o json # frozen diff for the reviewer
|
|
89
|
+
devlift approval approve orders --no-input -y -o json # → requests[] all "approved"
|
|
90
|
+
devlift request show orders --env stage --no-input -o json # after approve: the frozen diff to show, and you.can_deploy
|
|
91
|
+
# alternatives: approval request-changes orders --comment "…" -y ; approval reject orders --comment "…" -y ; approval revoke orders -y ; request withdraw orders -y
|
|
92
|
+
```
|
|
93
|
+
Before submitting, show the user what `request show` lists and get their
|
|
94
|
+
go-ahead. Submit moves the WHOLE change set, so a Kong route added earlier
|
|
95
|
+
ships with the settings change; someone who only remembers editing CPU must
|
|
96
|
+
not discover the route at approval time.
|
|
97
|
+
|
|
98
|
+
After submitting, report that it is waiting for approval, how many rows moved
|
|
99
|
+
and of which kinds, and who approves next. Read `you.can_approve` from
|
|
100
|
+
`request show` rather than writing "if you have approval rights" — the CLI
|
|
101
|
+
knows, so the user should not have to.
|
|
102
|
+
|
|
103
|
+
After approving, show the frozen diff in full (every row, Field | From | To):
|
|
104
|
+
approval is the last checkpoint before a cluster, and "approved" on its own
|
|
105
|
+
tells the user nothing about what they just cleared. Then read `you.can_deploy`
|
|
106
|
+
the same way to say whether they can ship it themselves.
|
|
107
|
+
|
|
108
|
+
Self-approval works only where the resource group's rule allows it (403 → exit 4 otherwise).
|
|
109
|
+
|
|
110
|
+
## 7. Deploy and verify
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
devlift eks deploy orders --env stage --no-input # lists approved rows, exit 5
|
|
114
|
+
devlift eks deploy orders --env stage --no-input -y -o json # → {workflow_id, requests[]}
|
|
115
|
+
devlift deployment status <workflow_id> --no-input -o json # stages with status + timing, pr_url, resources
|
|
116
|
+
devlift eks status orders --env stage --no-input -o json # .deployment (stages, pr_url) and .application (ArgoCD state)
|
|
117
|
+
```
|
|
118
|
+
After deploying, show the pipeline stages as the command gives them, each with
|
|
119
|
+
its status and timing, plus what is shipping and the workflow id. Do not
|
|
120
|
+
replace the stage list with a sentence: the user wants to see which step is
|
|
121
|
+
running. Name both follow-ups and the difference — `deployment status --wait`
|
|
122
|
+
answers "did DevLift's pipeline finish", `eks status` also asks ArgoCD "is the
|
|
123
|
+
pod serving". A finished pipeline is not a running application.
|
|
124
|
+
|
|
125
|
+
Production: `--confirm-name orders` in place of relying on `-y`.
|
|
126
|
+
Read `.application.state`: `up` = healthy pod; `waiting_for_argocd` /
|
|
127
|
+
`starting` = keep polling (`--wait`); `not_configured` = no ArgoCD for that
|
|
128
|
+
environment, only the pipeline result is available.
|
|
129
|
+
|
|
130
|
+
## 8. Find things
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
devlift services list --search order --no-input -o json
|
|
134
|
+
devlift deployment list --env stage --status running --no-input -o json
|
|
135
|
+
devlift deployment status <workflow-id> --no-input -o json
|
|
136
|
+
devlift request history orders --env stage --no-input -o json
|
|
137
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlift-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: DevLift command-line tool: create, change, review and deploy DevLift resources from the terminal.
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Keywords: devlift,devops,deployment,infrastructure,cli
|
|
@@ -104,14 +104,18 @@ Variables and secrets stay in the dashboard by design.
|
|
|
104
104
|
|
|
105
105
|
## Claude skill
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
drive this CLI non-interactively. Install with:
|
|
107
|
+
A Claude skill ships inside the package, teaching Claude (Claude Code or any
|
|
108
|
+
Agent Skills host) to drive this CLI non-interactively. Install it with:
|
|
109
109
|
|
|
110
110
|
```
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
devlift skill install # ~/.claude/skills/devlift, every project
|
|
112
|
+
devlift skill install --project # ./.claude/skills/devlift, this repo only
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
Start a new Claude session afterwards to pick it up. `devlift skill status`
|
|
116
|
+
says where it is installed and whether it still matches the CLI;
|
|
117
|
+
`devlift skill install --force` refreshes it after an upgrade.
|
|
118
|
+
|
|
115
119
|
## Development
|
|
116
120
|
|
|
117
121
|
Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
|
|
@@ -42,6 +42,7 @@ devlift_cli/commands/manual.py
|
|
|
42
42
|
devlift_cli/commands/repositories.py
|
|
43
43
|
devlift_cli/commands/request.py
|
|
44
44
|
devlift_cli/commands/s3.py
|
|
45
|
+
devlift_cli/commands/skill.py
|
|
45
46
|
devlift_cli/commands/sqs.py
|
|
46
47
|
devlift_cli/data/placement/vance.json
|
|
47
48
|
devlift_cli/ops/__init__.py
|
|
@@ -56,4 +57,8 @@ devlift_cli/render/__init__.py
|
|
|
56
57
|
devlift_cli/render/output.py
|
|
57
58
|
devlift_cli/resolve/__init__.py
|
|
58
59
|
devlift_cli/resolve/allowlist.py
|
|
59
|
-
devlift_cli/resolve/names.py
|
|
60
|
+
devlift_cli/resolve/names.py
|
|
61
|
+
devlift_cli/skill/SKILL.md
|
|
62
|
+
devlift_cli/skill/reference/commands.md
|
|
63
|
+
devlift_cli/skill/reference/exit-codes.md
|
|
64
|
+
devlift_cli/skill/reference/workflows.md
|
|
@@ -51,4 +51,8 @@ version = { attr = "devlift_cli.__version__" }
|
|
|
51
51
|
include = ["devlift_cli*"]
|
|
52
52
|
|
|
53
53
|
[tool.setuptools.package-data]
|
|
54
|
-
|
|
54
|
+
# The skill ships inside the package on purpose: `devlift skill install` has to
|
|
55
|
+
# work for someone who installed from PyPI and has no access to this private
|
|
56
|
+
# repository, and shipping both in one wheel means the skill can never describe
|
|
57
|
+
# a different set of commands from the CLI that carried it.
|
|
58
|
+
devlift_cli = ["MANUAL.md", "skill/SKILL.md", "skill/reference/*.md", "data/placement/*.json"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|