action

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 21, 2025 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package action provides GitHub Action handlers for the IssueOps approval system. This includes request creation, comment processing, and pipeline management.

Package action provides GitHub Action handlers for the IssueOps approval system.

Index

Constants

View Source
const DefaultIssueTemplate = `## 🚀 {{.Title}}

{{- if .Description}}
{{.Description}}
{{- end}}

### Request Information

{{- if .Requestor}}
- **Requested by:** @{{.Requestor}}
{{- end}}
{{- if .Version}}
- **Version:** ` + "`{{.Version}}`" + `{{if .PreviousVersion}} (from ` + "`{{.PreviousVersion}}`" + `){{end}}
{{- end}}
{{- if .Environment}}
- **Environment:** {{.Environment}}
{{- end}}
{{- if .Branch}}
- **Branch:** ` + "`{{.Branch}}`" + `
{{- end}}
{{- if .CommitSHA}}
- **Commit:** [{{slice .CommitSHA 0 7}}]({{.CommitURL}})
{{- end}}
{{- if .CommitsCount}}
- **Commits:** {{.CommitsCount}} commits in this release
{{- end}}
{{- if .RunURL}}
- **Workflow Run:** [View Run]({{.RunURL}})
{{- end}}
- **Requested at:** {{.Timestamp}}
{{if .HasJiraIssues}}
---

### 📋 Jira Issues in this Release

| Key | Summary | Type | Status |
|-----|---------|------|--------|
{{- range .JiraIssues}}
| [{{.Key}}]({{.URL}}) | {{.Summary}} | {{.TypeEmoji}} {{.Type}} | {{.StatusEmoji}} {{.Status}} |
{{- end}}
{{end}}{{if .PipelineTable}}
---

### 🛤️ Deployment Pipeline

{{.PipelineTable}}
{{end}}
---

### ✅ Approval Requirements

This request can be approved by **any one** of the following groups:

| Group | Required | Current | Status |
|-------|----------|---------|--------|
{{- range .Groups}}
| {{.Name}} | {{.Required}} | {{.Current}} | {{.StatusEmoji}} {{.StatusText}} |
{{- end}}

---

### How to Respond

**To Approve:** Comment with one of:
` + "` approve ` ` approved ` ` lgtm ` ` yes ` ` /approve `" + `

**To Deny:** Comment with one of:
` + "` deny ` ` denied ` ` reject ` ` no ` ` /deny `" + `

---
`

DefaultIssueTemplate is a comprehensive default template that can be fully customized.

View Source
const MinimalIssueTemplate = `## {{.Title}}

**Requested by:** @{{.Requestor}}
{{- if .Version}} | **Version:** {{.Version}}{{end}}
{{- if .Environment}} | **Environment:** {{.Environment}}{{end}}

### Approval Status

{{.GroupsTable}}

**Approve:** Comment ` + "`approve`" + ` | **Deny:** Comment ` + "`deny`" + `
`

MinimalIssueTemplate is a simpler template for basic use cases.

Variables

This section is empty.

Functions

func GenerateCommitList

func GenerateCommitList(commits []CommitInfo) string

GenerateCommitList generates a markdown list of commits.

func GenerateIssueBody

func GenerateIssueBody(data TemplateData) (string, error)

GenerateIssueBody generates the issue body from template data using the default template.

func GenerateIssueBodyFromConfig

func GenerateIssueBodyFromConfig(data TemplateData, issueConfig *config.IssueConfig) (string, error)

GenerateIssueBodyFromConfig generates the issue body using template from config.

func GenerateIssueBodyWithTemplate

func GenerateIssueBodyWithTemplate(data TemplateData, templateStr string) (string, error)

GenerateIssueBodyWithTemplate generates the issue body using a custom template string.

func GeneratePRTable

func GeneratePRTable(prs []PRInfo) string

GeneratePRTable generates a markdown table showing PRs in the release.

func GeneratePipelineIssueBody

func GeneratePipelineIssueBody(data *TemplateData, state *IssueState, pipeline *config.PipelineConfig) string

GeneratePipelineIssueBody generates the issue body for a pipeline workflow.

func GeneratePipelineIssueBodyWithSubIssues

func GeneratePipelineIssueBodyWithSubIssues(data *TemplateData, state *IssueState, pipeline *config.PipelineConfig, subIssues []SubIssueInfo) string

GeneratePipelineIssueBodyWithSubIssues generates the issue body including sub-issue links.

func GeneratePipelineMermaid

func GeneratePipelineMermaid(state *IssueState, pipeline *config.PipelineConfig) string

GeneratePipelineMermaid generates a Mermaid flowchart diagram for the pipeline. The diagram shows stages with colors based on their status: - Completed stages: green - Current stage: amber/yellow - Pending stages: gray - Auto-approve stages: cyan (when pending)

func GeneratePipelineTable

func GeneratePipelineTable(state *IssueState, pipeline *config.PipelineConfig) string

GeneratePipelineTable generates a markdown table showing pipeline status.

func GetCommentFromEvent

func GetCommentFromEvent() (id int64, user string, body string, err error)

GetCommentFromEvent extracts comment information from the GitHub event.

func GetEventAction

func GetEventAction() (string, error)

GetEventAction returns the action type from the GitHub event (e.g., "created", "closed").

func GetEventSender

func GetEventSender() (string, error)

GetEventSender returns the login of the user who triggered the event.

func GetInput

func GetInput(name string) string

GetInput gets an action input from environment variables.

func GetInputBool

func GetInputBool(name string) bool

GetInputBool gets a boolean input.

func GetInputDuration

func GetInputDuration(name string) (time.Duration, error)

GetInputDuration gets a duration input.

func GetInputInt

func GetInputInt(name string) (int, error)

GetInputInt gets an integer input.

func GetIssueNumberFromEvent

func GetIssueNumberFromEvent() (int, error)

GetIssueNumberFromEvent extracts the issue number from the GitHub event.

func IsIssueOpsIssue

func IsIssueOpsIssue() (bool, error)

IsIssueOpsIssue checks if the issue has the approval-required label.

func ReplaceTemplateVars

func ReplaceTemplateVars(s string, vars map[string]string) string

ReplaceTemplateVars replaces template variables in a string.

func SetOutput

func SetOutput(name, value string) error

SetOutput writes an output to the GitHub Actions output file.

func SetOutputs

func SetOutputs(outputs map[string]string) error

SetOutputs writes multiple outputs to the GitHub Actions output file.

func UpdateIssueState

func UpdateIssueState(body string, state IssueState) (string, error)

UpdateIssueState updates the state marker in an issue body.

Types

type CheckInput

type CheckInput struct {
	IssueNumber int
	Wait        bool
	Timeout     time.Duration
}

CheckInput contains inputs for the check action.

type CheckOutput

type CheckOutput struct {
	Status         string
	Approvers      []string
	Denier         string
	SatisfiedGroup string
}

CheckOutput contains outputs from the check action.

type CloseIssueInput

type CloseIssueInput struct {
	IssueNumber int
	Action      string // "closed" or "reopened"
}

CloseIssueInput contains inputs for the close-issue action.

type CloseIssueOutput

type CloseIssueOutput struct {
	TagDeleted string // Tag that was deleted, if any
	Status     string // Result status
}

CloseIssueOutput contains outputs from the close-issue action.

type CommitInfo

type CommitInfo struct {
	SHA     string `json:"sha"`
	Message string `json:"message"`
	Author  string `json:"author"`
	URL     string `json:"url"`
}

CommitInfo contains information about a commit included in the release.

type DeploymentStageData

type DeploymentStageData struct {
	Environment string // e.g., "development", "staging", "production"
	Status      string // e.g., "deployed", "pending", "not_started"
	StatusEmoji string
	Version     string // Version deployed to this environment
	DeployedAt  string
	IsCurrent   bool // Is this the current target environment?
}

DeploymentStageData contains data for a deployment stage.

func BuildDeploymentPipeline

