ticket

package
v0.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package ticket computes and applies ticket-source synchronization plans: comparing a requirement's rollup CANARY status against its owning non-flatfile source's remote status (proposing "transition" actions), and codifying flatfile-to-ticket promotion as paired "create_issue" + "remap" actions. Plan computation (ComputePlan) is pure and side-effect free; JiraClient (jira.go) is the only piece that talks to the network, and only when the CLI layer (pkg/cmds/ticket) is told to --apply. CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_306_ComputePlan_FlatfileCreateAndRemapPairing,TestCANARY_CBIN_306_ComputePlan_FlatfileNoNonFlatfileSource_NoAction,TestCANARY_CBIN_306_ComputePlan_JiraStatusMismatch_Transition,TestCANARY_CBIN_306_ComputePlan_MatchingStatus_NoAction,TestCANARY_CBIN_306_ComputePlan_EmptyRemoteStatus_AllTransitionsProposed,TestCANARY_CBIN_306_ComputePlan_StatusMapOverrideHonored,TestCANARY_CBIN_306_ComputePlan_RollupIsWorstOfTokens,TestCANARY_CBIN_306_ComputePlan_UnresolvedPrefixSkipped,TestCANARY_CBIN_306_ComputePlan_DeterministicOrdering,TestCANARY_CBIN_306_RollupStatus; UPDATED=2026-08-29 CANARY: REQ=ENG-3958; FEATURE="TicketDestination"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_ENG_3958_ComputePlan_CreateIssueStampsDestinationProject,TestCANARY_ENG_3958_ComputePlan_CreateIssueProjectEmptyWhenDestinationUnset; UPDATED=2026-08-29

Index

Constants

This section is empty.

Variables

View Source
var DefaultStatusMap = map[string]string{
	"STUB":    "To Do",
	"IMPL":    "In Progress",
	"TESTED":  "Done",
	"BENCHED": "Done",
}

DefaultStatusMap is the CANARY-status -> remote-status-name mapping used when a source's StatusMap doesn't override a given status.

View Source
var ErrDecodeResponse = errors.New("decode response")

ErrDecodeResponse is the sentinel wrapped by a JSON decode failure on an otherwise-successful (2xx) response. The printed error string is always static (method + safePath + this reason) — the underlying json error's text is deliberately discarded because it can quote a fragment of the offending response body (for example an overflowing numeric literal), which must never reach a log line.

Functions

func DoneStatuses added in v0.3.3

func DoneStatuses(reg *sources.Registry) map[string]bool

DoneStatuses returns the set of remote status names that a "done" CANARY status (TESTED or BENCHED) maps to across reg's non-flatfile sources, honoring each source's StatusMap and the DefaultStatusMap fallback. A transition whose target status is in this set advances an issue to done, so the apply layer gates it on passing evidence rather than flipping the issue blind.

func FetchRemoteStatus

func FetchRemoteStatus(ctx context.Context, c *JiraClient, project string) (map[string]string, error)

FetchRemoteStatus pages through JIRA's token-based search endpoint (POST /rest/api/3/search/jql) for every issue in project, returning a map of issue key -> current status name. Used only under `--apply`, immediately before ComputePlan, so transition actions are proposed against real remote state.

The project key is validated against config.SourceKeyPattern before any request is made, and quoted inside the JQL (project = "KEY") so it can never break out of the clause. Pagination follows nextPageToken, capped at maxSearchPages.

func RollupStatus

func RollupStatus(tokens []*storage.Token) string

RollupStatus returns the worst (least advanced) status among tokens per STUB < IMPL < TESTED < BENCHED. Tokens with an unrecognized status are ignored. An empty slice, or a slice with no recognized statuses, returns "".

Types

type Action

