Contributing to delstack
May 4, 2026 ยท View on GitHub
This guide covers what you need to start contributing. For the architectural overview (layered packages, deletion algorithm, mocking strategy), see CLAUDE.md.
Project Structure
cmd/
delstack/ CLI entrypoint
internal/
app/ Application layer (stack deletion orchestration)
cdk/ CDK integration (synthesis, manifest parsing)
operation/ Operators for force-deleting specific resource types
preprocessor/ Pre-deletion processing (e.g., Lambda VPC detachment)
resourcetype/ Resource type constants
io/ Logger and I/O utilities
version/ Version and revision info
pkg/
client/ AWS SDK client wrappers with interfaces for testing
e2e/
<scenario>/ Per-scenario E2E test environments (CDK + deploy script)
Package Dependency Rules
pkg/client -> No internal dependencies (AWS SDK only)
internal/operation -> pkg/client
internal/preprocessor -> pkg/client
internal/app -> internal/operation, internal/preprocessor (NOT directly on pkg/client)
No circular dependencies. internal/app must not depend on pkg/client directly: use operation or preprocessor as intermediaries.
Development Setup
make run OPT="<options>": Run delstack locallymake test: Run all testsmake mockgen: Regenerate mocksmake lint: Run linter
Adding New Resource Support
When adding support for a new AWS resource, first decide whether it is an Operator or a Preprocessor:
- Operator: force-deletes resources that cause
DELETE_FAILEDduring stack deletion (e.g., emptying S3 buckets, deleting ECR images). Reference PR: #569 (Athena WorkGroup). - Preprocessor: runs before CloudFormation deletion. Either a Checker (fatal on failure, e.g., deletion-protection check) or a Modifier (best-effort, e.g., Lambda VPC detachment).
Detailed step-by-step procedures for each are in the Claude Code skills under .claude/skills/:
add-operator: all files to touch when adding an Operator (resourcetype, client, operator, factory, collection, tests, README, E2E).add-preprocessor: all files to touch when adding a Preprocessor, plus the Checker vs Modifier decision.
Testing
Unit Tests
make test: Run all testsmake test_view: Run tests with coverage reportmake mockgen: Regenerate mocks (driven by//go:generate mockgencomments inpkg/client/, which must be on line 1)
Two mocking patterns are used; do not mix them:
pkg/client/*_test.go: AWS SDK middleware mocks. Inject amiddleware.FinalizeMiddlewareFuncviaconfig.WithAPIOptionsto intercept SDK calls. For paginated APIs, captureNextTokenin an Initialize-stage middleware viamiddleware.WithStackValue/GetStackValue. Seebackup_test.goands3_test.go. Background: Testing with AWS SDK for Go v2 without interface mocks.internal/operation/*_test.goandinternal/preprocessor/*_test.go: gomock against theI<Service>interfaces inpkg/client/, with mocks regenerated bymake mockgen.
E2E Tests
E2E tests deploy real AWS resources and incur cost. Contributors are expected to update E2E code when needed, but the maintainer runs them before merging.
make testgen_<scenario>: deploy a test stack for the scenario.make e2e_<scenario>: deploy and then rundelstackagainst it. Auto-generates a unique stage name (STAGE=<name>to override;OPT="-p <profile>"for AWS profile).make testgen_help/make e2e_help: list all available scenarios.
When adding a new target resource type, extend e2e/full/ (CDK constructor + post-deploy SDK setup in deploy.go); see the add-operator skill for the full file list. For new preprocessors or other logic, prefer a dedicated e2e/<name>/ directory.
Important: E2E tests must reproduce the
DELETE_FAILEDstate. If a blocking dependency is created inside the same CloudFormation stack via CDK, CloudFormation resolves the dependency order and deletion succeeds, defeating the test. Blocking dependencies must be created outside of CloudFormation (via SDK calls indeploy.go). CDK should only create the base resource itself. If a dependency cannot feasibly be created outside CloudFormation (e.g., MFA requiring TOTP, FIDO/Passkey requiring physical devices), skip it in E2E and rely on unit tests.
When creating a new e2e/<name>/ directory, add a cdk/.gitignore that excludes cdk.context.json (CDK generates this file at deploy time with account-specific context).
Code Style & Conventions
- Code comments in English, minimal.
var _ IXxx = (*Xxx)(nil)for compile-time interface implementation check (used in bothpkg/client/andinternal/operation/).- Concurrency:
errgroup+semaphore.NewWeighted(runtime.NumCPU()). - Errors: public methods in
pkg/clientwrap withClientError. Ininternal/operationandinternal/preprocessor, return client errors as-is (already wrapped); only wrap withClientErrorfor errors generated locally (e.g.,ctx.Done(), validation). - Idempotency: check existence before deletion.
//go:generate mockgencomment must be on line 1 of the file.- When a client file already exists for a service (e.g.,
iam.go), extend its interface and struct rather than creating a new file.
Test Naming
Top-level test function: Test[ReceiverType]_[MethodName] (e.g., TestEcr_DeleteRepository, TestS3BucketOperator_DeleteS3Bucket).