func BuildDeploymentPipeline(targetEnv string, version string, previousVersion string) []DeploymentStageData

BuildDeploymentPipeline creates deployment stage data for common environments.

type GitHubEvent

type GitHubEvent struct {
	Action string `json:"action"`
	Issue  *struct {
		Number int    `json:"number"`
		Title  string `json:"title"`
		Body   string `json:"body"`
		State  string `json:"state"`
		Labels []struct {
			Name string `json:"name"`
		} `json:"labels"`
		User struct {
			Login string `json:"login"`
		} `json:"user"`
	} `json:"issue"`
	Comment *struct {
		ID   int64  `json:"id"`
		Body string `json:"body"`
		User struct {
			Login string `json:"login"`
		} `json:"user"`
	} `json:"comment"`
	Repository struct {
		FullName string `json:"full_name"`
		Owner    struct {
			Login string `json:"login"`
		} `json:"owner"`
		Name string `json:"name"`
	} `json:"repository"`
	Sender struct {
		Login string `json:"login"`
	} `json:"sender"`
}

GitHubEvent represents the common structure of GitHub webhook events.

func ParseGitHubEvent

func ParseGitHubEvent() (*GitHubEvent, error)

ParseGitHubEvent reads and parses the GitHub event from GITHUB_EVENT_PATH.

type GroupTemplateData

type GroupTemplateData struct {
	Name        string
	Approvers   []string
	Required    string // "all" or "X of Y"
	Current     int
	StatusEmoji string
	StatusText  string
	Satisfied   bool
}

GroupTemplateData contains data for a single approval group.

func BuildGroupTemplateData

func BuildGroupTemplateData(cfg *config.Config, workflow *config.Workflow, result *approval.ApprovalResult) []GroupTemplateData

BuildGroupTemplateData converts config requirements to template data.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler handles action execution.

func NewHandler

func NewHandler(ctx context.Context, configPath string) (*Handler, error)

NewHandler creates a new action handler.

func NewHandlerWithOptions

func NewHandlerWithOptions(ctx context.Context, opts HandlerOptions) (*Handler, error)

NewHandlerWithOptions creates a new action handler with additional options.

func (*Handler) Check

func (h *Handler) Check(ctx context.Context, input CheckInput) (*CheckOutput, error)

Check checks the approval status of an issue.

func (*Handler) CloseIssue

func (h *Handler) CloseIssue(ctx context.Context, input CloseIssueInput) (*CloseIssueOutput, error)

CloseIssue handles the closing of an approval issue. This is triggered by the 'issues' event with action 'closed'. If on_closed.delete_tag is true and a tag was created, it will be deleted.

func (*Handler) ProcessComment

func (h *Handler) ProcessComment(ctx context.Context, input ProcessCommentInput) (*ProcessCommentOutput, error)

ProcessComment processes an approval/denial comment.

func (*Handler) ProcessSubIssueClose

func (h *Handler) ProcessSubIssueClose(ctx context.Context, input ProcessSubIssueCloseInput) (*ProcessSubIssueCloseOutput, error)

ProcessSubIssueClose handles the close event for an approval sub-issue.

func (*Handler) Request

func (h *Handler) Request(ctx context.Context, input RequestInput) (*RequestOutput, error)

Request creates an approval request issue.

type HandlerOptions

type HandlerOptions struct {
	ConfigPath string
	ConfigRepo string // Optional: owner/repo for external config (e.g., "myorg/.github")
}

HandlerOptions configures how the handler loads configuration.

type IssueState

