Documentation
¶
Overview ¶
Package app resolves the selected project and names into ids and computes minimal writes for the curated commands. It talks to Taiga only through the narrow API interface.
Index ¶
- func DeleteNotConfirmed(flag string) error
- func FieldPath(kind string) (string, error)
- func ID(v any) int64
- func MergeIDs(current, add, remove []int64) []int64
- func MergeNames(current, add, remove []string) []string
- func Names(v any) ([]string, error)
- func OrderStatuses(current []string, specs []StatusSpec) ([]string, error)
- func ParseFieldValue(typ, raw string) (any, error)
- func PreviewProject(plan ProjectPlan, statuses []Object, projectID any) ([]WritePlan, []Action, error)
- func Scrub(v any) any
- func Snapshot(o Object) (map[string]json.RawMessage, error)
- func SortStatuses(statuses []Object)
- func SystemComment(o Object) bool
- func TagName(s string) string
- func UnsetFieldValue(typ, name string) error
- func Unsupported(cause, recovery string) error
- func Usage(cause string) error
- func ValidateField(name, typ string) error
- func ValuePath(kind string, id int64) (string, error)
- func WriteApplied(method, path string, status int, readErr error) error
- type API
- type Action
- type ApplyResult
- type FieldSpec
- type LinkPlan
- type Object
- func AssignmentPatch(before Object, p Patch) (Object, error)
- func BuildPatch(before Object, p Patch) (Object, error)
- func Decode(b []byte) (Object, error)
- func FieldsView(values Object, defs []Object) []Object
- func MergeValues(current Object, updates Object) Object
- func Projects(ctx context.Context, api API, search string) ([]Object, error)
- func Read(ctx context.Context, api API, path string, q url.Values) (Object, error)
- func Resolve(items []Object, selector, nameKey string) (Object, error)
- type Patch
- type ProjectPlan
- type ProjectSpec
- type Service
- func (s *Service) ApplyProject(ctx context.Context, spec ProjectSpec, dry bool) (ApplyResult, error)
- func (s *Service) Attachments(ctx context.Context, kind string, owner Object) ([]Object, error)
- func (s *Service) Catalog(ctx context.Context, path string) ([]Object, error)
- func (s *Service) CloseStory(ctx context.Context, ref, selector string, dry, force bool) (any, error)
- func (s *Service) CloseTask(ctx context.Context, ref, selector string, dry, force bool) (any, error)
- func (s *Service) Comment(ctx context.Context, name, ref, body string, dry bool) (any, error)
- func (s *Service) Comments(ctx context.Context, name, ref string, includeSystem bool) ([]Object, error)
- func (s *Service) CreateField(ctx context.Context, kind, name, typ string, description *string, dry bool) (any, error)
- func (s *Service) CreateStory(ctx context.Context, body Object, dry bool) (any, error)
- func (s *Service) CreateStoryWithEpic(ctx context.Context, body Object, epic Object, dry bool) (any, error)
- func (s *Service) CreateTask(ctx context.Context, story Object, body Object, dry bool) (any, error)
- func (s *Service) DownloadAttachment(ctx context.Context, kind string, owner Object, id int64, dest string, ...) (Object, error)
- func (s *Service) Epic(ctx context.Context, ref string) (Object, error)
- func (s *Service) EpicDetail(ctx context.Context, ref string) (Object, error)
- func (s *Service) Epics(ctx context.Context, search string, closed *bool) ([]Object, error)
- func (s *Service) FieldValues(ctx context.Context, kind string, id int64) (Object, error)
- func (s *Service) Fields(ctx context.Context, kind string) ([]Object, error)
- func (s *Service) Item(ctx context.Context, name, ref string) (Object, error)
- func (s *Service) LinkEpic(ctx context.Context, story, epic Object, replace, dry bool) (any, error)
- func (s *Service) LinkableEpic(ctx context.Context, ref string) (Object, error)
- func (s *Service) Member(ctx context.Context, selector string) (Object, error)
- func (s *Service) Milestones(ctx context.Context, search string, closed *bool) ([]Object, error)
- func (s *Service) ProjectPlan(ctx context.Context, spec ProjectSpec) (ProjectPlan, error)
- func (s *Service) ProjectView() Object
- func (s *Service) SetFieldValues(ctx context.Context, kind string, id int64, entries, unsets []string, ...) (any, error)
- func (s *Service) Stories(ctx context.Context, filters url.Values) ([]Object, error)
- func (s *Service) Story(ctx context.Context, ref string, id int64) (Object, error)
- func (s *Service) StoryView(o Object) (Object, error)
- func (s *Service) Swimlanes(ctx context.Context) ([]Object, error)
- func (s *Service) Task(ctx context.Context, ref string, id int64) (Object, error)
- func (s *Service) TaskView(o Object) (Object, error)
- func (s *Service) Tasks(ctx context.Context, filters url.Values) ([]Object, error)
- func (s *Service) UpdateStory(ctx context.Context, ref string, p Patch, dry, force bool) (any, error)
- func (s *Service) UpdateStoryWithEpic(ctx context.Context, ref string, p Patch, epic Object, ...) (any, error)
- func (s *Service) UpdateTask(ctx context.Context, ref string, p Patch, dry, force bool) (any, error)
- func (s *Service) Upload(ctx context.Context, kind string, owner Object, path, description string, ...) (any, error)
- func (s *Service) User(ctx context.Context, selector string) (Object, error)
- func (s *Service) Users(ctx context.Context, search string) ([]Object, error)
- func (s *Service) View(name string, o Object) (Object, error)
- func (s *Service) Write(ctx context.Context, k kind, path string, before, patch Object, ...) (any, error)
- type StatusSpec
- type StatusWriter
- type UploadFile
- type UploadPlan
- type WritePlan
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DeleteNotConfirmed ¶ added in v0.3.0
DeleteNotConfirmed refuses a replacement without --confirm-delete, before any request (also with --dry-run, so a script learns of the missing flag before the real run).
func MergeIDs ¶
MergeIDs returns current ∪ add − remove, keeping order and dropping duplicates. It never returns nil, so removing the last id sends [].
func MergeNames ¶
MergeNames returns current ∪ add − remove, keeping order and dropping duplicates.
func OrderStatuses ¶
func OrderStatuses(current []string, specs []StatusSpec) ([]string, error)
OrderStatuses returns the desired order of every status: current ones (in their order) plus the declared ones that do not exist yet, appended in file order, each declaration with After moved right behind its predecessor. Statuses after the same predecessor keep the file order. An unknown predecessor or a cycle is an error.
func ParseFieldValue ¶
ParseFieldValue converts raw to the JSON value of a field of type typ. Taiga does not validate values (docs/api-notes.md), so the CLI does. An assignment never unsets a field: "null" is text, and an empty date is refused; unsetting takes UnsetFieldValue.
func PreviewProject ¶
func PreviewProject(plan ProjectPlan, statuses []Object, projectID any) ([]WritePlan, []Action, error)
PreviewProject materializes the requests of plan without sending anything. statuses is the read that produced plan. New statuses get an order after the current ones, in file order.
func Scrub ¶ added in v0.3.0
Scrub returns a deep copy of v with the value of every token parameter hidden, in every string at any depth (taiga.RedactTokens). Keys and other values stay. Every curated output goes through it: user photos (owner_extra_info, assigned_to_extra_info, the history user), project logos and attachment links are signed media URLs that open without authentication.
func Snapshot ¶
func Snapshot(o Object) (map[string]json.RawMessage, error)
Snapshot converts an object read from Taiga back to raw JSON, for WriteVersionedFrom.
func SortStatuses ¶
func SortStatuses(statuses []Object)
SortStatuses orders statuses as Taiga does (order, then name), numerically.
func SystemComment ¶
SystemComment reports a history entry written by Taiga's own GitLab integration for a push hook: the inactive gitlab-<hash> user and one of the hook's comment templates (taiga/hooks/event_hooks.py). Both must match. A service account, a human quoting the template, a diff without text or an unknown author are never system comments: agents publish human comments with integration accounts (docs/api-notes.md).
func UnsetFieldValue ¶
UnsetFieldValue checks that a field of type typ can be cleared.
func Unsupported ¶
Unsupported reports a flag whose Taiga contract the CLI cannot honour safely.
func ValidateField ¶
ValidateField checks a definition before any request. Taiga limits names to 64 characters; "=" is refused because Name=value assignments end the name at the first "=".
Types ¶
type API ¶
type API interface {
Do(context.Context, taiga.Request) (*taiga.Response, error)
GetAll(context.Context, string, url.Values) ([]json.RawMessage, error)
WriteVersioned(context.Context, string, string, map[string]any, bool) (*taiga.Response, error)
WriteVersionedFrom(context.Context, string, string, map[string]any, map[string]json.RawMessage, bool) (*taiga.Response, error)
Upload(ctx context.Context, path string, fields map[string]string, fileField, fileName string, r io.Reader, size int64) (*taiga.Response, error)
Download(ctx context.Context, rawURL string, w io.Writer) (int64, error)
BaseURL() string
}
API is the part of *taiga.Client the services use.
type Action ¶
type Action struct {
Kind string `json:"kind"`
Name string `json:"name"`
Body Object `json:"body"`
}
Action is one semantic step of a project plan; new statuses have no id or version yet.
type ApplyResult ¶
type ApplyResult struct {
Plan ProjectPlan `json:"plan"`
Applied []Action `json:"applied"`
Remaining []Action `json:"remaining"`
Complete bool `json:"complete"`
Requests []WritePlan `json:"requests"`
Deferred []Action `json:"deferred"`
}
ApplyResult is what project apply did. Applied and Remaining describe a partial run; Complete is set only after a new read finds nothing left to do. Requests is the dry-run preview.
func Apply ¶
func Apply(ctx context.Context, writer StatusWriter, spec ProjectSpec, dry bool) (ApplyResult, error)
Apply validates everything (permission, drift, reorder support) before the first write, then runs the plan in order. A failure stops it: nothing is undone and nothing is repeated, and the result says what was applied and what remains. Running it again re-reads and re-plans.
type FieldSpec ¶
type FieldSpec struct {
Name string `toml:"name"`
Type string `toml:"type"`
Description string `toml:"description"`
}
FieldSpec declares a story custom field; an absent description means "".
type LinkPlan ¶ added in v0.3.0
LinkPlan is what --dry-run prints for a write made of several requests.
type Object ¶
Object is a decoded Taiga object; numbers stay json.Number and unknown keys are kept.
func AssignmentPatch ¶
AssignmentPatch returns the assignee and block fields that differ from before.
Taiga 6.7 answers assigned_users as the stored list plus assigned_to (docs/api-notes.md), so: removing the owner without changing assigned_to is refused, because Taiga would keep showing them; and any change of assigned_to also sends the full list, because the previous owner may not be in the stored list and would silently disappear.
func BuildPatch ¶
BuildPatch returns only the fields that differ from before.
func FieldsView ¶
FieldsView binds the values to their definitions: each definition gets the stored value under "value" when there is one. Ids without a definition stay in attributes_values.
func MergeValues ¶
MergeValues returns current with updates applied; keys not in updates are kept as read.
type Patch ¶
type Patch struct {
Set Object
AddTags, RemoveTags []string
Append *string
AddAssignees, RemoveAssignees []int64
Owner *int64
ClearOwner bool
Block *string
Unblock bool
}
Patch records which changes were asked for; absent fields stay untouched.
type ProjectPlan ¶
type ProjectPlan struct {
Actions []Action `json:"actions"`
Unmanaged []Object `json:"unmanaged"`
Drift []Object `json:"drift"`
}
ProjectPlan is what apply would do. Unmanaged lists what exists and the file does not declare (kept as is); Drift lists declared definitions that exist with other values, which apply refuses instead of updating.
func BuildProjectPlan ¶
func BuildProjectPlan(spec ProjectSpec, statuses, fields []Object) (ProjectPlan, error)
BuildProjectPlan compares spec with the statuses (already sorted by SortStatuses) and story field definitions read from Taiga. It never plans an update or a removal.
type ProjectSpec ¶
type ProjectSpec struct {
StoryStatus []StatusSpec `toml:"story_status"`
StoryField []FieldSpec `toml:"story_field"`
}
ProjectSpec is the project TOML: only what it declares is managed, nothing is removed.
func ParseProjectSpec ¶
func ParseProjectSpec(r io.Reader) (ProjectSpec, error)
ParseProjectSpec decodes the project TOML strictly (unknown keys are errors) and validates every declaration before anything is read from Taiga.
type Service ¶
Service is created per command run; catalogs are cached only for that run.
func (*Service) ApplyProject ¶
func (s *Service) ApplyProject(ctx context.Context, spec ProjectSpec, dry bool) (ApplyResult, error)
ApplyProject runs Apply against Taiga.
func (*Service) Attachments ¶ added in v0.3.0
Attachments lists the attachments of a story or task (kind "story" or "task").
func (*Service) Catalog ¶
Catalog lists path for the project once per run. Entries that declare another project are dropped: a query parameter the server ignores must not widen the scope.
func (*Service) CloseStory ¶
func (s *Service) CloseStory(ctx context.Context, ref, selector string, dry, force bool) (any, error)
CloseStory moves the story to a closed status: the given one, or the project's only closed status. A story already closed is left alone unless another closed status is named.
func (*Service) CloseTask ¶ added in v0.3.0
func (s *Service) CloseTask(ctx context.Context, ref, selector string, dry, force bool) (any, error)
CloseTask moves the task to a closed status, like CloseStory. It only changes the status: the MCP's archived tag is never added.
func (*Service) Comment ¶
Comment publishes body on a story or task (name) with PATCH {comment, version} and returns it read again. Taiga's OCC never refuses a comment (it is not a field, so an old version is accepted; docs/api-notes.md), so the version protects nothing and the PATCH is sent once, never repeated. When its outcome is unknown (network error after the connection opened, 5xx or 3xx), the result is always comment_unconfirmed, exit 1, naming the new comments of ours with this exact text that the history shows (findComment).
func (*Service) Comments ¶
func (s *Service) Comments(ctx context.Context, name, ref string, includeSystem bool) ([]Object, error)
Comments lists the comments of a story or task (name), newest first as Taiga sends them. Comments of the GitLab integration (SystemComment) are left out unless includeSystem; every entry is marked with is_system and its story or task (story_id/story_ref or task_id/task_ref). Edited and deleted comments stay, with Taiga's edit_comment_date and delete_comment_date.
func (*Service) CreateField ¶
func (s *Service) CreateField(ctx context.Context, kind, name, typ string, description *string, dry bool) (any, error)
CreateField creates a definition unless one with the same name (case-sensitive, as Taiga compares) already exists: a compatible one is returned unchanged, a different one is an error. Existing definitions are never modified. A nil description is not compared.
func (*Service) CreateStory ¶
CreateStory posts body to the project, once and without version. When the answer says nothing conclusive (network after the connection opened, 5xx, 3xx), the result is always story_create_unconfirmed, naming the candidates found in the project (unconfirmedCreate). A re-run is never offered: it would create a second story.
func (*Service) CreateStoryWithEpic ¶ added in v0.3.0
func (s *Service) CreateStoryWithEpic(ctx context.Context, body Object, epic Object, dry bool) (any, error)
CreateStoryWithEpic creates the story and then links it to epic. The story exists once the POST succeeds: a failed link is story_created_link_failed, naming the story, never an invitation to run the command again (that would create a second story).
func (*Service) CreateTask ¶ added in v0.3.0
CreateTask posts body as a task of story, sent once and without version. When the answer says nothing conclusive (network after the connection opened, 5xx, 3xx), the result is always task_create_unconfirmed, naming the candidates found in the story (findTask). A re-run is never offered: it would create a second task.
func (*Service) DownloadAttachment ¶ added in v0.3.0
func (s *Service) DownloadAttachment(ctx context.Context, kind string, owner Object, id int64, dest string, overwrite bool, out io.Writer) (Object, error)
DownloadAttachment saves attachment id of owner to dest: a file, an existing directory (the server's name, made safe), "" for the working directory, or "-" for out. The attachment must be in the owner's list, which is filtered by kind (the detail endpoint is not). An existing file is replaced only with overwrite. The bytes go to a temporary file in the same directory and become dest only after their size and sha1 match the attachment.
func (*Service) EpicDetail ¶ added in v0.3.0
EpicDetail reads an epic by ref and adds user_stories: its linked stories in link order, each with id, ref, subject and project (plus project_slug when the story is in another project, which Taiga allows). A linked story the account cannot read keeps only its id.
func (*Service) Epics ¶ added in v0.3.0
Epics lists the epics of the project; closed goes to Taiga as status__is_closed and is checked again here, search matches the subject (Taiga's q= is a full-text search).
func (*Service) FieldValues ¶
FieldValues reads the values of the story or task id, after checking that it belongs to the selected project. A read error is returned as is, never as empty values.
func (*Service) Item ¶ added in v0.3.0
Item reads a story or task (name) of the selected project by ref.
func (*Service) LinkEpic ¶ added in v0.3.0
LinkEpic makes epic one of the story's epics; with replace, the only one. story is the read the change is computed from. The new link is created before any old one is removed, so the story never ends without an epic; no request is ever repeated. The caller checks --confirm-delete.
func (*Service) LinkableEpic ¶ added in v0.3.0
LinkableEpic resolves an epic of the project for a link: by ref, in a project whose epics module is on. Taiga links an epic of any project (probe); resolving here keeps it in this one.
func (*Service) Member ¶
Member resolves a project member by exact username, id or "me": the id must appear in the project's memberships.
func (*Service) Milestones ¶ added in v0.3.0
Milestones lists the sprints of the project. closed, when set, goes to Taiga (it honours it) and is checked again here, with search on the name.
func (*Service) ProjectPlan ¶
func (s *Service) ProjectPlan(ctx context.Context, spec ProjectSpec) (ProjectPlan, error)
ProjectPlan reads the project's statuses and story fields and compares them with spec. It only reads: no permission is needed beyond seeing the project.
func (*Service) ProjectView ¶ added in v0.3.0
ProjectView is the selected project as read, without its credentials and signed URLs.
func (*Service) SetFieldValues ¶
func (s *Service) SetFieldValues(ctx context.Context, kind string, id int64, entries, unsets []string, dry, force bool) (any, error)
SetFieldValues merges the Name=value entries into the values of the story or task id, clears the fields named in unsets, and writes the whole dictionary with the version of the values resource. A cleared field keeps its key with null: Taiga refuses an empty dictionary, so the key cannot be dropped. Clearing a field without a stored value adds nothing, because an absent key and null both mean no value. Every entry is resolved and parsed before anything is read or written; an unchanged dictionary writes nothing.
func (*Service) Stories ¶
Stories lists every story of the project matching filters (keys: ref, status, closed, search, assignee, epic, swimlane, tag; ids already resolved, swimlane "null" for none). Proven filters go to Taiga to shorten the list; every filter is then checked locally, so an ignored parameter never widens it.
func (*Service) Story ¶
Story reads a story of the selected project by ref (userstories/by_ref) or by id. Tags stay as Taiga sent them, because the object is the baseline of a versioned write.
func (*Service) StoryView ¶
StoryView is the output form: tag names instead of pairs, plus the web URL.
func (*Service) Swimlanes ¶ added in v0.3.0
Swimlanes lists the swimlanes of the project in the order Taiga returns them, which is the board order (order, then name in the database collation: re-sorting here could break a tie differently). Each carries is_default; the catalog entries are copied, not changed.
func (*Service) Task ¶ added in v0.3.0
Task reads a task of the selected project by ref (tasks/by_ref) or by id.
func (*Service) TaskView ¶ added in v0.3.0
TaskView is the output form of a task: tag names instead of pairs, plus the web URL.
func (*Service) Tasks ¶ added in v0.3.0
Tasks lists every task of the project matching filters (keys: story, status, assignee, closed, search, tag, ref; ids already resolved). Every filter is checked locally too.
func (*Service) UpdateStory ¶
func (*Service) UpdateStoryWithEpic ¶ added in v0.3.0
func (s *Service) UpdateStoryWithEpic(ctx context.Context, ref string, p Patch, epic Object, replace, dry, force bool) (any, error)
UpdateStoryWithEpic writes the field changes (if any) and then links epic, with replace the only one. A failed link after an applied PATCH keeps its code and says the fields are saved.
func (*Service) UpdateTask ¶ added in v0.3.0
func (s *Service) UpdateTask(ctx context.Context, ref string, p Patch, dry, force bool) (any, error)
UpdateTask applies p to the task ref. assigned_to is a plain versioned field of a task (docs/api-notes.md), so it takes the guarded retry like any other.
func (*Service) Upload ¶ added in v0.3.0
func (s *Service) Upload(ctx context.Context, kind string, owner Object, path, description string, dry bool) (any, error)
Upload attaches the file at path to owner. An attachment with the same name and sha1 already there is returned without sending (created: false). The POST is sent once and never repeated; its outcome is checked by the answer (sha1, size, object_id), and when the answer is lost by a new id with the same name and sha1 in the list.
func (*Service) User ¶
User resolves a user by exact username, id or "me" in users?project=, which also lists non-members (probe). Use it only where membership does not matter, like removing an assignee.
func (*Service) Users ¶ added in v0.3.0
Users lists the members of the project: users?project= also lists non-members, and memberships has no username, so both are read. Pending invitations (no user) are left out. Each user carries is_admin, role and role_name from its membership.
type StatusSpec ¶
type StatusSpec struct {
Name string `toml:"name"`
Color string `toml:"color"`
Closed bool `toml:"closed"`
After string `toml:"after"`
}
StatusSpec declares a story status. Closed defaults to false; After names the status it follows (existing or declared in the same file).
type StatusWriter ¶
type StatusWriter interface {
CheckPermission(context.Context) error
ProjectID() any
Load(context.Context) (statuses, fields []Object, err error)
CheckReorderSupport(context.Context) error
CreateStatus(context.Context, Object) error
CreateField(context.Context, Object) error
Reorder(context.Context, []string) error
}
StatusWriter is the Taiga side of apply; its contracts are in docs/api-notes.md.
type UploadFile ¶ added in v0.3.0
type UploadPlan ¶ added in v0.3.0
type UploadPlan struct {
DryRun bool `json:"dry_run"`
Method string `json:"method"`
Path string `json:"path"`
Fields map[string]string `json:"fields"`
File UploadFile `json:"file"`
}
UploadPlan is what --dry-run prints for an upload: the form fields and the file's metadata, never its content.