173 lines
5.1 KiB
Go
173 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/nomadcode/nomadcode-core/internal/adapters/plane"
|
|
)
|
|
|
|
type EnvConfig struct {
|
|
BaseURL string
|
|
Token string
|
|
WorkspaceSlug string
|
|
ProjectID string
|
|
WorkItemID string
|
|
SmokeApply bool
|
|
SmokeComment string
|
|
SmokeStateID string
|
|
}
|
|
|
|
func LoadConfig(getenv func(string) string) (EnvConfig, error) {
|
|
var cfg EnvConfig
|
|
cfg.BaseURL = strings.TrimSpace(getenv("PLANE_BASE_URL"))
|
|
cfg.Token = strings.TrimSpace(getenv("PLANE_TOKEN"))
|
|
cfg.WorkspaceSlug = strings.TrimSpace(getenv("PLANE_WORKSPACE_SLUG"))
|
|
cfg.ProjectID = strings.TrimSpace(getenv("PLANE_PROJECT_ID"))
|
|
cfg.WorkItemID = strings.TrimSpace(getenv("PLANE_WORK_ITEM_ID"))
|
|
|
|
var missing []string
|
|
if cfg.BaseURL == "" {
|
|
missing = append(missing, "PLANE_BASE_URL")
|
|
}
|
|
if cfg.Token == "" {
|
|
missing = append(missing, "PLANE_TOKEN")
|
|
}
|
|
if cfg.WorkspaceSlug == "" {
|
|
missing = append(missing, "PLANE_WORKSPACE_SLUG")
|
|
}
|
|
if cfg.ProjectID == "" {
|
|
missing = append(missing, "PLANE_PROJECT_ID")
|
|
}
|
|
if cfg.WorkItemID == "" {
|
|
missing = append(missing, "PLANE_WORK_ITEM_ID")
|
|
}
|
|
|
|
if len(missing) > 0 {
|
|
return cfg, fmt.Errorf("missing required environment variables: %s", strings.Join(missing, ", "))
|
|
}
|
|
|
|
applyStr := getenv("PLANE_SMOKE_APPLY")
|
|
cfg.SmokeApply = applyStr == "1" || strings.ToLower(applyStr) == "true"
|
|
|
|
if cfg.SmokeApply {
|
|
cfg.SmokeComment = strings.TrimSpace(getenv("PLANE_SMOKE_COMMENT"))
|
|
cfg.SmokeStateID = strings.TrimSpace(getenv("PLANE_SMOKE_STATE_ID"))
|
|
|
|
var missingSmoke []string
|
|
if cfg.SmokeComment == "" {
|
|
missingSmoke = append(missingSmoke, "PLANE_SMOKE_COMMENT")
|
|
}
|
|
if cfg.SmokeStateID == "" {
|
|
missingSmoke = append(missingSmoke, "PLANE_SMOKE_STATE_ID")
|
|
}
|
|
if len(missingSmoke) > 0 {
|
|
return cfg, fmt.Errorf("PLANE_SMOKE_APPLY is enabled but missing required smoke variables: %s", strings.Join(missingSmoke, ", "))
|
|
}
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
type PlaneClient interface {
|
|
GetWorkItem(ctx context.Context, ref plane.WorkItemRef) (plane.WorkItem, error)
|
|
AddComment(ctx context.Context, input plane.AddCommentInput) error
|
|
UpdateIssueStatus(ctx context.Context, input plane.UpdateIssueStatusInput) error
|
|
}
|
|
|
|
func RunSmoke(ctx context.Context, cfg EnvConfig, client PlaneClient, out io.Writer) error {
|
|
ref := plane.WorkItemRef{
|
|
WorkspaceSlug: cfg.WorkspaceSlug,
|
|
ProjectID: cfg.ProjectID,
|
|
WorkItemID: cfg.WorkItemID,
|
|
}
|
|
|
|
item, err := client.GetWorkItem(ctx, ref)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to lookup work item: %w", err)
|
|
}
|
|
|
|
fmt.Fprintf(out, "lookup ok: id=%s name=%s\n", item.ID, item.Name)
|
|
|
|
if !cfg.SmokeApply {
|
|
fmt.Fprintln(out, "write smoke skipped: set PLANE_SMOKE_APPLY=1")
|
|
return nil
|
|
}
|
|
|
|
err = client.AddComment(ctx, plane.AddCommentInput{
|
|
Ref: ref,
|
|
CommentHTML: fmt.Sprintf("<p>%s</p>", cfg.SmokeComment),
|
|
ExternalSource: "nomadcode",
|
|
ExternalID: "plane-smoke-test",
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to add comment: %w", err)
|
|
}
|
|
fmt.Fprintln(out, "comment ok")
|
|
|
|
err = client.UpdateIssueStatus(ctx, plane.UpdateIssueStatusInput{
|
|
Ref: ref,
|
|
Status: cfg.SmokeStateID,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update state: %w", err)
|
|
}
|
|
fmt.Fprintf(out, "state update ok: state_id=%s\n", cfg.SmokeStateID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
cfg, err := LoadConfig(os.Getenv)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
|
fmt.Fprintln(os.Stderr, "\nUsage:")
|
|
fmt.Fprintln(os.Stderr, " Required environment variables:")
|
|
fmt.Fprintln(os.Stderr, " PLANE_BASE_URL - Base URL of Plane instance")
|
|
fmt.Fprintln(os.Stderr, " PLANE_TOKEN - API Token (will be redacted in logs)")
|
|
fmt.Fprintln(os.Stderr, " PLANE_WORKSPACE_SLUG - Workspace slug (e.g. general)")
|
|
fmt.Fprintln(os.Stderr, " PLANE_PROJECT_ID - Project UUID")
|
|
fmt.Fprintln(os.Stderr, " PLANE_WORK_ITEM_ID - Work Item UUID")
|
|
fmt.Fprintln(os.Stderr, " Optional variables for write operations:")
|
|
fmt.Fprintln(os.Stderr, " PLANE_SMOKE_APPLY - Set to 1 to enable write smoke tests")
|
|
fmt.Fprintln(os.Stderr, " PLANE_SMOKE_COMMENT - HTML comment content to post")
|
|
fmt.Fprintln(os.Stderr, " PLANE_SMOKE_STATE_ID - State UUID to transition to")
|
|
os.Exit(1)
|
|
}
|
|
|
|
safeURL := cfg.BaseURL
|
|
safeToken := "<redacted>"
|
|
|
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
|
logger.Info("starting plane smoke test",
|
|
"base_url", safeURL,
|
|
"token", safeToken,
|
|
"workspace", cfg.WorkspaceSlug,
|
|
"project_id", cfg.ProjectID,
|
|
"work_item_id", cfg.WorkItemID,
|
|
"apply", cfg.SmokeApply,
|
|
)
|
|
|
|
planeClient := plane.NewClient(plane.Config{
|
|
BaseURL: cfg.BaseURL,
|
|
Token: cfg.Token,
|
|
}, logger)
|
|
|
|
if err := RunSmoke(context.Background(), cfg, planeClient, os.Stdout); err != nil {
|
|
logger.Error("smoke test failed", "error", redactToken(err.Error(), cfg.Token))
|
|
os.Exit(2)
|
|
}
|
|
|
|
logger.Info("plane smoke test finished successfully")
|
|
}
|
|
|
|
func redactToken(s, token string) string {
|
|
if token == "" {
|
|
return s
|
|
}
|
|
return strings.ReplaceAll(s, token, "<redacted>")
|
|
}
|