Repository navigation
Add Nitro Agent Task Manager #10252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add Nitro Agent Task Manager #10252
Changes from all commits
Commits
Show all changes
109 commits
Select commit
Hold shift + click to select a range
53b1a7f
Add dapper to central package management
michaelstaib 22d9b29
Initial Implementation
michaelstaib b4383d2
Define backend-agnostic ITaskStore contract (bd-oyf.1)
PascalSenn 38ad736
Add e2e VHS pipeline scaffolding for the Nitro CLI (perles-net-4gb.1)
PascalSenn cc16c19
Add TUI runtime event loop, live render, resize, and teardown
PascalSenn 33ba961
Add CliWrap smoke-test tier for Nitro CommandLine
PascalSenn ff8f6b5
Add TUI keymap dispatcher and TuiMessage intents
PascalSenn 4fb2d8c
Add TUI render primitives: task badge, viewport, column pane, theme t…
PascalSenn d18ea3b
Fix TaskBadge markup: escape type-code brackets, handle empty style t…
PascalSenn 35d6404
Fix e2e run.sh --update mode to propagate recording failures in exit …
PascalSenn d686d69
Ensure TUI background tasks are torn down when the event handler throws
PascalSenn 44c913b
Extract TaskGlyphs from TaskBadge for search/tree/detail reuse
PascalSenn dcc8519
Add deterministic fixture task workspace for e2e tapes
PascalSenn d2b0adb
Implement ITaskStore read side in TaskStore (bd-oyf.2)
PascalSenn 654d9c7
feat(nitro-tui): add TUI shell mode container, toaster, and quit conf…
PascalSenn 3e0e6bd
Add TaskQuery parser for TUI search mode (perles-net-4j7.2)
PascalSenn 91fc419
Add board model: views, column filters, loader over ITaskStore
PascalSenn 1d62b92
Add direct TaskStore tests for the new read surface (bd-oyf.2)
PascalSenn 846782f
Fix Dapper record materialization for COUNT(*) in TaskStore reads
PascalSenn d740f87
feat(nitro-tui): add scoped form widget engine
PascalSenn e27ba8f
Implement ITaskStore write side in TaskStore (bd-oyf.3)
PascalSenn 939011f
Add e2e tapes + goldens for non-interactive nitro task flows
PascalSenn da8ee1e
Migrate task read commands onto ITaskStore's new surface
PascalSenn 023a71c
Migrate task write commands onto ITaskStore's new surface
PascalSenn 1b09a7c
Add nitro-cli-e2e CI workflow with sticky before/after GIF PR comment
PascalSenn 2eb156c
Add kanban board mode: column rendering, navigation, refresh
PascalSenn 6d93fc8
Remove legacy connection-exposing ITaskStore members
PascalSenn 6f7ea1d
Add search mode: query input, live results, focus switching (perles-n…
PascalSenn 499c9ba
Remove em dashes from nitro-cli-e2e workflow text
PascalSenn d1b498d
test(nitro-tui): cover board viewport more-above/more-below indicators
PascalSenn 2076d43
fix(nitro-tasks): exclude tombstones from empty-query search results
PascalSenn a2b43e0
Add AOT self-contained publish smoke job to nitro-cli-e2e.yml
PascalSenn 21cf044
Mark aot-publish-smoke informational until Dapper AOT trim errors are…
PascalSenn a570def
feat(nitro-tasks): add task board entry command
PascalSenn e999bf6
Add board layout modes: maximize, stacked fallback, resize handling
PascalSenn c5294c0
Add task detail view: scrollable body, sidebar, dependency navigation
PascalSenn 998f8af
Fix TaskDetailView scroll saturation via Viewport.ScrollBy
PascalSenn 184aedc
Scope IL3053/IL2104 suppression to Dapper in Nitro CLI AOT publish
PascalSenn f6dabb2
Add ConfirmDialog and TaskLifecycleActions for task close/reopen/dele…
PascalSenn e11eee3
Add dependency tree explorer view for the tasks TUI
PascalSenn 539bc05
Unhide the nitro task command family per user ruling
PascalSenn 6417eff
Prepare (blocked) board TUI VHS tapes for perles-net-4gb.7
PascalSenn 1012983
Add TaskEditorForm for task title/status/priority/type/labels/descrip…
PascalSenn 37e34bb
Make TaskStore NativeAOT-compatible
PascalSenn de05472
fix(nitro-tui): paint the initial frame before any dirty event
PascalSenn 507758a
fix(nitro-tui): keep selected row's font color, background-only highl…
PascalSenn 0976d9a
Wire search, detail, tree, and editing modes into the TUI shell
PascalSenn d6f36c4
Revert quit-confirm modality and recursion guard added by umu.3 wiring
PascalSenn 8dd6d1b
Add status and priority quick pickers to the tasks TUI
PascalSenn fbf2393
fix(tui): pin ColumnPane panel height so board columns fill full term…
PascalSenn 7ff088d
Add create task and create epic flows to the tasks TUI
PascalSenn 1198efd
Record board TUI e2e goldens now that the pty paint hang is fixed (pe…
PascalSenn 1fbe284
Add tests for create task/epic key bindings and shell wiring (4j7.9)
PascalSenn 4d8e1b5
Prepare (blocked) search/detail TUI VHS tapes for perles-net-4gb.8
PascalSenn 8551007
Fix dead '/' search chord on real pty (Oem2 vs Divide)
PascalSenn 235fdf7
nitro task: add --output json to query commands (bd-5dd)
PascalSenn 30ee010
Record search/detail TUI e2e goldens for perles-net-4gb.8
PascalSenn 38f5145
feat(nitro-task): add task sync command for JSONL export/import
PascalSenn 3ea73f7
Degrade dependency tree breadcrumb header at narrow widths (bd-595)
PascalSenn bceca65
feat(nitro-task): accept priority ranges in list/ready --priority (bd…
PascalSenn d5d9cb3
nitro task search: match comment text (bd-g34)
PascalSenn c4291b6
fix(bd-595): budget dependency tree header against panel's rendered w…
PascalSenn 10cc638
nitro task: add --output json to mutation commands (perles-net-teq)
PascalSenn 7cc37f1
feat(nitro-task): accept multiple task IDs on update (bd-izm)
PascalSenn fbace0f
feat(nitro-task): add --add-label/--remove-label, --parent, --claim t…
PascalSenn 95a01cd
fix(nitro-cli): guard task update --parent against duplicate edges an…
PascalSenn fc573cf
nitro task q: quick capture that prints only the new task ID
PascalSenn 9f98edf
feat(nitro-task): add task doctor and lint workspace diagnostics (per…
PascalSenn 0762cca
test(e2e): re-record help-flow and detail-flow goldens for final sweep
PascalSenn 7797707
fix(nitro-task): unify task label list JSON shape across arities (per…
PascalSenn 87d50a2
feat(nitro-tui): expose BoardMode.SelectedTaskId from focused column
PascalSenn 64eec0b
task sync: include config table in tasks.jsonl so the id prefix survi…
PascalSenn ab9a821
fix(tui): touched validation gate, confirm-discard on dirty Esc, trim…
PascalSenn 74d0bb1
fix(nitro-tui): selection highlight background Aqua -> Grey35 (bd-d4l)
PascalSenn 18c7d3b
feat(nitro-tui): Enter on the board opens the task detail view (perle…
PascalSenn 5f55a05
fix(nitro-tui): task form field min heights, horizontal radios, width…
PascalSenn 62124e9
fix(nitro-tui): drop detail.section.header token committed from anoth…
PascalSenn 171debc
fix(nitro-tui): detail view redesign, fill frame, sections, sidebar r…
PascalSenn e7a7021
fix: only keep selected detail row visible on selection change (perle…
PascalSenn 975198b
test: make dependency-row scroll-offset test discriminate against the…
PascalSenn c2ceb39
feat(nitro-tasks-tui): add always-visible per-view keybinding footer
PascalSenn b331e79
fix(nitro-tasks): add Enter hint to search input footer (perles-net-t…
PascalSenn 4701cbe
feat(tui): per-column border and header accent colors on the board
PascalSenn 5d9ce53
fix(nitro-tui): stacked board columns share height equally (perles-ne…
PascalSenn a5ed9b8
test(e2e): re-record board, board-maximize, search, detail goldens fo…
PascalSenn 818752a
fix(nitro-tasks): wrap-aware sidebar line count in stacked task detai…
PascalSenn 596b9e6
fix(nitro-tui): make the quit confirmation dialog fully modal
PascalSenn a76bad7
fix(nitro-tasks): avoid comment id collisions on cross-clone sync import
PascalSenn dd5d8b3
fix(tui): keep editor/create form open with values when store rejects…
PascalSenn eca1aec
fix(tui): let the create form clear its parent to create a root task
PascalSenn 837d2eb
fix(nitro-task): restore child ID counters on sync import (perles-net…
PascalSenn 59fb58e
fix(nitro-cli): reject dependency and parent cycles before commit (pe…
PascalSenn 77cb77f
fixup: restore other-session comment block in TaskStore.cs accidental…
PascalSenn 5bcca59
fix(cli): reject whitespace-only task titles in create/update/q
PascalSenn b9acdbb
fix: include manual status=blocked tasks in the Blocked board lane
PascalSenn 7114e55
feat(nitro-tasks): render Description, Design, Acceptance criteria, a…
PascalSenn 00de8e8
feat(nitro-tui): watch the task db and publish DataChangedEvent (perl…
PascalSenn 2ddf4dc
fix(nitro-tasks): blank separator row between stacked board columns
PascalSenn 6493632
fix(nitro-tui): clamp the edit/create modal to the frame and scroll f…
PascalSenn a6c007e
feat(nitro-tui): Ctrl+Enter (and Ctrl+S fallback) saves the task form…
PascalSenn 3840ffe
test(nitro-tasks): re-record board/board-maximize e2e goldens for sta…
PascalSenn 6f50ddb
refactor(nitro-tasks): rename CLI command family to nitro agent tasks
PascalSenn 16c71ff
test(nitro-tasks): re-record e2e goldens for nitro agent tasks rename
PascalSenn 91267c9
refactor: hoist (await ...).Member chains into named locals in Comman…
PascalSenn d43a119
refactor(nitro-task): convert TaskStore SQL to raw string literals (b…
PascalSenn 12156a0
Format remaining column-list label INSERTs as raw string literals
PascalSenn dbca30d
chore(nitro-tasks): remove stale task references
PascalSenn 356cfbe
Merge branch 'main' into mst/nitro-tasks
PascalSenn f2d499a
fix(ci): use the renamed agent tasks command path in the AOT publish …
PascalSenn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| name: nitro-cli-e2e | ||
|
|
||
| on: | ||
| push: | ||
| paths: | ||
| - 'src/Nitro/CommandLine/**' | ||
| - 'src/Nitro/Common/**' | ||
| - '.github/workflows/nitro-cli-e2e.yml' | ||
| - 'global.json' | ||
| - 'src/Directory.Packages.props' | ||
| pull_request: | ||
| paths: | ||
| - 'src/Nitro/CommandLine/**' | ||
| - 'src/Nitro/Common/**' | ||
| - '.github/workflows/nitro-cli-e2e.yml' | ||
| - 'global.json' | ||
| - 'src/Directory.Packages.props' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
|
|
||
| concurrency: | ||
| group: nitro-cli-e2e-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| aot-publish-smoke: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
|
|
||
| - name: Install .NET | ||
| uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 | ||
| with: | ||
| dotnet-version: 11.0.100-preview.6.26359.118 | ||
|
|
||
| - name: Publish AOT self-contained binary (linux-x64) | ||
| run: | | ||
| dotnet publish src/Nitro/CommandLine/src/CommandLine \ | ||
| -c Release \ | ||
| -r linux-x64 \ | ||
| -f net11.0 \ | ||
| -o publish \ | ||
| --self-contained \ | ||
| -p:PublishAot=true \ | ||
| -p:TargetFrameworks=net11.0 \ | ||
| -p:RuntimeIdentifiers=linux-x64 | ||
|
|
||
| - name: Smoke test the published binary | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| nitro_bin="$(pwd)/publish/nitro" | ||
| "$nitro_bin" --version | ||
|
|
||
| work_dir="$(mktemp -d)" | ||
| cd "$work_dir" | ||
| export NITRO_TASK_ACTOR=ci | ||
|
|
||
| "$nitro_bin" agent tasks init | ||
| "$nitro_bin" agent tasks list | ||
|
|
||
| create_output="$("$nitro_bin" agent tasks create "AOT smoke test task")" | ||
| echo "$create_output" | ||
| task_id=$(printf '%s' "$create_output" | sed -n "s/.*'\([^']*\)'.*/\1/p") | ||
| if [ -z "$task_id" ]; then | ||
| echo "::error::Could not parse a task ID out of the create output above." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| "$nitro_bin" agent tasks show "$task_id" | ||
|
|
||
| record-and-verify: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
|
|
||
| - name: Install .NET | ||
| uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0 | ||
| with: | ||
| dotnet-version: 11.0.100-preview.6.26359.118 | ||
|
|
||
| - name: Record + verify all flows | ||
| id: record | ||
| continue-on-error: true | ||
| run: ./src/Nitro/CommandLine/test/e2e/run.sh | ||
|
|
||
| - name: Upload snapshot artifacts | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | ||
| with: | ||
| name: nitro-e2e-snapshots | ||
| path: | | ||
| src/Nitro/CommandLine/test/e2e/out/*.gif | ||
| src/Nitro/CommandLine/test/e2e/out/report/** | ||
| if-no-files-found: warn | ||
|
|
||
| - name: Comment changed snapshots on PR | ||
| if: always() && github.event_name == 'pull_request' | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 | ||
| env: | ||
| RUN_ID: ${{ github.run_id }} | ||
| with: | ||
| script: | | ||
| const { owner, repo } = context.repo; | ||
| const pr = context.payload.pull_request; | ||
| const prNum = pr.number; | ||
| const baseRef = pr.base.ref; | ||
| const MARKER = '<!-- nitro-e2e-snapshots -->'; | ||
| const bust = `?run=${process.env.RUN_ID}`; | ||
|
|
||
| const rawBase = `https://raw.githubusercontent.com/${pr.base.repo.full_name}/${pr.base.sha}`; | ||
| const headFull = pr.head.repo && pr.head.repo.full_name; | ||
| const rawHead = headFull | ||
| ? `https://raw.githubusercontent.com/${headFull}/${pr.head.sha}` | ||
| : null; | ||
|
|
||
| const files = await github.paginate(github.rest.pulls.listFiles, | ||
| { owner, repo, pull_number: prNum, per_page: 100 }); | ||
| const re = /^src\/Nitro\/CommandLine\/test\/e2e\/([^/]+)-flow\.gif$/; | ||
| const changed = []; | ||
| for (const f of files) { | ||
| const m = f.filename.match(re); | ||
| if (m) changed.push( | ||
| { flow: m[1], status: f.status, path: f.filename, prev: f.previous_filename }); | ||
| } | ||
| changed.sort((a, b) => a.flow.localeCompare(b.flow)); | ||
|
|
||
| async function upsertComment(body) { | ||
| const { data: comments } = await github.rest.issues.listComments( | ||
| { owner, repo, issue_number: prNum, per_page: 100 }); | ||
| const existing = comments.find(c => c.body && c.body.includes(MARKER)); | ||
| if (existing) { | ||
| await github.rest.issues.updateComment( | ||
| { owner, repo, comment_id: existing.id, body }); | ||
| } else { | ||
| await github.rest.issues.createComment( | ||
| { owner, repo, issue_number: prNum, body }); | ||
| } | ||
| } | ||
|
|
||
| if (changed.length === 0) { | ||
| try { | ||
| await upsertComment( | ||
| `${MARKER}\n✅ **Nitro CLI E2E snapshots**: no flow recordings changed vs \`${baseRef}\`.`); | ||
| } catch (e) { | ||
| core.warning(`comment failed (expected on forks): ${e}`); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| const img = url => `<img src="${url}${bust}" width="480" alt="recording">`; | ||
| const ICON = { added: '🆕', removed: '🗑️' }; | ||
|
|
||
| const blocks = []; | ||
| for (const { flow, status, path, prev } of changed) { | ||
| const before = `${rawBase}/${prev || path}`; | ||
| const after = rawHead ? `${rawHead}/${path}` : null; | ||
| let inner; | ||
| if (status === 'added') { | ||
| inner = after ? `\n\n${img(after)}\n` : '\n\n_after unavailable_\n'; | ||
| } else if (status === 'removed') { | ||
| inner = `\n\n${img(before)}\n`; | ||
| } else { | ||
| const afterCell = after ? img(after) : '<em>unavailable</em>'; | ||
| inner = | ||
| `\n\n<table><tr><th>Before: <code>${baseRef}</code></th><th>After: this PR</th></tr>` | ||
| + `<tr><td>${img(before)}</td><td>${afterCell}</td></tr></table>\n`; | ||
| } | ||
| blocks.push( | ||
| `<details><summary>${ICON[status] || '🔁'} ${flow}: ${status}</summary>${inner}</details>`); | ||
| } | ||
|
|
||
| const body = [ | ||
| MARKER, | ||
| '### 🎬 Nitro CLI E2E snapshots changed', | ||
| '', | ||
| `These flow recordings differ from \`${baseRef}\` (added, removed, or modified). ` | ||
| + 'Expand each for the before/after.', | ||
| '', | ||
| ...blocks, | ||
| '', | ||
| `<sub>Run ${process.env.RUN_ID} · full set in the <b>nitro-e2e-snapshots</b> artifact.</sub>`, | ||
| ].join('\n'); | ||
|
|
||
| try { await upsertComment(body); } | ||
| catch (e) { core.warning(`comment failed (expected on forks): ${e}`); } | ||
|
|
||
| - name: Fail if any flow drifted from its golden | ||
| if: always() && steps.record.outcome == 'failure' | ||
| run: | | ||
| echo "::error::E2E recordings drifted from their committed golden, review the nitro-e2e-snapshots artifact, then run ./src/Nitro/CommandLine/test/e2e/run.sh --update <flow> if intended." >&2 | ||
| exit 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/Nitro/CommandLine/src/CommandLine/Commands/Agent/AgentCommand.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using ChilliCream.Nitro.CommandLine.Commands.Tasks; | ||
|
|
||
| namespace ChilliCream.Nitro.CommandLine.Commands.Agent; | ||
|
|
||
| internal sealed class AgentCommand : Command | ||
| { | ||
| public AgentCommand() : base("agent") | ||
| { | ||
| Description = "Commands for coding agents."; | ||
|
|
||
| Subcommands.Add(new TasksCommand()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
src/Nitro/CommandLine/src/CommandLine/Commands/Tasks/AddTaskCommentCommand.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| using ChilliCream.Nitro.CommandLine.Commands.Tasks.Options; | ||
| using ChilliCream.Nitro.CommandLine.Helpers; | ||
| using ChilliCream.Nitro.CommandLine.Results; | ||
| using ChilliCream.Nitro.CommandLine.Services; | ||
| using ChilliCream.Nitro.CommandLine.Services.Tasks; | ||
|
|
||
| namespace ChilliCream.Nitro.CommandLine.Commands.Tasks; | ||
|
|
||
| internal sealed class AddTaskCommentCommand : Command | ||
| { | ||
| public AddTaskCommentCommand() : base("add") | ||
| { | ||
| Description = "Add a comment to a task."; | ||
|
|
||
| Arguments.Add(Opt<TaskIdArgument>.Instance); | ||
| Arguments.Add(Opt<CommentTextArgument>.Instance); | ||
| Options.Add(Opt<TaskActorOption>.Instance); | ||
| Options.Add(Opt<OptionalOutputFormatOption>.Instance); | ||
|
|
||
| this.AddExamples("agent tasks comment add \"acme-1a2\" \"Looks good to me.\""); | ||
|
|
||
| this.SetActionWithExceptionHandling(ExecuteAsync); | ||
| } | ||
|
|
||
| private static async Task<int> ExecuteAsync( | ||
| ICommandServices services, | ||
| ParseResult parseResult, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| var console = services.GetRequiredService<INitroConsole>(); | ||
| var store = services.GetRequiredService<ITaskStore>(); | ||
| var environmentVariableProvider = services.GetRequiredService<IEnvironmentVariableProvider>(); | ||
| var resultHolder = services.GetRequiredService<IResultHolder>(); | ||
|
|
||
| var id = parseResult.GetRequiredValue(Opt<TaskIdArgument>.Instance); | ||
| var text = parseResult.GetRequiredValue(Opt<CommentTextArgument>.Instance); | ||
| var actor = TaskActor.Resolve( | ||
| parseResult.GetValue(Opt<TaskActorOption>.Instance), environmentVariableProvider); | ||
|
|
||
| var comment = await store.AddCommentAsync(id, text, actor, cancellationToken); | ||
|
|
||
| if (!console.IsHumanReadable) | ||
| { | ||
| resultHolder.SetResult(new ObjectResult(comment)); | ||
| return ExitCodes.Success; | ||
| } | ||
|
|
||
| console.OkLine($"Added comment to '{comment.TaskId.EscapeMarkup()}'."); | ||
|
|
||
| return ExitCodes.Success; | ||
| } | ||
| } |
77 changes: 77 additions & 0 deletions
77
src/Nitro/CommandLine/src/CommandLine/Commands/Tasks/AddTaskDependencyCommand.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| using ChilliCream.Nitro.CommandLine.Commands.Tasks.Options; | ||
| using ChilliCream.Nitro.CommandLine.Helpers; | ||
| using ChilliCream.Nitro.CommandLine.Results; | ||
| using ChilliCream.Nitro.CommandLine.Services; | ||
| using ChilliCream.Nitro.CommandLine.Services.Tasks; | ||
|
|
||
| namespace ChilliCream.Nitro.CommandLine.Commands.Tasks; | ||
|
|
||
| internal sealed class AddTaskDependencyCommand : Command | ||
| { | ||
| public AddTaskDependencyCommand() : base("add") | ||
| { | ||
| Description = "Add a dependency between two tasks."; | ||
|
|
||
| Arguments.Add(Opt<TaskIdArgument>.Instance); | ||
| Arguments.Add(Opt<DependsOnIdArgument>.Instance); | ||
|
|
||
| Options.Add(Opt<TaskDependencyTypeOption>.Instance); | ||
| Options.Add(Opt<TaskActorOption>.Instance); | ||
| Options.Add(Opt<OptionalOutputFormatOption>.Instance); | ||
|
|
||
| this.AddExamples( | ||
| "agent tasks dep add \"acme-1a2\" \"acme-9z8\"", | ||
| "agent tasks dep add \"acme-1a2\" \"acme-9z8\" --type waits-for"); | ||
|
|
||
| this.SetActionWithExceptionHandling(ExecuteAsync); | ||
| } | ||
|
|
||
| private static async Task<int> ExecuteAsync( | ||
| ICommandServices services, | ||
| ParseResult parseResult, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| var console = services.GetRequiredService<INitroConsole>(); | ||
| var store = services.GetRequiredService<ITaskStore>(); | ||
| var environmentVariableProvider = services.GetRequiredService<IEnvironmentVariableProvider>(); | ||
| var resultHolder = services.GetRequiredService<IResultHolder>(); | ||
|
|
||
| var id = parseResult.GetRequiredValue(Opt<TaskIdArgument>.Instance); | ||
| var dependsOnId = parseResult.GetRequiredValue(Opt<DependsOnIdArgument>.Instance); | ||
| var typeValue = parseResult.GetValue(Opt<TaskDependencyTypeOption>.Instance); | ||
| var type = typeValue is null | ||
| ? TaskDependencyTypes.Blocks | ||
| : TaskDependencyTypes.Normalize(typeValue); | ||
| var actor = TaskActor.Resolve( | ||
| parseResult.GetValue(Opt<TaskActorOption>.Instance), environmentVariableProvider); | ||
|
|
||
| var result = await store.AddDependencyAsync(id, dependsOnId, type, actor, cancellationToken); | ||
|
|
||
| if (!console.IsHumanReadable) | ||
| { | ||
| resultHolder.SetResult(new ObjectResult(new TaskDependencyAddedResult | ||
| { | ||
| Id = id, | ||
| DependsOnId = dependsOnId, | ||
| Type = type, | ||
| Cycle = result.Cycle | ||
| })); | ||
|
|
||
| return ExitCodes.Success; | ||
| } | ||
|
|
||
| console.OkLine( | ||
| $"Added {type.EscapeMarkup()} dependency: " | ||
| + $"'{id.EscapeMarkup()}' -> '{dependsOnId.EscapeMarkup()}'."); | ||
|
|
||
| return ExitCodes.Success; | ||
| } | ||
|
|
||
| public sealed record TaskDependencyAddedResult | ||
| { | ||
| public required string Id { get; init; } | ||
| public required string DependsOnId { get; init; } | ||
| public required string Type { get; init; } | ||
| public IReadOnlyList<string>? Cycle { get; init; } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.