Tea.Deploy.Cli 0.4.2
tea.deploy.cli
Interactive .NET CLI for the TEA SCM-Instructions release workflow.
Picks the right release tag for each environment, fetches delta SQL filenames automatically from Jenkins or the ApplicationStaging share, optionally triggers the tag-creation Jenkins job, and renders the DOS ticket body — without 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
From the TEA NuGet server (recommended)
# Install
dotnet tool install --global --source "TEA Local" Tea.Deploy.Cli
# Update later
dotnet tool update --global --source "TEA Local" Tea.Deploy.Cli
# Uninstall
dotnet tool uninstall --global Tea.Deploy.Cli
The TEA Local NuGet source (https://nuget.tea.noctusoft.com) is already registered on TEA machines. Confirm with dotnet nuget list source.
From source
# Build + pack into nupkg\
dotnet pack C:\TEA\tea.deploy.cli -c Release
# Install the global tool from the local nupkg folder
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
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). |
Deploy flags (combine with <app-slug>)
| Flag | What it does |
|---|---|
--jenkins-job "<name>" |
Override the Jenkins job used for delta file discovery (default: "{JiraKey} Pipeline", e.g. "CDRMS Pipeline"). |
--no-trigger |
Skip the "trigger tag creation?" prompt even when the selected tag doesn't exist in Bitbucket yet. |
--trigger-only |
Trigger the tag-creation Jenkins job and exit — no ticket rendered. |
--staging-share <path> |
Override the UNC path used as a fallback for delta file discovery (default: \\TEA4AFSHR02\ApplicationStaging). |
--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.
When JENKINS_TOKEN is configured, --check additionally verifies that the MA job folder is accessible (not just that Jenkins is reachable).
--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.
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
...
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
? 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
# If the selected tag doesn't exist in Bitbucket yet (e.g. a hotfix bump),
# and JENKINS_TOKEN is configured:
Note: Rel_8.43.6.1 does not exist in Bitbucket yet.
? Trigger "Create Tag (Git) MultiApps" to push Rel_8.43.6.1 to Bitbucket?
> Yes — trigger the tag creation job now
No — I'll push the tag manually then re-run
Cancel
Build #42 queued. Polling for completion...
Create Tag (Git) MultiApps #42 [====================] SUCCESS
? 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
? Deployment date (YYYY-MM-DD): > 2026-05-06
Timed for 2026-05-06 18:00 CT (per the standing 6 PM rule).
Looking for delta files in Jenkins workspace (CDRMS Pipeline)...
Found 2 delta file(s) from Jenkins workspace.
─── DOS ticket preview ───
# [spedcdrms] TEST deploy of Rel_8.43.6.1
**Issue Type:** SCM Test Deployment
...
Delta files for Rel_8.43.6.1:
- Cdrms_Stored Procedures_Delta_Rel_8.43.6.1_vs_Rel_8.43.6.0
- Cdrms_Tables_Delta_Rel_8.43.6.1_vs_Rel_8.43.6.0
? What now?
> Save to file (spedcdrms-test-Rel_8.43.6.1.md)
Copy to clipboard
Save and copy
Submit to Jira (not yet implemented)
Cancel
Delta file discovery
The tool resolves delta files automatically in this order:
- Jenkins workspace API — browses the
{JiraKey} Pipelineworkspace for aDatabase/subfolder matching the release tag. Requires VPN +JENKINS_TOKEN. - UNC file share — falls back to
\\TEA4AFSHR02\ApplicationStaging\{slug}\{tag}\Database\. Also requires VPN. - Placeholder — if neither source is reachable, the ticket body includes a placeholder you fill in manually.
Both VPN-gated sources are tried automatically; the fallback chain is invisible to the user unless neither works.
Tag-trigger step
When you pick a tag that doesn't yet exist in Bitbucket (a hotfix or minor bump) and JENKINS_TOKEN is configured, the tool offers to trigger the "Create Tag (Git) MultiApps" Jenkins job before continuing:
- Yes — triggers the job (parameters:
Repository,Branch,Release,Email), then polls with a progress bar untilSUCCESSorFAILURE. - No — skips; a reminder is shown to push the tag manually and re-run.
- Cancel — exits cleanly.
The tool never triggers silently. --no-trigger suppresses the prompt entirely; --trigger-only triggers then exits without rendering a ticket.
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, trigger, ticket action) and lets you cancel.
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...",
"JENKINS_TOKEN": "...Jenkins API token..."
}
| 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 Jira, Bitbucket, and Jenkins email notifications). |
JIRA_API_TOKEN |
Atlassian API token, from id.atlassian.com/manage-profile/security/api-tokens. |
BB_PASS |
Bitbucket app password with Repositories: Read and Workspaces: Read scopes. |
TEA_UID |
TEA domain username in TEA\username format (the domain prefix is stripped automatically for Jenkins). |
TEA_PWD |
TEA AD password (reserved for future use). |
JENKINS_TOKEN |
Jenkins API token — generate at http://tea4svdragonet.tea.state.tx.us:8080/user/<username>/configure. Enables delta file discovery and tag-trigger. Optional — the tool works without it, but those steps are skipped. |
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
jenkins_job: CDRMS Pipeline # optional — defaults to "{jira} Pipeline"
timeandeffort:
jira: TE
charterfirst:
jira: CF
| Field | What it's for |
|---|---|
jira |
Jira project key — used for the picker annotation, ticket linking, and deriving the Jenkins job name. |
contact_phone / business_contact |
Pre-fill the required DOS-ticket fields. |
jenkins_job |
Override the Jenkins job name for delta file discovery and tag triggering. Defaults to "{jira} Pipeline" if omitted. |
state.yaml (currently-deployed tags per env)
Default location: same directory as tea.env.json.
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. Edit by hand after each successful deploy.
Common workflows
Deploy a new release of spedcdrms to test
tea-deploy --check # confirm all services reachable
tea-deploy spedcdrms # env=test, tag=latest
# delta files auto-fetched from Jenkins if on VPN
# paste the saved .md into the SCM Instructions Board
Promote spedcdrms to prod after test passes
tea-deploy spedcdrms # env=prod; picker offers the test tag as "promote"
# ticket saves with Deployment Type = "Database and Atos"
Hotfix scenario (tag doesn't exist yet)
tea-deploy spedcdrms # env=test; pick "Rel_X.Y.Z.N+1 (hotfix bump)"
# tool offers to trigger "Create Tag (Git) MultiApps"
# confirm → tag pushed to Bitbucket → delta files fetched → ticket rendered
Trigger tag only, no ticket
tea-deploy spedcdrms --trigger-only # triggers the Jenkins tag job and exits
Skip the trigger prompt
tea-deploy spedcdrms --no-trigger # walks through normally; no tag-creation offer
Override the Jenkins job name for one run
tea-deploy spedcdrms --jenkins-job "CDRMS Pipeline"
Daily situational awareness
tea-deploy --tagmath # one-pane view: 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) and the UNC file share (\\TEA4AFSHR02\ApplicationStaging) both live on the internal TEA network. Both require the TEA VPN. Tag math, lints, and ticket rendering still work without VPN. - When
JENKINS_TOKENis configured and Jenkins is reachable, delta files are fetched automatically from the Jenkins workspace. The UNC share is used as a fallback. If neither is reachable the ticket gets a placeholder. - When something is unreachable, the tool prints plain English about why (DNS lookup failure, expired token, timeout, connection refused) and a VPN hint when appropriate.
- Run
tea-deploy --checkany time to confirm what you can reach.
Tests
77 tests covering tag math, the picker UX, --check, --tagmath, friendly error messages, delta file discovery, tag triggering, and the Jira schema.
# Fast suite — no network, ~5 s
dotnet test C:\TEA\tea.deploy.cli --filter "Category!=Live"
# Full suite — fast + live (hits real Bitbucket and Jira)
dotnet test C:\TEA\tea.deploy.cli
# Live tag math with verbose output:
dotnet test C:\TEA\tea.deploy.cli `
--filter "Category=Live&FullyQualifiedName~LiveTagMath" `
--logger "console;verbosity=detailed"
# Live tag math for a custom set of apps:
$env:TEA_TEST_APPS = "spedcdrms,charterfirst,timeandeffort"
dotnet test C:\TEA\tea.deploy.cli `
--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:
// Basic walkthrough — existing tag, no Jenkins
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);
// With a fake Jenkins client (delta files + tag trigger)
var fakeJenkins = new FakeJenkinsClient
{
WorkspaceDeltaFiles = new List<string> { "Cdrms_Stored Procedures_Delta_Rel_8.43.6.1_vs_Rel_8.43.6.0" },
TriggerBuildNumber = 42,
TriggerResult = JenkinsBuildStatus.Success
};
using var result = await new ScenarioBuilder()
.WithArgs("spedcdrms")
.WithRepo("spedcdrms")
.WithTags("spedcdrms", "Rel_8.43.6.0") // hotfix bump won't exist in Bitbucket
.WithState("spedcdrms", test: "Rel_8.43.6.0")
.WithAppsOverride("spedcdrms", phone: "512", contact: "x@y.com")
.WithJenkins(fakeJenkins)
.Pick(0) // env: test
.Pick(1) // tag: hotfix bump (doesn't exist → trigger prompt appears)
.Pick(0) // trigger: Yes
.PressEnter() // date
.Pick(0) // Save to file
.RunAsync();
Pick(N) = press Down N times then Enter on a SelectionPrompt.
Type(text) = TextPrompt input.
PressEnter() = accept default.
WithNoTrigger() / WithTriggerOnly() / WithStagingShare(path) = set the corresponding walkthrough flags.
Publishing a new version
# 1. Run the fast tests
dotnet test C:\TEA\tea.deploy.cli --filter "Category!=Live"
# 2. Pack
dotnet pack C:\TEA\tea.deploy.cli -c Release
# 3. Push to the TEA NuGet server
dotnet nuget push "C:\TEA\tea.deploy.cli\nupkg\Tea.Deploy.Cli.*.nupkg" `
--source "TEA Local" --api-key BAGET-API-KEY
Bump the version in tea.deploy.cli.csproj (<Version>) before step 2 if this is a new release.
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. Confirm with dotnet --list-sdks. |
tea-deploy: command not found after dotnet tool install succeeded |
Close and re-open the terminal so PATH refreshes. Run dotnet tool list -g to confirm the install, then add $env:USERPROFILE\.dotnet\tools to your PATH if needed. |
dotnet tool install says "already installed" |
Use dotnet tool update instead. |
| Spectre prompts don't accept arrow keys | Use a real Windows Terminal / PowerShell 7 console — VS Code's "Output" panel and some restricted shells don't 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. Open tea.env.json and fill all required fields. |
Config file is not valid JSON |
Usually a missing comma or unescaped backslash. JSON requires \\ for a single \ — TEA_UID must be "TEA\\rvega". |
| Delta files never appear in the ticket | JENKINS_TOKEN is missing from tea.env.json, or you're off VPN. Both Jenkins and the UNC share require VPN. |
Network and auth
| Symptom | Likely cause / fix |
|---|---|
Authentication failed for Bitbucket Cloud |
App password expired — issue a new one with Repositories: Read and Workspaces: Read scopes. |
Authentication failed for Jira Cloud |
Atlassian API token expired. Issue a new one at 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; the rest of the tool still works without it. |
| Jenkins reachable but delta files still missing | Check apps.yaml — the jenkins_job field (or jira key for auto-derivation) must match the actual Jenkins job name under MA/. |
| Tag trigger fails with auth error | The JENKINS_TOKEN may be wrong or expired — regenerate at http://tea4svdragonet.tea.state.tx.us:8080/user/<username>/configure. |
Tag math and pickers
| Symptom | Likely cause / fix |
|---|---|
--tagmath columns are all ? |
state.yaml doesn't have entries yet. Edit it by hand at <config-dir>/state.yaml. |
--tagmath jira column is blank |
Add jira: <KEY> under overrides.<slug> in apps.yaml. |
Tool exits with Unknown repo: <slug> |
The slug isn't in the teaapplications workspace. Run --tagmath to see the live list. |
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 required. |
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.- INSTALL.md — two-minute setup guide for new users.