type IssueState struct {
	Workflow     string   `json:"workflow"`
	Version      string   `json:"version,omitempty"`
	Requestor    string   `json:"requestor"`
	RunID        string   `json:"run_id,omitempty"`
	Tag          string   `json:"tag,omitempty"`           // Tag that was created (for deletion on close)
	ApprovedAt   string   `json:"approved_at,omitempty"`   // Timestamp when approved
	DeploymentID int64    `json:"deployment_id,omitempty"` // GitHub deployment ID
	Environment  string   `json:"environment,omitempty"`   // Target environment
	JiraIssues   []string `json:"jira_issues,omitempty"`   // Jira issue keys in this release
	PreviousTag  string   `json:"previous_tag,omitempty"`  // Previous tag for comparison

	// Progressive deployment fields
	Pipeline     []string          `json:"pipeline,omitempty"`      // Ordered list of environments: ["dev", "qa", "stage", "prod"]
	CurrentStage int               `json:"current_stage,omitempty"` // Index of current stage in pipeline (0-based)
	StageHistory []StageCompletion `json:"stage_history,omitempty"` // History of completed stages
	PRs          []PRInfo          `json:"prs,omitempty"`           // PRs included in this release
	Commits      []CommitInfo      `json:"commits,omitempty"`       // Commits included in this release

	// Release strategy fields
	ReleaseStrategy   string `json:"release_strategy,omitempty"`   // Strategy used: "tag", "branch", "label", "milestone"
	ReleaseIdentifier string `json:"release_identifier,omitempty"` // e.g., branch name, label, milestone title

	// Auto-approval tracking
	AutoApprovedStages []string `json:"auto_approved_stages,omitempty"` // Stages that were automatically approved

	// Sub-issue tracking (when using approval_mode: sub_issues)
	SubIssues    []SubIssueInfo `json:"sub_issues,omitempty"`    // Sub-issues created for stages
	ApprovalMode string         `json:"approval_mode,omitempty"` // "comments", "sub_issues", or "hybrid"
}

IssueState contains hidden state embedded in the issue body.

func ParseIssueState

func ParseIssueState(body string) (*IssueState, error)

ParseIssueState extracts the hidden state from an issue body.

type JiraIssueData

type JiraIssueData struct {
	Key         string // e.g., "PROJ-123"
	Summary     string
	Type        string // e.g., "Bug", "Feature", "Task"
	TypeEmoji   string // e.g., "🐛", "✨", "📋"
	Status      string // e.g., "Done", "In Progress"
	StatusEmoji string
	URL         string // Full URL to the issue
	Assignee    string
}

JiraIssueData contains data for a Jira issue.

type PRInfo

type PRInfo struct {
	Number int    `json:"number"`
	Title  string `json:"title"`
	Author string `json:"author"`
	URL    string `json:"url"`
}

PRInfo contains information about a PR included in the release.

type PipelineProcessor

type PipelineProcessor struct {
	// contains filtered or unexported fields
}

PipelineProcessor handles progressive deployment pipelines. It manages multi-stage approval workflows where a single issue tracks deployment through multiple environments (e.g., dev → qa → stage → prod).

func NewPipelineProcessor

func NewPipelineProcessor(handler *Handler) *PipelineProcessor

NewPipelineProcessor creates a new pipeline processor.

func (*PipelineProcessor) EvaluatePipelineStage

func (p *PipelineProcessor) EvaluatePipelineStage(
	ctx context.Context,
	state *IssueState,
	workflow *config.Workflow,
	comments []approval.Comment,
) (*approval.ApprovalResult, error)

EvaluatePipelineStage evaluates whether the current user can approve the current stage.

func (*PipelineProcessor) GetCurrentStageRequirement

func (p *PipelineProcessor) GetCurrentStageRequirement(
	state *IssueState,
	workflow *config.Workflow,
) (*config.Requirement, error)

GetCurrentStageRequirement returns the approval requirement for the current pipeline stage.

func (*PipelineProcessor) ProcessInitialAutoApproveStages

func (p *PipelineProcessor) ProcessInitialAutoApproveStages(
	state *IssueState,
	pipeline *config.PipelineConfig,
) []string

ProcessInitialAutoApproveStages auto-advances through initial stages marked auto_approve: true when creating a new pipeline issue. Returns the list of auto-approved stage names.

func (*PipelineProcessor) ProcessPipelineApproval

func (p *PipelineProcessor) ProcessPipelineApproval(
	ctx context.Context,
	state *IssueState,
	workflow *config.Workflow,
	approver string,
) (*PipelineResult, error)

