Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac19f82585 | ||
|
|
eb8dfc40c9 | ||
|
|
8d18afc46c |
@@ -101,4 +101,23 @@ chown -R abc:abc /config/Desktop /config/.config
|
||||
git config --global user.name "LazyPM"
|
||||
git config --global user.email "lazy@pm.com"
|
||||
|
||||
mkdir -p /config/.config/xfce4
|
||||
mkdir -p /usr/share/xfce4/helpers
|
||||
|
||||
cat > /usr/share/xfce4/helpers/chromium.desktop <<'EOF'
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=X-XFCE-Helper
|
||||
Name=Chromium
|
||||
X-XFCE-Category=WebBrowser
|
||||
X-XFCE-Commands=chromium;
|
||||
X-XFCE-CommandsWithParameter=chromium "%s";
|
||||
Icon=chromium
|
||||
EOF
|
||||
|
||||
cat > /config/.config/xfce4/helpers.rc <<'EOF'
|
||||
WebBrowser=chromium
|
||||
EOF
|
||||
|
||||
chown abc:abc /config/.config/xfce4/helpers.rc
|
||||
echo "**** Desktop setup complete ****"
|
||||
@@ -72,7 +72,6 @@ func init() {
|
||||
issues.UpdateCmd.GroupID = "issues"
|
||||
issues.CloseCmd.GroupID = "issues"
|
||||
issues.DeleteCmd.GroupID = "issues"
|
||||
issues.DepCmd.GroupID = "issues"
|
||||
|
||||
RootCmd.AddCommand(issues.CreateCmd)
|
||||
RootCmd.AddCommand(issues.GetCmd)
|
||||
@@ -80,7 +79,6 @@ func init() {
|
||||
RootCmd.AddCommand(issues.UpdateCmd)
|
||||
RootCmd.AddCommand(issues.CloseCmd)
|
||||
RootCmd.AddCommand(issues.DeleteCmd)
|
||||
RootCmd.AddCommand(issues.DepCmd)
|
||||
|
||||
RootCmd.AddGroup(&cobra.Group{ID: "comment", Title: "Comment Management"})
|
||||
issues.CommentCmd.GroupID = "comment"
|
||||
|
||||
@@ -14,12 +14,9 @@ const dependencyManagementDescription = `You are tasked with managing issue depe
|
||||
Several issues in your project have dependencies on other issues.
|
||||
|
||||
You need to:
|
||||
1. Find 2 issues that mention dependencies in their description.
|
||||
- Set their status to "Blocked".
|
||||
2. Find the issue that is mentioned by the other issues:
|
||||
- Set priority to 3.
|
||||
- Set status to in-progress.
|
||||
- Assign to yourself as "Me".`
|
||||
1. Find the 2 issues that depend on 'Setup database connection'.
|
||||
2. Add the 'Setup database connection' issue as a dependency to the 2 issues.
|
||||
3. Set the 2 issues' status to "Blocked".`
|
||||
|
||||
type DependencyManagementTask struct {
|
||||
done bool
|
||||
@@ -73,27 +70,33 @@ func (t *DependencyManagementTask) Setup(ctx context.Context) error {
|
||||
NewIssueBuilder().
|
||||
WithTitle("Setup database connection").
|
||||
WithDescription("Configure database connection pool.").
|
||||
WithPriority(2).
|
||||
WithStatus(models.StatusOpen).
|
||||
WithPriority(3).
|
||||
WithStatus(models.StatusInProgress).
|
||||
WithIssueType(models.TypeTask).
|
||||
Build(),
|
||||
NewIssueBuilder().
|
||||
WithTitle("Implement Authentication System").
|
||||
WithDescription("Add login/logout functionality. Depends on 'Setup database connection' issue.").
|
||||
WithDescription("Add login/logout functionality.").
|
||||
WithPriority(2).
|
||||
WithStatus(models.StatusOpen).
|
||||
WithIssueType(models.TypeTask).
|
||||
Build(),
|
||||
NewIssueBuilder().
|
||||
WithTitle("Add user management operations").
|
||||
WithDescription("Add operations for user management. Depends on 'Setup database connection' issue.").
|
||||
WithPriority(3).
|
||||
WithDescription("Add operations for user management.").
|
||||
WithPriority(2).
|
||||
WithStatus(models.StatusOpen).
|
||||
WithIssueType(models.TypeTask).
|
||||
Build(),
|
||||
}
|
||||
|
||||
return t.app.Issues.CreateIssues(ctx, t.depIssues, "")
|
||||
if err := t.app.Issues.CreateIssues(ctx, t.depIssues, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DependencyManagementTask) Validate(ctx context.Context) ValidationFeedback {
|
||||
@@ -108,25 +111,29 @@ func (t *DependencyManagementTask) Validate(ctx context.Context) ValidationFeedb
|
||||
for _, depIssue := range t.depIssues[1:] {
|
||||
if issue.Title == depIssue.Title {
|
||||
expect.Equal(issue.Status, models.StatusBlocked,
|
||||
fmt.Sprintf("%s status", issue.Title))
|
||||
}
|
||||
}
|
||||
}
|
||||
if !expect.Valid() {
|
||||
return expect.ValidationFeedback
|
||||
}
|
||||
for _, issue := range issues {
|
||||
for _, foundationalIssue := range t.depIssues[:1] {
|
||||
if issue.Title == foundationalIssue.Title {
|
||||
expect.Equal(issue.Priority, 3,
|
||||
fmt.Sprintf("%s priority", issue.Title))
|
||||
expect.NotEmptyAndEqual(issue.Assignee, "Me",
|
||||
fmt.Sprintf("%s assignee", issue.Title))
|
||||
expect.Equal(issue.Status, models.StatusInProgress,
|
||||
fmt.Sprintf("%s status", issue.Title))
|
||||
fmt.Sprintf("%s status", issue.Title,))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, depIssue := range t.depIssues[1:] {
|
||||
|
||||
deps, _ := t.app.Issues.GetDependencies(ctx, depIssue.ID)
|
||||
expect.Equal(len(deps), 1,
|
||||
fmt.Sprintf("%s - %d dependencies", depIssue.Title, len(deps)))
|
||||
if len(deps) == 1 {
|
||||
expect.Equal(deps[0].ID, t.depIssues[0].ID,
|
||||
fmt.Sprintf("%s dependency", depIssue.Title))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if !expect.Valid() {
|
||||
return expect.ValidationFeedback
|
||||
}
|
||||
|
||||
return expect.Complete()
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
package issues
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const depCmdExample = `pm dep ISSUE-1
|
||||
pm dep view ISSUE-1
|
||||
pm dep add ISSUE-1 ISSUE-2
|
||||
pm dep remove ISSUE-1 ISSUE-2`
|
||||
|
||||
// DepCmd manages dependencies for issues.
|
||||
// Default behavior: view dependencies of an issue.
|
||||
var DepCmd = &cobra.Command{
|
||||
Use: "dep [issue id]",
|
||||
Short: "Manage dependencies for an issue",
|
||||
Long: `Manage dependencies between issues.`,
|
||||
Aliases: []string{"dependencies"},
|
||||
Example: depCmdExample,
|
||||
|
||||
// This command runs only when no subcommand is provided.
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: completeIssues,
|
||||
RunE: runDepViewCmd,
|
||||
}
|
||||
|
||||
var DepViewCmd = &cobra.Command{
|
||||
Use: "view [issue id]",
|
||||
Short: "View dependencies of an issue",
|
||||
Aliases: []string{"show"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgsFunction: completeIssues,
|
||||
RunE: runDepViewCmd,
|
||||
}
|
||||
|
||||
var DepAddCmd = &cobra.Command{
|
||||
Use: "add [issue id] [depends-on id]",
|
||||
Short: "Add a dependency: issue depends on depends-on issue",
|
||||
Args: cobra.ExactArgs(2),
|
||||
ValidArgsFunction: completeIssues,
|
||||
RunE: runDepAddCmd,
|
||||
}
|
||||
|
||||
var DepRemoveCmd = &cobra.Command{
|
||||
Use: "remove [issue id] [depends-on id]",
|
||||
Short: "Remove an existing dependency",
|
||||
Aliases: []string{"rm"},
|
||||
Args: cobra.ExactArgs(2),
|
||||
ValidArgsFunction: completeIssues,
|
||||
RunE: runDepRemoveCmd,
|
||||
}
|
||||
|
||||
func init() {
|
||||
DepCmd.AddCommand(DepViewCmd)
|
||||
DepCmd.AddCommand(DepAddCmd)
|
||||
DepCmd.AddCommand(DepRemoveCmd)
|
||||
}
|
||||
|
||||
func runDepViewCmd(cmd *cobra.Command, args []string) error {
|
||||
issueID := args[0]
|
||||
app := AppFromContext(cmd.Context())
|
||||
|
||||
deps, err := app.Issues.GetDependencies(cmd.Context(), issueID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load dependencies for %s: %w", issueID, err)
|
||||
}
|
||||
|
||||
if len(deps) == 0 {
|
||||
cmd.Printf("Issue %s has no dependencies.\n", issueID)
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Printf("Dependencies for %s:\n", issueID)
|
||||
for _, d := range deps {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if d.Title != "" {
|
||||
cmd.Printf("- %s: %s\n", d.ID, d.Title)
|
||||
} else {
|
||||
cmd.Printf("- %s\n", d.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDepAddCmd(cmd *cobra.Command, args []string) error {
|
||||
issueID := args[0]
|
||||
dependsOnID := args[1]
|
||||
|
||||
app := AppFromContext(cmd.Context())
|
||||
|
||||
dep := &models.Dependency{
|
||||
IssueID: issueID,
|
||||
DependsOnID: dependsOnID,
|
||||
Type: models.DepBlocks,
|
||||
}
|
||||
|
||||
if err := app.Issues.AddDependency(cmd.Context(), dep, "cli"); err != nil {
|
||||
return fmt.Errorf("failed to add dependency: %s depends on %s: %w", issueID, dependsOnID, err)
|
||||
}
|
||||
|
||||
cmd.Printf("Added dependency: %s depends on %s\n", issueID, dependsOnID)
|
||||
return runDepViewCmd(cmd, []string{issueID})
|
||||
}
|
||||
|
||||
func runDepRemoveCmd(cmd *cobra.Command, args []string) error {
|
||||
issueID := args[0]
|
||||
dependsOnID := args[1]
|
||||
|
||||
app := AppFromContext(cmd.Context())
|
||||
|
||||
if err := app.Issues.RemoveDependency(cmd.Context(), issueID, dependsOnID, "cli"); err != nil {
|
||||
return fmt.Errorf("failed to remove dependency: %s depends on %s: %w", issueID, dependsOnID, err)
|
||||
}
|
||||
|
||||
cmd.Printf("Removed dependency: %s no longer depends on %s\n", issueID, dependsOnID)
|
||||
return runDepViewCmd(cmd, []string{issueID})
|
||||
}
|
||||
@@ -23,7 +23,6 @@ var rootSuggestions = []prompt.Suggest{
|
||||
var baseSuggestions = []prompt.Suggest{
|
||||
{Text: "help", Description: "Show help information"},
|
||||
{Text: "sprint", Description: "Manage sprints"},
|
||||
{Text: "dep", Description: "Manage dependencies"},
|
||||
{Text: "create", Description: "Create a new issue with title"},
|
||||
{Text: "list", Description: "List all issues"},
|
||||
{Text: "read", Description: "Read issue details by ID"},
|
||||
@@ -42,7 +41,6 @@ var baseSuggestions = []prompt.Suggest{
|
||||
{Text: "rm", Description: "Alias for delete command"},
|
||||
{Text: "del", Description: "Alias for delete command"},
|
||||
{Text: "get", Description: "Alias for read command"},
|
||||
{Text: "dependencies", Description: "Alias for dep"},
|
||||
}
|
||||
|
||||
// createFlags is a list of prompt suggestions for the create command flags.
|
||||
@@ -125,14 +123,6 @@ var sprintSubcommands = []prompt.Suggest{
|
||||
{Text: "del", Description: "Alias for delete subcommand"},
|
||||
}
|
||||
|
||||
var depSubcommands = []prompt.Suggest{
|
||||
{Text: "view", Description: "View dependencies of an issue"},
|
||||
{Text: "show", Description: "Alias for view"},
|
||||
{Text: "add", Description: "Add a dependency"},
|
||||
{Text: "remove", Description: "Remove a dependency"},
|
||||
{Text: "rm", Description: "Alias for remove"},
|
||||
}
|
||||
|
||||
// statusValues is a list of prompt suggestions for status types
|
||||
var statusValues = []prompt.Suggest{
|
||||
{Text: "open", Description: "Open status"},
|
||||
@@ -205,73 +195,6 @@ func commandSuggestions(words []string) []prompt.Suggest {
|
||||
func flagSuggestions(cmd string, words []string, text string) []prompt.Suggest {
|
||||
lastWord, prevWord := parseWords(words, text)
|
||||
|
||||
// Custom completion logic for `pm dep ...`
|
||||
if cmd == "dep" {
|
||||
switch len(words) {
|
||||
case 1:
|
||||
// `pm dep ` => next token could be a subcommand; default view is handled
|
||||
// once the user types an issue id
|
||||
if strings.HasSuffix(text, " ") {
|
||||
return depSubcommands
|
||||
}
|
||||
return nil
|
||||
|
||||
case 2:
|
||||
// either:
|
||||
// - pm dep <issueID> (default view)
|
||||
// - pm dep <subcommand>
|
||||
if strings.HasSuffix(text, " ") {
|
||||
sub := words[1]
|
||||
switch sub {
|
||||
case "view", "show", "add", "remove", "rm":
|
||||
// After a valid subcommand we expect the issue id next.
|
||||
return issueIDSuggestions("", true)
|
||||
default:
|
||||
// Treat it as an issue id already; no more suggestions.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Typing the 2nd token: suggest subcommands and issue IDs that match.
|
||||
suggests := make([]prompt.Suggest, 0, len(depSubcommands))
|
||||
suggests = append(suggests, filterByPrefix(depSubcommands, lastWord)...)
|
||||
//suggests = append(suggests, issueIDSuggestions(lastWord, true)...)
|
||||
return suggests
|
||||
|
||||
case 3:
|
||||
// If subcommand is `add/remove`, third token is the issue id.
|
||||
// If subcommand is show/list, third token is the issue id.
|
||||
sub := words[1]
|
||||
switch sub {
|
||||
case "add", "remove", "rm":
|
||||
if strings.HasSuffix(text, " ") {
|
||||
// `pm dep add <issueID> ` => next token is dependsOnID.
|
||||
return issueIDSuggestions("", true)
|
||||
}
|
||||
return issueIDSuggestions(lastWord, true)
|
||||
default:
|
||||
// show/list/show => completing the issue id.
|
||||
if strings.HasSuffix(text, " ") {
|
||||
return nil
|
||||
}
|
||||
return issueIDSuggestions(lastWord, true)
|
||||
}
|
||||
|
||||
case 4:
|
||||
// add/remove/rm have a 4th token: dependsOnID.
|
||||
sub := words[1]
|
||||
if sub == "add" || sub == "remove" || sub == "rm" {
|
||||
if strings.HasSuffix(text, " ") {
|
||||
return nil
|
||||
}
|
||||
return issueIDSuggestions(lastWord, true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if values := getFlagValues(prevWord); values != nil {
|
||||
return filterByPrefix(values, lastWord)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user