Tea.Deploy.Cli 0.1.0
tea.deploy.cli
Interactive .NET CLI for the TEA SCM-Instructions release workflow.
Picks the right release tag for each environment, runs preflight lints, fetches delta SQL filenames from Jenkins, and renders the DOS ticket body — without you clicking through Jira / Bitbucket / Jenkins by hand.
- Tool command:
tea-deploy - Package id:
Tea.Deploy.Cli - Source path:
C:\TEA\tea.deploy.cli - Direction / spec:
C:\App\AI\Deploy\DIRECTIONS.md
Install
# Build + pack
dotnet pack C:\TEA\tea.deploy.cli
# Install the global tool
dotnet tool install --global --add-source C:\TEA\tea.deploy.cli\nupkg Tea.Deploy.Cli
# Update later
dotnet tool update --global --add-source C:\TEA\tea.deploy.cli\nupkg Tea.Deploy.Cli
# Uninstall
dotnet tool uninstall --global Tea.Deploy.Cli
Or run straight from source without installing:
dotnet run --project C:\TEA\tea.deploy.cli -- <args>
Requires the .NET 10 SDK.
Quick start
# 1. Put your TEA credentials in this file:
# C:\Users\admin\.config\tea.env.json (see "Configuration" below)
# 2. Verify the tool can reach everything:
tea-deploy --check
# 3. Get a tag-math overview of the whole workspace:
tea-deploy --tagmath
# 4. Walk through a deploy:
tea-deploy spedcdrms # (or any other repo slug)
Run tea-deploy with no arguments at any time to get the help screen.
Commands
| Command | What it does |
|---|---|
tea-deploy |
Show help (no parameters = help). |
tea-deploy --help, -h, /? |
Show help. |
tea-deploy --version, -v |
Print the tool version and exit. |
tea-deploy --check |
Probe Bitbucket Cloud, Jira Cloud, and the internal Jenkins. Prints OK/FAIL with friendly messages. Exit 0 = all OK, 2 = something unreachable. |
tea-deploy --tagmath |
Print a single table for every active app in the workspace: latest tag, currently in test / prod / training, hotfix bump, minor bump. |
tea-deploy --pick |
Open the interactive app drop-down without naming a slug. |
tea-deploy <app-slug> |
Walk through a deploy for that app (e.g. tea-deploy spedcdrms). |
--check
─── tea-deploy --check ───
Verifying connectivity to required services...
Bitbucket Cloud https://api.bitbucket.org/2.0/ OK
Jira Cloud https://tea-applications.atlassian.net OK
Jenkins (TEA internal) http://tea4svdragonet.tea.state.tx.us:8080 FAIL
Cannot reach Jenkins (TEA internal) (http://tea4svdragonet.tea.state.tx.us:8080).
DNS lookup failed — the host name could not be resolved.
This is the expected behavior when you are NOT connected to the TEA VPN.
Connect to the TEA VPN and try again.
Bitbucket and Jira are reachable; Jenkins is not.
Tag math, lints, and ticket rendering will still work without Jenkins.
Connect to the TEA VPN if you need to trigger builds or harvest deltas.
If a service is unreachable, you get plain English about why — including a clear "this is because you're not on the TEA VPN" hint when the failure is DNS resolution against an internal host.
--tagmath
Tag-math overview for every active app (updated in the last 30 days):
─── tea-deploy --tagmath ───
Active in last 30 days: 25 of 39
╭─────────────────┬───────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮
│ slug │ jira │ latest │ in test │ in prod │ in training │ hotfix bump │ minor bump │
├─────────────────┼───────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤
│ apex │ APEX │ Rel_6.2.0.10 │ ? │ ? │ ? │ Rel_6.2.0.11 │ Rel_6.2.1.0 │
│ spedcdrms │ CDRMS │ Rel_8.43.6.0 │ Rel_8.43.5.2 │ Rel_8.43.5.1 │ ? │ Rel_8.43.6.1 │ Rel_8.43.7.0 │
│ timeandeffort │ TE │ Rel_3.42.1.0 │ ? │ ? │ ? │ Rel_3.42.1.1 │ Rel_3.42.2.0 │
│ ... (more) │
╰─────────────────┴───────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯
?= unknown (no entry instate.yaml).- An env cell turns green when the deployed tag matches the latest from Bitbucket.
- The
jiracolumn is blank until you set the Jira key for that slug inapps.yaml.
Populate the ?s by running tea-deploy <app> --mark-deployed <env> after each deploy (see Common Workflows below).
The walkthrough (tea-deploy <app> or --pick)
─── tea-deploy spedcdrms ───
OK config loaded (ricardo.vega@tea.texas.gov)
Selected: spedcdrms
Recent Rel_* tags (top 5):
Rel_8.43.6.0 2026-05-04 <- latest
Rel_8.43.5.2 2026-04-24
Rel_8.43.5.1 2026-04-13
Rel_8.43.5.0 2026-03-30
Rel_8.43.4.0 2026-03-19
State:
test: Rel_8.43.5.2
prod: Rel_8.43.5.1
training: Rel_8.43.5.1
? Which environment are you deploying to?
> test
prod
training
Cancel
? Which tag do you want to deploy to test?
> Rel_8.43.6.0 (latest, ready to deploy)
Rel_8.43.6.1 (hotfix bump from latest)
Rel_8.43.7.0 (minor bump from latest)
Custom tag…
Cancel
? Contact phone (will be saved to apps.yaml): > 512-555-1234
? Business contact (Jira email or username): > ramya.kasaraneni@tea.texas.gov
? Save to apps.yaml so I don't ask next time? [Y/n]: Y
OK saved.
? Deployment date (YYYY-MM-DD): > 2026-05-06
Timed for 2026-05-06 18:00 CT (per the standing 6 PM rule).
─── DOS ticket preview ───
# [spedcdrms] TEST deploy of Rel_8.43.6.0
**Issue Type:** SCM Test Deployment
...
? What now?
> Save to file (spedcdrms-test-Rel_8.43.6.0.md)
Copy to clipboard
Save and copy
[grey]Submit to Jira (not yet implemented)[/]
Cancel
Design rules:
- Read-only until you confirm. The tool never mutates Bitbucket / Jira / Jenkins /
state.yamlwithout an explicit pick. - It asks for missing config (Contact Phone, Business Contact) once per app — answers persist to
apps.yaml. - It stops at every irreversible step (env, tag, ticket action) and lets you cancel.
- The tool never pushes git tags. It suggests; you push.
Configuration files
tea.env.json (credentials)
Default location: C:\Users\admin\.config\tea.env.json (override with the TEA_DEPLOY_CONFIG env var).
{
"JIRA_BASE_URL": "https://tea-applications.atlassian.net",
"JIRA_EMAIL": "your.email@tea.texas.gov",
"JIRA_API_TOKEN": "...atlassian API token...",
"BB_PASS": "...bitbucket app password...",
"TEA_UID": "TEA\\rvega",
"TEA_PWD": "...AD password...",
"CONFLUENCE_OAUTH_CLIENT_ID": "(optional, future use)",
"CONFLUENCE_OAUTH_SECRET": "(optional, future use)"
}
| Key | What it's for |
|---|---|
JIRA_BASE_URL |
Your Jira Cloud base URL (no trailing slash needed). |
JIRA_EMAIL |
The email tied to your Atlassian account (used for both Jira and Bitbucket). |
JIRA_API_TOKEN |
Atlassian API token, from id.atlassian.com/manage-profile/security/api-tokens. |
BB_PASS |
Bitbucket app password (works the same as the Atlassian token in Cloud). |
TEA_UID/TEA_PWD |
TEA AD credentials, for the on-prem Jenkins (--trigger, --watch). |
apps.yaml (per-app overrides)
Default location: same directory as tea.env.json. The tool creates and updates it as you answer prompts.
overrides:
spedcdrms:
jira: CDRMS
contact_phone: 512-555-1234
business_contact: ramya.kasaraneni@tea.texas.gov
timeandeffort:
jira: TE
charterfirst:
jira: CF
jira– Jira project key (used for the picker annotation and ticket linking).contact_phone/business_contact– pre-fill the required DOS-ticket fields.
state.yaml (currently-deployed tags per env)
Default location: same directory as tea.env.json. Optionally also release/state.yaml next to a repo's checkout — entries there override the global file for that app.
spedcdrms:
test: Rel_8.43.5.2
prod: Rel_8.43.5.1
training: Rel_8.43.5.1
hrrs:
test: Rel_6.57.1.0
This is what fills the "in test / in prod / in training" columns of --tagmath and lights up the "promote" choice in the walkthrough's tag picker. After each successful deploy run:
tea-deploy spedcdrms --mark-deployed test --tag Rel_8.43.6.0
(--mark-deployed lands as part of the --submit slice; in the meantime you can edit state.yaml by hand.)
Common workflows
Deploy a new release of spedcdrms to test
tea-deploy --check # confirm Bitbucket + Jira reachable
tea-deploy spedcdrms # walk through: env=test, tag=latest, save the ticket body
# paste the saved file's contents into the SCM Instructions Board
Promote spedcdrms to prod after test passes
tea-deploy spedcdrms # env=prod; the picker will offer the test tag as "promote"
# ticket saves with Deployment Type = "Database and Atos"
Hotfix scenario
tea-deploy spedcdrms # env=test; pick "Rel_X.Y.Z.N+1 (hotfix bump from latest)"
# tool suggests; you tag and push manually, then re-run
Daily situational awareness
tea-deploy --tagmath # one-pane view: what's the latest, what's where, what bumps next
Network behavior
- Bitbucket Cloud and Jira Cloud need only the public internet. They auth with your Atlassian email + API token.
- Jenkins (
tea4svdragonet:8080) lives on the internal TEA network. It needs the TEA VPN. Tag math, lints, ticket rendering, and the walkthrough still work without VPN — only--triggerand--watchneed it. - When something is unreachable, the tool prints plain English about why (DNS lookup failure, expired token, timeout, connection refused) and a hint about VPN when the failure indicates an internal-network problem.
- Run
tea-deploy --checkany time to confirm what you can reach.
Tests
The repo includes ~80 tests covering tag math, the picker UX, --check, --tagmath, friendly error messages, and the Jira schema.
# Fast suite — no network, ~400 ms
dotnet test C:\TEA\tea.deploy.cli\tea.deploy.cli.slnx --filter "Category!=Live"
# Full suite — fast + live (hits real Bitbucket and Jira), ~30 s
dotnet test C:\TEA\tea.deploy.cli\tea.deploy.cli.slnx
# Live tag math against every active app, with verbose output:
dotnet test C:\TEA\tea.deploy.cli\tea.deploy.cli.slnx `
--filter "Category=Live&FullyQualifiedName~LiveTagMath" `
--logger "console;verbosity=detailed"
# Tag math for a custom set of apps:
$env:TEA_TEST_APPS = "spedcdrms,charterfirst,timeandeffort"
dotnet test C:\TEA\tea.deploy.cli\tea.deploy.cli.slnx `
--filter "Category=Live&FullyQualifiedName~LiveTagMath" `
--logger "console;verbosity=detailed"
Live tests pass with a [skipped] note on machines without tea.env.json — safe to run anywhere.
Writing a new scenario
Tests for the walkthrough use a fluent ScenarioBuilder that scripts every prompt:
using var result = await new ScenarioBuilder()
.WithArgs("spedcdrms")
.WithRepo("spedcdrms")
.WithTags("spedcdrms", "Rel_8.43.6.0", "Rel_8.43.5.2")
.WithState("spedcdrms", test: "Rel_8.43.5.2", prod: "Rel_8.43.5.1")
.WithAppsOverride("spedcdrms", phone: "512", contact: "x@y.com")
.Pick(0) // env: test
.Pick(0) // tag: promote (Rel_8.43.6.0)
.PressEnter() // date: accept default
.Pick(0) // action: Save to file
.RunAsync();
Assert.Equal(0, result.ExitCode);
Assert.Contains("Database and DevOps", await File.ReadAllTextAsync(result.SavedFiles[0]));
Pick(N) = press Down N times then Enter on a SelectionPrompt.
Type(text) = TextPrompt input.
PressEnter() = accept default.
Troubleshooting
Install / launch
| Symptom | Likely cause / fix |
|---|---|
dotnet pack fails with "SDK 10.0 not found" |
Install the .NET 10 SDK from https://dot.net/download and re-run. Confirm with dotnet --list-sdks. |
tea-deploy: command not found after dotnet tool install succeeded |
Close and re-open the terminal so PATH refreshes. If it's still missing, run dotnet tool list -g to confirm the install, then add $env:USERPROFILE\.dotnet\tools to your PATH. |
dotnet tool install says "already installed" |
Use dotnet tool update instead of install, or uninstall first. |
| Spectre prompts don't accept arrow keys (broken menu rendering) | The terminal isn't reporting itself as interactive. Use a real Windows Terminal / PowerShell 7 console — VS Code's "Output" panel and some restricted shells do not render Spectre menus correctly. |
Configuration
| Symptom | Likely cause / fix |
|---|---|
Config file not found: ...tea.env.json |
Create the file at the default path or set TEA_DEPLOY_CONFIG to its location. See INSTALL.md. |
Config is missing JIRA_EMAIL or BB_PASS |
One or more required keys are blank in tea.env.json. Open it and fill all six required fields. |
Config file is not valid JSON |
Usually a missing comma, an unescaped backslash, or a stray comment. JSON requires \\ for a single \, so TEA_UID must be "TEA\\rvega", not "TEA\rvega". |
tea-deploy reads the wrong config |
Check $env:TEA_DEPLOY_CONFIG. If set, it overrides the default path. |
Network and auth
| Symptom | Likely cause / fix |
|---|---|
Authentication failed for Bitbucket Cloud |
API token / app password expired or revoked — issue a new one and update tea.env.json. Bitbucket app passwords need Repositories: Read and Workspaces: Read scopes. |
Authentication failed for Jira Cloud |
Atlassian API token expired. Issue a new one at https://id.atlassian.com/manage-profile/security/api-tokens. |
Cannot reach Jenkins ... DNS lookup failed ... Connect to the TEA VPN |
You're not on the TEA VPN. Connect; or skip Jenkins-dependent flags (--trigger, --watch) — the rest of the tool still works. |
Timed out reaching ... |
Slow network, or the host is unreachable. If the service is internal (Jenkins), connect to the VPN and retry. |
Bitbucket / Jira return 429 Too Many Requests |
Slow down — --tagmath makes one API call per active repo. Wait a minute and retry; if it's a regular problem, open an issue. |
| Behind a corporate proxy and HTTPS calls fail | Set HTTPS_PROXY (and HTTP_PROXY) in your environment before running tea-deploy. |
Tag math and pickers
| Symptom | Likely cause / fix |
|---|---|
--tagmath columns are all ? |
state.yaml doesn't have entries yet. Either edit it by hand at <config-dir>/state.yaml or use --mark-deployed after each successful deploy. |
--tagmath jira column is — |
apps.yaml doesn't have a Jira key for that slug — add jira: <KEY> under overrides.<slug>. |
--tagmath shows (no Rel_*) for an app |
That repo doesn't follow the Rel_X.Y.Z.N tag scheme. The tool ignores any tag that doesn't match — verify in Bitbucket. |
Tool exits with Unknown repo: <slug> |
The slug isn't in the teaapplications workspace, or it's been archived. Run --tagmath to see the live list. |
| Picker shows zero apps | No repos updated in the last 30 days. Pass nothing to tea-deploy --pick and the tool falls back to all repos automatically. |
| Suggested "latest" looks wrong | Tag numbering at TEA isn't strictly monotonic — humans skip ranges. The tool sorts by version segments (not commit date), which is correct, but a human may have intentionally cut a higher number out of order. Pick "Custom tag…" if you need to override. |
Custom tag… rejects your input |
The pattern is case-sensitive: Rel_8.43.6.0 works, rel_8.43.6.0 does not. Four numeric segments are required. |
state.yaml says test = X but the picker still offers "promote" |
The tool treats the latest tag as the promote candidate when it differs from state.yaml's test entry. Make sure state.yaml matches what was actually deployed; mismatches between cwd release/state.yaml and <config-dir>/state.yaml are merged with cwd taking precedence. |
See also
C:\App\AI\Deploy\DIRECTIONS.md— design spec, conceptual model, phasing, open questions.C:\App\AI\Deploy\Deploy.txt.txt— original transcript of the workflow walkthrough with Ramya.