ProcessPipelineApproval processes an approval for a pipeline workflow. Returns the updated state and whether the pipeline is complete.

type PipelineResult

type PipelineResult struct {
	StageName          string   // Name of the stage that was approved
	StageIndex         int      // Index of the stage
	ApprovedBy         string   // Who approved
	StageMessage       string   // Message to post for this stage
	CreateTag          bool     // Whether to create a tag at this stage
	Complete           bool     // Whether the pipeline is complete
	NextStage          string   // Name of the next stage (if not complete)
	NextApprovers      []string // Approvers for the next stage
	AutoApprovedStages []string // Stages that were automatically approved after this one
}

PipelineResult contains the result of processing a pipeline approval.

type ProcessCommentInput

type ProcessCommentInput struct {
	IssueNumber int
	CommentID   int64
	CommentUser string
	CommentBody string
}

ProcessCommentInput contains inputs for the process-comment action.

type ProcessCommentOutput

type ProcessCommentOutput struct {
	Status         string
	Approvers      []string
	Denier         string
	SatisfiedGroup string
	Tag            string
}

ProcessCommentOutput contains outputs from the process-comment action.

type ProcessSubIssueCloseInput

type ProcessSubIssueCloseInput struct {
	IssueNumber int
	ClosedBy    string
	Action      string // "closed" or "reopened"
}

ProcessSubIssueCloseInput contains inputs for processing a sub-issue close event.

type ProcessSubIssueCloseOutput

type ProcessSubIssueCloseOutput struct {
	ParentIssueNumber int
	StageName         string
	Status            string // "approved", "denied", "reopened", "unauthorized"
	PipelineComplete  bool
	NextStage         string
	Message           string
}

ProcessSubIssueCloseOutput contains outputs from processing a sub-issue close.

type ReactionType

type ReactionType string

ReactionType defines the type of reaction to add to a comment.

const (
	ReactionEyes     ReactionType = "eyes"     // 👀 - processing/seen
	ReactionApproved ReactionType = "+1"       // 👍 - approved
	ReactionDenied   ReactionType = "-1"       // 👎 - denied
	ReactionConfused ReactionType = "confused" // 😕 - not authorized
	ReactionRocket   ReactionType = "rocket"   // 🚀 - deployed
)

type ReleaseContents

type ReleaseContents struct {
	PRs     []github.PullRequest
	Commits []github.Commit
	// Metadata about how the release was identified
	StrategyType config.ReleaseStrategyType
	Identifier   string // branch name, label, milestone title, or tag range
}

ReleaseContents holds the PRs and commits for a release.

type ReleaseTracker

type ReleaseTracker struct {
	// contains filtered or unexported fields
}

ReleaseTracker fetches release candidates based on the configured strategy.

func NewReleaseTracker

func NewReleaseTracker(client *github.Client, strategy config.ReleaseStrategyConfig, version string) *ReleaseTracker

NewReleaseTracker creates a new release tracker.

func (*ReleaseTracker) CleanupCurrentRelease

func (r *ReleaseTracker) CleanupCurrentRelease(ctx context.Context, prs []github.PullRequest) error

CleanupCurrentRelease handles cleanup after a successful release. This includes closing milestones, removing labels, or deleting branches as configured.

func (*ReleaseTracker) CreateNextReleaseArtifact

func (r *ReleaseTracker) CreateNextReleaseArtifact(ctx context.Context, nextVersion string) error

CreateNextReleaseArtifact creates the artifact for the next release version. This is called when the final stage (prod) is approved.

func (*ReleaseTracker) GetReleaseContents

func (r *ReleaseTracker) GetReleaseContents(ctx context.Context, previousTag string) (*ReleaseContents, error)

GetReleaseContents fetches the PRs and commits for the release based on strategy.

type RequestInput

type RequestInput struct {
	Workflow    string
	Version     string
	Environment string
}

RequestInput contains inputs for the request action.

type RequestOutput

