status filter
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
@@ -11,6 +13,14 @@ import (
|
||||
func Run() {
|
||||
ui := NewUI() // Instantiate UI
|
||||
|
||||
// Parse flags
|
||||
includeStatusFlag := flag.String("status", "", "Filter issues by status (comma-separated names or IDs)")
|
||||
excludeStatusFlag := flag.String("exclude-status", "", "Exclude issues by status (comma-separated names or IDs)")
|
||||
flag.Usage = func() {
|
||||
ui.PrintUsage(os.Args[0])
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// Load .env file
|
||||
err := godotenv.Load()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
@@ -27,9 +37,22 @@ func Run() {
|
||||
apiKey := ""
|
||||
|
||||
// Try to get values from config file first
|
||||
var includeStatuses []string
|
||||
var excludeStatuses []string
|
||||
|
||||
if config != nil {
|
||||
baseURL = config.Host
|
||||
apiKey = config.Token
|
||||
includeStatuses = config.IncludeStatuses
|
||||
excludeStatuses = config.ExcludeStatuses
|
||||
}
|
||||
|
||||
// Flag overrides config file
|
||||
if *includeStatusFlag != "" {
|
||||
includeStatuses = strings.Split(*includeStatusFlag, ",")
|
||||
}
|
||||
if *excludeStatusFlag != "" {
|
||||
excludeStatuses = strings.Split(*excludeStatusFlag, ",")
|
||||
}
|
||||
|
||||
// Environment variables override config file
|
||||
@@ -43,8 +66,14 @@ func Run() {
|
||||
var projectID string
|
||||
|
||||
// projectID must be provided as a command-line argument, or list projects
|
||||
if len(os.Args) > 1 {
|
||||
projectID = os.Args[1]
|
||||
if flag.NArg() > 0 {
|
||||
projectID = flag.Arg(0)
|
||||
// Check for misplaced flags
|
||||
for i := 1; i < flag.NArg(); i++ {
|
||||
if strings.HasPrefix(flag.Arg(i), "-") {
|
||||
ui.PrintError("Error: flags must come BEFORE the project ID. Found misplaced flag: %s\n", flag.Arg(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
projects, err := model.FetchAllProjects(baseURL, apiKey)
|
||||
if err != nil {
|
||||
@@ -70,12 +99,15 @@ func Run() {
|
||||
ui.PrintError("Error fetching issues: %v\n", err)
|
||||
}
|
||||
|
||||
ui.PrintTotalIssuesFetched(len(issues))
|
||||
// Apply filtering
|
||||
filteredIssues := model.FilterIssues(issues, includeStatuses, excludeStatuses)
|
||||
|
||||
roots := model.BuildTree(issues) // Corrected call
|
||||
ui.PrintTotalIssuesFetched(len(filteredIssues))
|
||||
|
||||
roots := model.BuildTree(filteredIssues) // Corrected call
|
||||
ui.PrintIssueTreeHeader(len(roots))
|
||||
model.PrintTree(roots, "", false) // Corrected call
|
||||
|
||||
// Print summary
|
||||
ui.PrintSummary(len(issues), len(roots))
|
||||
ui.PrintSummary(len(filteredIssues), len(roots))
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
type Config struct {
|
||||
Host string `json:"host"`
|
||||
Token string `json:"token"`
|
||||
IncludeStatuses []string `json:"include_statuses,omitempty"`
|
||||
ExcludeStatuses []string `json:"exclude_statuses,omitempty"`
|
||||
}
|
||||
|
||||
// Load configuration from ~/.redmine-tree.json
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// --- Redmine API structs ---
|
||||
@@ -28,6 +29,58 @@ type IssuesResponse struct {
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// FilterIssues filters issues based on included and excluded status names or IDs.
|
||||
func FilterIssues(issues []Issue, includeStatuses, excludeStatuses []string) []Issue {
|
||||
if len(includeStatuses) == 0 && len(excludeStatuses) == 0 {
|
||||
return issues
|
||||
}
|
||||
|
||||
var filtered []Issue
|
||||
for _, issue := range issues {
|
||||
statusID := fmt.Sprintf("%d", issue.Status.ID)
|
||||
statusName := strings.ToLower(issue.Status.Title)
|
||||
|
||||
keep := true
|
||||
|
||||
// If includeStatuses is specified, issue MUST match at least one
|
||||
if len(includeStatuses) > 0 {
|
||||
match := false
|
||||
for _, s := range includeStatuses {
|
||||
s = strings.TrimSpace(strings.ToLower(s))
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if s == statusID || s == statusName {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
keep = false
|
||||
}
|
||||
}
|
||||
|
||||
// If excludeStatuses is specified, issue MUST NOT match any
|
||||
if keep && len(excludeStatuses) > 0 {
|
||||
for _, s := range excludeStatuses {
|
||||
s = strings.TrimSpace(strings.ToLower(s))
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if s == statusID || s == statusName {
|
||||
keep = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if keep {
|
||||
filtered = append(filtered, issue)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Fetch all issues with pagination
|
||||
func FetchAllIssues(baseURL, apiKey, projectID string) ([]Issue, error) {
|
||||
var all []Issue
|
||||
|
||||
@@ -48,10 +48,13 @@ func (ui *UI) PrintProjects(projects []model.Project) {
|
||||
|
||||
// PrintUsage prints the application usage message to stderr and exits.
|
||||
func (ui *UI) PrintUsage(appName string) {
|
||||
ui.Printf("Usage: %s <project-id>\n", appName)
|
||||
ui.Printf("Usage: %s [options] <project-id>\n", appName)
|
||||
ui.Printf("Options:\n")
|
||||
ui.Printf(" -status <statuses> Include only issues with these statuses (comma-separated names or IDs)\n")
|
||||
ui.Printf(" -exclude-status <statuses> Exclude issues with these statuses (comma-separated names or IDs)\n\n")
|
||||
ui.Printf("Environment:\n")
|
||||
ui.Printf(" REDMINE_URL and REDMINE_TOKEN must be set in .env, as environment variables, or in ~/.redmine-tree.json.\n")
|
||||
ui.Printf("Example: REDMINE_URL=https://redmine.example.com REDMINE_TOKEN=abc123 %s my-project\n", appName)
|
||||
ui.Printf("Or: %s my-project (if REDMINE_URL, REDMINE_TOKEN are in .env or ~/.redmine-tree.json)\n", appName)
|
||||
ui.Printf("Example: REDMINE_URL=https://redmine.example.com REDMINE_TOKEN=abc123 %s -status 'In Progress' my-project\n", appName)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user