Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions DevOps/Actions/DeleteAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using DevOps.Options;
using DevOps.Services;
using DevOps.Utils;

namespace DevOps.Actions;

internal static class DeleteAction
{
internal static async Task<int> Execute(DeleteOptions opts, CancellationToken ct)
{
try
{
var project = ConfigService.ResolveProject(opts.Project);
var ids = opts.Ids.ToList();

if (!opts.Force)
{
Console.WriteLine($"About to delete {ids.Count} work item(s): {string.Join(", ", ids.Select(i => "#" + i))}");
Console.Write("They will be moved to the recycle bin. Continue? [y/N] ");
var answer = Console.ReadLine();
if (!string.Equals(answer, "y", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Aborted.");
return 0;
}
}

var tasks = ids.Select(async id =>
{
try
{
await HttpService.DeleteWorkItem(id, project, ct);
ConsoleHelper.WriteSuccess($"Deleted work item #{id}");
}
catch (Exception ex)
{
ConsoleHelper.WriteError($"Error on #{id}: {ex.Message}");
}
});

await Task.WhenAll(tasks);
return 0;
}
catch (Exception ex)
{
ConsoleHelper.WriteError($"Error: {ex.Message}");
return 1;
}
}
}
16 changes: 16 additions & 0 deletions DevOps/Options/DeleteOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using CommandLine;

namespace DevOps.Options;

[Verb("delete", HelpText = "Delete one or more work items (moves them to the recycle bin).")]
public class DeleteOptions
{
[Option('i', "id", Required = true, HelpText = "Work item ID(s). Space-separated after the flag: -i 1 2 3.")]
public IEnumerable<int> Ids { get; set; }

[Option('p', "project", Required = false, HelpText = "Project name. Uses default if configured.")]
public string Project { get; set; }

[Option("force", Required = false, HelpText = "Skip the confirmation prompt.")]
public bool Force { get; set; }
}
2 changes: 2 additions & 0 deletions DevOps/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
typeof(CreateOptions),
typeof(UpdateOptions),
typeof(StateOptions),
typeof(DeleteOptions),
typeof(CommentOptions),
typeof(PipelinesOptions),
typeof(RunsOptions),
Expand All @@ -40,6 +41,7 @@ await result.MapResult(
CreateOptions o => CreateAction.Execute(o, cts.Token),
UpdateOptions o => UpdateAction.Execute(o, cts.Token),
StateOptions o => StateAction.Execute(o, cts.Token),
DeleteOptions o => DeleteAction.Execute(o, cts.Token),
CommentOptions o => CommentAction.Execute(o, cts.Token),
PipelinesOptions o => PipelinesAction.Execute(o, cts.Token),
RunsOptions o => RunsAction.Execute(o, cts.Token),
Expand Down
13 changes: 13 additions & 0 deletions DevOps/Services/HttpService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,4 +341,17 @@ public static async Task<WorkItemResponse> UpdateWorkItem(int id, string project

return JsonConvert.DeserializeObject<WorkItemResponse>(response.Content);
}

/// <summary>Deletes a work item, moving it to the project recycle bin (recoverable).</summary>
public static async Task DeleteWorkItem(int id, string project, CancellationToken cancellationToken = default)
{
using var client = await CreateClientAsync(cancellationToken);
var request = new RestRequest($"{project}/_apis/wit/workitems/{id}", Method.Delete);
request.AddQueryParameter("api-version", API_VERSION);

var response = await client.ExecuteAsync(request, cancellationToken);

if (!response.IsSuccessStatusCode)
throw new Exception($"Failed to delete work item {id}. Status: {response.StatusCode}. {response.Content}");
}
}
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,24 @@ Output: `Work item #1234: To Do -> In Progress`

---

### `delete` — Delete one or more work items

Moves the work items to the project **recycle bin** (recoverable, not a permanent delete). Prompts for confirmation unless `--force`. Multiple IDs are processed in parallel.

```powershell
devops delete -i 1234
devops delete -i 1234 5678 9012
devops delete -i 1234 --force
```

| Option | Alias | Description |
|---|---|---|
| `--id` | `-i` | Work item ID(s) (required). Space-separated: `-i 1 2 3` |
| `--project` | `-p` | Project name (uses default if configured) |
| `--force` | | Skip the confirmation prompt |

---

### `open` — Open a work item in the browser

```powershell
Expand Down
Loading