diff --git a/CHANGELOG.md b/CHANGELOG.md index f04a368..bd6387a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## v0.3.0 - 2026-10-08 + +### Breaking Changes + +- Workload `environment` has been removed from config, deployment metadata, deployment state, and deploy command environment variables. +- Use optional `release_prefix` to scope GitHub release metadata lookup when a repository publishes multiple deployable artifact streams. + + ```yaml + workloads: + - name: my-app + release_prefix: my-app-prod + ``` + +### Changed + +- Notifications now identify deployments by workload name instead of environment. +- When `release_prefix` is empty, GitHub release lookup considers all releases newest-first and deploys the first release with valid `deployment-metadata.json`. + ## v0.2.0 - 2026-10-08 ### Breaking Changes diff --git a/README.md b/README.md index 45f5343..0a26bf6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ image state. It is not a container orchestrator. For each configured workload, `reconD` repeatedly: -1. Fetches the latest `deployment-metadata.json` for an environment. +1. Fetches the latest relevant `deployment-metadata.json`. 2. Reads the currently committed deployment state from disk. 3. Inspects the configured container to detect runtime drift. 4. Pulls the desired image. @@ -60,16 +60,23 @@ networking, routing, or workload composition, it belongs outside the agent. ## Deployment Metadata -The Git provider implementation fetches and unmarshals `deployment-metadata.json`. -The current GitHub implementation looks at image releases for the configured -environment. In practice, the environment usually corresponds to the prefix used -in image tags, such as `dev` or `prod`. +The Git provider implementation fetches and unmarshals `deployment-metadata.json` +from GitHub Releases. + +By default, reconD checks releases newest-first and deploys the first release +that has a valid `deployment-metadata.json` asset. If a workload config sets +`release_prefix`, reconD only considers release tags that start with that +prefix. This is recommended when one repository publishes multiple deployable +artifacts, packages, or deployment streams. + +For example, `release_prefix: api-prod` matches release tags such as +`api-prod-sha-10a3e42`, while `release_prefix: worker-staging` matches +`worker-staging-sha-8d1af01`. Example metadata: ```json { - "environment": "prod", "image": "ghcr.io/my-account/app-repo", "image_tag": "prod-sha-10a3e42", "manifest_digest": "sha256:19779d908d890f704a2170d7dde679e266a9137613ea299b38939eb889545f8e", @@ -91,7 +98,7 @@ Minimal example: ```yaml workloads: - name: my-app - environment: prod + release_prefix: my-app-prod container_name: my-container deploy_command: make redeploy-app check_interval: 60s @@ -264,7 +271,6 @@ or manage the Compose file. Required workload fields: - `name` -- `environment` - `container_name` - `deploy_command` - `state_dir` @@ -274,6 +280,7 @@ Required workload fields: Common optional fields: +- `release_prefix`: release tag prefix used to scope metadata lookup. Recommended when a repository publishes more than one deployable artifact stream. - `notification_url`: Slack webhook URL. - `check_interval`: defaults to `60s` when below `10s`. - `health_check.retries`: defaults to `12`. diff --git a/config/config.go b/config/config.go index cf8d6a0..8897d67 100644 --- a/config/config.go +++ b/config/config.go @@ -13,36 +13,94 @@ import ( ) type Config struct { + // Workloads is the required list of container workloads reconD should watch + // and reconcile. Configure one entry for each independently deployed + // service. Workloads []Workload `yaml:"workloads"` } +// Workload is a standalone unit of deployment (e.g., a container) reconD can watch and reconcile type Workload struct { - Name string `yaml:"name"` - Environment string `yaml:"environment"` - ContainerName string `yaml:"container_name"` - NotificationURL string `yaml:"notification_url"` - DeployCommand string `yaml:"deploy_command"` - CheckInterval Duration `yaml:"check_interval"` - StateDir string `yaml:"state_dir"` - GitProvider GitProvider `yaml:"git_provider"` - HealthCheck HealthCheck `yaml:"health_check"` - Strategy Strategy `yaml:"strategy"` - - // Labels optionally scope docker image pruning to this workload's images. + // Name is required and identifies this workload in logs, notifications, and + // persisted deployment state. + Name string `yaml:"name"` + + // ReleasePrefix is optional and scopes GitHub release selection by tag + // prefix. For example, "api-prod" matches release tags like + // "api-prod-sha-10a3e42". Leave it empty only when this repository publishes + // one deployable artifact stream or when the newest valid metadata release is + // always the desired workload. + ReleasePrefix string `yaml:"release_prefix"` + + // ContainerName is required and must match the Docker container reconD + // inspects to determine the workload's current runtime state. + ContainerName string `yaml:"container_name"` + + // NotificationURL is optional. When set, reconD sends deployment events to + // this Slack webhook URL; when empty, notifications are disabled. + NotificationURL string `yaml:"notification_url"` + + // DeployCommand is required. reconD runs it with /bin/sh after applying the + // deployment strategy, passing workload and image metadata in WORKLOAD_* + // environment variables. + DeployCommand string `yaml:"deploy_command"` + + // CheckInterval is optional and controls how often reconD checks for new + // deployment metadata. Values may be Go duration strings like "60s" or a + // number of seconds; values below 10s default to 60s. + CheckInterval Duration `yaml:"check_interval"` + + // StateDir is required and points to the local directory where reconD stores + // deployed and rollback state for this workload. + StateDir string `yaml:"state_dir"` + + // GitProvider is required and tells reconD which repository to read + // deployment metadata from. + GitProvider GitProvider `yaml:"git_provider"` + + // HealthCheck is optional. When configured, reconD waits for the workload to + // become healthy after deploy and rollback commands; a failed check triggers + // rollback when previous state is available. + HealthCheck HealthCheck `yaml:"health_check"` + + // Strategy is optional. Use it when deployment metadata must be written + // somewhere before DeployCommand runs, such as updating an env file with the + // image tag. + Strategy Strategy `yaml:"strategy"` + + // Labels is optional and scopes Docker image pruning to images with these + // labels after a successful deployment. Labels map[string]string `yaml:"labels"` } type GitProvider struct { + // Owner is required and names the GitHub owner or organization containing + // the deployment metadata repository. Owner string `yaml:"owner"` - Repo string `yaml:"repo"` - // Token is optional; set via GITHUB_TOKEN env var for private repos. + + // Repo is required and names the GitHub repository containing deployment + // metadata for this workload. + Repo string `yaml:"repo"` + + // Token is optional and is used to authenticate GitHub API requests for + // private repositories. If omitted, reconD reads GITHUB_TOKEN from the + // process environment. Token string `yaml:"token"` } type Strategy struct { - Type StrategyType `yaml:"type"` - EnvFilePath string `yaml:"env_file_path"` - ImageTagKey string `yaml:"image_tag_key"` + // Type is optional. Leave it empty or set it to "none" to run no strategy; + // set it to "env_file" to write deployment metadata into an env file before + // DeployCommand runs. + Type StrategyType `yaml:"type"` + + // EnvFilePath is required when Type is "env_file" and ignored otherwise. It + // points to the env file reconD should create or update. + EnvFilePath string `yaml:"env_file_path"` + + // ImageTagKey is optional for the "env_file" strategy and defaults to + // "IMAGE_TAG". It is the key reconD writes with the selected image tag. + ImageTagKey string `yaml:"image_tag_key"` } type StrategyType string @@ -53,11 +111,25 @@ const ( ) type HealthCheck struct { - Type HealthCheckType `yaml:"type"` - URL string `yaml:"url"` - Retries int `yaml:"retries"` - Interval Duration `yaml:"interval"` - Timeout Duration `yaml:"timeout"` + // Type is optional. Leave it empty or set it to "none" to skip health + // checks; set it to "http" or "tcp" to poll URL after deployment. + Type HealthCheckType `yaml:"type"` + + // URL is required when Type is "http" or "tcp" and ignored otherwise. Use + // an HTTP URL for HTTP checks or a tcp://host:port URL for TCP checks. + URL string `yaml:"url"` + + // Retries is optional and defaults to 12. It controls how many health check + // attempts reconD makes before treating the deployment as unhealthy. + Retries int `yaml:"retries"` + + // Interval is optional and defaults to 10s. It controls how long reconD waits + // between health check attempts. + Interval Duration `yaml:"interval"` + + // Timeout is optional and defaults to 5s. It controls the per-attempt timeout + // for HTTP requests and TCP connections. + Timeout Duration `yaml:"timeout"` } type HealthCheckType string @@ -159,9 +231,6 @@ func (w Workload) validate() error { if w.StateDir == "" { return errors.New("state_dir is required") } - if w.Environment == "" { - return errors.New("environment is required") - } if w.GitProvider.Owner == "" { return errors.New("git_provider.owner is required") } diff --git a/config/config_test.go b/config/config_test.go index 5c278c7..d572e9b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -15,7 +15,6 @@ func TestLoadRejectsYAMLWithoutWorkloadsKey(t *testing.T) { path := filepath.Join(t.TempDir(), "workload.yaml") require.NoError(t, os.WriteFile(path, []byte(` name: gymportal-api -environment: prod container_name: gymportal-api deploy_command: docker stop gymportal-api || true && docker rm gymportal-api || true && docker run -d --name gymportal-api "$WORKLOAD_IMAGE_REF" state_dir: /tmp/recond/gymportal-api @@ -42,7 +41,7 @@ func TestLoadWorkloadListYAML(t *testing.T) { require.NoError(t, os.WriteFile(path, []byte(` workloads: - name: api - environment: prod + release_prefix: api-prod container_name: api deploy_command: docker stop api || true && docker rm api || true && docker run -d --name api "$WORKLOAD_IMAGE_REF" state_dir: /tmp/recond/api @@ -54,7 +53,6 @@ workloads: type: tcp url: tcp://localhost:8080 - name: worker - environment: prod container_name: worker deploy_command: docker stop worker || true && docker rm worker || true && docker run -d --name worker "$WORKLOAD_IMAGE_REF" state_dir: /tmp/recond/worker @@ -68,9 +66,11 @@ workloads: require.NoError(t, err) require.Len(t, cfg.Workloads, 2) require.Equal(t, "api", cfg.Workloads[0].Name) + require.Equal(t, "api-prod", cfg.Workloads[0].ReleasePrefix) require.Equal(t, 45*time.Second, cfg.Workloads[0].CheckInterval.Duration) require.Equal(t, HealthCheckTCP, cfg.Workloads[0].HealthCheck.Type) require.Equal(t, "worker", cfg.Workloads[1].Name) + require.Empty(t, cfg.Workloads[1].ReleasePrefix) require.Equal(t, 2*time.Minute, cfg.Workloads[1].CheckInterval.Duration) } @@ -82,7 +82,7 @@ func TestLoadRejectsJSONConfigExtension(t *testing.T) { "workloads": [ { "name": "api", - "environment": "prod", + "release_prefix": "api-prod", "container_name": "api", "deploy_command": "docker stop api || true && docker rm api || true && docker run -d --name api \"$WORKLOAD_IMAGE_REF\"", "state_dir": "/tmp/recond/api", @@ -122,7 +122,7 @@ func TestLoadRejectsWorkloadsMapping(t *testing.T) { require.NoError(t, os.WriteFile(path, []byte(` workloads: name: api - environment: prod + release_prefix: api-prod `), 0o600)) _, err := Load(path) @@ -136,7 +136,6 @@ func TestLoadRejectsInvalidDuration(t *testing.T) { require.NoError(t, os.WriteFile(path, []byte(` workloads: - name: api - environment: prod container_name: api deploy_command: docker stop api || true && docker rm api || true && docker run -d --name api "$WORKLOAD_IMAGE_REF" state_dir: /tmp/recond/api @@ -170,7 +169,6 @@ func TestLoadRejectsTCPHealthCheckWithoutURL(t *testing.T) { require.NoError(t, os.WriteFile(path, []byte(` workloads: - name: api - environment: prod container_name: api deploy_command: docker stop api || true && docker rm api || true && docker run -d --name api "$WORKLOAD_IMAGE_REF" state_dir: /tmp/recond/api diff --git a/deployment/docker_deployer.go b/deployment/docker_deployer.go index b72c8fb..fb70583 100644 --- a/deployment/docker_deployer.go +++ b/deployment/docker_deployer.go @@ -153,7 +153,7 @@ func (d *dockerDeployer) Deploy(ctx context.Context, workload config.Workload, m } noPreviousState := errors.Is(err, state.ErrFileNotFound) - d.notifier.NotifyOnNewDeploymentStarted(meta) + d.notifier.NotifyOnNewDeploymentStarted(workload.Name, meta) newState, err := d.deploy(ctx, workload, meta, stateMgr) if err == nil { @@ -164,7 +164,7 @@ func (d *dockerDeployer) Deploy(ctx context.Context, workload config.Workload, m return nil } - d.notifier.NotifyOnDeploymentFailed(meta, err) + d.notifier.NotifyOnDeploymentFailed(workload.Name, meta, err) d.logger.Error("deployment failed", "workload", workload.Name, "error", err) if !errorShouldTriggerRollback(err) { return fmt.Errorf("deployment failed but no rollback required: %w", err) @@ -181,7 +181,7 @@ func (d *dockerDeployer) Deploy(ctx context.Context, workload config.Workload, m return nil } - d.notifier.NotifyOnDeploymentFailed(meta, fmt.Errorf("ROLLBACK FAILED: %w", rollbackErr)) + d.notifier.NotifyOnDeploymentFailed(workload.Name, meta, fmt.Errorf("ROLLBACK FAILED: %w", rollbackErr)) return fmt.Errorf("deployment and rollback failed: %w", rollbackErr) } @@ -203,7 +203,6 @@ func (d *dockerDeployer) deploy(ctx context.Context, workload config.Workload, m newState = state.DeploymentState{ Workload: workload.Name, - Environment: meta.Environment, Image: meta.Image, ImageTag: meta.ImageTag, ManifestDigest: meta.ManifestDigest, @@ -233,7 +232,6 @@ func (d *dockerDeployer) rollback(ctx context.Context, stateMgr state.Manager, w ) previousStateMeta := git_provider.DeploymentMetadata{ - Environment: previousState.Environment, Image: previousState.Image, ImageTag: previousState.ImageTag, ManifestDigest: previousState.ManifestDigest, @@ -255,7 +253,6 @@ func (d *dockerDeployer) rollback(ctx context.Context, stateMgr state.Manager, w rollbackState = state.DeploymentState{ Workload: workload.Name, - Environment: previousState.Environment, Image: previousState.Image, ImageTag: previousState.ImageTag, ManifestDigest: previousState.ManifestDigest, @@ -382,7 +379,6 @@ func deploymentEnv(workload config.Workload, meta git_provider.DeploymentMetadat return []string{ "WORKLOAD_NAME=" + workload.Name, "WORKLOAD_CONTAINER_NAME=" + workload.ContainerName, - "WORKLOAD_ENVIRONMENT=" + meta.Environment, "WORKLOAD_IMAGE=" + meta.Image, "WORKLOAD_IMAGE_TAG=" + meta.ImageTag, "WORKLOAD_MANIFEST_DIGEST=" + meta.ManifestDigest, diff --git a/git_provider/git_provider.go b/git_provider/git_provider.go index bd53683..85a7874 100644 --- a/git_provider/git_provider.go +++ b/git_provider/git_provider.go @@ -13,9 +13,6 @@ var ( // DeploymentMetadata is the structure published as a release asset by CI. // The GitHub Actions workflow serializes this after a successful image push. type DeploymentMetadata struct { - // Environment is the target environment name (e.g. "dev", "production"). - Environment string `json:"environment"` - // Image is the full image reference including tag. Image string `json:"image"` @@ -34,5 +31,5 @@ type DeploymentMetadata struct { } type GitProvider interface { - FetchLatestDeploymentMetadata(ctx context.Context, environment string) (meta DeploymentMetadata, err error) + FetchLatestDeploymentMetadata(ctx context.Context, releasePrefix string) (meta DeploymentMetadata, err error) } diff --git a/git_provider/github.go b/git_provider/github.go index a1bc3ea..fc7f36f 100644 --- a/git_provider/github.go +++ b/git_provider/github.go @@ -61,16 +61,17 @@ func NewGithubClient(owner, repo, token string) (GitProvider, error) { }, nil } -// FetchLatestDeploymentMetadata retrieves the most recent image release for -// environment and unmarshals its deployment-metadata.json artifact. -func (c *githubClient) FetchLatestDeploymentMetadata(ctx context.Context, environment string) (meta DeploymentMetadata, err error) { +// FetchLatestDeploymentMetadata retrieves the most recent release matching +// releasePrefix and unmarshals its deployment-metadata.json artifact. An empty +// prefix considers all releases newest-first. +func (c *githubClient) FetchLatestDeploymentMetadata(ctx context.Context, releasePrefix string) (meta DeploymentMetadata, err error) { releases, err := c.listReleases(ctx) if err != nil { return meta, fmt.Errorf("list releases: %w", err) } for _, r := range releases { - if !strings.HasPrefix(r.GetTagName(), environment) { + if releasePrefix != "" && !strings.HasPrefix(r.GetTagName(), releasePrefix) { continue } @@ -86,12 +87,6 @@ func (c *githubClient) FetchLatestDeploymentMetadata(ctx context.Context, enviro return meta, fmt.Errorf("download metadata for release %q: %w", r.GetTagName(), err) } - // Verify environment matches — defence against misconfigured CI - // publishing a production asset under a dev release tag. - if meta.Environment != environment { - continue - } - return meta, nil } diff --git a/logger/logger.go b/logger/logger.go index f96af5e..b3f8c44 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -4,7 +4,7 @@ // - JSON output by default for log aggregation (journald, Loki, Datadog) // - Text output when LOG_FORMAT=text (local development) // - Level controlled by LOG_LEVEL env var (debug, info, warn, error) -// - Structured fields: environment, phase, digest, duration included consistently +// - Structured fields: workload, phase, digest, duration included consistently package logger import ( @@ -37,8 +37,8 @@ func Setup() *slog.Logger { return logger } -// WithContainerName returns a child logger with the environment field pre-set. -// All log lines emitted from the reconciler for an environment will carry this. +// WithContainerName returns a child logger with the container field pre-set. +// All log lines emitted from the reconciler for a container will carry this. func WithContainerName(logger *slog.Logger, containerName string) *slog.Logger { return logger.With("container", containerName) } diff --git a/notifier/notifier.go b/notifier/notifier.go index 3559cf6..a4b2c19 100644 --- a/notifier/notifier.go +++ b/notifier/notifier.go @@ -11,13 +11,14 @@ import ( // // Since alerts are not core business logic, error encountered should be logged type Notifier interface { - NotifyOnNewDeploymentStarted(meta git_provider.DeploymentMetadata) - NotifyOnDeploymentFailed(meta git_provider.DeploymentMetadata, err error) + NotifyOnNewDeploymentStarted(workloadName string, meta git_provider.DeploymentMetadata) + NotifyOnDeploymentFailed(workloadName string, meta git_provider.DeploymentMetadata, err error) NotifyOnDeploymentSuccess(dep state.DeploymentState) } type Noop struct{} -func (Noop) NotifyOnNewDeploymentStarted(meta git_provider.DeploymentMetadata) {} -func (Noop) NotifyOnDeploymentFailed(meta git_provider.DeploymentMetadata, err error) {} -func (Noop) NotifyOnDeploymentSuccess(dep state.DeploymentState) {} +func (Noop) NotifyOnNewDeploymentStarted(workloadName string, meta git_provider.DeploymentMetadata) {} +func (Noop) NotifyOnDeploymentFailed(workloadName string, meta git_provider.DeploymentMetadata, err error) { +} +func (Noop) NotifyOnDeploymentSuccess(dep state.DeploymentState) {} diff --git a/notifier/slack_notifier.go b/notifier/slack_notifier.go index 19297d7..03421e9 100644 --- a/notifier/slack_notifier.go +++ b/notifier/slack_notifier.go @@ -32,7 +32,7 @@ const ( type slackNotification struct { State deploymentState `json:"state"` - Environment string `json:"environment"` + Workload string `json:"workload"` Image string `json:"image"` ImageTag string `json:"image_tag"` ManifestDigest string `json:"manifest_digest"` @@ -49,15 +49,15 @@ func NewSlackNotifier(url string, log *slog.Logger) *Slack { } } -func (sl *Slack) NotifyOnNewDeploymentStarted(meta git_provider.DeploymentMetadata) { - msg := slackNotificationFromDeploymentMeta(meta, deploymentStateInitialize) +func (sl *Slack) NotifyOnNewDeploymentStarted(workloadName string, meta git_provider.DeploymentMetadata) { + msg := slackNotificationFromDeploymentMeta(workloadName, meta, deploymentStateInitialize) err := sl.send(msg) if err != nil { sl.log.Error("failed to send slack notification", "error", err) } } -func (sl *Slack) NotifyOnDeploymentFailed(meta git_provider.DeploymentMetadata, err error) { - msg := slackNotificationFromDeploymentMeta(meta, deploymentStateFailed) +func (sl *Slack) NotifyOnDeploymentFailed(workloadName string, meta git_provider.DeploymentMetadata, err error) { + msg := slackNotificationFromDeploymentMeta(workloadName, meta, deploymentStateFailed) msg.Error = err.Error() err = sl.send(msg) if err != nil { @@ -73,10 +73,10 @@ func (sl *Slack) NotifyOnDeploymentSuccess(dep state.DeploymentState) { } } -func slackNotificationFromDeploymentMeta(meta git_provider.DeploymentMetadata, state deploymentState) slackNotification { +func slackNotificationFromDeploymentMeta(workloadName string, meta git_provider.DeploymentMetadata, state deploymentState) slackNotification { return slackNotification{ State: state, - Environment: meta.Environment, + Workload: workloadName, Image: meta.Image, ImageTag: meta.ImageTag, ManifestDigest: meta.ManifestDigest, @@ -88,7 +88,7 @@ func slackNotificationFromDeploymentMeta(meta git_provider.DeploymentMetadata, s func slackNotificationFromDeploymentState(depState state.DeploymentState, st deploymentState) slackNotification { return slackNotification{ State: st, - Environment: depState.Environment, + Workload: depState.Workload, Image: depState.Image, ImageTag: "", ManifestDigest: depState.ManifestDigest, @@ -151,15 +151,15 @@ func buildSlackMessage(msg slackNotification) map[string]interface{} { return map[string]interface{}{ "text": fmt.Sprintf( - "Deployment on %s", - msg.Environment, + "Deployment for %s", + msg.Workload, ), "blocks": []slackBlock{ { Type: "header", Text: &slackText{ Type: "plain_text", - Text: fmt.Sprintf(" 🛠️Environment %s", msg.Environment), + Text: fmt.Sprintf("Workload %s", msg.Workload), }, }, { diff --git a/reconciler/reconciler.go b/reconciler/reconciler.go index 364ead0..9293118 100644 --- a/reconciler/reconciler.go +++ b/reconciler/reconciler.go @@ -71,7 +71,7 @@ func (r *Reconciler) reconcileOnce(ctx context.Context) { if errors.Is(err, git_provider.ErrNoDeploymentMetaFound) { r.log.Info("no deployment metadata found", "phase", logger.PhasePoll, - "environment", r.workload.Environment, + "release_prefix", r.workload.ReleasePrefix, ) return } @@ -132,7 +132,7 @@ func (r *Reconciler) fetchDesiredState(ctx context.Context) (git_provider.Deploy if r.gitProvider == nil { return git_provider.DeploymentMetadata{}, fmt.Errorf("workload %q has no git provider", r.workload.Name) } - meta, err := r.gitProvider.FetchLatestDeploymentMetadata(ctx, r.workload.Environment) + meta, err := r.gitProvider.FetchLatestDeploymentMetadata(ctx, r.workload.ReleasePrefix) if err != nil { return meta, fmt.Errorf("failed to fetch metadata: %w", err) } @@ -146,10 +146,6 @@ func (r *Reconciler) validateDesiredState(meta git_provider.DeploymentMetadata) if meta.ManifestDigest == "" { return fmt.Errorf("deployment metadata manifest_digest is required") } - if r.workload.Environment != "" && meta.Environment != "" && meta.Environment != r.workload.Environment { - return fmt.Errorf("deployment metadata environment %q does not match workload environment %q", meta.Environment, r.workload.Environment) - } - return nil } diff --git a/state/local_fs.go b/state/local_fs.go index 201a590..c90a896 100644 --- a/state/local_fs.go +++ b/state/local_fs.go @@ -8,7 +8,7 @@ import ( "path/filepath" ) -// managerWithLocalFileSystem handles reading and writing deployment state for one environment. +// managerWithLocalFileSystem handles reading and writing deployment state for one workload. type managerWithLocalFileSystem struct { dir string } diff --git a/state/local_fs_test.go b/state/local_fs_test.go index b7a0a60..9e39a1d 100644 --- a/state/local_fs_test.go +++ b/state/local_fs_test.go @@ -14,7 +14,6 @@ func TestLocalFileSystemManagerCommitDeployedWritesFirstDeployment(t *testing.T) require.NoError(t, err) expected := DeploymentState{ - Environment: "dev", Image: "ghcr.io/example/app:v1.0.0", ManifestDigest: "v1.0.0", GitSHA: "sha-v1.0.0", @@ -25,7 +24,6 @@ func TestLocalFileSystemManagerCommitDeployedWritesFirstDeployment(t *testing.T) actual, err := manager.LoadDeployed() require.NoError(t, err) - require.Equal(t, expected.Environment, actual.Environment) require.Equal(t, expected.Image, actual.Image) require.Equal(t, expected.ManifestDigest, actual.ManifestDigest) require.Equal(t, expected.GitSHA, actual.GitSHA) @@ -41,14 +39,12 @@ func TestLocalFileSystemManagerCommitDeployedPromotesPreviousDeployment(t *testi require.NoError(t, err) first := DeploymentState{ - Environment: "dev", Image: "ghcr.io/example/app:v1.0.0", ManifestDigest: "v1.0.0", GitSHA: "sha-v1.0.0", DeployedAt: time.Now().UTC(), } second := DeploymentState{ - Environment: "dev", Image: "ghcr.io/example/app:v1.1.0", ManifestDigest: "v1.1.0", GitSHA: "sha-v1.1.0", diff --git a/state/state.go b/state/state.go index de56a29..996784e 100644 --- a/state/state.go +++ b/state/state.go @@ -29,8 +29,6 @@ var ErrFileNotFound = errors.New("file not found") type DeploymentState struct { Workload string `json:"workload"` - Environment string `json:"environment,omitempty"` - // Image is the full image reference that is deployed. Image string `json:"image"`