134 lines
6.3 KiB
Clojure
Executable File
134 lines
6.3 KiB
Clojure
Executable File
#! /usr/bin/bb
|
|
(require '[clojure.tools.cli :refer [parse-opts]])
|
|
(require '[babashka.curl :as curl])
|
|
|
|
(def cli-options
|
|
[["-s" "--skip-build" "Skips the building process and will upload the currently built version."
|
|
:id :skip-build
|
|
:default false]
|
|
["-n" "--no-git-tag" "Prevents the creation of a git tag, even if it was defined inside the configuration file."
|
|
:id :no-git-tag
|
|
:default false]
|
|
["-k" "--key SSH-KEY" "The SSH key to use to connect to the server."
|
|
:id :ssh-key
|
|
:parse-fn str
|
|
:default nil]])
|
|
|
|
(def parsed-params (parse-opts *command-line-args* cli-options))
|
|
(def options (:options parsed-params))
|
|
(def code (first (:arguments parsed-params)))
|
|
|
|
;;executes shell command but throws exception on error
|
|
(defn- safe-sh [& commands]
|
|
(as-> (apply shell/sh commands) $
|
|
(if (= (:exit $) 0) $ (throw (Exception. (:err $))))))
|
|
|
|
;;gets the working directory, but removes the new line in the end
|
|
(def wd (as-> (:out (safe-sh "pwd")) $
|
|
(subs $ 0 (- (count $) 1))))
|
|
(def metadata-file "output-metadata.json")
|
|
(def config-file "enterprise-releases.json")
|
|
|
|
(defn- create-path-string [& segments]
|
|
"creates a os independent path string"
|
|
(str/join fs/file-separator segments))
|
|
|
|
(defn- get-output-folder [module product-flavor build-type]
|
|
"creates the path string of the output folder for the given module & variant"
|
|
(->>
|
|
(filter #(not (nil? %)) (list wd module "build" "outputs" "apk" product-flavor build-type))
|
|
(apply create-path-string)))
|
|
|
|
(defn- parse-generated-metadata-file [module product-flavor build-type]
|
|
(json/parse-string (slurp (create-path-string (get-output-folder module product-flavor build-type) metadata-file)) true))
|
|
|
|
(defn- create-enterprise-release [module variant]
|
|
"builds the app using the given module & variant and then returns the parsed metadata"
|
|
(println "building project..")
|
|
(safe-sh (create-path-string wd "gradlew") (str ":" module ":assemble" (str/capitalize variant))))
|
|
|
|
(defn- non-nil-vector [& args]
|
|
(apply vector (keep identity (flatten args))))
|
|
|
|
(non-nil-vector "-u" (str "a" ":") (if (not false)
|
|
(list "--key" "key")))
|
|
|
|
(defn- fetch-remote-config [server user ssh-key path file-name]
|
|
"fetches and parses the configuration on the remote server"
|
|
(let [remote-path (str server path "/" file-name ".json")]
|
|
(println "fetching remote config..")
|
|
(json/parse-string (->
|
|
(:body (curl/get remote-path {:raw-args (non-nil-vector "-u" (str user ":") (if (not (nil? ssh-key))
|
|
(list "--key" ssh-key)))}))
|
|
;; remove non printable characters causing parsing issues
|
|
(str/replace #"\p{C}" ""))
|
|
true)))
|
|
|
|
(defn- upload-version [config options metadata]
|
|
"uploads the built apk and updates the remote json to reflect the new version"
|
|
(let [info (second config)
|
|
server (:server info)
|
|
user (:user info)
|
|
ssh-key (:ssh-key options)
|
|
path (:path info)
|
|
file-name (:fileName info)
|
|
output-folder (get-output-folder (:module info) (:productFlavor info) (:buildType info))
|
|
output-file (create-path-string output-folder (get-in metadata [:elements 0 :outputFile]))
|
|
remote-config (fetch-remote-config server user ssh-key path file-name)
|
|
new-version (get-in metadata [:elements 0 :versionCode])
|
|
version-name (get-in metadata [:elements 0 :versionName])]
|
|
(if (<= new-version (get-in remote-config [:android :version_code]))
|
|
(println "warning: version on remote was higher or the same as uploaded version"))
|
|
(println "uploading apk..")
|
|
(curl/post (str server path "/" file-name ".apk") {:raw-args (non-nil-vector "-T" output-file "-u" (str user ":") (if (not (nil? ssh-key)) (list "--key" ssh-key)))})
|
|
(println "updating version on remote..")
|
|
(let [updated-remote-file (create-path-string output-folder (str file-name ".json"))]
|
|
(as-> remote-config $
|
|
(update-in $ [:android :version_code] (fn [x] new-version))
|
|
(json/generate-string $ {:pretty true})
|
|
(spit updated-remote-file $))
|
|
(curl/post (str server path "/" file-name ".json") {:raw-args (non-nil-vector "-T" updated-remote-file "-u" (str user ":") (if (not (nil? ssh-key)) (list "--key" ssh-key)))}))
|
|
(println (str "new version " version-name " (" new-version ")" " successfully uploaded to " (name (first config))))))
|
|
|
|
(defn- add-git-tag [config metadata]
|
|
"creates a new git tag based on the defined format and pushes it to the remote repository"
|
|
(let [unstaged-changes (filter #(not (str/blank? %)) (str/split-lines (:out (safe-sh "git" "status" "-s" "-uno"))))
|
|
nr-of-changes (count unstaged-changes)]
|
|
(if (= nr-of-changes 0)
|
|
(let [tag-format (:format config)
|
|
tag-args (map #(get-in metadata [:elements 0 %]) (map #(keyword %) (:arguments config)))
|
|
tag (apply format (conj tag-args tag-format))
|
|
remote "origin"]
|
|
(safe-sh "git" "tag" tag)
|
|
(safe-sh "git" "push" remote tag)
|
|
(println (str "created tag " tag " and pushed it to remote " remote)))
|
|
(println "warning: there were uncommitted changes. git tag was not automatically created"))))
|
|
|
|
(defn- build-and-upload [config options]
|
|
"builds & uploads the application based on the handed over configuration"
|
|
(let [info (second config)
|
|
skip-build (:skip-build options)
|
|
module (:module info)
|
|
product-flavor (:productFlavor info)
|
|
build-type (:buildType info)
|
|
variant (str product-flavor (str/capitalize build-type))
|
|
git-tag (:gitTag info)]
|
|
(if (not skip-build) (create-enterprise-release module variant))
|
|
(let [metadata (parse-generated-metadata-file module product-flavor build-type)]
|
|
(upload-version config options metadata)
|
|
(if (and (not (:no-git-tag options)) (not (nil? git-tag)))
|
|
(add-git-tag git-tag metadata)))))
|
|
|
|
(def config (json/parse-string (slurp (create-path-string wd config-file)) true))
|
|
|
|
;;if there's only one config, pick that one
|
|
;;otherwise get the one specified as argument
|
|
(def selected-config
|
|
(if
|
|
(= (count config) 1) (first config)
|
|
(first (filter #(= (first %) (keyword code)) config))))
|
|
|
|
(if (nil? selected-config)
|
|
(println "error: no matching config found")
|
|
(build-and-upload selected-config options))
|