Skip to content

Release 0.3.7: supported plugins + env cleanup#53

Merged
ewega merged 5 commits intomainfrom
refactor/tool-agnostic
Feb 20, 2026
Merged

Release 0.3.7: supported plugins + env cleanup#53
ewega merged 5 commits intomainfrom
refactor/tool-agnostic

Conversation

@ewega
Copy link
Copy Markdown
Contributor

@ewega ewega commented Feb 20, 2026

Summary\n- README: supported plugins section (w/ required PAT scopes) moved above command reference; PAT moved out of prerequisites\n- Docs: tool-agnostic token handling + state files, multi-plugin .devlake.env example\n- CLI: init/configure full delete .devlake.env by default (with --skip-cleanup); token env var parity (GITHUB_PAT)\n\n## Testing\n- go test ./...\n- go vet ./...\n- go build -o gh-devlake.exe .\n- Smoke: help output shows new flags; root help no longer references --org for configure full\n

Major refactor to remove GitHub/Copilot-specific assumptions across the CLI:

Bug fixes:
- Add Name field to ConnectionTestRequest (fixes Copilot test 500 error)
- Write early state checkpoint in deploy_azure after RG creation
- Allow cleanup --resource-group without state file

ConnectionDef as single source of truth:
- Add RateLimitPerHour, EnableGraphql, TokenPrompt, OrgPrompt,
  EnterprisePrompt fields to ConnectionDef
- BuildCreateRequest/BuildTestRequest read fields from def (no plugin branches)
- pluginDisplayName sources from ConnectionDef registry

Token resolution data-driven:
- Rewrite token.Resolve to accept ResolveOpts struct
- Remove pluginEnvFileKeys/pluginEnvVarKeys/pluginDisplayName switch funcs
- Callers pass def.EnvFileKeys, def.EnvVarNames, def.DisplayName directly

Per-plugin token + org resolution:
- runConnectionsInternal resolves token, org, enterprise per-plugin
- Remove upfront GitHub org prompt from init wizard
- Org/enterprise derived from connection results for downstream phases

Tool-agnostic strings (~25 replacements):
- Flag descriptions: Organization slug, Enterprise slug, PAT
- Plugin lists generated dynamically from availablePluginSlugs()
- Error messages use registry data, not hardcoded plugin names

Generic project descriptions:
- Replace HasGitHub/HasCopilot booleans with PluginNames []string
- Project description built from active plugin display names

Scope handling cleanup:
- Remove SkipGitHub/SkipCopilot from ScopeOpts
- runConfigureScopes uses single-plugin flow with generic validation
- ensureScopeConfig accepts plugin parameter (no hardcoded github)
- discoverConnections iterates AvailableConnections() dynamically
Copilot AI review requested due to automatic review settings February 20, 2026 18:54
@ewega ewega merged commit 2b77dcc into main Feb 20, 2026
1 check passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prepares the 0.3.7 release by making token handling and plugin configuration more tool-agnostic, updating CLI behavior around .devlake.env cleanup, and expanding/reshaping the documentation set to match the new UX and supported plugins.

Changes:

  • Refactors token resolution to be fully data-driven via token.ResolveOpts (no hardcoded plugin switching inside the token package).
  • Updates connection/scope/project orchestration to use the plugin registry as the single source of truth (rate limits, GraphQL enablement, token/env keys, prompts), and adds early Azure state checkpointing.
  • Adds/refreshes command docs (init/status/deploy/etc.) and reorganizes README with supported plugins + token/state file guidance.

Reviewed changes

