#!/usr/bin/env bb (require '[clojure.java.shell :refer [sh]]) (require '[clojure.string :as str]) (require '[clojure.java.io :as io]) (defn dirname [path] (.getParent (io/file path))) (defn git [dir & args] (let [proc (apply sh "git" "-C" (or dir ".") args)] (when (= (:exit proc) 0) (str/trim (:out proc))))) (defn git-remote-origin [dir] (not-empty (git dir "remote" "get-url" "origin"))) (defn git-first-remote [dir] (some->> (git dir "remote" "-v") str/split-lines (keep #(re-matches #"^\w+\s+(.*)\s+\(fetch\)" %)) first second)) (defn prepare-link [remote-url rev file-path & [line-start line-end]] (let [parse-path #(some->> (re-matches % remote-url) second) line-hash (cond (nil? line-start) nil (= line-start line-end) (str "#L" line-start) (some? line-end) (str "#L" line-start "-L" line-end) (nil? line-end) (str "#L" line-start))] (some->> (cond (empty? remote-url) nil (.contains remote-url "github.com:") ["https://github.com" (parse-path #".*github.com:(.*)\.git$") "blob" rev (str file-path line-hash)] (.contains remote-url "github.com/") ["https://github.com" (parse-path #".*github.com/(.*)(\.git)?$") "blob" rev (str file-path line-hash)] (.contains remote-url "gitlab.com:") ["https://gitlab.com" (parse-path #".*gitlab.com:(.*)\.git$") "-/blob" rev (str file-path line-hash)] (.contains remote-url "gitlab.com/") ["https://gitlab.com" (parse-path #".*gitlab.com/(.*)(\.git)?$") "-/blob" rev (str file-path line-hash)] (.contains remote-url "git.sr.ht:") ["https://git.sr.ht" (parse-path #".*git.sr.ht:(.*)\.git$") "tree" rev (str file-path line-hash)] (.contains remote-url "git.sr.ht/") ["https://git.sr.ht" (parse-path #".*git.sr.ht/(.*)(\.git)?$") "tree" rev (str file-path line-hash)] :else nil) (remove nil?) (str/join "/")))) (defn git-current-rev [dir] (let [commit (git dir "rev-parse" "--short" "HEAD")] (when (not-empty (git dir "branch" "-r" "--contains" commit)) commit))) #_(todo add branch?) #_(defn git-current-branch [dir]) (defn git-base-branch [dir] #_(todo handle non-clone set head (git remote set-head origin --auto)) (or (some->> (git dir "rev-parse" "--abbrev-ref" "origin/HEAD") :out not-empty (sh "basename") :out not-empty) "main")) (defn git-remote-url [dir file-path revision line-start line-end] (some-> (or (git-remote-origin dir) (git-first-remote dir)) (prepare-link revision file-path line-start line-end))) (defn git-root [& [path]] (let [file-dir (if (empty? path) "." (dirname path))] (some->> (git file-dir "rev-parse" "--absolute-git-dir") dirname))) (defn cmd-link [[file-path line-start line-end rev]] (let [dir (git-root file-path) revision (or rev (git-current-rev dir) (git-base-branch dir)) relative-file-path (str/trim (:out (sh "realpath" "--relative-to" dir file-path))) remote-url (git-remote-url dir relative-file-path revision line-start line-end)] (println remote-url))) (defn cmd-base-branch [[]] (println (git-base-branch (git-root)))) (def commands {"link" cmd-link, "base-branch" cmd-base-branch}) (let [[cmd & args] *command-line-args*] (if-let [command-fn (commands cmd)] (command-fn args) (binding [*out* *err*] (println (str "invalid command: " (or cmd ""))) (println (str "Valid commands: " (keys commands))))))