package netrc import ( "bufio" "fmt" "os" "path/filepath" "strings" ) // Upsert updates or inserts a machine entry in the netrc file. // Only the password field is written (Nix uses password from netrc as auth). func Upsert(path, machine, password string) error { entries, err := parse(path) if err != nil && !os.IsNotExist(err) { return err } found := false for i, e := range entries { if e.machine == machine { entries[i].password = password found = true break } } if !found { entries = append(entries, entry{machine: machine, password: password}) } return write(path, entries) } // Remove removes the entry for the given machine from the netrc file. func Remove(path, machine string) error { entries, err := parse(path) if err != nil { if os.IsNotExist(err) { return nil } return err } var filtered []entry for _, e := range entries { if e.machine != machine { filtered = append(filtered, e) } } return write(path, filtered) } // GetPassword returns the password for the given machine, or empty string if not found. func GetPassword(path, machine string) (string, error) { entries, err := parse(path) if err != nil { if os.IsNotExist(err) { return "", nil } return "", err } for _, e := range entries { if e.machine == machine { return e.password, nil } } return "", nil } type entry struct { machine string password string } func parse(path string) ([]entry, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() var entries []entry var current *entry scanner := bufio.NewScanner(f) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } fields := strings.Fields(line) if len(fields) < 2 { continue } switch fields[0] { case "machine": if current != nil { entries = append(entries, *current) } current = &entry{machine: fields[1]} case "password": if current != nil { current.password = fields[1] } } } if current != nil { entries = append(entries, *current) } if err := scanner.Err(); err != nil { return nil, err } return entries, nil } func write(path string, entries []entry) error { if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return fmt.Errorf("creating directory for %s: %w", path, err) } var b strings.Builder for i, e := range entries { if i > 0 { b.WriteString("\n") } fmt.Fprintf(&b, "machine %s\npassword %s\n", e.machine, e.password) } if err := os.WriteFile(path, []byte(b.String()), 0600); err != nil { return err } return os.Chmod(path, 0600) }