Copilot reviewed 32 out of 34 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
internal/token/resolve.go Switches token resolution to Resolve(opts) with caller-provided lookup keys/names.
internal/token/resolve_test.go Updates tests to use ResolveOpts helpers for multiple plugins.
internal/envfile/envfile.go Formatting-only changes to .devlake.env parser utilities.
internal/devlake/client.go Generalizes connection create comment; adds Name to test request payload.
cmd/connection_types.go Expands plugin registry to include rate limits, GraphQL flag, prompts, env keys.
cmd/connection_types_test.go Updates tests for rate limit defaults and BuildTestRequest name propagation.
cmd/configure_connections.go Uses registry-driven plugin lists and token.ResolveOpts for token lookup.
cmd/configure_connection_update.go Updates help text and plugin flag description to use registry-driven list.
cmd/configure_connection_test_cmd.go Updates --plugin help to use registry-driven list.
cmd/configure_connection_list.go Updates --plugin help to use registry-driven list.
cmd/configure_scopes.go Simplifies scope flow to single-plugin selection with --connection-id and registry validation.
cmd/configure_scopes_test.go Adjusts tests for new --connection-id and updated validation behavior.
cmd/configure_projects.go Removes --org/--plugin flags; derives defaults from state and uses plugin-name list.
cmd/configure_full.go Makes configure full interactive-only; resolves token/org/enterprise per plugin and cleans env file.
cmd/init.go Adds --skip-cleanup; refactors init flow to be more registry-driven and per-plugin.
cmd/deploy_azure.go Writes an early partial .devlake-azure.json checkpoint after RG creation.
cmd/cleanup.go Allows Azure cleanup to proceed with --resource-group even if state file is missing.
cmd/root.go Updates root help “Typical workflow” text (removes old --org reference).
README.md Reorganizes README (prereqs/quickstart/day-2/supported plugins/command reference) and adds new doc links.
docs/token-handling.md New: consolidated token resolution, env file, cleanup behavior, and security notes.
docs/state-files.md New: explains state file purpose, discovery chain, and cleanup.
docs/status.md New: documents status command output and interpretation.
docs/init.md New: documents the 4-phase init wizard and how it differs from configure full.
docs/deploy.md New: documents local + Azure deploy flows and options.
docs/day-2.md New: day-2 operational guidance (status, rotate token, add repos, teardown).
docs/configure-connection.md New: full connection CRUD reference + token resolution order + scope requirements.
docs/configure-scope.md New: scope management reference + DORA patterns + repo resolution.
docs/configure-project.md New: project/blueprint/sync reference including pipeline monitoring.
docs/configure-full.md New: describes interactive “connections → scopes → project” flow and flags.
docs/concepts.md New: conceptual model (connection/scope/project/blueprint).
docs/cleanup.md New: cleanup behavior for local vs Azure, auto-detection, flags.
docs/images/.gitkeep Adds docs images folder placeholder.
AGENTS.md Updates engineering guidance around plugin registry and documentation expectations.
.github/skills/plugin-registry/SKILL.md New: internal skill doc describing plugin registry patterns and flows.
Comments suppressed due to low confidence (1)

cmd/configure_projects.go:248

  • finalizeProject writes StateProject.Organization from opts.Org, but runConfigureProjects no longer populates Org when calling finalizeProject. This means the organization field in the state file will always be empty from configure project. Either populate Org from the state connections (similar to defaultName) or remove/stop writing that field if it’s no longer meaningful.
	return finalizeProject(finalizeProjectOpts{
		Client:      client,
		StatePath:   statePath,
		State:       state,
		ProjectName: projectName,
		Connections: connections,
		Repos:       allRepos,
		PluginNames: pluginNames,
		Cron:        opts.Cron,
		TimeAfter:   opts.TimeAfter,
		SkipSync:    opts.SkipSync,
		Wait:        opts.Wait,
		Timeout:     opts.Timeout,
	})

Comment on lines 51 to 57
if vals, err := envfile.Load(envFilePath); err == nil {
for _, key := range pluginEnvFileKeys(plugin) {
for _, key := range opts.EnvFileKeys {
if v, ok := vals[key]; ok && v != "" {
return &ResolveResult{Token: v, Source: "envfile", EnvFilePath: envFilePath}, nil
}
}
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

envfile.Load errors (e.g., permission denied, malformed file read issues) are currently ignored and token resolution silently falls back to environment/prompt. This can make debugging hard and may prompt unexpectedly even though an --env-file was provided. Consider returning a clear error when envfile.Load returns a non-nil error (it already returns nil error for non-existent files).

Copilot uses AI. Check for mistakes.
Comment on lines 71 to 78
if !term.IsTerminal(int(syscall.Stdin)) {
envVarExample := pluginEnvVarKeys(plugin)[0]
return nil, fmt.Errorf("no %s PAT found and stdin is not a terminal.\n"+
envVarExample := ""
if len(opts.EnvVarNames) > 0 {
envVarExample = opts.EnvVarNames[0]
}
return nil, fmt.Errorf("no %s token found and stdin is not a terminal.\n"+
"Provide a token via --token, .devlake.env file, or $%s", displayName, envVarExample)
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When opts.EnvVarNames is empty, the non-interactive error message will render or $ with a blank variable name (because envVarExample stays empty). Consider omitting the $... portion when there is no env var example, or requiring EnvVarNames to be non-empty and returning a config error.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +17
// helper to build ResolveOpts for GitHub-family plugins
func ghOpts(flagValue, envFile string) ResolveOpts {
return ResolveOpts{
FlagValue: flagValue,
EnvFilePath: envFile,
EnvFileKeys: []string{"GITHUB_PAT", "GITHUB_TOKEN", "GH_TOKEN"},
EnvVarNames: []string{"GITHUB_TOKEN", "GH_TOKEN"},
DisplayName: "GitHub",
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ghOpts doesn’t include GITHUB_PAT in EnvVarNames, but the CLI now documents and configures GITHUB_PAT as a supported environment variable for GitHub/Copilot. Updating this helper (and/or adding a test) would ensure GITHUB_PAT env-var support is covered and stays in sync with ConnectionDef.EnvVarNames.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +112
```
for each selected ConnectionDef:
1. Resolve token using def.EnvVarNames, def.TokenPrompt
2. Prompt for org if def.NeedsOrg (using def.OrgPrompt)
3. Prompt for enterprise if def.NeedsEnterprise (using def.EnterprisePrompt)
4. Test & create connection
```
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section says orchestrators resolve tokens using def.TokenPrompt, but current code passes def.DisplayName into token.ResolveOpts.DisplayName and TokenPrompt is unused. Please update this doc to match the implementation (or update the implementation to actually use TokenPrompt) so future plugin additions don’t follow incorrect guidance.

Copilot uses AI. Check for mistakes.
Comment on lines +399 to +401
if err := os.WriteFile(stateFile, data, 0644); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Could not save early state checkpoint: %v\n", err)
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terminal output: this warning prints an emoji without a leading blank line, which can jam against prior output. For consistency with the repo’s terminal output standards, consider prefixing with a newline (e.g., start the format string with \n).

Copilot generated this review using guidance from repository custom instructions.

Typical workflow:
1. gh devlake deploy local # spin up DevLake with Docker Compose
2. gh devlake configure full # create connections + scopes + project
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The root help text has inconsistent indentation on the numbered list (line 2 uses a tab while the others use spaces), which can render misaligned in terminals. Consider using consistent spaces for all list items in the raw string.

Suggested change
2. gh devlake configure full # create connections + scopes + project
2. gh devlake configure full # create connections + scopes + project

Copilot uses AI. Check for mistakes.
# Deploy DevLake locally (downloads official Docker Compose files)
gh devlake deploy local --dir ./my-devlake
cd my-devlake && docker compose up -d
> **Blog post:** [Beyond Copilot Dashboards: Measuring What AI Actually Changes](<!-- TODO: replace with actual blog URL -->) — why DORA + Copilot correlation matters and what this tool enables.
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This README section includes a placeholder blog URL (<!-- TODO: replace with actual blog URL -->). For a release doc, this should either be replaced with a real link or removed to avoid shipping broken/invalid markdown.

Suggested change
> **Blog post:** [Beyond Copilot Dashboards: Measuring What AI Actually Changes](<!-- TODO: replace with actual blog URL -->) — why DORA + Copilot correlation matters and what this tool enables.
> **Blog post:** Beyond Copilot Dashboards: Measuring What AI Actually Changes — why DORA + Copilot correlation matters and what this tool enables.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +28
RequiredScopes []string // PAT scopes needed for this plugin
ScopeHint string // short hint for error messages
TokenPrompt string // label for masked token prompt (e.g. "GitHub PAT")
OrgPrompt string // label for org prompt; empty = not prompted
EnterprisePrompt string // label for enterprise prompt; empty = not prompted
EnvVarNames []string // environment variable names for token resolution
EnvFileKeys []string // .devlake.env keys for token resolution
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TokenPrompt is defined on ConnectionDef but isn’t used in token resolution or prompting (callers pass def.DisplayName into token.ResolveOpts.DisplayName). This is confusing because it suggests a prompt label is configurable but currently has no effect. Either wire TokenPrompt into the token prompt/error messaging, or remove the field to avoid drift.

Copilot uses AI. Check for mistakes.
@ewega ewega deleted the refactor/tool-agnostic branch March 4, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants