diff --git a/cmd/gitw/main.go b/cmd/gitw/main.go index 3d699d2..020553c 100644 --- a/cmd/gitw/main.go +++ b/cmd/gitw/main.go @@ -23,11 +23,11 @@ func main() { } project := gitw.NewProject(server, repository) - if err = project.Fetch(); err != nil { + fmt.Printf("Pulling changes from %s\n", repository.Full_name) + if err = project.Pull(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(-1) } - fmt.Printf("%#v\n", project) // NOTE: this can be used to add / modify tasks // var update_tasks []taskwarrior.Task diff --git a/internal/gitea/issue.go b/internal/gitea/issue.go index f8895ba..f8acad7 100644 --- a/internal/gitea/issue.go +++ b/internal/gitea/issue.go @@ -13,6 +13,11 @@ type PullRequest struct { Merged_at time.Time `json:"merged_at"` } +type IssueState string + +const CLOSED IssueState = "closed" +const OPEN IssueState = "open" + type Issue struct { Id uint `json:"id"` Url string `json:"url"` @@ -104,7 +109,7 @@ func (gitea *Gitea) NewIssue(repo Repository, issue Issue) (Issue, error) { payload["assignee"] = issue.Assignee payload["assignees"] = issue.Assignees payload["body"] = issue.Body - payload["closed"] = issue.State == "closed" + payload["closed"] = issue.State == string(CLOSED) payload["due_date"] = issue.Due_date payload["lables"] = issue.Labels payload["milestone"] = issue.Milestone.Title != "" diff --git a/internal/gitea/milestone.go b/internal/gitea/milestone.go index 87a024b..bb85a18 100644 --- a/internal/gitea/milestone.go +++ b/internal/gitea/milestone.go @@ -6,6 +6,8 @@ import ( "fmt" "net/http" "time" + + "gitea.yves-biener.de/yves-biener/gitwarrior/internal/taskwarrior" ) type Milestone struct { @@ -21,6 +23,11 @@ type Milestone struct { Closed_at time.Time `json:"closed_at"` } +// TODO: implement merge for milestone tasks +func (milestone *Milestone) Merge(task taskwarrior.Task) taskwarrior.Task { + return task +} + func (gitea *Gitea) GetMilestones(repo Repository) ([]Milestone, error) { url := fmt.Sprintf("%s/repos/%s/milestones?state=all", gitea.Url(), repo.Full_name) response, err := http.Get(url) diff --git a/internal/gitw/project.go b/internal/gitw/project.go index c91f13b..02cb243 100644 --- a/internal/gitw/project.go +++ b/internal/gitw/project.go @@ -2,6 +2,7 @@ package gitw import ( "errors" + "fmt" "gitea.yves-biener.de/yves-biener/gitwarrior/internal/gitea" "gitea.yves-biener.de/yves-biener/gitwarrior/internal/taskwarrior" @@ -52,38 +53,115 @@ func (project *Project) Fetch() error { } } project.issues = append(project.issues, Issue{ - issue: issue, - comments: issue_comments, + git_issue: issue, + comments: issue_comments, }) } return nil } +// TODO: tasks should include the corresponding time's of the git related issues and milestones func (project *Project) merge() error { - // TODO: run merge to update the local taskwarrior tasks into the state of the - // issues and milestones + var tasks []taskwarrior.Task + var filter taskwarrior.Filter + // TODO: merge milestones + for _, milestone := range project.milestones { + filter.Reset() + filter.IncludeGitId(milestone.Id) + filter.IncludeGitType(taskwarrior.MILESTONE) + git_tasks, err := taskwarrior.GetTasks(filter) + if err != nil { + return err + } + var task taskwarrior.Task + if len(git_tasks) > 1 { + return errors.New("Git milestone id was at least used twice in taskwarrior tasks.") + } else if len(git_tasks) == 0 { + // NOTE: ignore closed milestones which do not have a taskwarrior task + if milestone.State == string(gitea.CLOSED) { + continue + } + // NOTE: this milestone does not yet exist + task = taskwarrior.NewTask( + milestone.Title, + project.repository.Name, + milestone.Id, + taskwarrior.MILESTONE, + ) + task.AppendComment(milestone.Description, milestone.Updated_at) + fmt.Printf("\tCreated milestone: '%s'\n", task.Description) + tasks = append(tasks, task) + } else { + // NOTE: there is exactly one git_task + task = milestone.Merge(git_tasks[0]) + fmt.Printf("\tUpdated milestone: '%s'\n", task.Description) + tasks = append(tasks, task) + } + } + // NOTE: merge tasks + // TODO: link milestones into tasks + for _, issue := range project.issues { + filter.Reset() + filter.IncludeGitId(issue.git_issue.Id) + filter.IncludeGitType(taskwarrior.ISSUE) + git_tasks, err := taskwarrior.GetTasks(filter) + if err != nil { + return err + } + var task taskwarrior.Task + if len(git_tasks) > 1 { + return errors.New("Git issue id was at least used twice in taskwarrior tasks.") + } else if len(git_tasks) == 0 { + // NOTE: ignore closed issues which do not have a taskwarrior task + if issue.git_issue.State == string(gitea.CLOSED) { + continue + } + // NOTE: this task does not yet exist + task = taskwarrior.NewTask( + issue.git_issue.Title, + project.repository.Name, + issue.git_issue.Id, + taskwarrior.ISSUE, + issue.git_issue.Labels..., + ) + for _, comment := range issue.comments { + task.AppendComment(comment.Body, comment.Updated_at) + } + fmt.Printf("\tCreated task: '%s'\n", task.Description) + tasks = append(tasks, task) + } else { + // NOTE: there is excactly one git_task + task = issue.merge(git_tasks[0]) + fmt.Printf("\tUpdated task: '%s'\n", task.Description) + tasks = append(tasks, task) + } + } + // TODO: dry-run switch + if false { + return taskwarrior.UpdateTasks(tasks) + } return nil } type Issue struct { - issue gitea.Issue - comments []gitea.Comment + git_issue gitea.Issue + comments []gitea.Comment } func (i *Issue) into() (taskwarrior.Task, error) { // TODO: identify if issue is already an existing task var filter taskwarrior.Filter - filter.IncludeGitId(i.issue.Id) + filter.IncludeGitId(i.git_issue.Id) filter.IncludeGitType(taskwarrior.ISSUE) tasks, err := taskwarrior.GetTasks(filter) if err != nil { // this means that a task for this issue does not exist yet return taskwarrior.NewTask( - i.issue.Title, - i.issue.Repository.Name, - i.issue.Id, + i.git_issue.Title, + i.git_issue.Repository.Name, + i.git_issue.Id, taskwarrior.ISSUE, - i.issue.Labels..., + i.git_issue.Labels..., ), nil } else { // this means that a task exists and it needs to be merged @@ -94,6 +172,7 @@ func (i *Issue) into() (taskwarrior.Task, error) { } } +// TODO: implement merging of git issue or milestone into a taskwarrior task func (i *Issue) merge(task taskwarrior.Task) taskwarrior.Task { // TODO: issue values into task: // - is the issue more recent than the task? diff --git a/internal/taskwarrior/task.go b/internal/taskwarrior/task.go index 665895a..ed9ab58 100644 --- a/internal/taskwarrior/task.go +++ b/internal/taskwarrior/task.go @@ -35,6 +35,14 @@ type Annotation struct { Entry string `json:"entry,omitempty"` } +func (task *Task) AppendComment(description string, time time.Time) { + annotation := Annotation{ + Description: description, + Entry: GoTimeToTaskTime(time), + } + task.Annotations = append(task.Annotations, annotation) +} + func NewTask(description string, project string, git_id uint, git_type Type, tags ...string) Task { // TODO: update task struct to include the new user defined value, which shall // also be provided as an argument