CLI: changeset commands default to JARL_CHANGESET, which is checked first

jarl changeset show/diff/apply/discard take the changeset from -C or
JARL_CHANGESET when no ID is given, and remove takes [ID] SEQ. A changeset
from JARL_CHANGESET is checked before anything uses it (staging included):
it must exist, be open and be the caller's own, so a stale export can't
stage into an applied changeset or into another user's draft that later
got its ID. Commands say on stderr which changeset they use, and apply and
discard remind to unset JARL_CHANGESET. -C and ID arguments are used as
given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-11 23:53:26 +01:00
1 parent e2e5007f94
commit cee11e7c86
7 files changed
+278 -43

No files matched your search

+28 -5
View File
@@ -30,8 +30,14 @@ type Client struct {
// Changeset, if set, stages changes into this changeset instead of
// applying them.
Changeset int64
DryRun bool
Force bool
// CheckChangeset, if set, runs once before Changeset is first used,
// and its error stops the request. The CLI checks a changeset taken
// from JARL_CHANGESET with it.
CheckChangeset func(ctx context.Context) error
csChecked bool
csErr error
DryRun bool
Force bool
// Preflight lets only requests through that can't change anything:
// reads and dry runs. Running a command on several servers uses it to
// check the command everywhere before running it anywhere.
@@ -243,6 +249,19 @@ func (c *Client) Get(ctx context.Context, path string, query url.Values, out any
return c.Do(ctx, http.MethodGet, path, query, nil, out)
}
// UseChangeset returns the changeset to stage into (0 for none), once
// CheckChangeset has accepted it.
func (c *Client) UseChangeset(ctx context.Context) (int64, error) {
if c.Changeset == 0 {
return 0, nil
}
if c.CheckChangeset != nil && !c.csChecked {
c.csChecked = true
c.csErr = c.CheckChangeset(ctx)
}
return c.Changeset, c.csErr
}
// Mutate sends a configuration change, honoring Changeset, DryRun and
// Force.
func (c *Client) Mutate(ctx context.Context, method, path string, query url.Values, body any) (*api.MutationResponse, error) {
@@ -250,8 +269,12 @@ func (c *Client) Mutate(ctx context.Context, method, path string, query url.Valu
for k, v := range query {
q[k] = v
}
if c.Changeset > 0 {
q.Set("changeset", fmt.Sprint(c.Changeset))
cs, err := c.UseChangeset(ctx)
if err != nil {
return &api.MutationResponse{}, err
}
if cs > 0 {
q.Set("changeset", fmt.Sprint(cs))
}
if c.DryRun {
q.Set("dry_run", "1")
@@ -260,7 +283,7 @@ func (c *Client) Mutate(ctx context.Context, method, path string, query url.Valu
q.Set("force", "1")
}
var out api.MutationResponse
err := c.Do(ctx, method, path, q, body, &out)
err = c.Do(ctx, method, path, q, body, &out)
return &out, err
}