type RequestOutput struct {
	IssueNumber int
	IssueURL    string
}

RequestOutput contains outputs from the request action.

type StageCompletion

type StageCompletion struct {
	Stage      string `json:"stage"`
	ApprovedBy string `json:"approved_by"`
	ApprovedAt string `json:"approved_at"`
}

StageCompletion records when a stage was completed.

type SubIssueHandler

type SubIssueHandler struct {
	// contains filtered or unexported fields
}

SubIssueHandler handles sub-issue creation and processing.

func NewSubIssueHandler

func NewSubIssueHandler(client *github.Client, cfg *config.Config, workflow *config.Workflow) *SubIssueHandler

NewSubIssueHandler creates a new sub-issue handler.

func (*SubIssueHandler) CreateSubIssuesForPipeline

func (h *SubIssueHandler) CreateSubIssuesForPipeline(
	ctx context.Context,
	parentIssueNumber int,
	state *IssueState,
	pipeline *config.PipelineConfig,
) ([]SubIssueInfo, error)

CreateSubIssuesForPipeline creates sub-issues for pipeline stages that use sub-issue approval. Returns the list of SubIssueInfo for tracking in the parent issue state.

func (*SubIssueHandler) ProcessSubIssueClose

func (h *SubIssueHandler) ProcessSubIssueClose(
	ctx context.Context,
	input ProcessSubIssueCloseInput,
) (*ProcessSubIssueCloseOutput, error)

processSubIssueClose handles the close event for an approval sub-issue (internal implementation).

func (*SubIssueHandler) ValidateParentIssueClose

func (h *SubIssueHandler) ValidateParentIssueClose(
	ctx context.Context,
	parentIssueNumber int,
	closedBy string,
) (bool, string)

ValidateParentIssueClose checks if a parent issue can be closed. Returns true if close is allowed, false if it should be reopened.

type SubIssueInfo

type SubIssueInfo struct {
	IssueNumber int      `json:"issue_number"`        // GitHub issue number
	IssueID     int64    `json:"issue_id"`            // GitHub issue ID (for sub-issue API)
	Stage       string   `json:"stage"`               // Pipeline stage name
	Status      string   `json:"status"`              // "open", "approved", "denied"
	Assignees   []string `json:"assignees,omitempty"` // Assigned approvers
	ClosedBy    string   `json:"closed_by,omitempty"` // User who closed the issue
	ClosedAt    string   `json:"closed_at,omitempty"` // When the issue was closed
}

SubIssueInfo tracks a sub-issue created for stage approval.

type TemplateData

type TemplateData struct {
	// Core fields
	Title       string
	Description string
	Version     string
	Requestor   string
	Environment string

	// URLs and references
	RunURL    string
	RepoURL   string
	CommitSHA string
	CommitURL string
	Branch    string

	// Approval groups
	Groups         []GroupTemplateData
	GroupsTable    string // Pre-rendered markdown table
	ApprovalStatus string // "pending", "approved", "denied"

	// Timestamps
	CreatedAt string
	Timestamp string

	// Custom variables (from workflow inputs or environment)
	Vars map[string]string

	// Jira integration
	JiraIssues      []JiraIssueData // Jira issues in this release
	JiraIssuesTable string          // Pre-rendered markdown table of Jira issues
	JiraBaseURL     string          // Jira base URL for linking
	HasJiraIssues   bool            // Whether there are Jira issues

	// Deployment tracking
	DeploymentPipeline []DeploymentStageData // Deployment stages
	PipelineTable      string                // Pre-rendered deployment pipeline
	PipelineMermaid    string                // Pre-rendered Mermaid diagram for pipeline
	CurrentDeployment  *DeploymentStageData  // Current deployment stage

	// Release info
	PreviousVersion string // Previous version/tag
	CommitsCount    int    // Number of commits in this release
	ReleaseNotes    string // Auto-generated release notes

	// Internal state (serialized to hidden comment)
	State IssueState
}

TemplateData contains data for issue body templates. All fields are available as template variables.

Jump to

Keyboard shortcuts

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