How the Registry Component in Dewy Works: Artifact Discovery and Deployment Reporting
The Registry component in Dewy provides a unified interface for discovering the latest build artifact across multiple storage backends and reporting deployment status, using URL schemes like ghr://, s3://, and img:// to select concrete implementations at runtime.
The Registry component in Dewy serves as the abstraction layer that enables automatic artifact discovery and deployment tracking. As part of the linyows/dewy open-source deployment agent, this component decouples artifact storage specifics from the deployment logic, allowing operators to use GitHub Releases, Amazon S3, OCI registries, or custom gRPC services interchangeably.
Core Interface and Design
At the heart of the Registry component in Dewy lies a minimal interface defined in [registry/registry.go](https://github.com/linyows/dewy/blob/main/registry/registry.go#L22-L27). This contract ensures every backend implements two essential operations: retrieving the current artifact and reporting deployment results.
type Registry interface {
// Current returns the current artifact.
Current(context.Context) (*CurrentResponse, error)
// Report reports the result of deploying the artifact.
Report(context.Context, *ReportRequest) error
}
The CurrentResponse struct encapsulates everything Dewy needs to download and deploy an artifact: the artifact ID, version tag, download URL, creation timestamp, and the deployment slot for blue/green strategies. By standardizing on this interface, Dewy can switch between GitHub Releases, S3 buckets, or container registries without modifying the core deployment loop.
Factory Pattern: Selecting the Backend
Dewy uses a factory function to instantiate the correct Registry implementation based on the URL scheme. The New function in [registry/registry.go](https://github.com/linyows/dewy/blob/main/registry/registry.go#L57-L77) parses the scheme prefix and delegates to the appropriate constructor.
func New(ctx context.Context, url string, log *logging.Logger) (Registry, error) {
splitted := strings.SplitN(url, "://", 2)
switch splitted[0] {
case ghrScheme:
return NewGHR(ctx, url, log)
case s3Scheme:
return NewS3(ctx, url, log)
case gsScheme:
return NewGS(ctx, url, log)
case grpcScheme:
return NewGRPC(ctx, url)
case imgScheme:
return NewOCI(ctx, url, log)
}
return nil, fmt.Errorf("unsupported registry: %s", url)
}
This scheme-based routing enables flexible configuration. Operators can specify ghr://github.com/owner/repo for GitHub Releases, s3://us-east-1/bucket/prefix for Amazon S3, or img://registry/repo for OCI-compliant container registries. If the scheme is unrecognized, the factory returns an error immediately, preventing runtime failures during deployment.
Version Selection and Slot Extraction
Once a Registry implementation identifies available versions, it must determine which artifact represents the "current" deployment target. Dewy supports both SemVer (Semantic Versioning) and CalVer (Calendar Versioning) schemes, allowing teams to use either 1.2.3 or 2024.01.02 style tags.
After selecting the latest version, the Registry extracts the slot identifier from the build metadata. This slot value (typically blue or green) enables zero-downtime blue/green deployments. The extractSlot helper in [registry/registry.go](https://github.com/linyows/dewy/blob/main/registry/registry.go#L80-L95) handles both versioning schemes:
func extractSlot(tag, calverFormat string) string {
if calverFormat != "" {
if f, err := NewCalVerFormat(calverFormat); err == nil {
if cv := f.Parse(tag); cv != nil {
return cv.BuildMetadata
}
}
}
if sv := ParseSemVer(tag); sv != nil {
return sv.BuildMetadata
}
return ""
}
Each backend calls this function after identifying the latest tag, ensuring consistent slot extraction regardless of whether the artifact resides in GitHub Releases, S3, or a container registry.
Concrete Backend Implementations
GitHub Releases (ghr://)
The GitHub Releases backend, implemented in [registry/ghr.go](https://github.com/linyows/dewy/blob/main/registry/ghr.go), interacts with the GitHub API to list releases and download assets.
Construction: NewGHR parses the URL and extracts query parameters including artifact (the asset name), pre-release (whether to include pre-releases), and calver (the calendar version format). It initializes a GitHub client using client.NewGitHub.
Finding the latest release: The latest method lists all non-draft releases, builds a map of tag names to release objects, then selects the newest tag using either FindLatestCalVer or FindLatestSemVer based on configuration.
Current implementation: Current constructs an artifact URL in the format ghr://owner/repo/tag/artifact, extracts the deployment slot using extractSlot, and returns a CurrentResponse containing the download URL and metadata.
Reporting: Report uploads a small text file to the release assets indicating successful deployment, including timestamps and host information.
Key code locations:
- URL parsing:
ghr.go:59-70 - Latest release selection:
ghr.go:172-186 - Artifact URL construction:
ghr.go:158-164
Amazon S3 (s3://)
The S3 backend in [registry/s3.go](https://github.com/linyows/dewy/blob/main/registry/s3.go) treats versioned directories as release artifacts, making it ideal for private binaries and air-gapped environments.
Construction: NewS3 parses URLs in the format s3://<region>/<bucket>/<prefix> and configures the AWS SDK. It supports custom endpoints via the AWS_ENDPOINT_URL environment variable for local testing with MinIO or LocalStack.
Latest version discovery: LatestVersion lists directories (common prefixes) under the configured prefix rather than individual objects. It extracts version strings from directory names and applies FindLatestCalVer or FindLatestSemVer to identify the current release.
Current implementation: Current locates the specific artifact file within the version directory, either by exact name or by platform matching (OS/architecture detection). It retrieves the object's LastModified timestamp, constructs an s3:// URL, and extracts the slot from the version directory name.
Reporting: Report creates an empty text object at <prefix>/<tag>/<host>_<command>_<timestamp>.txt to mark successful deployment.
Key code locations:
- URL parsing:
s3.go:40-55 - Latest version discovery:
s3.go:69-104 - Artifact URL builder:
s3.go:68-80
OCI / Docker Registry (img://)
The OCI backend in [registry/oci.go](https://github.com/linyows/dewy/blob/main/registry/oci.go) enables Dewy to deploy artifacts stored as container images, supporting Docker Hub, GitHub Container Registry, Amazon ECR, and other OCI-compliant registries.
Construction: NewOCI parses img://registry/repo[:tag] URLs and loads credentials from environment variables: DOCKER_USERNAME and DOCKER_PASSWORD for basic auth, or GITHUB_TOKEN for GitHub Container Registry.
Listing tags: listTags implements pagination-aware tag enumeration by repeatedly calling fetchTagsPage, following the Link header for Docker Registry API pagination (limited to 100 pages to prevent infinite loops).
Authentication: When receiving HTTP 401 responses, getBearerToken parses the WWW-Authenticate header to obtain a Bearer token. It validates the token realm URL against private IP blocks to prevent Server-Side Request Forgery (SSRF) attacks before retrying the request.
Current implementation: Current selects the newest tag using findLatestTag, then calls getImageDigest to fetch the manifest and extract the Docker-Content-Digest header as the artifact ID. The slot is extracted from the tag's build metadata using the same extractSlot helper as other backends.
Reporting: Currently a no-op for Phase 1, with future versions planned to support updating image labels or annotations to reflect deployment status.
Key code locations:
- Token handling:
oci.go:57-90 - Pagination:
oci.go:47-66 - Manifest request:
oci.go:107-135
Error Handling with ArtifactNotFoundError
When an artifact cannot be located, all Registry implementations return a specific ArtifactNotFoundError rather than a generic error. This structured error type, defined in [registry/ghr.go](https://github.com/linyows/dewy/blob/main/registry/ghr.go#L19-L28), captures the artifact name, release time, and a descriptive message.
The error struct includes an IsWithinGracePeriod method that allows the deployment controller to distinguish between permanent failures and transient "not yet uploaded" situations. This is particularly useful when CI pipelines upload assets asynchronously; Dewy can retry the deployment rather than failing immediately when an artifact appears missing.
Complete Usage Example
The following example demonstrates the complete workflow for initializing a Registry, querying the current artifact, and reporting deployment success:
// Create a logger (omitted for brevity)
log := logging.NewLogger(...)
// Choose a registry URL (could be a flag, env var, etc.)
regURL := "ghr://github.com/owner/repo?artifact=mybinary&calver=2006.01.02"
// Build the appropriate Registry implementation
reg, err := registry.New(context.Background(), regURL, log)
if err != nil {
// handle unsupported scheme, malformed URL, etc.
}
// Query the current artifact
cur, err := reg.Current(context.Background())
if err != nil {
// maybe ArtifactNotFoundError – decide to retry later
}
fmt.Printf("Deploying %s (tag %s) from %s\n", cur.ArtifactURL, cur.Tag, cur.ID)
// After a successful deployment, report it
_ = reg.Report(context.Background(), ®istry.ReportRequest{
ID: cur.ID,
Tag: cur.Tag,
Command: "server", // or "assets"
Err: nil, // nil means success
})
This pattern abstracts away the underlying storage mechanism, allowing the same deployment logic to work whether artifacts are stored in GitHub Releases, S3 buckets, or container registries.
Summary
- The Registry component in Dewy defines a minimal interface with
CurrentandReportmethods, enabling artifact discovery and deployment tracking across diverse storage backends. - A factory function in
registry/registry.goinstantiates concrete implementations based on URL schemes (ghr://,s3://,img://, etc.), making the system extensible and configuration-driven. - Version selection supports both SemVer and CalVer, with automatic extraction of build metadata slots (blue/green) from tag names to facilitate zero-downtime deployments.
- Concrete backends handle protocol-specific logic: GitHub Releases uses the GitHub API with asset uploads for reporting, S3 uses directory listing with platform matching, and OCI registries implement Docker Registry API authentication with pagination support.
- Structured error handling via
ArtifactNotFoundErrorincludes grace period detection, allowing the system to distinguish between transient upload delays and permanent missing artifacts.
Frequently Asked Questions
What URL schemes does the Dewy Registry support?
The Dewy Registry supports multiple URL schemes including ghr:// for GitHub Releases, s3:// for Amazon S3, gs:// for Google Cloud Storage, img:// for OCI/Docker registries, and grpc:// for custom gRPC-based registries. Each scheme triggers a specific backend implementation via the factory function in registry/registry.go.
How does the Registry component handle blue/green deployments?
The Registry extracts the deployment slot (typically blue or green) from the build metadata portion of version tags using the extractSlot function in registry/registry.go. This works with both SemVer tags like 1.0.0+blue and CalVer tags like 2024.01.15+green, allowing Dewy to route traffic to the appropriate deployment slot without downtime.
What happens when an artifact cannot be found in the Registry?
When an artifact is missing, implementations return an ArtifactNotFoundError defined in registry/ghr.go, which includes the artifact name, release time, and a grace period check via IsWithinGracePeriod. This allows Dewy to retry the deployment if the artifact is temporarily unavailable (e.g., still uploading from CI) rather than failing immediately.
How does Dewy report successful deployments to the Registry?
After deploying an artifact, Dewy calls the Report method with a ReportRequest containing the artifact ID, tag, command type, and error status (nil for success). The GitHub Releases backend uploads a text file to the release assets, S3 creates an empty object at a specific key path, and the OCI backend currently implements this as a no-op with future support planned for label updates.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →