summaryrefslogtreecommitdiffstats
path: root/internal
diff options
context:
space:
mode:
authors <[email protected]>2025-11-04 11:06:35 -0500
committers <[email protected]>2025-11-04 11:06:35 -0500
commit4486b6659640102dd542fea007f4c33ac02511ff (patch)
tree3e991f3722e3b0062a6078078ff6aa1478c3ab00 /internal
parent3c06eede8ac8cb79272601aad3b2d3359657443a (diff)
downloaddborg-4486b6659640102dd542fea007f4c33ac02511ff.tar.gz
dborg-4486b6659640102dd542fea007f4c33ac02511ff.zip
feat: add version checking and auto-update functionality
Diffstat (limited to 'internal')
-rw-r--r--internal/client/reddit.go83
-rw-r--r--internal/models/reddit.go21
-rw-r--r--internal/utils/version.go115
-rw-r--r--internal/utils/version_test.go52
4 files changed, 271 insertions, 0 deletions
diff --git a/internal/client/reddit.go b/internal/client/reddit.go
new file mode 100644
index 0000000..da95782
--- /dev/null
+++ b/internal/client/reddit.go
@@ -0,0 +1,83 @@
+package client
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "git.db.org.ai/dborg/internal/models"
+)
+
+func (c *Client) GetSubredditPosts(params *models.RedditSubredditParams) (*models.RedditResponse, error) {
+ path := fmt.Sprintf("/reddit/r/%s", params.Subreddit)
+ data, err := c.Get(path, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var response models.RedditResponse
+ if err := json.Unmarshal(data, &response); err != nil {
+ return nil, fmt.Errorf("failed to parse response: %w", err)
+ }
+
+ return &response, nil
+}
+
+func (c *Client) GetSubredditComments(params *models.RedditSubredditParams) (*models.RedditResponse, error) {
+ path := fmt.Sprintf("/reddit/r/%s/comments", params.Subreddit)
+ data, err := c.Get(path, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var response models.RedditResponse
+ if err := json.Unmarshal(data, &response); err != nil {
+ return nil, fmt.Errorf("failed to parse response: %w", err)
+ }
+
+ return &response, nil
+}
+
+func (c *Client) GetUserPosts(params *models.RedditUserParams) (*models.RedditResponse, error) {
+ path := fmt.Sprintf("/reddit/user/%s/posts", params.Username)
+ data, err := c.Get(path, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var response models.RedditResponse
+ if err := json.Unmarshal(data, &response); err != nil {
+ return nil, fmt.Errorf("failed to parse response: %w", err)
+ }
+
+ return &response, nil
+}
+
+func (c *Client) GetUserComments(params *models.RedditUserParams) (*models.RedditResponse, error) {
+ path := fmt.Sprintf("/reddit/user/%s/comments", params.Username)
+ data, err := c.Get(path, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var response models.RedditResponse
+ if err := json.Unmarshal(data, &response); err != nil {
+ return nil, fmt.Errorf("failed to parse response: %w", err)
+ }
+
+ return &response, nil
+}
+
+func (c *Client) GetUserAbout(params *models.RedditUserParams) (*models.RedditResponse, error) {
+ path := fmt.Sprintf("/reddit/user/%s/about", params.Username)
+ data, err := c.Get(path, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var response models.RedditResponse
+ if err := json.Unmarshal(data, &response); err != nil {
+ return nil, fmt.Errorf("failed to parse response: %w", err)
+ }
+
+ return &response, nil
+}
diff --git a/internal/models/reddit.go b/internal/models/reddit.go
new file mode 100644
index 0000000..4d81aa8
--- /dev/null
+++ b/internal/models/reddit.go
@@ -0,0 +1,21 @@
+package models
+
+type RedditSubredditParams struct {
+ Subreddit string `json:"subreddit"`
+}
+
+type RedditUserParams struct {
+ Username string `json:"username"`
+}
+
+type RedditResponse struct {
+ Subreddit string `json:"subreddit,omitempty"`
+ Username string `json:"username,omitempty"`
+ Type string `json:"type,omitempty"`
+ Results interface{} `json:"results"`
+ Credits struct {
+ Remaining int `json:"remaining"`
+ Unlimited bool `json:"unlimited"`
+ } `json:"credits"`
+ Error string `json:"error,omitempty"`
+}
diff --git a/internal/utils/version.go b/internal/utils/version.go
new file mode 100644
index 0000000..00e6cc2
--- /dev/null
+++ b/internal/utils/version.go
@@ -0,0 +1,115 @@
+package utils
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+ "syscall"
+
+ "github.com/spf13/cobra"
+)
+
+var Version = "dev"
+
+func CheckForUpdates(cmd *cobra.Command) error {
+ if !isTerminal() || Version == "dev" {
+ return nil
+ }
+
+ latestVersion, err := getLatestRemoteTag()
+ if err != nil {
+ return nil
+ }
+
+ if latestVersion == "" || latestVersion == Version {
+ return nil
+ }
+
+ if isNewerVersion(latestVersion, Version) {
+ promptAndUpdate(latestVersion)
+ }
+
+ return nil
+}
+
+func getLatestRemoteTag() (string, error) {
+ cmd := exec.Command("git", "ls-remote", "--tags", "--refs", "--sort=-v:refname", "git.db.org.ai/dborg")
+ output, err := cmd.Output()
+ if err != nil {
+ return "", err
+ }
+
+ lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+ if len(lines) == 0 {
+ return "", fmt.Errorf("no tags found")
+ }
+
+ parts := strings.Split(lines[0], "refs/tags/")
+ if len(parts) < 2 {
+ return "", fmt.Errorf("invalid tag format")
+ }
+
+ return strings.TrimSpace(parts[1]), nil
+}
+
+func isNewerVersion(remote, local string) bool {
+ remote = strings.TrimPrefix(remote, "v")
+ local = strings.TrimPrefix(local, "v")
+
+ return remote != local && remote > local
+}
+
+func promptAndUpdate(newVersion string) {
+ fmt.Fprintf(os.Stderr, "\nšŸ”” A new version of dborg is available: %s (current: %s)\n", newVersion, Version)
+ fmt.Fprintf(os.Stderr, "Would you like to update now? [Y/n]: ")
+
+ var response string
+ fmt.Scanln(&response)
+
+ response = strings.ToLower(strings.TrimSpace(response))
+ if response != "" && response != "y" && response != "yes" {
+ fmt.Fprintf(os.Stderr, "Update skipped. Run 'go install git.db.org.ai/dborg@latest' to update manually.\n\n")
+ return
+ }
+
+ fmt.Fprintf(os.Stderr, "Updating to %s...\n", newVersion)
+
+ installCmd := exec.Command("go", "install", "git.db.org.ai/dborg")
+ installCmd.Stdout = os.Stderr
+ installCmd.Stderr = os.Stderr
+
+ if err := installCmd.Run(); err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to update: %v\n", err)
+ fmt.Fprintf(os.Stderr, "Please update manually: go install git.db.org.ai/dborg@latest\n\n")
+ return
+ }
+
+ fmt.Fprintf(os.Stderr, "āœ“ Update successful! Restarting...\n\n")
+
+ restartSelf()
+}
+
+func restartSelf() {
+ executable, err := os.Executable()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err)
+ os.Exit(1)
+ }
+
+ args := os.Args[1:]
+
+ err = syscall.Exec(executable, append([]string{executable}, args...), os.Environ())
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to restart: %v\n", err)
+ os.Exit(1)
+ }
+}
+
+func isTerminal() bool {
+ fileInfo, err := os.Stdout.Stat()
+ if err != nil {
+ return false
+ }
+ return (fileInfo.Mode() & os.ModeCharDevice) != 0
+}
diff --git a/internal/utils/version_test.go b/internal/utils/version_test.go
new file mode 100644
index 0000000..dc1627d
--- /dev/null
+++ b/internal/utils/version_test.go
@@ -0,0 +1,52 @@
+package utils
+
+import "testing"
+
+func TestIsNewerVersion(t *testing.T) {
+ tests := []struct {
+ name string
+ remote string
+ local string
+ expected bool
+ }{
+ {
+ name: "newer version available",
+ remote: "v0.2.0",
+ local: "v0.1.0",
+ expected: true,
+ },
+ {
+ name: "same version",
+ remote: "v0.1.0",
+ local: "v0.1.0",
+ expected: false,
+ },
+ {
+ name: "local is newer",
+ remote: "v0.1.0",
+ local: "v0.2.0",
+ expected: false,
+ },
+ {
+ name: "without v prefix",
+ remote: "0.2.0",
+ local: "0.1.0",
+ expected: true,
+ },
+ {
+ name: "mixed prefix",
+ remote: "v0.2.0",
+ local: "0.1.0",
+ expected: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := isNewerVersion(tt.remote, tt.local)
+ if result != tt.expected {
+ t.Errorf("isNewerVersion(%s, %s) = %v, expected %v", tt.remote, tt.local, result, tt.expected)
+ }
+ })
+ }
+}