feat: add git user.name to actor fallback chain (#994)
- Insert git config user.name in fallback chain before final $USER default - Consolidate duplicate actor resolution logic into single function - Add BEADS_ACTOR as env var alias for MCP compatibility - Add tests for actor resolution priority - Update CONFIG.md with Actor Identity Resolution section The new fallback order is: --actor flag > BD_ACTOR > BEADS_ACTOR > git user.name > $USER > "unknown" Co-authored-by: Ohffs <ohffsnotnow@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
202
cmd/bd/actor_test.go
Normal file
202
cmd/bd/actor_test.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestGetActorWithGit tests the actor resolution fallback chain.
|
||||||
|
// Priority: --actor flag > BD_ACTOR env > BEADS_ACTOR env > git config user.name > $USER > "unknown"
|
||||||
|
func TestGetActorWithGit(t *testing.T) {
|
||||||
|
// Save original environment and actor variable
|
||||||
|
origActor := actor
|
||||||
|
origBdActor, bdActorSet := os.LookupEnv("BD_ACTOR")
|
||||||
|
origBeadsActor, beadsActorSet := os.LookupEnv("BEADS_ACTOR")
|
||||||
|
origUser, userSet := os.LookupEnv("USER")
|
||||||
|
|
||||||
|
// Cleanup after test
|
||||||
|
defer func() {
|
||||||
|
actor = origActor
|
||||||
|
if bdActorSet {
|
||||||
|
os.Setenv("BD_ACTOR", origBdActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BD_ACTOR")
|
||||||
|
}
|
||||||
|
if beadsActorSet {
|
||||||
|
os.Setenv("BEADS_ACTOR", origBeadsActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BEADS_ACTOR")
|
||||||
|
}
|
||||||
|
if userSet {
|
||||||
|
os.Setenv("USER", origUser)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("USER")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Helper to get current git user.name (may be empty if not configured)
|
||||||
|
getGitUserName := func() string {
|
||||||
|
out, err := exec.Command("git", "config", "user.name").Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
gitUserName := getGitUserName()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
actorFlag string
|
||||||
|
bdActor string
|
||||||
|
beadsActor string
|
||||||
|
user string
|
||||||
|
expected string
|
||||||
|
skipIfNoGit bool // Skip if git user.name is not configured
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "actor flag takes priority",
|
||||||
|
actorFlag: "flag-actor",
|
||||||
|
bdActor: "bd-actor",
|
||||||
|
beadsActor: "beads-actor",
|
||||||
|
user: "system-user",
|
||||||
|
expected: "flag-actor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BD_ACTOR takes priority when no flag",
|
||||||
|
actorFlag: "",
|
||||||
|
bdActor: "bd-actor",
|
||||||
|
beadsActor: "beads-actor",
|
||||||
|
user: "system-user",
|
||||||
|
expected: "bd-actor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BEADS_ACTOR takes priority when no BD_ACTOR",
|
||||||
|
actorFlag: "",
|
||||||
|
bdActor: "",
|
||||||
|
beadsActor: "beads-actor",
|
||||||
|
user: "system-user",
|
||||||
|
expected: "beads-actor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "git config user.name used when no env vars",
|
||||||
|
actorFlag: "",
|
||||||
|
bdActor: "",
|
||||||
|
beadsActor: "",
|
||||||
|
user: "system-user",
|
||||||
|
expected: gitUserName, // Will be git user.name if configured
|
||||||
|
skipIfNoGit: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "USER fallback when no git config",
|
||||||
|
actorFlag: "",
|
||||||
|
bdActor: "",
|
||||||
|
beadsActor: "",
|
||||||
|
user: "fallback-user",
|
||||||
|
expected: "fallback-user",
|
||||||
|
// Note: This test may fail if git user.name is configured
|
||||||
|
// We handle this by checking the actual git config in the test
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown as final fallback",
|
||||||
|
actorFlag: "",
|
||||||
|
bdActor: "",
|
||||||
|
beadsActor: "",
|
||||||
|
user: "",
|
||||||
|
expected: "unknown",
|
||||||
|
// Note: This test may get git user.name instead if configured
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Skip tests that require git user.name to not be configured
|
||||||
|
if tt.skipIfNoGit && gitUserName == "" {
|
||||||
|
t.Skip("Skipping: git config user.name is not configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// For tests expecting USER or unknown, skip if git user.name is configured
|
||||||
|
// because git takes priority over USER
|
||||||
|
if (tt.expected == tt.user || tt.expected == "unknown") && gitUserName != "" && tt.bdActor == "" && tt.beadsActor == "" && tt.actorFlag == "" {
|
||||||
|
t.Skipf("Skipping: git config user.name (%s) takes priority over expected %s", gitUserName, tt.expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up test environment
|
||||||
|
actor = tt.actorFlag
|
||||||
|
|
||||||
|
if tt.bdActor != "" {
|
||||||
|
os.Setenv("BD_ACTOR", tt.bdActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BD_ACTOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.beadsActor != "" {
|
||||||
|
os.Setenv("BEADS_ACTOR", tt.beadsActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BEADS_ACTOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.user != "" {
|
||||||
|
os.Setenv("USER", tt.user)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("USER")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the function
|
||||||
|
result := getActorWithGit()
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("getActorWithGit() = %q, want %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGetActorWithGit_PriorityOrder tests that the priority order is respected
|
||||||
|
func TestGetActorWithGit_PriorityOrder(t *testing.T) {
|
||||||
|
// Save original state
|
||||||
|
origActor := actor
|
||||||
|
origBdActor, bdActorSet := os.LookupEnv("BD_ACTOR")
|
||||||
|
origBeadsActor, beadsActorSet := os.LookupEnv("BEADS_ACTOR")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
actor = origActor
|
||||||
|
if bdActorSet {
|
||||||
|
os.Setenv("BD_ACTOR", origBdActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BD_ACTOR")
|
||||||
|
}
|
||||||
|
if beadsActorSet {
|
||||||
|
os.Setenv("BEADS_ACTOR", origBeadsActor)
|
||||||
|
} else {
|
||||||
|
os.Unsetenv("BEADS_ACTOR")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Test: flag > BD_ACTOR > BEADS_ACTOR
|
||||||
|
actor = "from-flag"
|
||||||
|
os.Setenv("BD_ACTOR", "from-bd-actor")
|
||||||
|
os.Setenv("BEADS_ACTOR", "from-beads-actor")
|
||||||
|
|
||||||
|
result := getActorWithGit()
|
||||||
|
if result != "from-flag" {
|
||||||
|
t.Errorf("Expected flag to take priority, got %q", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test: BD_ACTOR > BEADS_ACTOR (no flag)
|
||||||
|
actor = ""
|
||||||
|
result = getActorWithGit()
|
||||||
|
if result != "from-bd-actor" {
|
||||||
|
t.Errorf("Expected BD_ACTOR to take priority over BEADS_ACTOR, got %q", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test: BEADS_ACTOR when BD_ACTOR is empty
|
||||||
|
os.Unsetenv("BD_ACTOR")
|
||||||
|
result = getActorWithGit()
|
||||||
|
if result != "from-beads-actor" {
|
||||||
|
t.Errorf("Expected BEADS_ACTOR to be used, got %q", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -132,20 +131,10 @@ Examples:
|
|||||||
commentText = args[1]
|
commentText = args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get author from author flag, BD_ACTOR var, or system USER var
|
// Get author from author flag, or use git-aware default
|
||||||
author, _ := cmd.Flags().GetString("author")
|
author, _ := cmd.Flags().GetString("author")
|
||||||
if author == "" {
|
if author == "" {
|
||||||
author = os.Getenv("BD_ACTOR")
|
author = getActorWithGit()
|
||||||
if author == "" {
|
|
||||||
author = os.Getenv("USER")
|
|
||||||
}
|
|
||||||
if author == "" {
|
|
||||||
if u, err := user.Current(); err == nil {
|
|
||||||
author = u.Username
|
|
||||||
} else {
|
|
||||||
author = "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var comment *types.Comment
|
var comment *types.Comment
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -802,30 +801,6 @@ func uniqueStrings(slice []string) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// getActorWithGit returns the actor for audit trail with git config fallback.
|
|
||||||
// Priority: global actor var (from --actor flag or BD_ACTOR env) > git config user.name > $USER > "unknown"
|
|
||||||
func getActorWithGit() string {
|
|
||||||
// If actor is already set (from flag or env), use it
|
|
||||||
if actor != "" && actor != "unknown" {
|
|
||||||
return actor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try git config user.name
|
|
||||||
cmd := exec.Command("git", "config", "user.name")
|
|
||||||
if output, err := cmd.Output(); err == nil {
|
|
||||||
if gitUser := strings.TrimSpace(string(output)); gitUser != "" {
|
|
||||||
return gitUser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to USER env
|
|
||||||
if user := os.Getenv("USER"); user != "" {
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
deleteCmd.Flags().BoolP("force", "f", false, "Actually delete (without this flag, shows preview)")
|
deleteCmd.Flags().BoolP("force", "f", false, "Actually delete (without this flag, shows preview)")
|
||||||
deleteCmd.Flags().String("from-file", "", "Read issue IDs from file (one per line)")
|
deleteCmd.Flags().String("from-file", "", "Read issue IDs from file (one per line)")
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"runtime/trace"
|
"runtime/trace"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@@ -106,6 +108,41 @@ func isReadOnlyCommand(cmdName string) bool {
|
|||||||
return readOnlyCommands[cmdName]
|
return readOnlyCommands[cmdName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getActorWithGit returns the actor for audit trails with git config fallback.
|
||||||
|
// Priority: --actor flag > BD_ACTOR env > BEADS_ACTOR env > git config user.name > $USER > "unknown"
|
||||||
|
// This provides a sensible default for developers: their git identity is used unless
|
||||||
|
// explicitly overridden
|
||||||
|
func getActorWithGit() string {
|
||||||
|
// If actor is already set (from --actor flag), use it
|
||||||
|
if actor != "" {
|
||||||
|
return actor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check BD_ACTOR env var (primary env override)
|
||||||
|
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
||||||
|
return bdActor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check BEADS_ACTOR env var (alias for MCP/integration compatibility)
|
||||||
|
if beadsActor := os.Getenv("BEADS_ACTOR"); beadsActor != "" {
|
||||||
|
return beadsActor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try git config user.name - the natural default for a git-native tool
|
||||||
|
if out, err := exec.Command("git", "config", "user.name").Output(); err == nil {
|
||||||
|
if gitUser := strings.TrimSpace(string(out)); gitUser != "" {
|
||||||
|
return gitUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to system username
|
||||||
|
if user := os.Getenv("USER"); user != "" {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Initialize viper configuration
|
// Initialize viper configuration
|
||||||
if err := config.Initialize(); err != nil {
|
if err := config.Initialize(); err != nil {
|
||||||
@@ -120,7 +157,7 @@ func init() {
|
|||||||
|
|
||||||
// Register persistent flags
|
// Register persistent flags
|
||||||
rootCmd.PersistentFlags().StringVar(&dbPath, "db", "", "Database path (default: auto-discover .beads/*.db)")
|
rootCmd.PersistentFlags().StringVar(&dbPath, "db", "", "Database path (default: auto-discover .beads/*.db)")
|
||||||
rootCmd.PersistentFlags().StringVar(&actor, "actor", "", "Actor name for audit trail (default: $BD_ACTOR or $USER)")
|
rootCmd.PersistentFlags().StringVar(&actor, "actor", "", "Actor name for audit trail (default: $BD_ACTOR, git user.name, $USER)")
|
||||||
rootCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "Output in JSON format")
|
rootCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "Output in JSON format")
|
||||||
rootCmd.PersistentFlags().BoolVar(&noDaemon, "no-daemon", false, "Force direct storage mode, bypass daemon if running")
|
rootCmd.PersistentFlags().BoolVar(&noDaemon, "no-daemon", false, "Force direct storage mode, bypass daemon if running")
|
||||||
rootCmd.PersistentFlags().BoolVar(&noAutoFlush, "no-auto-flush", false, "Disable automatic JSONL sync after CRUD operations")
|
rootCmd.PersistentFlags().BoolVar(&noAutoFlush, "no-auto-flush", false, "Disable automatic JSONL sync after CRUD operations")
|
||||||
@@ -377,15 +414,7 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set actor for audit trail
|
// Set actor for audit trail
|
||||||
if actor == "" {
|
actor = getActorWithGit()
|
||||||
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
|
||||||
actor = bdActor
|
|
||||||
} else if user := os.Getenv("USER"); user != "" {
|
|
||||||
actor = user
|
|
||||||
} else {
|
|
||||||
actor = "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip daemon and SQLite initialization - we're in memory mode
|
// Skip daemon and SQLite initialization - we're in memory mode
|
||||||
return
|
return
|
||||||
@@ -419,15 +448,7 @@ var rootCmd = &cobra.Command{
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
// Set actor for audit trail
|
// Set actor for audit trail
|
||||||
if actor == "" {
|
actor = getActorWithGit()
|
||||||
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
|
||||||
actor = bdActor
|
|
||||||
} else if user := os.Getenv("USER"); user != "" {
|
|
||||||
actor = user
|
|
||||||
} else {
|
|
||||||
actor = "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,16 +509,7 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set actor for audit trail
|
// Set actor for audit trail
|
||||||
// Priority: --actor flag > BD_ACTOR env > USER env > "unknown"
|
actor = getActorWithGit()
|
||||||
if actor == "" {
|
|
||||||
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
|
||||||
actor = bdActor
|
|
||||||
} else if user := os.Getenv("USER"); user != "" {
|
|
||||||
actor = user
|
|
||||||
} else {
|
|
||||||
actor = "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track bd version changes
|
// Track bd version changes
|
||||||
// Best-effort tracking - failures are silent
|
// Best-effort tracking - failures are silent
|
||||||
|
|||||||
@@ -64,17 +64,8 @@ func signalOrchestratorActivity() {
|
|||||||
// Build command line from os.Args
|
// Build command line from os.Args
|
||||||
cmdLine := strings.Join(os.Args, " ")
|
cmdLine := strings.Join(os.Args, " ")
|
||||||
|
|
||||||
// Determine actor (use package-level var if set, else fall back to env)
|
// Determine actor (uses git config user.name as default)
|
||||||
actorName := actor
|
actorName := getActorWithGit()
|
||||||
if actorName == "" {
|
|
||||||
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
|
||||||
actorName = bdActor
|
|
||||||
} else if user := os.Getenv("USER"); user != "" {
|
|
||||||
actorName = user
|
|
||||||
} else {
|
|
||||||
actorName = "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build activity signal
|
// Build activity signal
|
||||||
activity := struct {
|
activity := struct {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Tool-level settings you can configure:
|
|||||||
| `directory.labels` | - | - | (none) | Map directories to labels for automatic filtering |
|
| `directory.labels` | - | - | (none) | Map directories to labels for automatic filtering |
|
||||||
| `external_projects` | - | - | (none) | Map project names to paths for cross-project deps |
|
| `external_projects` | - | - | (none) | Map project names to paths for cross-project deps |
|
||||||
| `db` | `--db` | `BD_DB` | (auto-discover) | Database path |
|
| `db` | `--db` | `BD_DB` | (auto-discover) | Database path |
|
||||||
| `actor` | `--actor` | `BD_ACTOR` | `$USER` | Actor name for audit trail |
|
| `actor` | `--actor` | `BD_ACTOR` | `git config user.name` | Actor name for audit trail (see below) |
|
||||||
| `flush-debounce` | - | `BEADS_FLUSH_DEBOUNCE` | `5s` | Debounce time for auto-flush |
|
| `flush-debounce` | - | `BEADS_FLUSH_DEBOUNCE` | `5s` | Debounce time for auto-flush |
|
||||||
| `auto-start-daemon` | - | `BEADS_AUTO_START_DAEMON` | `true` | Auto-start daemon if not running |
|
| `auto-start-daemon` | - | `BEADS_AUTO_START_DAEMON` | `true` | Auto-start daemon if not running |
|
||||||
| `daemon-log-max-size` | - | `BEADS_DAEMON_LOG_MAX_SIZE` | `50` | Max daemon log size in MB before rotation |
|
| `daemon-log-max-size` | - | `BEADS_DAEMON_LOG_MAX_SIZE` | `50` | Max daemon log size in MB before rotation |
|
||||||
@@ -51,6 +51,24 @@ Tool-level settings you can configure:
|
|||||||
| `daemon-log-max-age` | - | `BEADS_DAEMON_LOG_MAX_AGE` | `30` | Max days to keep old log files |
|
| `daemon-log-max-age` | - | `BEADS_DAEMON_LOG_MAX_AGE` | `30` | Max days to keep old log files |
|
||||||
| `daemon-log-compress` | - | `BEADS_DAEMON_LOG_COMPRESS` | `true` | Compress rotated log files |
|
| `daemon-log-compress` | - | `BEADS_DAEMON_LOG_COMPRESS` | `true` | Compress rotated log files |
|
||||||
|
|
||||||
|
### Actor Identity Resolution
|
||||||
|
|
||||||
|
The actor name (used for `created_by` in issues and audit trails) is resolved in this order:
|
||||||
|
|
||||||
|
1. `--actor` flag (explicit override)
|
||||||
|
2. `BD_ACTOR` environment variable
|
||||||
|
3. `BEADS_ACTOR` environment variable (alias for MCP/integration compatibility)
|
||||||
|
4. `git config user.name`
|
||||||
|
5. `$USER` environment variable (system username fallback)
|
||||||
|
6. `"unknown"` (final fallback)
|
||||||
|
|
||||||
|
For most developers, no configuration is needed - beads will use your git identity automatically. This ensures your issue authorship matches your commit authorship.
|
||||||
|
|
||||||
|
To override, set `BD_ACTOR` in your shell profile:
|
||||||
|
```bash
|
||||||
|
export BD_ACTOR="my-github-handle"
|
||||||
|
```
|
||||||
|
|
||||||
### Example Config File
|
### Example Config File
|
||||||
|
|
||||||
`~/.config/bd/config.yaml`:
|
`~/.config/bd/config.yaml`:
|
||||||
|
|||||||
Reference in New Issue
Block a user