| #!/bin/bash | | | # Fail-open: never block session start. | | | # | | | # Installs dtctl (dynatrace-oss/dtctl) and configures an authenticated | | | # context from environment secrets. Both halves must live here (in the | | | # environment setup script) because the container is ephemeral: the config | | | # file dtctl writes (~/.config/dtctl/) and its keyring do NOT survive a | | | # session, so auth has to be reconstructed on every start from durable | | | # environment variables. | | | # | | | # Required environment variables (set these as environment secrets/vars): | | | # DT_ENVIRONMENT e.g. https://cou8642d.dev.apps.dynatracelabs.com | | | # IMPORTANT: dtctl talks to the PLATFORM / Grail query API, | | | # which is on the .apps host. This is the OPPOSITE of the | | | # CLAUDE.md OTLP-ingest rule (OTLP goes to the non-.apps | |
| # host). Do not confuse the two: query -> .apps, ingest -> std. | |
| # DT_API_TOKEN a dt0c01.* (classic) or dt0s16.* (platform) token | |
| # with query read scopes: storage:logs:read, | |
| # storage:metrics:read, storage:events:read, | |
| # storage:spans:read, storage:buckets:read | |
| set -uo pipefail | |
| # --- Install (idempotent) -------------------------------------------------- | |
| # NOTE: go install does NOT work for dtctl — its go.mod uses replace | |
| # directives, which Go unconditionally refuses for go install. Build from | |
| # source instead (replace directives are allowed in the main module). | |
| # GOTOOLCHAIN=auto pulls the required Go toolchain (dtctl needs >= 1.26.5). | |
| if ! command -v dtctl >/dev/null 2>&1; then | |
| DTCTL_BUILD_DIR=$(mktemp -d) | |
| ( | | | git clone --quiet --depth=1 https://github.com/dynatrace-oss/dtctl "$DTCTL_BUILD_DIR/src" \ | | | && GOTOOLCHAIN=auto go build -C "$DTCTL_BUILD_DIR/src" -o /usr/local/bin/dtctl . | | | ) || echo "dtctl install failed; setup will retry next session start" | | | rm -rf "$DTCTL_BUILD_DIR" 2>/dev/null || true | | | fi | | | # --- Authenticate (idempotent) --------------------------------------------- | | | # Rebuild the context each session from the stored secret. Uses file-backed | | | # token storage since no OS keyring is available in the container. | | | export DTCTL_TOKEN_STORAGE=file | |
| if command -v dtctl >/dev/null 2>&1; then | |
| if [ -n "${DT_ENVIRONMENT:-}" ] && [ -n "${DT_API_TOKEN:-}" ]; then | |
| dtctl config set-credentials dynadev --token "$DT_API_TOKEN" >/dev/null 2>&1 \ | |
| && dtctl config set-context dynadev \ | |
| --environment "$DT_ENVIRONMENT" \ | |
| --token-ref dynadev \ | |
| --safety-level readonly >/dev/null 2>&1 \ | |
| && dtctl config use-context dynadev >/dev/null 2>&1 \ | |
| && echo "dtctl context 'dynadev' configured (readonly)" \ | | | || echo "dtctl auth config failed; check DT_ENVIRONMENT / DT_API_TOKEN" | | | else | | | echo "dtctl installed but not authenticated: set DT_ENVIRONMENT and DT_API_TOKEN env secrets" | | | fi | | | # --- Install the dtctl AI-assistant skill (idempotent) ------------------- | | | # dtctl embeds its own skill (SKILL.md + references). Reinstall each session | | | # so the skill tracks the installed dtctl version. --global lands in | | | # ~/.claude/skills/dtctl regardless of cwd; both that dir and the repo's | | | # .claude/ are ephemeral (.claude/ is gitignored here), so this must run | | | # on every start rather than being committed. | | | dtctl skills install --for claude --global --force >/dev/null 2>&1 \ | | | && echo "dtctl skill installed for claude (~/.claude/skills/dtctl)" \ | | | || echo "dtctl skill install failed" | | | fi | | | command -v dtctl >/dev/null 2>&1 && dtctl version || true |