Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 54 additions & 17 deletions e2e/harness/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,24 +376,43 @@ func (r *Runner) executePromote(ctx context.Context, promote *PromoteStep, confi
// pair into the "<source>-to-<target>" form. Source defaults to the first
// env (typically dev) since the workflow generator only emits dev-rooted
// cascade options.
mode := promote.Mode
if mode == "cascade" {
source := "dev"
if len(config.Environments) > 0 {
source = config.Environments[0]
var inputs map[string]string
if len(config.Environments) == 1 {
// Single-environment repos generate a Release workflow (see
// command.go IsSingleEnvironment → NewReleaseGenerator) written to
// promote.yaml. That workflow's dispatch input is release_action
// (create-draft|prerelease|release), not the multi-env promote's
// mode. A "promote to release" step on a single-env repo means
// publish the final release, so dispatch release_action: release.
// Sending mode here was silently ignored, the workflow fell back to
// its create-draft default, and the run "succeeded" without ever
// publishing v0.1.0, wiping prod state, or cleaning up the RC tags.
inputs = map[string]string{
"release_action": "release",
}
if promote.AllowBreaking {
inputs["allow_breaking_changes"] = "true"
}
} else {
mode := promote.Mode
if mode == "cascade" {
source := "dev"
if len(config.Environments) > 0 {
source = config.Environments[0]
}
mode = fmt.Sprintf("%s-to-%s", source, promote.Target)
}
inputs = map[string]string{
"mode": mode,
}
if promote.AllowBreaking {
// Workflow input is named allow_breaking_changes (see internal/generate
// promote.go); the workflow forwards it to the CLI's --allow-breaking
// flag. The harness's previous "allow_breaking" key was silently
// ignored, leaving the breaking-change gate active even when the
// scenario asked for it to be bypassed.
inputs["allow_breaking_changes"] = "true"
}
mode = fmt.Sprintf("%s-to-%s", source, promote.Target)
}
inputs := map[string]string{
"mode": mode,
}
if promote.AllowBreaking {
// Workflow input is named allow_breaking_changes (see internal/generate
// promote.go); the workflow forwards it to the CLI's --allow-breaking
// flag. The harness's previous "allow_breaking" key was silently
// ignored, leaving the breaking-change gate active even when the
// scenario asked for it to be bypassed.
inputs["allow_breaking_changes"] = "true"
}

// Determine the branch ref
Expand Down Expand Up @@ -521,6 +540,14 @@ func (r *Runner) syncStateFromGitea(ctx context.Context, config Config) error {
SHA string `yaml:"sha"`
} `yaml:"deploys"`
} `yaml:"state"`
// LatestRelease is the single-environment Release workflow's published
// pointer (ci.latest_release). Single-env repos publish via the Release
// workflow's finalize step, which writes latest_release.{version,sha}
// rather than a state[release] env (see internal/generate/release.go).
LatestRelease struct {
SHA string `yaml:"sha"`
Version string `yaml:"version"`
} `yaml:"latest_release"`
}
if err := yaml.Unmarshal([]byte(manifestContent), &manifest); err != nil {
return fmt.Errorf("failed to parse manifest: %w", err)
Expand Down Expand Up @@ -550,6 +577,16 @@ func (r *Runner) syncStateFromGitea(ctx context.Context, config Config) error {
}
}

// Surface the single-env Release workflow's latest_release pointer under the
// synthetic "release" state key so scenarios can assert it the same way they
// assert any other environment's state. Multi-env repos leave latest_release
// empty (they wipe and repopulate state[release] directly), so this only
// records when the Release workflow has actually published.
if lr := ciData.LatestRelease; lr.Version != "" || lr.SHA != "" {
r.ctx.RecordState("release", lr.SHA, lr.Version)
r.t.Logf(" Synced state[release] (from latest_release) = %s @ %s", truncateSHA(lr.SHA), lr.Version)
}

return nil
}

Expand Down
6 changes: 6 additions & 0 deletions e2e/harness/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ type Config struct {
Builds []BuildConfig `yaml:"builds"`
Deploys []DeployConfig `yaml:"deploys"`
Publish *PublishConfig `yaml:"publish,omitempty"`
// DispatchInputs carries operator-facing workflow_dispatch inputs through to
// the generated manifest untouched. A generic map (rather than a typed
// struct) is used so the harness stays decoupled from the generator's
// DispatchInput shape while preserving every key (type, options, default,
// description, required) across the marshal round-trip.
DispatchInputs map[string]map[string]any `yaml:"dispatch_inputs,omitempty"`
}

// PublishConfig defines a publish callback invoked after a release is published
Expand Down
2 changes: 1 addition & 1 deletion e2e/scenarios/09-inline-run-callback.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ steps:
- path: ".github/workflows/orchestrate.yaml"
contains:
- "build-app:"
- "uses: build.yaml"
- "uses: ./build.yaml"
- "build-smoke:"
- "shell: bash"
- 'echo "smoke check"'
7 changes: 6 additions & 1 deletion e2e/scenarios/09-single-env-repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,16 @@ steps:
mode: default
expect:
state:
# Single-env repos publish via the Release workflow, which writes the
# ci.latest_release pointer (surfaced here as state[release]) rather than
# a state[release] env. It does not wipe the trunk-tracking env, so prod
# legitimately still holds the latest RC it was orchestrated to.
release:
sha: commit2
version: "v0.1.0"
prod:
wiped: true
sha: commit2
version: "v0.1.0-rc.1"
releases:
- tag: "v0.1.0"
prerelease: false
Expand Down
2 changes: 1 addition & 1 deletion e2e/scenarios/10-inline-job-attributes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ steps:
- "group: sign-${{ github.ref }}"
- "cancel-in-progress: true"
- "build-app:"
- "uses: build.yaml"
- "uses: ./build.yaml"
Loading