Documentation
¶
Overview ¶
Package postcmd is the posting state machine behind `keryx approve` and `keryx publish`/`publish all`/`publish due` (0055 R-POST/R-AUTH, 0060 D1–D7, 0061 D1–D4). It drives an article's announcement set (draft → approved → posted, per outlet: one piece on one platform), enforces the approval gate, the on-the-hour schedule and idempotency, and fans out across platforms via the publish seam. The publisher resolver, the enabled-check, the clock, the filesystem and the post enumerator are injected so the whole machine is unit-tested with a fake publisher and no network.
Index ¶
- func DefaultHourFor(cfg config.Reader, plat social.Platform) (int, *time.Location)
- func FormatLines(results []Result) []string
- func FormatResults(w io.Writer, results []Result)
- func Refusal(accepts publish.Accepts, kind social.Kind, img *social.Image) string
- type ApproveOptions
- type Poster
- func (p Poster) Approve(ctx context.Context, t Target, platform string, opts ApproveOptions) ([]Result, error)
- func (p Poster) Publish(ctx context.Context, t Target, platform string, dryRun, force bool) ([]Result, error)
- func (p Poster) PublishAll(ctx context.Context, t Target, dryRun bool) ([]Result, error)
- func (p Poster) PublishDue(ctx context.Context, t Target, dryRun bool) ([]Result, error)
- func (p Poster) PublishSubset(ctx context.Context, t Target, names []string, dryRun bool) ([]Result, error)
- type Result
- type Target
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultHourFor ¶ added in v0.19.0
DefaultHourFor reads `platforms.<p>.announce.hour` and `announce.timezone` (0060 D4): the hour is per platform so the day's announcements stagger, the zone is one project-wide key because the stagger is meaningful in the author's day and the pipeline runs in UTC. Missing keys mean 09:00 UTC.
func FormatLines ¶ added in v0.5.0
FormatLines renders per-outlet outcomes as human lines, sorted by platform — the shared formatter for both the CLI (FormatResults) and the studio post job.
func FormatResults ¶
FormatResults renders per-outlet results as text lines.
func Refusal ¶ added in v0.21.0
Refusal is the pre-flight on shape alone: the reason accepts would turn down a piece of this kind (a reel carries a video, a post at most an image, with its public URL when it has one), or "" when it takes it. The studio greys a platform's tab with it before anyone composes copy that can never be sent (0061 D4).
Types ¶
type ApproveOptions ¶ added in v0.19.0
type ApproveOptions struct {
// At is the due time: RFC3339 on the hour, or a bare date that takes the
// platform's default hour. Empty takes the article's date at that hour, or,
// when the article has no date, is due immediately.
At string
// Revoke returns the outlets to draft and clears their schedule.
Revoke bool
}
ApproveOptions schedules an approve.
type Poster ¶
type Poster struct {
FS afero.Fs
Root string // reel-workspace root (e.g. "reels")
// Resolve constructs the publisher for a platform (nil-able adapters are an
// error). Enabled reports `platforms.<name>.enabled`. Now is the clock.
Resolve func(platform string) (publish.Publisher, error)
Enabled func(platform string) bool
// Contract reports a platform's media contract without constructing it
// (0060 D3); nil falls back to the publish registry.
Contract func(platform string) (publish.Accepts, bool)
// Constraints resolves a platform's posting constraints (config-tunable, R-SOC-5);
// nil falls back to the built-in defaults.
Constraints func(platform social.Platform) social.Constraint
Now func() time.Time
// DefaultHour is the hour of day an outlet on a platform is due when the
// schedule names only a date, and the zone that hour is in (0060 D4); nil
// means 09:00 UTC.
DefaultHour func(platform social.Platform) (int, *time.Location)
// ArticleDate is the article's own date (YYYY-MM-DD) for a bundle, "" when
// unknown; an approve with no --at is due on that date (0060 D4). nil means
// unknown.
ArticleDate func(ctx context.Context, bundle string) string
// Articles enumerates every article bundle in the project, for `publish due`
// with no target (0060 D6). nil means the sweep is unavailable.
Articles func(ctx context.Context) ([]string, error)
// Hydrate pulls a reel workspace's pinned-but-missing media before its video
// is read (spec 0039 §3.5, R-STO-7): on a fresh CI clone the rendered reel
// lives in the object store. nil means no object store.
Hydrate func(ctx context.Context, reelDir string) error
// Reachable checks that a public image URL serves an image, for the
// adapters that fetch by URL (Instagram, Threads): an undeployed cover is
// refused before the send, so the platform never fetches a 404 and caches
// it; a dry run only reports it. nil skips the check.
Reachable func(ctx context.Context, url string) error
// RequireCredentials makes a dry-run fail when the publisher cannot be
// constructed. Off, the dry-run reports the credential as absent and passes,
// which is what a merge-request pipeline with no secrets needs (0060 OQ2).
RequireCredentials bool
}
Poster carries the injected dependencies for the posting machine.
func New ¶
New builds a Poster wired to the live props: the publish registry (config- resolved), the platform-enabled + constraints config checks, the content reader, and the wall clock.
func NewFor ¶ added in v0.5.0
NewFor builds a Poster over an explicit filesystem + config + reel root — the studio drives it against the active project (per-project fs/config), the CLI via New. Platform-enabled, constraints, default hours and the content reader resolve from stores.Config; credentials resolve from stores.Accounts when one was opened.
func (Poster) Approve ¶
func (p Poster) Approve(ctx context.Context, t Target, platform string, opts ApproveOptions) ([]Result, error)
Approve sets the approval gate for a platform (or "all") in scope: it refuses any outlet whose copy has hard constraint violations (R-POST-10), sets the schedule on the hour (R-POST-11, R-ANN-4), refuses two outlets on one platform sharing an hour anywhere in the article (R-ANN-5), and persists. --revoke returns to draft.
func (Poster) Publish ¶ added in v0.19.0
func (p Poster) Publish(ctx context.Context, t Target, platform string, dryRun, force bool) ([]Result, error)
Publish sends one platform's outlets in scope (R-POST-1..4): refuses unless approved, is a no-op when already posted (unless force), and on success records posted/posted_at/post_url/post_id. dryRun validates without sending. A platform with several outlets in scope sends the earliest due and defers the rest (R-ANN-5).
func (Poster) PublishAll ¶ added in v0.19.0
PublishAll sends every enabled + approved outlet in scope independently, recording each success even if another fails (R-POST-5..8); it returns an error if any failed.
func (Poster) PublishDue ¶ added in v0.19.0
PublishDue sends the approved outlets whose hour has come (R-POST-12/13) — the unattended CI entrypoint, idempotent. With an empty Target it sweeps every article the project's content sources name (0060 D6).
func (Poster) PublishSubset ¶ added in v0.19.0
func (p Poster) PublishSubset(ctx context.Context, t Target, names []string, dryRun bool) ([]Result, error)
PublishSubset sends the named enabled + approved platforms — one load/save, the same per-platform resilience as PublishAll (a failure doesn't abort the rest). A named platform that isn't enabled+approved is simply not sent.
type Result ¶
type Result struct {
Article string `json:"article,omitempty"`
Piece string `json:"piece,omitempty"`
Kind string `json:"kind,omitempty"`
Platform string `json:"platform"`
Status string `json:"status"` // posted | skipped | deferred | failed | dry-run | approved | draft
URL string `json:"url,omitempty"`
Detail string `json:"detail,omitempty"`
}
Result is one outlet's posting outcome (for per-platform reporting / json).