Refactor repo for standalone portable provider workflow

This commit is contained in:
beatz174-bit
2026-04-21 13:10:19 +10:00
parent bf179254b5
commit 3b6d62ba07
9 changed files with 171 additions and 203 deletions
+40 -49
View File
@@ -1,67 +1,58 @@
# AGENTS.md # AGENTS.md
## Goal ## Goal
Build a Terraform provider for Dynu DNS and domains. Build and maintain a standalone Terraform provider for Dynu DNS and domains.
## Scope for current phase ## Scope
Read-only only. Implement provider configuration and data sources first. Current phase is **read-only**.
Do not implement any writable Terraform resources in the first phase.
## Language and framework
- Language: Go
- Terraform provider framework: HashiCorp Terraform Plugin Framework
- Do not use the legacy SDK unless explicitly requested.
## API usage
- Use Dynu's public API.
- Prefer stable, documented endpoints.
- Build a small internal API client package rather than scattering HTTP calls across resources/data sources.
- Add clear handling for pagination, 4xx/5xx responses, and malformed responses.
- Never log secrets.
## Provider design
Implement: Implement:
- Provider configuration - Provider configuration
- Environment variable support for credentials - Environment variable support for credentials
- Read-only data sources: - Read-only data sources:
- dynu_domains - `dynu_domains`
- dynu_domain - `dynu_domain`
- dynu_dns_records - `dynu_dns_records`
Do not implement: Do not implement in this phase:
- resource_dynu_dns_record - Any Terraform resources with create/update/delete
- any create/update/delete flows - Any write HTTP methods
- any speculative unsupported resources
## Safety ## Language and framework
- Read-only phase only. - Language: Go
- No write HTTP methods in this phase. - Framework: HashiCorp Terraform Plugin Framework
- Acceptance tests must only cover provider auth and read-only data sources. - Do not use the legacy Terraform Plugin SDK unless explicitly requested
- Any future write support must be added in a separate phase.
## Code quality ## API usage
- Keep code modular and idiomatic Go. - Use Dynu public API endpoints
- Use strong typing for API models. - Prefer stable, documented endpoints
- Prefer explicit schema definitions with clear descriptions. - Keep HTTP logic in a small internal client package
- Return actionable diagnostics. - Handle pagination and API errors clearly
- Keep public documentation accurate to actual implementation. - Never log secrets or credentials
## Portability requirements
- Keep the repository generic and standalone
- Do not reference personal infrastructure, private domains, homelab tooling, or external repo scripts
- Use neutral placeholders in docs and examples (for example: `example.com`, `my-test-domain.example`, `var.dynu_api_key`)
- Avoid hardcoded local filesystem paths
## Testing ## Testing
- Add unit tests where practical. - Add unit tests where practical
- Add acceptance tests gated behind environment variables. - Gate acceptance tests behind generic environment variables
- Do not require live credentials for normal unit tests. - `TF_ACC=1`
- `DYNU_API_KEY`
- optional `DYNU_DOMAIN` for domain-specific acceptance coverage
- Unit tests must not require live credentials
## Documentation ## Documentation
- Add examples for every implemented data source. - Keep README and examples aligned with actual provider behavior
- Update README with provider configuration and environment variables. - Document current read-only limitations clearly
- Document limitations and unsupported areas clearly. - Include build, test, and acceptance test instructions for any contributor
## Developer scripts
- `scripts/setup-dev.sh`: validate local toolchain requirements
- `scripts/check.sh`: run formatting and unit checks
- `scripts/testacc.sh`: run acceptance tests using generic env vars
## Output expectations ## Output expectations
Create a provider skeleton that can compile and expose the provider plus read-only data sources. Changes should keep the provider idiomatic, easy to contribute to, and ready for public/open-source usage.
## Codex environment
- Use `codex/setup.sh` to prepare the repository-local Codex environment.
- Use `codex/maintain.sh` to refresh and validate the environment.
- Use `codex/doctor.sh` for troubleshooting.
- Do not install global packages unless explicitly required.
- Prefer repo-local state under `.codex/`.
+52 -24
View File
@@ -1,23 +1,28 @@
# terraform-provider-dynu # terraform-provider-dynu
Terraform provider for Dynu DNS using the Terraform Plugin Framework. A standalone Terraform provider for [Dynu](https://www.dynu.com/) DNS data.
> Current phase is **read-only**: this provider implements provider configuration and data sources only. > Current phase: **read-only**. This provider supports provider configuration and data sources only.
## Features ## Feature scope
- Provider configuration with API key authentication. Implemented:
- Environment variable support (`DYNU_API_KEY`). - Provider authentication via API key
- Environment variable support (`DYNU_API_KEY`)
- Read-only data sources: - Read-only data sources:
- `dynu_domains` - `dynu_domains`
- `dynu_domain` - `dynu_domain`
- `dynu_dns_records` - `dynu_dns_records`
Not implemented in this phase:
- Terraform resources
- Any create/update/delete operations
## Requirements ## Requirements
- Terraform >= 1.5 - [Terraform](https://developer.hashicorp.com/terraform/downloads) `>= 1.5`
- Go >= 1.22 (for building) - [Go](https://go.dev/dl/) `>= 1.23` (for building and testing)
- A Dynu API key - Dynu API key
## Provider configuration ## Provider configuration
@@ -25,24 +30,29 @@ Terraform provider for Dynu DNS using the Terraform Plugin Framework.
provider "dynu" { provider "dynu" {
api_key = var.dynu_api_key api_key = var.dynu_api_key
} }
variable "dynu_api_key" {
type = string
sensitive = true
}
``` ```
You can also omit `api_key` and set `DYNU_API_KEY` in your environment. You can omit `api_key` in Terraform configuration and set the environment variable instead:
## Data sources ```bash
export DYNU_API_KEY="your-dynu-api-key"
```
## Data source usage
### dynu_domains ### dynu_domains
Returns all DNS domains associated with the account.
```hcl ```hcl
data "dynu_domains" "all" {} data "dynu_domains" "all" {}
``` ```
### dynu_domain ### dynu_domain
Resolves the root domain from a hostname, then returns full domain details.
```hcl ```hcl
data "dynu_domain" "selected" { data "dynu_domain" "selected" {
hostname = "www.example.com" hostname = "www.example.com"
@@ -51,34 +61,52 @@ data "dynu_domain" "selected" {
### dynu_dns_records ### dynu_dns_records
Resolves the root domain from a hostname, then returns DNS records for that domain.
```hcl ```hcl
data "dynu_dns_records" "records" { data "dynu_dns_records" "records" {
hostname = "www.example.com" hostname = "www.example.com"
} }
``` ```
## Development ## Build
```bash ```bash
./codex/setup.sh go build ./...
./codex/maintain.sh
``` ```
Run tests: ## Test
Run formatting and unit tests:
```bash
./scripts/check.sh
```
Or run unit tests directly:
```bash ```bash
go test ./... go test ./...
``` ```
Acceptance tests (requires live Dynu credentials): ## Acceptance tests
Acceptance tests are opt-in and require live Dynu credentials.
```bash ```bash
TF_ACC=1 DYNU_API_KEY=... go test ./internal/provider -run TestAcc TF_ACC=1 DYNU_API_KEY="your-dynu-api-key" ./scripts/testacc.sh
``` ```
Optional environment variable:
- `DYNU_DOMAIN` (for future domain-specific acceptance test cases)
## Developer workflow
- `./scripts/setup-dev.sh` validates required local tools
- `./scripts/check.sh` runs formatting and unit checks
- `./scripts/testacc.sh` runs acceptance tests
Repository-local Codex helpers are also available under `codex/` for agent-oriented workflows.
## Limitations ## Limitations
- No Terraform resources are implemented in this phase. - Read-only provider phase only
- No write operations are supported by provider code in this phase. - No writable Terraform resources yet
+8 -11
View File
@@ -13,7 +13,7 @@ echo "time: $(date -u +%FT%TZ)"
echo echo
echo "--- tools ---" echo "--- tools ---"
for cmd in bash git python3 docker; do for cmd in bash git go terraform; do
if have "$cmd"; then if have "$cmd"; then
echo "$cmd: $(command -v "$cmd")" echo "$cmd: $(command -v "$cmd")"
else else
@@ -23,17 +23,14 @@ done
echo echo
echo "--- repo ---" echo "--- repo ---"
[[ -f services-up.sh ]] && echo "services-up.sh: present" || echo "services-up.sh: missing" for path in README.md go.mod main.go internal/provider/provider.go; do
[[ -d core ]] && echo "core/: present" || true if [[ -f "$path" ]]; then
[[ -d apps ]] && echo "apps/: present" || true echo "$path: present"
[[ -d monitoring ]] && echo "monitoring/: present" || true else
echo "$path: missing"
fi
done
echo echo
echo "--- git status ---" echo "--- git status ---"
git status --short || true git status --short || true
echo
if have docker && docker compose version >/dev/null 2>&1; then
echo "--- docker compose ---"
docker compose version || true
fi
+7 -72
View File
@@ -7,80 +7,15 @@ source "$SCRIPT_DIR/lib.sh"
cd_repo_root cd_repo_root
log "Starting Codex environment setup in $(pwd)" log "Refreshing Codex environment and running repository checks"
if ! have git; then "$SCRIPT_DIR/setup.sh"
die "git is required"
fi
if ! have bash; then if [[ -x scripts/check.sh ]]; then
die "bash is required" ./scripts/check.sh
fi
# Keep all Codex-local state inside the repo or user home.
ensure_dir .codex
ensure_dir .codex/bin
ensure_dir .codex/cache
ensure_dir .codex/tmp
ensure_dir .codex/logs
# Useful environment defaults for repeatable non-interactive runs.
cat > .codex/env <<'EOF'
export CI=1
export TERM=xterm-256color
export PYTHONDONTWRITEBYTECODE=1
export PIP_DISABLE_PIP_VERSION_CHECK=1
export PIP_NO_INPUT=1
export GIT_PAGER=cat
EOF
# Optional Python venv for helper tooling.
if have python3; then
log "python3 detected"
if [[ ! -d .codex/venv ]]; then
python3 -m venv .codex/venv
fi
# shellcheck disable=SC1091
source .codex/venv/bin/activate
python -m pip install --upgrade pip setuptools wheel >/dev/null
# Install only lightweight tooling that is broadly useful.
python -m pip install \
pyyaml \
requests \
jinja2 \
>/dev/null
else else
warn "python3 not found; skipping virtualenv setup" log "scripts/check.sh not found; running go test ./..."
go test ./...
fi fi
# Basic repo checks based on your environment style. log "Maintenance complete"
if [[ -f services-up.sh ]]; then
log "Found services-up.sh"
chmod +x services-up.sh || true
else
warn "services-up.sh not found at repo root"
fi
# Make codex scripts executable.
find codex -maxdepth 1 -type f -name '*.sh' -exec chmod +x {} +
# Create a simple local ignore file for Codex-generated junk if needed.
touch .git/info/exclude
append_if_missing ".codex/" .git/info/exclude
append_if_missing ".pytest_cache/" .git/info/exclude
append_if_missing "__pycache__/" .git/info/exclude
# Helpful summary file for Codex tasks.
cat > .codex/README.md <<'EOF'
This directory contains local, disposable Codex environment state.
Contents:
- env: shell exports for non-interactive work
- venv: optional Python virtual environment
- cache/: tool cache
- tmp/: temporary files
- logs/: script logs
EOF
log "Setup complete"
+7 -46
View File
@@ -7,34 +7,24 @@ source "$SCRIPT_DIR/lib.sh"
cd_repo_root cd_repo_root
log "Starting Codex environment setup in $(pwd)" log "Preparing repository-local Codex environment in $(pwd)"
if ! have git; then for cmd in bash git; do
die "git is required" have "$cmd" || die "$cmd is required"
fi done
if ! have bash; then
die "bash is required"
fi
# Keep all Codex-local state inside the repo or user home.
ensure_dir .codex ensure_dir .codex
ensure_dir .codex/bin ensure_dir .codex/bin
ensure_dir .codex/cache ensure_dir .codex/cache
ensure_dir .codex/tmp ensure_dir .codex/tmp
ensure_dir .codex/logs ensure_dir .codex/logs
# Useful environment defaults for repeatable non-interactive runs. cat > .codex/env <<'ENVEOF'
cat > .codex/env <<'EOF'
export CI=1 export CI=1
export TERM=xterm-256color export TERM=xterm-256color
export PYTHONDONTWRITEBYTECODE=1
export PIP_DISABLE_PIP_VERSION_CHECK=1
export PIP_NO_INPUT=1
export GIT_PAGER=cat export GIT_PAGER=cat
EOF ENVEOF
# Optional Python venv for helper tooling.
if have python3; then if have python3; then
log "python3 detected" log "python3 detected"
if [[ ! -d .codex/venv ]]; then if [[ ! -d .codex/venv ]]; then
@@ -43,44 +33,15 @@ if have python3; then
# shellcheck disable=SC1091 # shellcheck disable=SC1091
source .codex/venv/bin/activate source .codex/venv/bin/activate
python -m pip install --upgrade pip setuptools wheel >/dev/null python -m pip install --upgrade pip setuptools wheel >/dev/null
# Install only lightweight tooling that is broadly useful.
python -m pip install \
pyyaml \
requests \
jinja2 \
>/dev/null
else else
warn "python3 not found; skipping virtualenv setup" warn "python3 not found; skipping optional venv setup"
fi fi
# Basic repo checks based on your environment style.
if [[ -f services-up.sh ]]; then
log "Found services-up.sh"
chmod +x services-up.sh || true
else
warn "services-up.sh not found at repo root"
fi
# Make codex scripts executable.
find codex -maxdepth 1 -type f -name '*.sh' -exec chmod +x {} + find codex -maxdepth 1 -type f -name '*.sh' -exec chmod +x {} +
# Create a simple local ignore file for Codex-generated junk if needed.
touch .git/info/exclude touch .git/info/exclude
append_if_missing ".codex/" .git/info/exclude append_if_missing ".codex/" .git/info/exclude
append_if_missing ".pytest_cache/" .git/info/exclude append_if_missing ".pytest_cache/" .git/info/exclude
append_if_missing "__pycache__/" .git/info/exclude append_if_missing "__pycache__/" .git/info/exclude
# Helpful summary file for Codex tasks.
cat > .codex/README.md <<'EOF'
This directory contains local, disposable Codex environment state.
Contents:
- env: shell exports for non-interactive work
- venv: optional Python virtual environment
- cache/: tool cache
- tmp/: temporary files
- logs/: script logs
EOF
log "Setup complete" log "Setup complete"
+4 -1
View File
@@ -6,10 +6,13 @@ import (
) )
func TestAccScaffold(t *testing.T) { func TestAccScaffold(t *testing.T) {
if os.Getenv("TF_ACC") == "" || os.Getenv("DYNU_API_KEY") == "" { if os.Getenv("TF_ACC") != "1" || os.Getenv("DYNU_API_KEY") == "" {
t.Skip("set TF_ACC=1 and DYNU_API_KEY to enable acceptance tests") t.Skip("set TF_ACC=1 and DYNU_API_KEY to enable acceptance tests")
} }
// Optional: DYNU_DOMAIN may be used by future acceptance test cases.
_ = os.Getenv("DYNU_DOMAIN")
// Acceptance tests for read-only data sources are intentionally scaffolded in phase 1. // Acceptance tests for read-only data sources are intentionally scaffolded in phase 1.
// Add terraform-plugin-testing based test cases in a follow-up with stable fixtures. // Add terraform-plugin-testing based test cases in a follow-up with stable fixtures.
} }
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
echo "[check] Running gofmt"
files="$(git ls-files '*.go')"
if [[ -n "$files" ]]; then
# shellcheck disable=SC2086
gofmt -w $files
fi
echo "[check] Running go test"
go test ./...
echo "[check] Completed"
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
echo "[setup-dev] Validating local toolchain"
for cmd in bash git go; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "[setup-dev][error] Missing required command: $cmd" >&2
exit 1
fi
echo "[setup-dev] $cmd: $(command -v "$cmd")"
done
if command -v terraform >/dev/null 2>&1; then
echo "[setup-dev] terraform: $(command -v terraform)"
else
echo "[setup-dev][warn] terraform not found (only required for terraform validate and acceptance workflows)"
fi
echo "[setup-dev] Toolchain validation complete"
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "${TF_ACC:-}" != "1" ]]; then
echo "[testacc][error] TF_ACC must be set to 1" >&2
exit 1
fi
if [[ -z "${DYNU_API_KEY:-}" ]]; then
echo "[testacc][error] DYNU_API_KEY must be set" >&2
exit 1
fi
if [[ -z "${DYNU_DOMAIN:-}" ]]; then
echo "[testacc][info] DYNU_DOMAIN is not set; running baseline acceptance scaffolding"
fi
echo "[testacc] Running acceptance tests"
go test ./internal/provider -run TestAcc -count=1