type Action struct {
	Type        string `json:"type"`              // create_issue | transition | remap
	ReqID       string `json:"req_id,omitempty"`  // the CANARY requirement ID this action concerns
	Issue       string `json:"issue,omitempty"`   // remote issue key; "" on a pending remap until applied
	To          string `json:"to,omitempty"`      // JIRA status name (transition target)
	Summary     string `json:"summary,omitempty"` // for create_issue
	Description string `json:"description,omitempty"`
	Source      string `json:"source"`            // owning source's Name
	Project     string `json:"project,omitempty"` // create_issue only: the destination source's Project (empty if unset)
}

Action is one proposed (or, once applied, completed) ticket-sync operation.

func ComputePlan

func ComputePlan(tokens []*storage.Token, reg *sources.Registry, remoteStatus map[string]string) ([]Action, error)

ComputePlan computes the deterministic, pure set of proposed sync actions.

tokens are grouped by ReqID; reg resolves each requirement's owning source; remoteStatus maps a non-flatfile issue key to its current remote status name (nil or empty means "unknown" — every eligible transition is then proposed, since no remote state contradicts it).

Rules:

  • A requirement owned by a non-flatfile source gets a "transition" action when its rollup status (RollupStatus: worst of its tokens, STUB<IMPL<TESTED<BENCHED), mapped through the source's StatusMap (falling back to DefaultStatusMap), differs from remoteStatus[reqID].
  • A requirement owned by a flatfile source gets a paired "create_issue"
  • "remap" action, but only when the registry configures at least one non-flatfile source — otherwise there's nothing to promote it to. The create_issue's Summary is "<ReqID>: <primary feature>"; Description is a bounded feature/aspect/status/file list. The paired remap's Issue is left "" — the apply step fills in the created key.
  • A requirement whose prefix resolves to no configured source is skipped.

Ordering is deterministic: requirements are visited in sorted ReqID order; a flatfile requirement's create_issue action is immediately followed by its paired remap action.

type JiraClient

type JiraClient struct {
	BaseURL string
	Email   string
	Token   string

	// HTTPClient lets tests point the client at an httptest server; nil
	// defaults to a client with a bounded 15s timeout.
	HTTPClient *http.Client

	// Sleep injects the backoff wait so tests can assert on (and skip) the
	// retry delays; nil defaults to time.Sleep.
	Sleep func(time.Duration)
}

JiraClient talks to the JIRA Cloud REST API (v3) using basic auth (email + API token). Every method is safe for concurrent use. CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_306_JiraClient_CreateIssue,TestCANARY_CBIN_306_JiraClient_CreateIssue_ErrorStatus,TestCANARY_CBIN_306_JiraClient_TransitionIssue_ResolvesIDByName,TestCANARY_CBIN_306_JiraClient_TransitionIssue_NoMatch,TestCANARY_CBIN_306_FetchRemoteStatus_Paged; UPDATED=2026-08-30 CANARY: REQ=CP-279; FEATURE="TicketSync"; ASPECT=Security; STATUS=TESTED; TEST=TestJiraSearchJQLPagination,TestJiraSearchInvalidProjectKey,TestJiraRetry429ThenSuccess,TestJiraNoRetryOn400,TestJiraBodyCap,TestJiraErrorRedaction,TestJiraStringRedactsToken,TestJiraTimeout15s,TestAuditF16; UPDATED=2026-08-30

func (*JiraClient) CreateIssue

func (c *JiraClient) CreateIssue(ctx context.Context, project, issueType, summary, description string) (string, error)

CreateIssue creates a new issue of issueType in project with summary and description (wrapped as an ADF paragraph), returning the created issue's key (e.g. "CP-12").

func (*JiraClient) String added in v0.3.3

func (c *JiraClient) String() string

String renders the client with its Token redacted, so a stray %v (in a log line or wrapped error) can never spill the API token.

func (*JiraClient) TransitionIssue

func (c *JiraClient) TransitionIssue(ctx context.Context, key, toStatusName string) error

TransitionIssue moves key to the transition whose target status name (transitions[].to.name) matches toStatusName case-insensitively.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL