diff --git a/.cljstyle b/.cljstyle
new file mode 100644
index 0000000..8dbd146
--- /dev/null
+++ b/.cljstyle
@@ -0,0 +1,109 @@
+;; cljstyle configuration
+{:files
+ {:extensions #{"cljc" "cljs" "clj" "cljx" "edn"},
+ :ignore #{"checkouts" "dev" ".hg" "target" ".git" "mulog_publisher.clj"}},
+ :rules
+ {:namespaces
+ {:enabled? false,
+ :indent-size 2,
+ :break-libs? true,
+ :import-break-width 60},
+ :whitespace
+ {:enabled? true,
+ :remove-surrounding? true,
+ :remove-trailing? true,
+ :insert-missing? true},
+ :comments
+ {:enabled? true,
+ :inline-prefix " ", :leading-prefix "; "},
+ :functions {:enabled? true},
+ :eof-newline {:enabled? true},
+ :types
+ {:enabled? true,
+ :types? true,
+ :protocols? true,
+ :reifies? true,
+ :proxies? true},
+ :blank-lines
+ {:enabled? true,
+ :trim-consecutive? true,
+ :max-consecutive 2,
+ :insert-padding? false,
+ :padding-lines 2},
+ :indentation
+ {:enabled? true,
+ :list-indent 1,
+ :indents
+ {
+ #"^def" [[:inner 0]],
+ #"^with-" [[:inner 0]],
+ alt! [[:block 0]],
+ alt!! [[:block 0]],
+ are [[:block 2]],
+ as-> [[:block 1]],
+ binding [[:block 1]],
+ bound-fn [[:inner 0]],
+ case [[:block 1]],
+ catch [[:block 2]],
+ comment [[:block 0]],
+ cond [[:block 0]],
+ cond-> [[:block 1]],
+ cond->> [[:block 1]],
+ condp [[:block 2]],
+ def [[:inner 0]]},
+ defmacro [[:inner 0]],
+ defmethod [[:inner 0]],
+ defmulti [[:inner 0]],
+ defn [[:inner 0]],
+ defn- [[:inner 0]],
+ defonce [[:inner 0]],
+ defprotocol [[:block 1] [:inner 1]],
+ defrecord [[:block 1] [:inner 1]],
+ defstruct [[:block 1]],
+ deftest [[:inner 0]],
+ deftype [[:block 1] [:inner 1]],
+ do [[:block 0]],
+ doseq [[:block 1]],
+ dotimes [[:block 1]],
+ doto [[:block 1]],
+ extend [[:block 1]],
+ extend-protocol [[:block 1] [:inner 1]],
+ extend-type [[:block 1] [:inner 1]],
+ finally [[:block 0]],
+ fn [[:inner 0]],
+ for [[:block 1]],
+ future [[:block 0]],
+ go [[:block 0]],
+ go-loop [[:block 1]],
+ if [[:block 1]],
+ if-let [[:block 1]],
+ if-not [[:block 1]],
+ if-some [[:block 1]],
+ let [[:block 1]],
+ letfn [[:block 1] [:inner 2 0]],
+ locking [[:block 1]],
+ loop [[:block 1]],
+ match [[:block 1]],
+ ns [[:block 1]],
+ proxy [[:block 2] [:inner 1]],
+ reify [[:inner 0] [:inner 1]],
+ struct-map [[:block 1]],
+ testing [[:block 1]],
+ thread [[:block 0]],
+ thrown-with-msg? [[:block 2]],
+ thrown? [[:block 1]],
+ try [[:block 0]],
+ use-fixtures [[:inner 0]],
+ when [[:block 1]],
+ when-first [[:block 1]],
+ when-let [[:block 1]],
+ when-not [[:block 1]],
+ when-some [[:block 1]],
+ while [[:block 1]],
+ with-local-vars [[:block 1]],
+ with-open [[:block 1]],
+ with-out-str [[:block 0]],
+ with-precision [[:block 1]],
+ with-redefs [[:block 1]]}},
+ :vars
+ {:enabled? false}}}
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000..b5451ea
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,2 @@
+((clojure-mode . ((cider-preferred-build-tool . clojure-cli)
+ (cider-clojure-cli-aliases . ":test/env:dev/reloaded"))))
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..3f0080c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+# Git Attributes
+
+# reclassifies `.edn` as Clojure files for Linguist statistics
+# https://github.com/github/linguist/blob/master/docs/overrides.md
+**/*.edn linguist-language=Clojure
diff --git a/.github/config/clj-kondo-ci-config.edn b/.github/config/clj-kondo-ci-config.edn
new file mode 100644
index 0000000..a740133
--- /dev/null
+++ b/.github/config/clj-kondo-ci-config.edn
@@ -0,0 +1,101 @@
+;; ---------------------------------------------------------
+;; Clojure Linter - clj-kondo configuration for Continuous Integration
+;;
+;; Essential linter checks during CI workflows
+;; disabling non-essential checks to optimise workflow feedback
+;; ---------------------------------------------------------
+
+
+{;; Ignore code in comment blocks
+ :skip-comments true
+
+ :linters {:invalid-arity {:level :error
+ :skip-args [#_riemann.test/test-stream]}
+ :not-a-function {:level :error
+ :skip-args [#_user/foo]}
+ :private-call {:level :error}
+ :inline-def {:level :error}
+ :redundant-do {:level :off}
+ :redundant-let {:level :warning}
+ :cond-else {:level :off}
+ :syntax {:level :warning}
+ :file {:level :error}
+ :missing-test-assertion {:level :warning}
+ :conflicting-alias {:level :error}
+ :duplicate-map-key {:level :error}
+ :duplicate-set-key {:level :error}
+ :missing-map-value {:level :error}
+ :redefined-var {:level :off}
+ :unreachable-code {:level :warning}
+ :datalog-syntax {:level :off}
+ :unbound-destructuring-default {:level :warning}
+ :unused-binding {:level :off
+ ;; :exclude-destructured-keys-in-fn-args false
+ ;; :exclude-destructured-as false
+ ;; :exclude-unused-as true
+ }
+
+ :unsorted-required-namespaces {:level :off}
+ :unused-namespace {:level :off
+ ;; don't warn about these namespaces:
+ :exclude [#_clj-kondo.impl.var-info-gen]}
+ ;; :simple-libspec true
+
+ :unresolved-symbol {:level :error
+ :exclude [;; ignore globally:
+ #_js*
+ ;; ignore occurrences of service and event in call to riemann.streams/where:
+ #_(riemann.streams/where [service event])
+ ;; ignore all unresolved symbols in one-of:
+ #_(clj-kondo.impl.utils/one-of)
+ #_(user/defproject) ; ignore project.clj's defproject
+ #_(clojure.test/are [thrown? thrown-with-msg?])
+ #_(cljs.test/are [thrown? thrown-with-msg?])
+ #_(clojure.test/is [thrown? thrown-with-msg?])
+ #_(cljs.test/is [thrown? thrown-with-msg?])]}
+ :unresolved-var {:level :warning}
+ :unresolved-namespace {:level :warning
+ :exclude [#_foo.bar]}
+ ;; for example: foo.bar is always loaded in a user profile
+
+ :misplaced-docstring {:level :warning}
+ :not-empty? {:level :off}
+ :deprecated-var {:level :off
+ #_:exclude
+ #_{foo.foo/deprecated-fn
+ ;; suppress warnings in the following namespaces
+ {:namespaces [foo.bar "bar\\.*"]
+ ;; or in these definitions:
+ :defs [foo.baz/allowed "foo.baz/ign\\.*"]}}}
+ :unused-referred-var {:level :off
+ :exclude {#_#_taoensso.timbre [debug]}}
+ :unused-private-var {:level :off}
+ :duplicate-require {:level :warning}
+ :refer {:level :off}
+ :refer-all {:level :warning
+ :exclude #{}}
+ :use {:level :error}
+ :missing-else-branch {:level :warning}
+ :type-mismatch {:level :error}
+ :missing-docstring {:level :warning}
+ :consistent-alias {:level :off
+ ;; warn when alias for clojure.string is
+ ;; different from str
+ :aliases {#_clojure.string #_str}}
+ :unused-import {:level :off}
+ :single-operand-comparison {:level :off}
+ :single-logical-operand {:level :off}
+ :single-key-in {:level :off}
+ :missing-clause-in-try {:level :off}
+ :missing-body-in-when {:level :off}
+ :hook {:level :error}
+ :format {:level :error}
+ :shadowed-var {:level :off
+ #_#_:suggestions {clojure.core/type tajpu
+ clojure.core/name nomspaco}
+ #_#_:exclude [frequencies]
+ #_#_:include [name]}
+ :deps.edn {:level :warning}}
+
+ ;; Format the output of clj-kondo for GitHub actions
+ :output {:pattern "::{{level}} file={{filename}},line={{row}},col={{col}}::{{message}}"}}
diff --git a/.github/workflows/lint-review.yaml b/.github/workflows/lint-review.yaml
new file mode 100644
index 0000000..7b47f94
--- /dev/null
+++ b/.github/workflows/lint-review.yaml
@@ -0,0 +1,37 @@
+---
+# Clojure Lint with clj-kondo and reviewdog
+#
+# Lint errors raised as comments on pull request conversation
+
+name: Lint Review
+on: [pull_request]
+
+jobs:
+ clj-kondo:
+ name: runner / clj-kondo
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo "🚀 Job automatically triggered by ${{ github.event_name }}"
+ - run: echo "🐧 Job running on ${{ runner.os }} server"
+ - run: echo "🐙 Using ${{ github.ref }} branch from ${{ github.repository }} repository"
+
+ # Git Checkout
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ with:
+ token: "${{ secrets.PAT || secrets.GITHUB_TOKEN }}"
+ - run: echo "🐙 ${{ github.repository }} repository was cloned to the runner."
+
+ - name: clj-kondo
+ uses: nnichols/clojure-lint-action@v2
+ with:
+ pattern: "*.clj"
+ clj_kondo_config: ".github/config/clj-kondo-ci-config.edn"
+ level: "error"
+ exclude: ".cljstyle"
+ github_token: ${{ secrets.github_token }}
+ reporter: github-pr-review
+
+ # Summary and status
+ - run: echo "🎨 Lint Review checks completed"
+ - run: echo "🍏 Job status is ${{ job.status }}."
diff --git a/.gitignore b/.gitignore
index f56c8cc..f5a9622 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,48 @@
-target
-.pybuilder
-__pycache__
+# ------------------------
+# Clojure Project Git Ignore file patterns
+#
+# Ignore all except patterns starting with !
+# Add comments on separate lines, not same line as pattern
+# ------------------------
+
+# ------------------------
+# Ignore everthing in root directory
+/*
+
+# ------------------------
+# Common project files
+!CHANGELOG.md
+!README.md
+!LICENSE
+
+# ------------------------
+# Include Clojure project & config
+!build.clj
+!deps.edn
+!pom.xml
+!dev/
+!docs/
+!resources/
+!src/
+!test/
+
+# ------------------------
+# Include Clojure tools
+!.cljstyle
+!.dir-locals.el
+!compose.yaml
+!Dockerfile
+!.dockerignore
+!Makefile
+!tests.edn
+
+# ------------------------
+# Include Git & CI workflow
+!.gitattributes
+!.gitignore
+!.github/
+
+# ------------------------
+# Include ClojureScript Figwheel
+!figwheel-main.edn
+!*.cljs.edn
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 93e0445..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-stages:
- - image
-
-.img: &img
- image: "domaindrivenarchitecture/ddadevops-dind:4.10.5"
- services:
- - docker:dind
- before_script:
- - export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW
- - export IMAGE_DOCKERHUB_USER=$DOCKERHUB_USER
- - export IMAGE_DOCKERHUB_PASSWORD=$DOCKERHUB_PASSWORD
- - export IMAGE_TAG=$CI_COMMIT_TAG
-
-.tag_only: &tag_only
- rules:
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- when: never
- - if: '$CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/'
-
-dda-backup-image-publish:
- <<: *img
- <<: *tag_only
- stage: image
- script:
- - cd infrastructure/docker && pyb image publish
-
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..b9d862f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,22 @@
+# practicalli/playground Changelog
+
+All notable changes to this project will be documented in this file.
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+
+* **Added** for new features
+* **Changed** for changes in existing functionality
+* **Deprecated** for soon-to-be removed features
+* **Resolved** resolved issue
+* **Security** vulnerability related change
+
+## [Unreleased]
+
+### Changed
+
+## 0.1.0 - 2024-08-16
+
+### Added
+
+* [#1](https://github.com/practicalli/clojure/issues/1) Created practicalli/playground project with deps-new using practicalli.template/service
+
+[Unreleased]: https://github.com/practicalli/playground/compare/0.1.1...HEAD
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7b3a87e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,187 @@
+# ------------------------------------------
+# Makefile
+#
+# Consistent set of targets to support local development of Clojure
+# and build the Clojure service during CI deployment
+#
+# Requirements
+# - cljstyle
+# - Clojure CLI aliases
+# - `:env/dev` to include `dev` directory on class path
+# - `:env/test` to include `test` directory and libraries to support testing
+# - `:test/run` to run kaocha kaocha test runner and supporting paths and dependencies
+# - `:repl/rebel` to start a Rebel terminal UI
+# - `:package/uberjar` to create an uberjar for the service
+# - docker
+# - mega-linter-runner
+# ------------------------------------------
+
+# .PHONY: ensures target used rather than matching file name
+# https://makefiletutorial.com/#phony
+.PHONY: all lint deps dist install deploy pre-commit-check repl test test-ci test-watch clean
+
+# ------- Makefile Variables --------- #
+# run help if no target specified
+.DEFAULT_GOAL := help
+
+# Column the target description is printed from
+HELP-DESCRIPTION-SPACING := 24
+
+# Tool variables
+MEGALINTER_RUNNER = npx mega-linter-runner --flavor java --env "'MEGALINTER_CONFIG=.github/config/megalinter.yaml'" --remove-container
+CLOJURE_TEST_RUNNER = clojure -M:test/env:test/run
+CLOJURE_EXEC_TEST_RUNNER = clojure -X:test/env:test/run
+
+DOCKER-BUILD-LOGFILE := docker-build-log-$(shell date +%y-%m-%d-%T).org
+
+# Makefile file and directory name wildcard
+# EDN-FILES := $(wildcard *.edn)
+# ------------------------------------ #
+
+# ------- Help ----------------------- #
+# Source: https://nedbatchelder.com/blog/201804/makefile_help_target.html
+
+help: ## Describe available tasks in Makefile
+ @grep '^[a-zA-Z]' $(MAKEFILE_LIST) | \
+ sort | \
+ awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-$(HELP-DESCRIPTION-SPACING)s\033[0m %s\n", $$1, $$2}'
+# ------------------------------------ #
+
+# ------- Clojure Development -------- #
+repl: ## Run Clojure REPL with rich terminal UI (Rebel Readline)
+ $(info --------- Run Rebel REPL ---------)
+ clojure -M:test/env:repl/reloaded
+
+deps: deps.edn ## Prepare dependencies for test and dist targets
+ $(info --------- Download test and service libraries ---------)
+ clojure -P -X:build
+
+dist: build-uberjar ## Build and package Clojure service
+ $(info --------- Build and Package Clojure service ---------)
+
+install: build-jar
+ $(info --------- install library jar ---------)
+ clojure -T:build install
+
+deploy: build-jar
+ $(info --------- install library jar ---------)
+ clojure -T:build deploy
+
+publish: build-jar
+ $(info --------- Build and Package Clojure lib ---------)
+
+# Remove files and directories after build tasks
+# `-` before the command ignores any errors returned
+clean: ## Clean build temporary files
+ $(info --------- Clean Clojure classpath cache ---------)
+ - rm -rf ./.cpcache ./.clj-kondo ./.lsp
+# ------------------------------------ #
+
+# ------- Testing -------------------- #
+test-config: ## Print Kaocha test runner configuration
+ $(info --------- Runner Configuration ---------)
+ $(CLOJURE_TEST_RUNNER) --print-config
+
+test-profile: ## Profile unit test speed, showing 3 slowest tests
+ $(info --------- Runner Profile Tests ---------)
+ $(CLOJURE_TEST_RUNNER) --plugin kaocha.plugin/profiling
+
+test: ## Run unit tests - stoping on first error
+ $(info --------- Runner for unit tests ---------)
+ $(CLOJURE_EXEC_TEST_RUNNER)
+
+test-all: ## Run all unit tests regardless of failing tests
+ $(info --------- Runner for all unit tests ---------)
+ $(CLOJURE_EXEC_TEST_RUNNER) :fail-fast? false
+
+test-watch: ## Run tests when changes saved, stopping test run on first error
+ $(info --------- Watcher for unit tests ---------)
+ $(CLOJURE_EXEC_TEST_RUNNER) :watch? true
+
+test-watch-all: ## Run all tests when changes saved, regardless of failing tests
+ $(info --------- Watcher for unit tests ---------)
+ $(CLOJURE_EXEC_TEST_RUNNER) :fail-fast? false :watch? true
+# ------------------------------------ #
+
+# -------- Build tasks --------------- #
+build-config: ## Pretty print build configuration
+ $(info --------- View current build config ---------)
+ clojure -T:build/task config
+
+build-jar: ## Build a jar archive of Clojure project
+ $(info --------- Build library jar ---------)
+ clojure -T:build/task jar
+
+build-uberjar: ## Build a uberjar archive of Clojure project & Clojure runtime
+ $(info --------- Build service Uberjar ---------)
+ clojure -T:build/task uberjar
+
+build-clean: ## Clean build assets or given directory
+ $(info --------- Clean Build ---------)
+ clojure -T:build/task clean
+# ------------------------------------ #
+
+# ------- Code Quality --------------- #
+pre-commit-check: format-check lint test ## Run format, lint and test targets
+
+format-check: ## Run cljstyle to check the formatting of Clojure code
+ $(info --------- cljstyle Runner ---------)
+ cljstyle check
+
+format-fix: ## Run cljstyle and fix the formatting of Clojure code
+ $(info --------- cljstyle Runner ---------)
+ cljstyle fix
+
+lint: ## Run MegaLinter with custom configuration (node.js required)
+ $(info --------- MegaLinter Runner ---------)
+ $(MEGALINTER_RUNNER)
+
+lint-fix: ## Run MegaLinter with applied fixes and custom configuration (node.js required)
+ $(info --------- MegaLinter Runner ---------)
+ $(MEGALINTER_RUNNER) --fix
+
+lint-clean: ## Clean MegaLinter report information
+ $(info --------- MegaLinter Clean Reports ---------)
+ - rm -rf ./megalinter-reports
+# ------------------------------------ #
+
+# ------- Docker Containers ---------- #
+docker-build: ## Build Clojure project and run with docker compose
+ $(info --------- Docker Compose Build ---------)
+ docker compose up --build --detach
+
+docker-build-log: ## Build Clojure project and run with docker compose - log to file
+ $(info --------- Docker Compose Build ---------)
+ docker compose up --build --detach &> $(DOCKER-BUILD-LOGFILE) | tee $(DOCKER-BUILD-LOGFILE)
+
+docker-build-clean: ## Build Clojure project and run with docker compose, removing orphans
+ $(info --------- Docker Compose Build - remove orphans ---------)
+ docker compose up --build --remove-orphans --detach
+
+docker-down: ## Shut down containers in docker compose
+ $(info --------- Docker Compose Down ---------)
+ docker compose down
+
+swagger-editor: ## Start Swagger Editor in Docker
+ $(info --------- Run Swagger Editor at locahost:8282 ---------)
+ docker compose -f swagger-editor.yml up -d swagger-editor
+
+swagger-editor-down: ## Stop Swagger Editor in Docker
+ $(info --------- Run Swagger Editor at locahost:8282 ---------)
+ docker compose -f swagger-editor.yml down
+# ------------------------------------ #
+
+# ------ Continuous Integration ------ #
+# .DELETE_ON_ERROR: halts if command returns non-zero exit status
+# https://makefiletutorial.com/#delete_on_error
+
+# TODO: focus runner on ^:integration` tests
+test-ci: deps ## Test runner for integration tests
+ $(info --------- Runner for integration tests ---------)
+ clojure -P -X:test/env:test/run
+
+# Run tests, build & package the Clojure code and clean up afterward
+# `make all` used in Docker builder stage
+.DELETE_ON_ERROR:
+all: test-ci dist clean ## Call test-ci dist and clean targets, used for CI
+# ------------------------------------ #
diff --git a/README.md b/README.md
index 0ce61bc..7d65d7b 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# dda-backup
-[![pipeline status](https://gitlab.com/domaindrivenarchitecture/dda-backup/badges/master/pipeline.svg)](https://gitlab.com/domaindrivenarchitecture/dda-backup/-/commits/master)
+[![Clojars Project](https://img.shields.io/clojars/v/org.domaindrivenarchitecture/dda-backup.svg)](https://clojars.org/org.domaindrivenarchitecture/dda-backup) [![pipeline status](https://gitlab.com/domaindrivenarchitecture/dda-backup/badges/master/pipeline.svg)](https://gitlab.com/domaindrivenarchitecture/dda-backup/-/commits/main)
-[ chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [ team@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@team) | [Website & Blog](https://domaindrivenarchitecture.org)
+[ chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [ meissa@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@meissa) | [Blog](https://domaindrivenarchitecture.org) | [Website](https://meissa.de)
Images are available at https://hub.docker.com/r/domaindrivenarchitecture/dda-backup
@@ -13,11 +13,12 @@ Development happens at: https://repo.prod.meissa.de/meissa/dda-backup
Mirrors are:
-* https://gitlab.com/domaindrivenarchitecture/dda-backup (issues and PR, CI)
-* https://github.com/DomainDrivenArchitecture/dda-backup
+* https://codeberg.org/meissa/dda-backup (Issues and PR)
+* https://gitlab.com/domaindrivenarchitecture/dda-backup (CI)
For more details about our repository model see: https://repo.prod.meissa.de/meissa/federate-your-repos
## License
-Copyright © 2021 meissa GmbH
+Copyright © 2024 meissa GmbH
Published under [apache2.0 license](LICENSE.md)
+Pls. find licenses of our subcomponents [here](doc/SUBCOMPONENT_LICENSE)
diff --git a/build.clj b/build.clj
new file mode 100644
index 0000000..bbcb6a5
--- /dev/null
+++ b/build.clj
@@ -0,0 +1,135 @@
+;; ---------------------------------------------------------
+;; Build Script
+;;
+;; Build project and package for deployment
+;; - `uberjar` - packaged application for deployment
+;; - `clean` remove all build assets and jar files
+;;
+;; All functions are passed command line arguments
+;; - `nil` is passed if there are no arguments
+;;
+;;
+;; tools.build API commands
+;; - `create-basis` create a project basis
+;; - `copy-dir` copy Clojure source and resources into a working dir
+;; - `compile-clj` compile Clojure source code to classes
+;; - `delete` - remove path from file space
+;; - `write-pom` - write pom.xml and pom.properties files
+;; - `jar` - to jar up the working dir into a jar file
+;;
+;; ---------------------------------------------------------
+
+(ns build
+ (:require
+ [clojure.tools.build.api :as build-api]
+ [clojure.pprint :as pprint]))
+
+;; ---------------------------------------------------------
+;; Project configuration
+
+(def project-config
+ "Project configuration to support all tasks"
+ {:class-directory "target/classes"
+ :main-namespace 'practicalli/playground
+ :project-basis (build-api/create-basis)
+ :uberjar-file "target/practicalli-playground-standalone.jar"})
+
+(defn config
+ "Display build configuration"
+ [config]
+ (pprint/pprint (or config project-config)))
+
+;; End of Build configuration
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Build tasks
+
+(def project-config
+ "Project configuration to support all tasks"
+ (-> (edn/read-string (slurp "deps.edn"))
+ :project
+ (merge
+ {:class-directory "target/classes"
+ :project-basis (build-api/create-basis)
+ :main-namespace 'org.domaindrivenarchitecture/dda-backup})))
+
+
+(defn config
+ "Display build configuration"
+ [config]
+ (pprint/pprint (or config project-config)))
+
+;; End of Build configuration
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Build tasks
+
+(defn clean
+ "Remove a directory
+ - `:path '\"directory-name\"'` for a specific directory
+ - `nil` (or no command line arguments) to delete `target` directory
+ `target` is the default directory for build artefacts
+ Checks that `.` and `/` directories are not deleted"
+ [directory]
+ (when
+ (not (contains? #{"." "/"} directory))
+ (build-api/delete {:path (or (:path directory) "target")})))
+
+(defn jar [_]
+ (let [{:keys [name version project-basis class-directory license]} project-config
+ jar-file (format "target/%s-%s.jar" name version)]
+ (clean "target")
+ (build-api/write-pom {:class-dir class-directory
+ :lib name
+ :version version
+ :basis project-basis
+ :src-dirs ["src"]})
+ (build-api/copy-dir {:src-dirs ["src" "resources"]
+ :target-dir class-directory})
+ (build-api/jar {:class-dir class-directory
+ :jar-file jar-file})))
+
+(defn install [_]
+ (let [{:keys [name version project-basis class-directory]} project-config
+ jar-file (format "target/%s-%s.jar" name version)]
+ (build-api/install {:basis project-basis
+ :lib name
+ :version version
+ :jar-file jar-file
+ :class-dir class-directory})))
+
+(defn deploy [opts]
+ (let [{:keys [name version class-directory]} project-config
+ jar-file (format "target/%s-%s.jar" name version)]
+ ((requiring-resolve 'deps-deploy.deps-deploy/deploy)
+ (merge {:installer :remote
+ :artifact jar-file
+ :pom-file (build-api/pom-path {:lib name :class-dir class-directory})}
+ opts))
+ opts))
+
+(defn uberjar
+ "Create an archive containing Clojure and the build of the project
+ Merge command line configuration to the default project config"
+ [options]
+ (let [config (merge project-config options)
+ {:keys [class-directory name main-namespace project-basis class-directory]} project-config
+ uberjar-file (str "target/" name "-standalone.jar")]
+ (clean "target")
+ (build-api/copy-dir {:src-dirs ["src" "resources"]
+ :target-dir class-directory})
+
+ (build-api/compile-clj {:basis project-basis
+ :class-dir class-directory
+ :src-dirs ["src"]})
+
+ (build-api/uber {:basis project-basis
+ :class-dir class-directory
+ :main main-namespace
+ :uber-file uberjar-file})))
+
+;; End of Build tasks
+;; ---------------------------------------------------------
+
diff --git a/build.py b/build.py
deleted file mode 100644
index 352abc9..0000000
--- a/build.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from os import environ
-from subprocess import run
-from pybuilder.core import init, task
-from ddadevops import *
-
-default_task = "dev"
-
-name = "dda-backup"
-MODULE = "not-used"
-PROJECT_ROOT_PATH = "."
-version = "1.0.15-dev"
-
-@init
-def initialize(project):
- input = {
- "name": name,
- "module": MODULE,
- "stage": "notused",
- "project_root_path": PROJECT_ROOT_PATH,
- "build_types": [],
- "mixin_types": ["RELEASE"],
- "release_primary_build_file": "build.py",
- "release_secondary_build_files": [
- "infrastructure/docker/build.py"
- ],
- "release_artifact_server_url": "https://repo.prod.meissa.de",
- "release_organisation": "meissa",
- "release_repository_name": name,
- "release_artifacts": [],
- "release_main_branch": "master",
- }
-
- build = ReleaseMixin(project, input)
- build.initialize_build_dir()
-
-@task
-def patch(project):
- build = get_devops_build(project)
- build.update_release_type("PATCH")
- release(project)
-
-
-@task
-def minor(project):
- build = get_devops_build(project)
- build.update_release_type("MINOR")
- release(project)
-
-
-@task
-def major(project):
- build = get_devops_build(project)
- build.update_release_type("MAJOR")
- release(project)
-
-
-@task
-def prepare(project):
- build = get_devops_build(project)
- build.prepare_release()
-
-
-@task
-def tag(project):
- build = get_devops_build(project)
- build.tag_bump_and_push_release()
-
-@task
-def publish_artifacts(project):
- build = get_devops_build(project)
- build.publish_artifacts()
-
-def release(project):
- prepare(project)
- tag(project)
diff --git a/deps.edn b/deps.edn
new file mode 100644
index 0000000..5568cee
--- /dev/null
+++ b/deps.edn
@@ -0,0 +1,44 @@
+{:project {:name org.domaindrivenarchitecture/dda-backup
+ :version "0.1.1-SNAPSHOT"}
+
+ ;; ---------------------------------------------------------
+ :paths
+ ["src" "resources"]
+ ;; ---------------------------------------------------------
+
+ ;; ---------------------------------------------------------
+ :deps
+ {;; Application
+ org.clojure/clojure {:mvn/version "1.11.4"}
+ org.clojure/spec.alpha {:mvn/version "0.5.238"}
+ orchestra/orchestra {:mvn/version "2021.01.01-1"}}
+ ;; ---------------------------------------------------------
+
+ ;; ---------------------------------------------------------
+ :aliases
+ {
+ ;; ------------
+ ;; Add libraries and paths to support additional test tools
+ :test/env
+ {}
+
+ ;; Test runner - local and CI
+ ;; call with :watch? true to start file watcher and re-run tests on saved changes
+ :test/run
+ {:extra-paths ["test"]
+ :extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
+ :main-opts ["-m" "kaocha.runner"]
+ :exec-fn kaocha.runner/exec-fn
+ :exec-args {:randomize? false
+ :fail-fast? true}}
+ ;; ------------
+
+ ;; ------------
+ ;; tools.build `build.clj` built script
+ :build/task
+ {:replace-paths ["."]
+ :replace-deps {io.github.clojure/tools.build {:mvn/version "0.10.3"}}
+ :extra-deps {slipset/deps-deploy {:mvn/version "0.2.2"}}
+ :ns-default build}}}
+ ;; ------------
+ ;; ---------------------------------------------------------
diff --git a/dev/mulog_events.clj b/dev/mulog_events.clj
new file mode 100644
index 0000000..36468ff
--- /dev/null
+++ b/dev/mulog_events.clj
@@ -0,0 +1,55 @@
+;; ---------------------------------------------------------
+;; Mulog Global Context and Custom Publisher
+;;
+;; - set event log global context
+;; - tap publisher for use with Portal and other tap sources
+;; - publish all mulog events to Portal tap source
+;; ---------------------------------------------------------
+
+(ns mulog-events
+ (:require
+ [com.brunobonacci.mulog :as mulog]
+ [com.brunobonacci.mulog.buffer :as mulog-buffer]))
+
+;; ---------------------------------------------------------
+;; Set event global context
+;; - information added to every event for REPL workflow
+(mulog/set-global-context! {:app-name "playground Service",
+ :version "0.1.0", :env "dev"})
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Mulog event publishing
+
+(deftype TapPublisher
+ [buffer transform]
+ com.brunobonacci.mulog.publisher.PPublisher
+ (agent-buffer [_] buffer)
+ (publish-delay [_] 200)
+ (publish [_ buffer]
+ (doseq [item (transform (map second (mulog-buffer/items buffer)))]
+ (tap> item))
+ (mulog-buffer/clear buffer)))
+
+#_{:clj-kondo/ignore [:unused-private-var]}
+(defn ^:private tap-events
+ [{:keys [transform] :as _config}]
+ (TapPublisher. (mulog-buffer/agent-buffer 10000) (or transform identity)))
+
+(def tap-publisher
+ "Start mulog custom tap publisher to send all events to Portal
+ and other tap sources
+ `mulog-tap-publisher` to stop publisher"
+ (mulog/start-publisher!
+ {:type :custom, :fqn-function "mulog-events/tap-events"}))
+
+#_{:clj-kondo/ignore [:unused-public-var]}
+(defn stop
+ "Stop mulog tap publisher to ensure multiple publishers are not started
+ Recommended before using `(restart)` or evaluating the `user` namespace"
+ []
+ tap-publisher)
+
+;; Example mulog event message
+;; (mulog/log ::dev-user-ns :message "Example event message" :ns (ns-publics *ns*))
+;; ---------------------------------------------------------
diff --git a/dev/portal.clj b/dev/portal.clj
new file mode 100644
index 0000000..63492cb
--- /dev/null
+++ b/dev/portal.clj
@@ -0,0 +1,24 @@
+(ns portal
+ (:require
+ ;; Data inspector
+ [portal.api :as inspect]))
+
+
+;; ---------------------------------------------------------
+;; Start Portal and capture all evaluation results
+
+;; Open Portal window in browser with dark theme
+;; https://cljdoc.org/d/djblue/portal/0.37.1/doc/ui-concepts/themes
+;; Portal options:
+;; - light theme {:portal.colors/theme :portal.colors/solarized-light}
+;; - dark theme {:portal.colors/theme :portal.colors/gruvbox}
+
+(def instance
+ "Open portal window if no portal sessions have been created.
+ A portal session is created when opening a portal window"
+ (or (seq (inspect/sessions))
+ (inspect/open {:portal.colors/theme :portal.colors/gruvbox})))
+
+;; Add portal as tapsource (add to clojure.core/tapset)
+(add-tap #'portal.api/submit)
+;; ---------------------------------------------------------
diff --git a/dev/user.clj b/dev/user.clj
new file mode 100644
index 0000000..4890146
--- /dev/null
+++ b/dev/user.clj
@@ -0,0 +1,113 @@
+;; ---------------------------------------------------------
+;; REPL workflow development tools
+;;
+;; Include development tool libraries vai aliases from practicalli/clojure-cli-config
+;; Start Rich Terminal UI REPL prompt:
+;; `clojure -M:repl/reloaded`
+;;
+;; Or call clojure jack-in from an editor to start a repl
+;; including the `:dev/reloaded` alias
+;; - alias included in the Emacs `.dir-locals.el` file
+;; ---------------------------------------------------------
+
+
+(ns user
+ "Tools for REPL Driven Development"
+ (:require
+ ;; REPL Workflow
+ [mulog-events] ; Event Logging
+ [com.brunobonacci.mulog :as mulog] ; Global context & Tap publisher
+ [portal]
+ [portal.api :as inspect] ; Data inspector
+ [clojure.tools.namespace.repl :as namespace]))
+
+;; ---------------------------------------------------------
+;; Help
+
+(println "---------------------------------------------------------")
+(println "Loading custom user namespace tools...")
+(println "---------------------------------------------------------")
+
+(defn help
+ []
+ (println "---------------------------------------------------------")
+ (println "Namesapece Management:")
+ (println "(namespace/refresh) ; refresh all changed namespaces")
+ (println "(namespace/refresh-all) ; refresh all namespaces")
+ (println)
+ (println "Hotload libraries: ; Clojure 1.12.x")
+ (println "(add-lib 'library-name)")
+ (println "(add-libs '{domain/library-name {:mvn/version \"v1.2.3\"}})")
+ (println "(sync-deps) ; load dependencies from deps.edn")
+ (println "- deps-* lsp snippets for adding library")
+ (println)
+ (println "Portal Inspector:")
+ (println "- portal started by default, listening to all evaluations")
+ (println "(inspect/clear) ; clear all values in portal")
+ (println "(remove-tap #'inspect/submit) ; stop sending to portal")
+ (println "(inspect/close) ; close portal")
+ (println)
+ (println "Mulog Publisher:")
+ (println "- mulog publisher started by default")
+ (println "(mulog-events/stop) ; stop publishing log events")
+ (println)
+ (println "(help) ; print help text")
+ (println "---------------------------------------------------------"))
+
+(help)
+
+;; End of Help
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Avoid reloading `dev` code
+;; - code in `dev` directory should be evaluated if changed to reload into repl
+(println
+ "Set REPL refresh directories to "
+ (namespace/set-refresh-dirs "src" "resources"))
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Mulog event logging
+;; `mulog-publisher` namespace used to launch tap> events to tap-source (portal)
+;; `mulog-events` namespace sets mulog global context for all events
+
+;; Example mulog event message
+(mulog/log ::dev-user-ns
+ :message "Example event from user namespace"
+ :ns (ns-publics *ns*))
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Hotload libraries into running REPL
+;; `deps-*` LSP snippets to add dependency forms
+(comment
+ ;; Require for Clojure 1.11.x and earlier
+ (require '[clojure.tools.deps.alpha.repl :refer [add-libs]])
+ (add-libs '{domain/library-name {:mvn/version "1.0.0"}})
+
+ ;; Clojure 1.12.x only
+ #_(add-lib 'library-name) ; find and add library
+ #_(sync-deps) ; load dependencies in deps.edn (if not yet loaded)
+ #_()) ; End of rich comment
+;; ---------------------------------------------------------
+
+;; ---------------------------------------------------------
+;; Portal Data Inspector
+(comment
+ ;; Open a portal inspector in browser window - light theme
+ ;; (inspect/open {:portal.colors/theme :portal.colors/solarized-light})
+
+ (inspect/clear) ; Clear all values in portal window (allows garbage collection)
+
+ (remove-tap #'inspect/submit) ; Remove portal from `tap>` sources
+
+ (inspect/close) ; Close the portal window
+
+ (inspect/docs) ; View docs locally via Portal
+
+ (mulog-events/stop) ; stop publishing log events
+
+ #_()) ; End of rich comment
+
+;; ---------------------------------------------------------
diff --git a/infrastructure/docker/build.py b/infrastructure/docker/build.py
deleted file mode 100644
index ef21704..0000000
--- a/infrastructure/docker/build.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from os import environ
-from datetime import datetime
-from pybuilder.core import task, init
-from ddadevops import *
-import logging
-
-name = 'dda-backup'
-MODULE = 'NOT_SET'
-PROJECT_ROOT_PATH = '../..'
-version = "1.0.15-dev"
-
-
-@init
-def initialize(project):
- image_tag = version
- if "dev" in image_tag:
- image_tag += datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
-
- input = {
- "name": name,
- "module": MODULE,
- "stage": "notused",
- "project_root_path": PROJECT_ROOT_PATH,
- "build_types": ["IMAGE"],
- "mixin_types": [],
- "image_naming": "NAME_ONLY",
- "image_tag": f"{image_tag}",
- }
-
- project.build_depends_on("ddadevops>=4.7.0")
-
- build = DevopsImageBuild(project, input)
- build.initialize_build_dir()
-
-
-@task
-def image(project):
- build = get_devops_build(project)
- build.image()
-
-@task
-def drun(project):
- build = get_devops_build(project)
- build.drun()
-
-
-@task
-def publish(project):
- build = get_devops_build(project)
- build.dockerhub_login()
- build.dockerhub_publish()
diff --git a/infrastructure/docker/image/Dockerfile b/infrastructure/docker/image/Dockerfile
deleted file mode 100644
index c5cf7e8..0000000
--- a/infrastructure/docker/image/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM ubuntu:jammy
-
-# install it
-ADD resources /tmp/
-RUN /tmp/install.sh
diff --git a/infrastructure/docker/image/resources/file-functions.sh b/infrastructure/docker/image/resources/file-functions.sh
deleted file mode 100644
index fd19b2e..0000000
--- a/infrastructure/docker/image/resources/file-functions.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-backup_file_path='files'
-
-function init-file-repo() {
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -r ${RESTIC_REPOSITORY}/${backup_file_path} -v init
- else
- restic -r ${RESTIC_REPOSITORY}/${backup_file_path} -v init --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-# First arg is the directory, second is optional for the path to a certificate file
-function backup-directory() {
- local directory="$1"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache
- cd ${directory} && restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} backup .
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune
- else
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache --cacert ${CERTIFICATE_FILE}
- cd ${directory} && restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} backup . --cacert ${CERTIFICATE_FILE}
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-# First arg is the directory, the remaining args are the sub-directories (relative to the first directory) to backup.
-function backup-fs-from-directory() {
- local directory="$1"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache
- cd ${directory} && restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} backup $@
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune
- else
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache --cacert ${CERTIFICATE_FILE}
- cd ${directory} && restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} backup $@ --cacert ${CERTIFICATE_FILE}
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune --cacert ${CERTIFICATE_FILE}
- fi
-
-}
-
-function restore-directory() {
- local directory="$1"; shift
- local snapshot_id="${1:-latest}"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache
- rm -rf ${directory}*
- restic -v -r $RESTIC_REPOSITORY/${backup_file_path} restore ${snapshot_id} --target ${directory}
- else
- restic -v -r ${RESTIC_REPOSITORY}/${backup_file_path} unlock --cleanup-cache --cacert ${CERTIFICATE_FILE}
- rm -rf ${directory}*
- restic -v -r $RESTIC_REPOSITORY/${backup_file_path} restore ${snapshot_id} --target ${directory} --cacert ${CERTIFICATE_FILE}
- fi
-
-}
-
-function list-snapshot-files() {
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -r ${RESTIC_REPOSITORY}/${backup_file_path} snapshots
- else
- restic -r ${RESTIC_REPOSITORY}/${backup_file_path} snapshots --cacert ${CERTIFICATE_FILE}
- fi
-}
diff --git a/infrastructure/docker/image/resources/functions.sh b/infrastructure/docker/image/resources/functions.sh
deleted file mode 100644
index a55b674..0000000
--- a/infrastructure/docker/image/resources/functions.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-# usage: file_env VAR [DEFAULT]
-# ie: file_env 'XYZ_DB_PASSWORD' 'example'
-# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
-# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
-function file_env() {
- local var="$1"
- local fileVar="${var}_FILE"
- local def="${2:-}"
- if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
- echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
- exit 1
- fi
- local val="$def"
- if [ "${!var:-}" ]; then
- val="${!var}"
- elif [ "${!fileVar:-}" ]; then
- val="$(< "${!fileVar}")"
- fi
- export "$var"="$val"
- unset "$fileVar"
-}
diff --git a/infrastructure/docker/image/resources/install.sh b/infrastructure/docker/image/resources/install.sh
deleted file mode 100755
index eaa94b0..0000000
--- a/infrastructure/docker/image/resources/install.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-set -exo pipefail
-
-function main() {
- {
- upgradeSystem
- apt-get install -qqy ca-certificates curl gnupg postgresql-client-14
- curl -Ss --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg
- sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg] https://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
- upgradeSystem
- } > /dev/null
-
- update-ca-certificates
-
- install -m 0400 /tmp/functions.sh /usr/local/lib/
- install -m 0400 /tmp/pg-functions.sh /usr/local/lib/
- install -m 0400 /tmp/file-functions.sh /usr/local/lib/
-
- cleanupDocker
-}
-
-source /tmp/install_functions_debian.sh
-DEBIAN_FRONTEND=noninteractive DEBCONF_NOWARNINGS=yes main
diff --git a/infrastructure/docker/image/resources/pg-functions.sh b/infrastructure/docker/image/resources/pg-functions.sh
deleted file mode 100644
index 7f8a6bf..0000000
--- a/infrastructure/docker/image/resources/pg-functions.sh
+++ /dev/null
@@ -1,149 +0,0 @@
-backup_pg_role_path='pg-role'
-backup_pg_database_path='pg-database'
-
-function init-command() {
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} -v init $@
-}
-
-function init-role-repo() {
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- init-command
- else
- init-command --cacert ${CERTIFICATE_FILE}
- fi
-
-}
-
-function init-database-command() {
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} -v init $@
-}
-
-function init-database-repo() {
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- init-database-command
- else
- init-database-command --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-function drop-create-db() {
- psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password -c "DROP DATABASE \"${POSTGRES_DB}\";"
- psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password -c "CREATE DATABASE \"${POSTGRES_DB}\";"
-}
-
-function create-pg-pass() {
- local pg_host=${POSTGRES_HOST:-localhost}
-
- echo "${pg_host}:${POSTGRES_DB}:${POSTGRES_USER}:${POSTGRES_PASSWORD}" > /root/.pgpass
- echo "${POSTGRES_HOST}:template1:${POSTGRES_USER}:${POSTGRES_PASSWORD}" >> /root/.pgpass
- chmod 0600 /root/.pgpass
-}
-
-function roles-unlock-command() {
- restic -v -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} unlock --cleanup-cache $@
-}
-
-function roles-forget-command() {
- restic -v -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune $@
-}
-
-function backup-roles() {
- local role_prefix="$1"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- roles-unlock-command
- pg_dumpall -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U${POSTGRES_USER} --no-password --roles-only | \
- grep ${role_prefix} | restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} backup --stdin
- roles-forget-command
- else
- roles-unlock-command --cacert ${CERTIFICATE_FILE}
- pg_dumpall -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U${POSTGRES_USER} --no-password --roles-only | \
- grep ${role_prefix} | restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} backup --stdin --cacert ${CERTIFICATE_FILE}
- roles-forget-command --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-function db-unlock-command() {
- restic -v -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} unlock --cleanup-cache $@
-}
-
-function db-forget-command() {
- restic -v -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} forget --group-by '' --keep-last 1 --keep-daily ${RESTIC_DAYS_TO_KEEP} --keep-monthly ${RESTIC_MONTHS_TO_KEEP} --prune $@
-}
-
-function backup-db-dump() {
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- db-unlock-command
- pg_dump -d ${POSTGRES_DB} -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} \
- -U ${POSTGRES_USER} --no-password --serializable-deferrable | \
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} backup --stdin
- db-forget-command
- else
- db-unlock-command --cacert ${CERTIFICATE_FILE}
- pg_dump -d ${POSTGRES_DB} -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} \
- -U ${POSTGRES_USER} --no-password --serializable-deferrable | \
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} backup --stdin --cacert ${CERTIFICATE_FILE}
- db-forget-command --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-function restore-roles() {
- local snapshot_id="${1:-latest}"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- roles-unlock-command
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} dump ${snapshot_id} stdin | \
- psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password
- else
- roles-unlock-command --cacert ${CERTIFICATE_FILE}
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} dump ${snapshot_id} stdin --cacert ${CERTIFICATE_FILE} | \
- psql -d template1 -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password
- fi
-}
-
-function restore-db() {
- local snapshot_id="${1:-latest}"; shift
-
- if [ -z ${CERTIFICATE_FILE} ];
- then
- db-unlock-command
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} dump ${snapshot_id} stdin | \
- psql -d ${POSTGRES_DB} -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password
- else
- db-unlock-command --cacert ${CERTIFICATE_FILE}
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} dump ${snapshot_id} stdin --cacert ${CERTIFICATE_FILE} | \
- psql -d ${POSTGRES_DB} -h ${POSTGRES_SERVICE} -p ${POSTGRES_PORT} -U ${POSTGRES_USER} \
- --no-password
- fi
-}
-
-function list-snapshot-roles() {
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_role_path} snapshots
- else
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} snapshots --cacert ${CERTIFICATE_FILE}
- fi
-}
-
-function list-snapshot-db() {
- if [ -z ${CERTIFICATE_FILE} ];
- then
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} snapshots
- else
- restic -r ${RESTIC_REPOSITORY}/${backup_pg_database_path} snapshots --cacert ${CERTIFICATE_FILE}
- fi
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..0a1afb2
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,8 @@
+
+
+
+ Apache License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.html
+
+
+
diff --git a/src/practicalli/playground.clj b/src/practicalli/playground.clj
new file mode 100644
index 0000000..17e9f51
--- /dev/null
+++ b/src/practicalli/playground.clj
@@ -0,0 +1,46 @@
+;; ---------------------------------------------------------
+;; practicalli.playground
+;;
+;; TODO: Provide a meaningful description of the project
+;; ---------------------------------------------------------
+
+(ns practicalli.playground
+ (:gen-class)
+ (:require
+ [com.brunobonacci.mulog :as mulog]))
+
+;; ---------------------------------------------------------
+;; Application
+
+#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
+(defn greet
+ "Greeting message via Clojure CLI clojure.exec"
+ ([] (greet {:team-name "secret engineering"}))
+ ([{:keys [team-name]}]
+ (str "practicalli playground service developed by the " team-name " team")))
+
+
+(defn -main
+ "Entry point into the application via clojure.main -M"
+ [& args]
+ (let [team (first args)]
+ (mulog/set-global-context!
+ {:app-name "practicalli playground" :version "0.1.0-SNAPSHOT"})
+ (mulog/log ::application-starup :arguments args)
+ (if team
+ (println (greet team))
+ (println (greet)))))
+
+;; ---------------------------------------------------------
+
+
+;; ---------------------------------------------------------
+;; Rick Comment
+#_{:clj-kondo/ignore [:redefined-var]}
+(comment
+
+ (-main)
+ (-main {:team-name "Clojure Engineering"})
+
+ #_()) ; End of rich comment block
+;; ---------------------------------------------------------
diff --git a/test/practicalli/playground_test.clj b/test/practicalli/playground_test.clj
new file mode 100644
index 0000000..a91af24
--- /dev/null
+++ b/test/practicalli/playground_test.clj
@@ -0,0 +1,27 @@
+;; ---------------------------------------------------------
+;; practicalli.playground.-test
+;;
+;; Example unit tests for practicalli.playground
+;;
+;; - `deftest` - test a specific function
+;; - `testing` logically group assertions within a function test
+;; - `is` assertion: expected value then function call
+;; ---------------------------------------------------------
+
+
+(ns practicalli.playground-test
+ (:require
+ [clojure.test :refer [deftest is testing]]
+ [practicalli.playground :as playground]))
+
+
+(deftest application-test
+ (testing "TODO: Start with a failing test, make it pass, then refactor"
+
+ ;; TODO: fix greet function to pass test
+ (is (= "practicalli application developed by the secret engineering team"
+ (playground/greet)))
+
+ ;; TODO: fix test by calling greet with {:team-name "Practicalli Engineering"}
+ (is (= (playground/greet "Practicalli Engineering")
+ "practicalli service developed by the Practicalli Engineering team"))))
diff --git a/tests.edn b/tests.edn
new file mode 100644
index 0000000..0513579
--- /dev/null
+++ b/tests.edn
@@ -0,0 +1,13 @@
+;; ---------------------------------------------------------
+;; Kaocha test runner configuration
+;;
+;; Default configuration
+;; - show current config using either command:
+;;
+;; make test-config
+;;
+;; clojure -M:test/env:test/run --print-config
+
+;; ---------------------------------------------------------
+
+#kaocha/v1 {}