From 8c175a3d89edf1301a23ff588cf1d39e883d6fa9 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 16 Aug 2024 08:29:49 +0200 Subject: [PATCH] revive initial --- .cljstyle | 109 ++++++++++ .dir-locals.el | 2 + .gitattributes | 5 + .github/config/clj-kondo-ci-config.edn | 101 ++++++++++ .github/workflows/lint-review.yaml | 37 ++++ .gitignore | 51 ++++- .gitlab-ci.yml | 27 --- CHANGELOG.md | 22 +++ Makefile | 187 ++++++++++++++++++ README.md | 11 +- build.clj | 135 +++++++++++++ build.py | 75 ------- deps.edn | 44 +++++ dev/mulog_events.clj | 55 ++++++ dev/portal.clj | 24 +++ dev/user.clj | 113 +++++++++++ infrastructure/docker/build.py | 51 ----- infrastructure/docker/image/Dockerfile | 5 - .../docker/image/resources/file-functions.sh | 69 ------- .../docker/image/resources/functions.sh | 21 -- .../docker/image/resources/install.sh | 24 --- .../docker/image/resources/pg-functions.sh | 149 -------------- pom.xml | 8 + src/practicalli/playground.clj | 46 +++++ test/practicalli/playground_test.clj | 27 +++ tests.edn | 13 ++ 26 files changed, 982 insertions(+), 429 deletions(-) create mode 100644 .cljstyle create mode 100644 .dir-locals.el create mode 100644 .gitattributes create mode 100644 .github/config/clj-kondo-ci-config.edn create mode 100644 .github/workflows/lint-review.yaml delete mode 100644 .gitlab-ci.yml create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100644 build.clj delete mode 100644 build.py create mode 100644 deps.edn create mode 100644 dev/mulog_events.clj create mode 100644 dev/portal.clj create mode 100644 dev/user.clj delete mode 100644 infrastructure/docker/build.py delete mode 100644 infrastructure/docker/image/Dockerfile delete mode 100644 infrastructure/docker/image/resources/file-functions.sh delete mode 100644 infrastructure/docker/image/resources/functions.sh delete mode 100755 infrastructure/docker/image/resources/install.sh delete mode 100644 infrastructure/docker/image/resources/pg-functions.sh create mode 100644 pom.xml create mode 100644 src/practicalli/playground.clj create mode 100644 test/practicalli/playground_test.clj create mode 100644 tests.edn 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) -[DeltaChat chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [team@social.meissa-gmbh.de team@social.meissa-gmbh.de](https://social.meissa-gmbh.de/@team) | [Website & Blog](https://domaindrivenarchitecture.org) +[DeltaChat chat over e-mail](mailto:buero@meissa-gmbh.de?subject=community-chat) | [M 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 {}