From fbb4578d7802529724317535e17efbd1b6066a8c Mon Sep 17 00:00:00 2001 From: Reily Siegel Date: Tue, 29 Jan 2019 14:56:54 +0000 Subject: [PATCH] Support clojure CLI, deps.edn, shadow-cljs, and kubernetes for builds. --- .dir-locals.el | 2 + .gitignore | 10 +- .gitlab-ci.yml | 992 ++++++++++++++++++-- Aptfile | 2 + Dockerfile | 13 +- Makefile | 17 + Procfile | 2 +- chart/.gitignore | 1 + chart/.gitlab-ci.yml | 19 + chart/.helmignore | 21 + chart/CONTRIBUTING.md | 26 + chart/Chart.yaml | 5 + chart/LICENSE | 19 + chart/README.md | 32 + chart/requirements.lock | 6 + chart/requirements.yaml | 5 + chart/templates/NOTES.txt | 5 + chart/templates/_helpers.tpl | 33 + chart/templates/db-initialize-job.yaml | 40 + chart/templates/db-migrate-hook.yaml | 40 + chart/templates/deployment.yaml | 56 ++ chart/templates/ingress.yaml | 43 + chart/templates/pdb.yaml | 22 + chart/templates/service.yaml | 21 + chart/values.yaml | 50 + deps.edn | 69 ++ env/dev/clj/flexblock/figwheel.clj | 12 - env/dev/clj/user.clj | 9 +- env/prod/resources/config.edn | 2 +- env/test/clj/flexblock/env.clj | 11 + package.json | 11 + project.clj | 162 ---- shadow-cljs.edn | 8 + src/cljs/flexblock/components/material.cljs | 18 +- src/cljs/flexblock/core.cljs | 17 +- src/cljs/flexblock/rooms/views.cljs | 5 +- src/cljs/flexblock/users/subs.cljs | 6 +- src/cljs/flexblock/users/views.cljs | 7 +- tests.edn | 3 + 39 files changed, 1515 insertions(+), 307 deletions(-) create mode 100644 .dir-locals.el create mode 100644 Aptfile create mode 100644 Makefile create mode 100644 chart/.gitignore create mode 100644 chart/.gitlab-ci.yml create mode 100644 chart/.helmignore create mode 100644 chart/CONTRIBUTING.md create mode 100644 chart/Chart.yaml create mode 100644 chart/LICENSE create mode 100644 chart/README.md create mode 100644 chart/requirements.lock create mode 100644 chart/requirements.yaml create mode 100644 chart/templates/NOTES.txt create mode 100644 chart/templates/_helpers.tpl create mode 100644 chart/templates/db-initialize-job.yaml create mode 100644 chart/templates/db-migrate-hook.yaml create mode 100644 chart/templates/deployment.yaml create mode 100644 chart/templates/ingress.yaml create mode 100644 chart/templates/pdb.yaml create mode 100644 chart/templates/service.yaml create mode 100644 chart/values.yaml create mode 100644 deps.edn delete mode 100644 env/dev/clj/flexblock/figwheel.clj create mode 100644 env/test/clj/flexblock/env.clj create mode 100644 package.json delete mode 100644 project.clj create mode 100644 shadow-cljs.edn create mode 100644 tests.edn diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..7e3c9fc --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,2 @@ +((nil + (cider-clojure-cli-parameters . "-A:dev -m nrepl.cmdline --middleware '%s'"))) diff --git a/.gitignore b/.gitignore index ad1d637..1eda8b2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,12 @@ profiles.clj .nrepl-port /log figwheel_server.log -/.m2 \ No newline at end of file +/.m2 +/.cpcache +/.shadow-cljs +package-lock.json +yarn.lock +node_modules +/resources/public/js/cljs-runtime +/resources/public/js/app.js +/resources/public/js/manifest.edn diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ddf914e..dc20e3a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,135 +1,937 @@ +# This file is a template, and might need editing before it works on your project. +# Auto DevOps +# This CI/CD configuration provides a standard pipeline for +# * building a Docker image (using a buildpack if necessary), +# * storing the image in the container registry, +# * running tests from a buildpack, +# * running code quality analysis, +# * creating a review app for each topic branch, +# * and continuous deployment to production +# +# Test jobs may be disabled by setting environment variables: +# * test: TEST_DISABLED +# * code_quality: CODE_QUALITY_DISABLED +# * license_management: LICENSE_MANAGEMENT_DISABLED +# * performance: PERFORMANCE_DISABLED +# * sast: SAST_DISABLED +# * dependency_scanning: DEPENDENCY_SCANNING_DISABLED +# * container_scanning: CONTAINER_SCANNING_DISABLED +# * dast: DAST_DISABLED +# * review: REVIEW_DISABLED +# * stop_review: REVIEW_DISABLED +# +# In order to deploy, you must have a Kubernetes cluster configured either +# via a project integration, or via group/project variables. +# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project +# level, or manually added below. +# +# Continuous deployment to production is enabled by default. +# If you want to deploy to staging first, set STAGING_ENABLED environment variable. +# If you want to enable incremental rollout, either manual or time based, +# set INCREMENTAL_ROLLOUT_MODE environment variable to "manual" or "timed". +# If you want to use canary deployments, set CANARY_ENABLED environment variable. +# +# If Auto DevOps fails to detect the proper buildpack, or if you want to +# specify a custom buildpack, set a project variable `BUILDPACK_URL` to the +# repository URL of the buildpack. +# e.g. BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby.git#v142 +# If you need multiple buildpacks, add a file to your project called +# `.buildpacks` that contains the URLs, one on each line, in order. +# Note: Auto CI does not work with multiple buildpacks yet -image: clojure:lein-alpine +image: alpine:latest variables: - REVIEW_APP_NAME: "$CI_COMMIT_REF_SLUG-$CI_PROJECT_NAME" + # AUTO_DEVOPS_DOMAIN is the application deployment domain and should be set as a variable at the group or project level. + # AUTO_DEVOPS_DOMAIN: domain.example.com -cache: - paths: - - .m2/ + POSTGRES_USER: user + POSTGRES_PASSWORD: testing-password + POSTGRES_ENABLED: "true" + POSTGRES_DB: $CI_ENVIRONMENT_SLUG + + KUBERNETES_VERSION: 1.10.9 + HELM_VERSION: 2.11.0 + + DOCKER_DRIVER: overlay2 stages: + - build - test - review - - deploy + - dast + - staging + - canary + - production + - incremental rollout 10% + - incremental rollout 25% + - incremental rollout 50% + - incremental rollout 100% + - performance + - cleanup -before_script: - - lein with-profile +ci deps +build: + stage: build + image: docker:stable-git + services: + - docker:stable-dind + script: + - setup_docker + - build + only: + - branches test: + stage: test + image: clojure:tools-deps-alpine + coverage: '/ALL FILES\W+\d+\.\d+\W+(\d+\.\d+)/' script: - - lein with-profile +ci test + - apk add --no-cache make + - make test | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" + only: + - branches + except: + variables: + - $TEST_DISABLED -coverage: +code_quality: + stage: test + image: docker:stable allow_failure: true - coverage: '/ALL FILES\W+\d+\.\d+\W+(\d+\.\d+)/' + services: + - docker:stable-dind script: - - lein with-profile +ci cloverage | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" + - setup_docker + - code_quality + artifacts: + paths: [gl-code-quality-report.json] + only: + - branches + except: + variables: + - $CODE_QUALITY_DISABLED -code_lint: - script: lein with-profile +ci eastwood "{:config-files [\"eastwood.clj\"]}" +license_management: + stage: test + image: + name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable" + entrypoint: [""] + allow_failure: true + script: + - license_management + artifacts: + paths: [gl-license-management-report.json] + only: + refs: + - branches + variables: + - $GITLAB_FEATURES =~ /\blicense_management\b/ + except: + variables: + - $LICENSE_MANAGEMENT_DISABLED -code_quality: - script: lein with-profile +ci kibit +performance: + stage: performance + image: docker:stable + allow_failure: true + services: + - docker:stable-dind + script: + - setup_docker + - performance + artifacts: + paths: + - performance.json + - sitespeed-results/ + only: + refs: + - branches + kubernetes: active + except: + variables: + - $PERFORMANCE_DISABLED -staging: - image: ruby - type: deploy - before_script: [] - cache: {} +sast: + stage: test + image: docker:stable + allow_failure: true + services: + - docker:stable-dind script: - - apt-get update -qy - - apt-get install -y ruby-dev rubygems - - gem install dpl - - dpl --provider=heroku --app=flexblock-staging --api-key=$HEROKU_API_KEY - environment: - name: staging - url: https://flexblock-staging.herokuapp.com + - setup_docker + - sast + artifacts: + reports: + sast: gl-sast-report.json + only: + refs: + - branches + variables: + - $GITLAB_FEATURES =~ /\bsast\b/ + except: + variables: + - $SAST_DISABLED + +dependency_scanning: + stage: test + image: docker:stable + allow_failure: true + services: + - docker:stable-dind + script: + - setup_docker + - dependency_scanning + artifacts: + reports: + dependency_scanning: gl-dependency-scanning-report.json + only: + refs: + - branches + variables: + - $GITLAB_FEATURES =~ /\bdependency_scanning\b/ + except: + variables: + - $DEPENDENCY_SCANNING_DISABLED + +container_scanning: + stage: test + image: docker:stable + allow_failure: true + services: + - docker:stable-dind + script: + - setup_docker + - container_scanning + artifacts: + paths: [gl-container-scanning-report.json] only: - - master + refs: + - branches + variables: + - $GITLAB_FEATURES =~ /\bcontainer_scanning\b/ + except: + variables: + - $CONTAINER_SCANNING_DISABLED + +dast: + stage: dast + allow_failure: true + image: registry.gitlab.com/gitlab-org/security-products/zaproxy + variables: + POSTGRES_DB: "false" + script: + - dast + artifacts: + paths: [gl-dast-report.json] + only: + refs: + - branches + kubernetes: active + variables: + - $GITLAB_FEATURES =~ /\bdast\b/ + except: + refs: + - master + variables: + - $DAST_DISABLED review: stage: review - image: ruby:2.3 - before_script: [] - cache: {} script: - - apt-get update -qy - - apt-get install -y ruby-dev curl - - gem install dpl - - echo "$REVIEW_APP_NAME " $REVIEW_APP_NAME - - >- - curl - -n - -X POST https://api.heroku.com/apps - -d ' - { - "name": "'"$REVIEW_APP_NAME"'", - "region": "us", - "stack": "heroku-16" - } - ' - -H "Content-Type: application/json" - -H "Accept: application/vnd.heroku+json; version=3" - -H "Authorization: Bearer $HEROKU_API_KEY" - - >- - curl - -n - -X POST https://api.heroku.com/apps/$REVIEW_APP_NAME/addons - -d ' - { - "attachment": {"name": "DATABASE"}, - "plan": "heroku-postgresql:hobby-dev" - } - ' - -H "Content-Type: application/json" - -H "Accept: application/vnd.heroku+json; version=3" - -H "Authorization: Bearer $HEROKU_API_KEY" - - dpl --provider=heroku --app=$REVIEW_APP_NAME --api-key=$HEROKU_API_KEY - + - check_kube_domain + - install_dependencies + - download_chart + - ensure_namespace + - initialize_tiller + - create_secret + - deploy + - persist_environment_url environment: name: review/$CI_COMMIT_REF_NAME - url: https://$CI_COMMIT_REF_SLUG-$CI_PROJECT_NAME.herokuapp.com + url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN on_stop: stop_review + artifacts: + paths: [environment_url.txt] only: - - branches + refs: + - branches + kubernetes: active except: - - master + refs: + - master + variables: + - $REVIEW_DISABLED stop_review: - stage: review - image: ruby:2.3 - before_script: [] - cache: {} - script: - - echo "environment is getting deployed !" - - >- - curl - -n - -X DELETE https://api.heroku.com/apps/$REVIEW_APP_NAME - -H "Content-Type: application/json" - -H "Accept: application/vnd.heroku+json; version=3" - -H "Authorization: Bearer $HEROKU_API_KEY" - + stage: cleanup variables: GIT_STRATEGY: none - when: manual + script: + - install_dependencies + - initialize_tiller + - delete environment: name: review/$CI_COMMIT_REF_NAME action: stop + when: manual + allow_failure: true + only: + refs: + - branches + kubernetes: active + except: + refs: + - master + variables: + - $REVIEW_DISABLED + +# Staging deploys are disabled by default since +# continuous deployment to production is enabled by default +# If you prefer to automatically deploy to staging and +# only manually promote to production, enable this job by setting +# STAGING_ENABLED. -pages: - stage: deploy - image: alpine - before_script: [] - cache: {} +staging: + stage: staging script: - - apk add npm git - - npm install gitbook-cli -g - - gitbook fetch 3.2.3 - - gitbook build doc public + - check_kube_domain + - install_dependencies + - download_chart + - ensure_namespace + - initialize_tiller + - create_secret + - deploy + environment: + name: staging + url: http://$CI_PROJECT_PATH_SLUG-staging.$AUTO_DEVOPS_DOMAIN + only: + refs: + - master + kubernetes: active + variables: + - $STAGING_ENABLED + +# Canaries are also disabled by default, but if you want them, +# and know what the downsides are, you can enable this by setting +# CANARY_ENABLED. + +canary: + stage: canary + script: + - check_kube_domain + - install_dependencies + - download_chart + - ensure_namespace + - initialize_tiller + - create_secret + - deploy canary + environment: + name: production + url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN + when: manual + only: + refs: + - master + kubernetes: active + variables: + - $CANARY_ENABLED + +.production: &production_template + stage: production + script: + - check_kube_domain + - install_dependencies + - download_chart + - ensure_namespace + - initialize_tiller + - create_secret + - deploy + - delete canary + - delete rollout + - persist_environment_url + environment: + name: production + url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN artifacts: - paths: - - public + paths: [environment_url.txt] + +production: + <<: *production_template + only: + refs: + - master + kubernetes: active + except: + variables: + - $STAGING_ENABLED + - $CANARY_ENABLED + - $INCREMENTAL_ROLLOUT_ENABLED + - $INCREMENTAL_ROLLOUT_MODE + +production_manual: + <<: *production_template + when: manual + allow_failure: false + only: + refs: + - master + kubernetes: active + variables: + - $STAGING_ENABLED + - $CANARY_ENABLED + except: + variables: + - $INCREMENTAL_ROLLOUT_ENABLED + - $INCREMENTAL_ROLLOUT_MODE + +# This job implements incremental rollout on for every push to `master`. + +.rollout: &rollout_template + script: + - check_kube_domain + - install_dependencies + - download_chart + - ensure_namespace + - initialize_tiller + - create_secret + - deploy rollout $ROLLOUT_PERCENTAGE + - scale stable $((100-ROLLOUT_PERCENTAGE)) + - delete canary + - persist_environment_url + environment: + name: production + url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN + artifacts: + paths: [environment_url.txt] + +.manual_rollout_template: &manual_rollout_template + <<: *rollout_template + stage: production + when: manual + # This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4) + only: + refs: + - master + kubernetes: active + variables: + - $INCREMENTAL_ROLLOUT_MODE == "manual" + - $INCREMENTAL_ROLLOUT_ENABLED + except: + variables: + - $INCREMENTAL_ROLLOUT_MODE == "timed" + +.timed_rollout_template: &timed_rollout_template + <<: *rollout_template + when: delayed + start_in: 5 minutes only: - - master + refs: + - master + kubernetes: active + variables: + - $INCREMENTAL_ROLLOUT_MODE == "timed" + +timed rollout 10%: + <<: *timed_rollout_template + stage: incremental rollout 10% + variables: + ROLLOUT_PERCENTAGE: 10 + +timed rollout 25%: + <<: *timed_rollout_template + stage: incremental rollout 25% + variables: + ROLLOUT_PERCENTAGE: 25 + +timed rollout 50%: + <<: *timed_rollout_template + stage: incremental rollout 50% + variables: + ROLLOUT_PERCENTAGE: 50 + +timed rollout 100%: + <<: *timed_rollout_template + <<: *production_template + stage: incremental rollout 100% + variables: + ROLLOUT_PERCENTAGE: 100 + +rollout 10%: + <<: *manual_rollout_template + variables: + ROLLOUT_PERCENTAGE: 10 + +rollout 25%: + <<: *manual_rollout_template + variables: + ROLLOUT_PERCENTAGE: 25 + +rollout 50%: + <<: *manual_rollout_template + variables: + ROLLOUT_PERCENTAGE: 50 + +rollout 100%: + <<: *manual_rollout_template + <<: *production_template + allow_failure: false + +# --------------------------------------------------------------------------- + +.auto_devops: &auto_devops | + # Auto DevOps variables and functions + [[ "$TRACE" ]] && set -x + auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB} + export DATABASE_URL=${DATABASE_URL-$auto_database_url} + export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG + export CI_APPLICATION_TAG=$CI_COMMIT_SHA + export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID} + export TILLER_NAMESPACE=$KUBE_NAMESPACE + # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products + export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') + + function registry_login() { + if [[ -n "$CI_REGISTRY_USER" ]]; then + echo "Logging to GitLab Container Registry with CI credentials..." + docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" + echo "" + fi + } + + function container_scanning() { + registry_login + + docker run -d --name db arminc/clair-db:latest + docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1 + apk add -U wget ca-certificates + docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} + wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64 + mv clair-scanner_linux_amd64 clair-scanner + chmod +x clair-scanner + touch clair-whitelist.yml + retries=0 + echo "Waiting for clair daemon to start" + while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done + ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true + } + + function code_quality() { + docker run --env SOURCE_CODE="$PWD" \ + --volume "$PWD":/code \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code + } + + function license_management() { + /run.sh analyze . + } + + function sast() { + case "$CI_SERVER_VERSION" in + *-ee) + + # Deprecation notice for CONFIDENCE_LEVEL variable + if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then + SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL" + echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL" + fi + + docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \ + --volume "$PWD:/code" \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code + ;; + *) + echo "GitLab EE is required" + ;; + esac + } + + function dependency_scanning() { + case "$CI_SERVER_VERSION" in + *-ee) + docker run --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" \ + --volume "$PWD:/code" \ + --volume /var/run/docker.sock:/var/run/docker.sock \ + "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code + ;; + *) + echo "GitLab EE is required" + ;; + esac + } + + function get_replicas() { + track="${1:-stable}" + percentage="${2:-100}" + + env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' ) + env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' ) + + if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then + # for stable track get number of replicas from `PRODUCTION_REPLICAS` + eval new_replicas=\$${env_slug}_REPLICAS + if [[ -z "$new_replicas" ]]; then + new_replicas=$REPLICAS + fi + else + # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS` + eval new_replicas=\$${env_track}_${env_slug}_REPLICAS + if [[ -z "$new_replicas" ]]; then + eval new_replicas=\${env_track}_REPLICAS + fi + fi + + replicas="${new_replicas:-1}" + replicas="$(($replicas * $percentage / 100))" + + # always return at least one replicas + if [[ $replicas -gt 0 ]]; then + echo "$replicas" + else + echo 1 + fi + } + + # Extracts variables prefixed with K8S_SECRET_ + # and creates a Kubernetes secret. + # + # e.g. If we have the following environment variables: + # K8S_SECRET_A=value1 + # K8S_SECRET_B=multi\ word\ value + # + # Then we will create a secret with the following key-value pairs: + # data: + # A: dmFsdWUxCg== + # B: bXVsdGkgd29yZCB2YWx1ZQo= + function create_application_secret() { + track="${1-stable}" + export APPLICATION_SECRET_NAME=$(application_secret_name "$track") + + bash -c ' + function k8s_prefixed_variables() { + env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p" + } + + kubectl create secret \ + -n "$KUBE_NAMESPACE" generic "$APPLICATION_SECRET_NAME" \ + --from-env-file <(k8s_prefixed_variables) -o yaml --dry-run | + kubectl replace -n "$KUBE_NAMESPACE" --force -f - + ' + } + + function deploy_name() { + name="$CI_ENVIRONMENT_SLUG" + track="${1-stable}" + + if [[ "$track" != "stable" ]]; then + name="$name-$track" + fi + + echo $name + } + + function application_secret_name() { + track="${1-stable}" + name=$(deploy_name "$track") + + echo "${name}-secret" + } + + function deploy() { + track="${1-stable}" + percentage="${2:-100}" + name=$(deploy_name "$track") + + replicas="1" + service_enabled="true" + postgres_enabled="$POSTGRES_ENABLED" + + # if track is different than stable, + # re-use all attached resources + if [[ "$track" != "stable" ]]; then + service_enabled="false" + postgres_enabled="false" + fi + + replicas=$(get_replicas "$track" "$percentage") + + if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then + secret_name='gitlab-registry' + else + secret_name='' + fi + + create_application_secret "$track" + + if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then + echo "Deploying first release with database initialization..." + helm upgrade --install \ + --wait \ + --set service.enabled="$service_enabled" \ + --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ + --set image.repository="$CI_APPLICATION_REPOSITORY" \ + --set image.tag="$CI_APPLICATION_TAG" \ + --set image.pullPolicy=IfNotPresent \ + --set image.secrets[0].name="$secret_name" \ + --set application.track="$track" \ + --set application.database_url="$DATABASE_URL" \ + --set application.secretName="$APPLICATION_SECRET_NAME" \ + --set service.url="$CI_ENVIRONMENT_URL" \ + --set replicaCount="$replicas" \ + --set postgresql.enabled="$postgres_enabled" \ + --set postgresql.nameOverride="postgres" \ + --set postgresql.postgresUser="$POSTGRES_USER" \ + --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ + --set postgresql.postgresDatabase="$POSTGRES_DB" \ + --set application.initializeCommand="$DB_INITIALIZE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + + echo "Deploying second release..." + helm upgrade --reuse-values \ + --wait \ + --set application.initializeCommand="" \ + --set application.migrateCommand="$DB_MIGRATE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + else + echo "Deploying new release..." + helm upgrade --install \ + --wait \ + --set service.enabled="$service_enabled" \ + --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ + --set image.repository="$CI_APPLICATION_REPOSITORY" \ + --set image.tag="$CI_APPLICATION_TAG" \ + --set image.pullPolicy=IfNotPresent \ + --set image.secrets[0].name="$secret_name" \ + --set application.track="$track" \ + --set application.database_url="$DATABASE_URL" \ + --set application.secretName="$APPLICATION_SECRET_NAME" \ + --set service.url="$CI_ENVIRONMENT_URL" \ + --set replicaCount="$replicas" \ + --set postgresql.enabled="$postgres_enabled" \ + --set postgresql.nameOverride="postgres" \ + --set postgresql.postgresUser="$POSTGRES_USER" \ + --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ + --set postgresql.postgresDatabase="$POSTGRES_DB" \ + --set application.migrateCommand="$DB_MIGRATE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + fi + + kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name" + } + + function scale() { + track="${1-stable}" + percentage="${2-100}" + name=$(deploy_name "$track") + + replicas=$(get_replicas "$track" "$percentage") + + if [[ -n "$(helm ls -q "^$name$")" ]]; then + helm upgrade --reuse-values \ + --wait \ + --set replicaCount="$replicas" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + fi + } + + function install_dependencies() { + apk add -U openssl curl tar gzip bash ca-certificates git + curl -L -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub + curl -L -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk + apk add glibc-2.28-r0.apk + rm glibc-2.28-r0.apk + + curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx + mv linux-amd64/helm /usr/bin/ + mv linux-amd64/tiller /usr/bin/ + helm version --client + tiller -version + + curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl" + chmod +x /usr/bin/kubectl + kubectl version --client + } + + function setup_docker() { + if ! docker info &>/dev/null; then + if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then + export DOCKER_HOST='tcp://localhost:2375' + fi + fi + } + + function setup_test_db() { + if [ -z ${KUBERNETES_PORT+x} ]; then + DB_HOST=postgres + else + DB_HOST=localhost + fi + export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:5432/${POSTGRES_DB}" + } + + function download_chart() { + if [[ ! -d chart ]]; then + auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app} + auto_chart_name=$(basename $auto_chart) + auto_chart_name=${auto_chart_name%.tgz} + auto_chart_name=${auto_chart_name%.tar.gz} + else + auto_chart="chart" + auto_chart_name="chart" + fi + + helm init --client-only + helm repo add gitlab https://charts.gitlab.io + if [[ ! -d "$auto_chart" ]]; then + helm fetch ${auto_chart} --untar + fi + if [ "$auto_chart_name" != "chart" ]; then + mv ${auto_chart_name} chart + fi + + helm dependency update chart/ + helm dependency build chart/ + } + + function ensure_namespace() { + kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE" + } + + function check_kube_domain() { + if [ -z ${AUTO_DEVOPS_DOMAIN+x} ]; then + echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set" + echo "You can do it in Auto DevOps project settings or defining a variable at group or project level" + echo "You can also manually add it in .gitlab-ci.yml" + false + else + true + fi + } + + function build() { + registry_login + + if [[ -f Dockerfile ]]; then + echo "Building Dockerfile-based application..." + docker build \ + --build-arg HTTP_PROXY="$HTTP_PROXY" \ + --build-arg http_proxy="$http_proxy" \ + --build-arg HTTPS_PROXY="$HTTPS_PROXY" \ + --build-arg https_proxy="$https_proxy" \ + --build-arg FTP_PROXY="$FTP_PROXY" \ + --build-arg ftp_proxy="$ftp_proxy" \ + --build-arg NO_PROXY="$NO_PROXY" \ + --build-arg no_proxy="$no_proxy" \ + -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" . + else + echo "Building Heroku-based application using gliderlabs/herokuish docker image..." + docker run -i \ + -e BUILDPACK_URL \ + -e HTTP_PROXY \ + -e http_proxy \ + -e HTTPS_PROXY \ + -e https_proxy \ + -e FTP_PROXY \ + -e ftp_proxy \ + -e NO_PROXY \ + -e no_proxy \ + --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build + docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" + docker rm "$CI_CONTAINER_NAME" >/dev/null + echo "" + + echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..." + docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web + docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" + docker rm "$CI_CONTAINER_NAME" >/dev/null + echo "" + fi + + echo "Pushing to GitLab Container Registry..." + docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" + echo "" + } + + function initialize_tiller() { + echo "Checking Tiller..." + + export HELM_HOST="localhost:44134" + tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 & + echo "Tiller is listening on ${HELM_HOST}" + + if ! helm version --debug; then + echo "Failed to init Tiller." + return 1 + fi + echo "" + } + + function create_secret() { + echo "Create secret..." + if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then + return + fi + + kubectl create secret -n "$KUBE_NAMESPACE" \ + docker-registry gitlab-registry \ + --docker-server="$CI_REGISTRY" \ + --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \ + --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \ + --docker-email="$GITLAB_USER_EMAIL" \ + -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f - + } + + function dast() { + export CI_ENVIRONMENT_URL=$(cat environment_url.txt) + + mkdir /zap/wrk/ + /zap/zap-baseline.py -J gl-dast-report.json -t "$CI_ENVIRONMENT_URL" || true + cp /zap/wrk/gl-dast-report.json . + } + + function performance() { + export CI_ENVIRONMENT_URL=$(cat environment_url.txt) + + mkdir gitlab-exporter + wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js + + mkdir sitespeed-results + + if [ -f .gitlab-urls.txt ] + then + sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt + docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt + else + docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL" + fi + + mv sitespeed-results/data/performance.json performance.json + } + + function persist_environment_url() { + echo $CI_ENVIRONMENT_URL > environment_url.txt + } + + function delete() { + track="${1-stable}" + name=$(deploy_name "$track") + + if [[ -n "$(helm ls -q "^$name$")" ]]; then + helm delete --purge "$name" + fi + + secret_name=$(application_secret_name "$track") + kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name" + } + +before_script: + - *auto_devops diff --git a/Aptfile b/Aptfile new file mode 100644 index 0000000..eeda2bd --- /dev/null +++ b/Aptfile @@ -0,0 +1,2 @@ +npm +openjdk-8-jdk diff --git a/Dockerfile b/Dockerfile index 00fdc17..93647fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,11 @@ -FROM java:8-alpine -MAINTAINER Your Name +FROM clojure:openjdk-8-tools-deps-alpine -ADD target/uberjar/flexblock.jar /flexblock/app.jar +RUN apk add --no-cache make npm -EXPOSE 3000 +COPY . /app +RUN npm config set unsafe-perm true +RUN make init build -C /app -CMD ["java", "-jar", "/flexblock/app.jar"] +EXPOSE 5000 + +CMD ["java", "-cp", "/app/flexblock.jar", "clojure.main", "-m", "flexblock.core"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..63d321a --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +.PHONY: run cljs init test build + +run: + clojure -m flexblock.core + +cljs: + npx shadow-cljs compile app + +init: + npm install + +test: + clojure -A:test --reporter kaocha.report/documentation + +build: + npx shadow-cljs release app + clojure -A:depstar:prod diff --git a/Procfile b/Procfile index c5ccc3d..1ba7fbf 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: java -cp target/uberjar/flexblock.jar clojure.main -m flexblock.core +web: java -cp flexblock.jar clojure.main -m flexblock.core diff --git a/chart/.gitignore b/chart/.gitignore new file mode 100644 index 0000000..e6b3ed6 --- /dev/null +++ b/chart/.gitignore @@ -0,0 +1 @@ +charts/* diff --git a/chart/.gitlab-ci.yml b/chart/.gitlab-ci.yml new file mode 100644 index 0000000..c0e9a6b --- /dev/null +++ b/chart/.gitlab-ci.yml @@ -0,0 +1,19 @@ +image: registry.gitlab.com/charts/alpine-helm + +stages: + - test + - release + +lint: + stage: test + script: + - helm lint . + except: + - master + +release-chart: + stage: release + script: + - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master https://gitlab.com/api/v4/projects/2860651/trigger/pipeline + only: + - master diff --git a/chart/.helmignore b/chart/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/chart/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/chart/CONTRIBUTING.md b/chart/CONTRIBUTING.md new file mode 100644 index 0000000..589986f --- /dev/null +++ b/chart/CONTRIBUTING.md @@ -0,0 +1,26 @@ +## Contributing + +Thank you for your interest in contributing to this GitLab project! We welcome +all contributions. By participating in this project, you agree to abide by the +[code of conduct](#code-of-conduct). + + +## Developer Certificate of Origin + License + +By contributing to GitLab B.V., You accept and agree to the following terms and +conditions for Your present and future Contributions submitted to GitLab B.V. +Except for the license granted herein to GitLab B.V. and recipients of software +distributed by GitLab B.V., You reserve all right, title, and interest in and to +Your Contributions. All Contributions are subject to the following DCO + License +terms. + +[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md) + +_This notice should stay as the first item in the CONTRIBUTING.md file._ + +## Code of conduct + +We want to create a welcoming environment for everyone who is interested +in contributing. Please visit our [Code of Conduct +page](https://about.gitlab.com/contributing/code-of-conduct) to learn +more about our committment to an open and welcoming environment. diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..3897f0d --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +description: GitLab's Auto-deploy Helm Chart +name: auto-deploy-app +version: 0.2.7 +icon: https://gitlab.com/gitlab-com/gitlab-artwork/raw/master/logo/logo-square.png diff --git a/chart/LICENSE b/chart/LICENSE new file mode 100644 index 0000000..a90ea93 --- /dev/null +++ b/chart/LICENSE @@ -0,0 +1,19 @@ +Copyright GitLab B.V. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/chart/README.md b/chart/README.md new file mode 100644 index 0000000..eb07ef3 --- /dev/null +++ b/chart/README.md @@ -0,0 +1,32 @@ +# GitLab's Auto-deploy Helm Chart + +## Requirements + +- Helm `2.9.0` and above is required in order support `"helm.sh/hook-delete-policy": before-hook-creation` for migrations + +## Configuration + +| Parameter | Description | Default | +| --- | --- | --- | +| replicaCount | | `1` | +| image.repository | | `gitlab.example.com/group/project` | +| image.tag | | `stable` | +| image.pullPolicy | | `Always` | +| image.secrets | | `[name: gitlab-registry]` | +| application.track | | `stable` | +| application.tier | | `web` | +| application.migrateCommand | If present, this variable will run as a shell command within an application Container as a Helm pre-upgrade Hook. Intended to run migration commands. | `nil` | +| application.initializeCommand | If present, this variable will run as shall command within an application Container as a Helm post-install Hook. Intended to run database initialization commands. | `nil` | +| application.secretName | Pass in the name of a Secret which the deployment will [load all key-value pairs from the Secret as environment variables](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables) in the application container. | `nil` | +| application.secretChecksum | Pass in the checksum of the secrets referenced by `application.secretName`. | `nil` | +| service.enabled | | `true` | +| service.name | | `web` | +| service.type | | `ClusterIP` | +| service.url | | `http://my.host.com/` | +| service.additionalHosts | If present, this list will add additional hostnames to the server configuration. | `nil` | +| service.externalPort | | `5000` | +| service.internalPort | | `5000` | +| postgresql.enabled | | `true` | +| podDisruptionBudget.enabled | | `false` | +| podDisruptionBudget.maxUnavailable | | `1` | +| podDisruptionBudget.minAvailable | If present, this variable will configure minAvailable in the PodDisruptionBudget. :warning: if you have `replicaCount: 1` and `podDisruptionBudget.minAvailable: 1` `kubectl drain` will be blocked. | `nil` | diff --git a/chart/requirements.lock b/chart/requirements.lock new file mode 100644 index 0000000..c92fba5 --- /dev/null +++ b/chart/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 0.7.1 +digest: sha256:358ce85fe4d3461ea6bb96713470a80de9c1324214a2e6f97d800298c02530e2 +generated: 2017-08-28T15:22:30.690341342-05:00 diff --git a/chart/requirements.yaml b/chart/requirements.yaml new file mode 100644 index 0000000..8d88384 --- /dev/null +++ b/chart/requirements.yaml @@ -0,0 +1,5 @@ +dependencies: + - name: postgresql + version: "0.7.1" + repository: "https://kubernetes-charts.storage.googleapis.com/" + condition: postgresql.enabled diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt new file mode 100644 index 0000000..0ef42bb --- /dev/null +++ b/chart/templates/NOTES.txt @@ -0,0 +1,5 @@ +{{- if .Values.service.enabled -}} +Application should be accessible at: {{ .Values.service.url }} +{{- else -}} +Application will be accessible at: {{ .Values.service.url }} when you deploy stable track. +{{- end -}} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 0000000..913145e --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,33 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 24 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trimSuffix "-app" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "appname" -}} +{{- $releaseName := default .Release.Name .Values.releaseOverride -}} +{{- printf "%s" $releaseName | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "trackableappname" -}} +{{- $trackableName := printf "%s-%s" (include "appname" .) .Values.application.track -}} +{{- $trackableName | trimSuffix "-stable" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Get a hostname from URL +*/}} +{{- define "hostname" -}} +{{- . | trimPrefix "http://" | trimPrefix "https://" | trimSuffix "/" | quote -}} +{{- end -}} diff --git a/chart/templates/db-initialize-job.yaml b/chart/templates/db-initialize-job.yaml new file mode 100644 index 0000000..7b33501 --- /dev/null +++ b/chart/templates/db-initialize-job.yaml @@ -0,0 +1,40 @@ +{{- if .Values.application.initializeCommand -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "trackableappname" . }}-db-initialize + labels: + app: {{ template "appname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version| replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + "helm.sh/hook": post-install + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "0" +spec: + backoffLimit: 1 + template: + metadata: + labels: + app: {{ template "appname" . }} + release: {{ .Release.Name }} + spec: + restartPolicy: Never + imagePullSecrets: +{{ toYaml .Values.image.secrets | indent 10 }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + command: ["/bin/sh"] + args: ["-c", "{{ .Values.application.initializeCommand }}"] + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.application.secretName }} + envFrom: + - secretRef: + name: {{ .Values.application.secretName }} + {{- end }} + env: + - name: DATABASE_URL + value: {{ .Values.application.database_url | quote }} +{{- end -}} diff --git a/chart/templates/db-migrate-hook.yaml b/chart/templates/db-migrate-hook.yaml new file mode 100644 index 0000000..3e5dbd0 --- /dev/null +++ b/chart/templates/db-migrate-hook.yaml @@ -0,0 +1,40 @@ +{{- if .Values.application.migrateCommand -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "trackableappname" . }}-db-migrate + labels: + app: {{ template "appname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version| replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + "helm.sh/hook": pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "0" +spec: + backoffLimit: 1 + template: + metadata: + labels: + app: {{ template "appname" . }} + release: {{ .Release.Name }} + spec: + restartPolicy: Never + imagePullSecrets: +{{ toYaml .Values.image.secrets | indent 10 }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + command: ["/bin/sh"] + args: ["-c", "{{ .Values.application.migrateCommand }}"] + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.application.secretName }} + envFrom: + - secretRef: + name: {{ .Values.application.secretName }} + {{- end }} + env: + - name: DATABASE_URL + value: {{ .Values.application.database_url | quote }} +{{- end -}} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..60ec537 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,56 @@ +{{- if not .Values.application.initializeCommand -}} +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "trackableappname" . }} + labels: + app: {{ template "appname" . }} + track: "{{ .Values.application.track }}" + tier: "{{ .Values.application.tier }}" + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + template: + metadata: + annotations: + checksum/application-secrets: "{{ .Values.application.secretChecksum }}" + labels: + app: {{ template "appname" . }} + track: "{{ .Values.application.track }}" + tier: "{{ .Values.application.tier }}" + release: {{ .Release.Name }} + spec: + imagePullSecrets: +{{ toYaml .Values.image.secrets | indent 10 }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.application.secretName }} + envFrom: + - secretRef: + name: {{ .Values.application.secretName }} + {{- end }} + env: + - name: DATABASE_URL + value: {{ .Values.application.database_url | quote }} + ports: + - name: "{{ .Values.service.name }}" + containerPort: {{ .Values.service.internalPort }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: {{ .Values.service.internalPort }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: {{ .Values.service.internalPort }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + resources: +{{ toYaml .Values.resources | indent 12 }} +{{- end -}} diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 0000000..a4a28fa --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,43 @@ +{{- if .Values.service.enabled -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "fullname" . }} + labels: + app: {{ template "appname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version| replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + kubernetes.io/tls-acme: "true" + kubernetes.io/ingress.class: "nginx" +spec: + tls: + - hosts: + - {{ template "hostname" .Values.service.url }} +{{- if .Values.service.additionalHosts }} +{{- range $host := .Values.service.additionalHosts }} + - {{ $host }} +{{- end -}} +{{- end }} + secretName: {{ template "fullname" . }}-tls + rules: + - host: {{ template "hostname" .Values.service.url }} + http: + paths: + - path: / + backend: + serviceName: {{ template "fullname" . }} + servicePort: {{ .Values.service.externalPort }} +{{- if .Values.service.additionalHosts }} +{{- range $host := .Values.service.additionalHosts }} + - host: {{ $host }} + http: + paths: + - path: / + backend: + serviceName: {{ template "fullname" $ }} + servicePort: {{ $.Values.service.externalPort }} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/chart/templates/pdb.yaml b/chart/templates/pdb.yaml new file mode 100644 index 0000000..69bb2ed --- /dev/null +++ b/chart/templates/pdb.yaml @@ -0,0 +1,22 @@ +{{- if .Values.podDisruptionBudget.enabled }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: {{ template "fullname" . }} + labels: + app: {{ template "appname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: +{{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} +{{- end }} +{{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} +{{- end }} + selector: + matchLabels: + app: {{ template "appname" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 0000000..65f9d61 --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,21 @@ +{{- if .Values.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "fullname" . }} + labels: + app: {{ template "appname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app: {{ template "appname" . }} + tier: "{{ .Values.application.tier }}" +{{- end -}} diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..2b2c007 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,50 @@ +# Default values for chart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +replicaCount: 1 +image: + repository: gitlab.example.com/group/project + tag: stable + pullPolicy: Always + secrets: + - name: gitlab-registry +application: + track: stable + tier: web + migrateCommand: + initializeCommand: + secretName: + secretChecksum: +service: + enabled: true + name: web + type: ClusterIP + url: http://my.host.com/ + additionalHosts: + externalPort: 5000 + internalPort: 5000 +livenessProbe: + path: "/" + initialDelaySeconds: 45 + timeoutSeconds: 15 +readinessProbe: + path: "/" + initialDelaySeconds: 45 + timeoutSeconds: 3 +postgresql: + enabled: true +resources: +# limits: +# cpu: 100m +# memory: 128Mi +# requests: +# cpu: 100m +# memory: 128Mi + +## Configure PodDisruptionBudget +## ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ +# +podDisruptionBudget: + enabled: false + # minAvailable: 1 + maxUnavailable: 1 diff --git a/deps.edn b/deps.edn new file mode 100644 index 0000000..de87f49 --- /dev/null +++ b/deps.edn @@ -0,0 +1,69 @@ +{:aliases + {:test + {:extra-paths ["test/clj" "test/cljc" "env/test/clj" "env/test/resources"] + :jvm-opts ["-server" "-Dconf=test-config.edn"] + :extra-deps {lambdaisland/kaocha {:mvn/version "0.0-367"} + lambdaisland/kaocha-cloverage {:mvn/version "0.0-22"} + ring/ring-mock {:mvn/version "0.3.2"}} + :main-opts ["-m" "kaocha.runner"]} + :run {:main-opts ["-m" "flexblock.core"]} + :dev + {:jvm-opts ["-server" "-Dconf=dev-config.edn"] + :extra-paths ["env/dev/clj" "env/dev/resources"] + :extra-deps + {ring/ring-devel {:mvn/version "1.6.3"} + prone {:mvn/version "1.6.0"} + re-frisk {:mvn/version "0.5.4"} + pjstadig/humane-test-output {:mvn/version "0.8.3"} + doo {:mvn/version "0.1.10"} + binaryage/devtools {:mvn/version "0.9.10"} + ring/ring-mock {:mvn/version "0.3.2"}}} + :prod {:extra-paths ["env/prod/clj" "env/prod/resources"]} + :depstar + {:extra-deps + {seancorfield/depstar {:mvn/version "0.1.5"}} + :main-opts ["-m" "hf.depstar.uberjar" "flexblock.jar"]}} + :deps + {selmer {:mvn/version "1.11.8"} + org.clojure/clojure {:mvn/version "1.10.0"} + com.7theta/re-frame-fx {:mvn/version "0.2.1"} + mount {:mvn/version "0.1.12"} + reagent {:mvn/version "0.8.1"} + com.draines/postal {:mvn/version "2.0.2"} + org.clojure/tools.logging {:mvn/version "0.4.1"} + metosin/ring-http-response {:mvn/version "0.9.0"} + metosin/ring-swagger-ui {:mvn/version "3.17.2"} + org.clojure/tools.cli {:mvn/version "0.3.7"} + clj-time {:mvn/version "0.14.4"} + day8.re-frame/http-fx {:mvn/version "0.1.6"} + camel-snake-kebab {:mvn/version "0.4.0"} + toucan {:mvn/version "1.1.9"} + ring/ring-defaults {:mvn/version "0.3.2"} + com.h2database/h2 {:mvn/version "1.4.197"} + cprop {:mvn/version "0.1.11"} + clj-fuzzy {:mvn/version "0.4.1"} + org.clojure/clojurescript {:mvn/version "1.10.439"} + luminus/ring-ttl-session {:mvn/version "0.3.2"} + org.postgresql/postgresql {:mvn/version "42.2.4"} + reagent-utils {:mvn/version "0.3.1"} + buddy {:mvn/version "2.0.0"} + re-pressed {:mvn/version "0.2.2"} + markdown-clj {:mvn/version "1.0.2"} + cljs-ajax {:mvn/version "0.7.4"} + re-com {:mvn/version "2.2.0-SNAPSHOT"} + org.clojure/java.jdbc {:mvn/version "0.7.7"} + cider/cider-nrepl {:mvn/version "0.20.0"} + luminus-nrepl {:mvn/version "0.1.5"} + org.clojure/tools.reader {:mvn/version "1.3.0"} + luminus-immutant {:mvn/version "0.2.4"} + phrase {:mvn/version "0.3-SNAPSHOT"} + amalloy/ring-gzip-middleware {:mvn/version "0.1.3"} + criterium {:mvn/version "0.4.4"} + org.clojure/test.check {:mvn/version "0.9.0"} + funcool/struct {:mvn/version "1.3.0"} + metosin/reitit {:mvn/version "0.2.3"} + re-frame {:mvn/version "0.10.5"} + java-jdbc/dsl {:mvn/version "0.1.3"} + ring/ring-core {:mvn/version "1.6.3"} + org.clojure/core.async {:mvn/version "0.4.474"}} + :paths ["src/clj" "src/cljc" "src/cljs" "resources"]} diff --git a/env/dev/clj/flexblock/figwheel.clj b/env/dev/clj/flexblock/figwheel.clj deleted file mode 100644 index 1fd1bf0..0000000 --- a/env/dev/clj/flexblock/figwheel.clj +++ /dev/null @@ -1,12 +0,0 @@ -(ns flexblock.figwheel - (:require [figwheel-sidecar.repl-api :as ra])) - -(defn start-fw [] - (ra/start-figwheel!)) - -(defn stop-fw [] - (ra/stop-figwheel!)) - -(defn cljs [] - (ra/cljs-repl)) - diff --git a/env/dev/clj/user.clj b/env/dev/clj/user.clj index 31a75e0..261a563 100644 --- a/env/dev/clj/user.clj +++ b/env/dev/clj/user.clj @@ -1,8 +1,7 @@ (ns user - (:require - [mount.core :as mount] - [flexblock.figwheel :refer [start-fw stop-fw cljs]] - [flexblock.core :refer [start-app]])) + (:require + [mount.core :as mount] + [flexblock.core :refer [start-app]])) (defn start [] (mount/start-without #'flexblock.core/repl-server)) @@ -13,5 +12,3 @@ (defn restart [] (stop) (start)) - - diff --git a/env/prod/resources/config.edn b/env/prod/resources/config.edn index b48cfbd..4a275cf 100644 --- a/env/prod/resources/config.edn +++ b/env/prod/resources/config.edn @@ -1,2 +1,2 @@ {:production true - :port 3000} + :port 5000} diff --git a/env/test/clj/flexblock/env.clj b/env/test/clj/flexblock/env.clj new file mode 100644 index 0000000..43cf906 --- /dev/null +++ b/env/test/clj/flexblock/env.clj @@ -0,0 +1,11 @@ +(ns flexblock.env + (:require [clojure.tools.logging :as log])) + +(def defaults + {:init + (fn [] + (log/info "\n-=[flexblock started successfully]=-")) + :stop + (fn [] + (log/info "\n-=[flexblock has shut down successfully]=-")) + :middleware identity}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..915dcdb --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "@material-ui/core": "^3.9.0", + "create-react-class": "^15.6.3", + "jquery": "^3.3.1", + "react": "^16.7.0", + "react-dom": "^16.7.0", + "shadow-cljs": "^2.7.17", + "zxcvbn": "^4.4.2" + } +} diff --git a/project.clj b/project.clj deleted file mode 100644 index b8c8f97..0000000 --- a/project.clj +++ /dev/null @@ -1,162 +0,0 @@ -(defproject flexblock "0.1.0-SNAPSHOT" - - :description "FIXME: write description" - :url "http://example.com/FIXME" - - :dependencies [[amalloy/ring-gzip-middleware "0.1.3"] - [buddy "2.0.0"] - [camel-snake-kebab "0.4.0"] - [cider/cider-nrepl "0.17.0"] - [clj-fuzzy "0.4.1"] - [clj-time "0.14.4"] - [clj-time "0.14.4"] - [cljs-ajax "0.7.4"] - [cljsjs/material-ui "3.2.0-0"] - [cljsjs/zxcvbn "4.4.0-1"] - [com.7theta/re-frame-fx "0.2.1"] - [com.draines/postal "2.0.2"] - [com.h2database/h2 "1.4.197"] - [cprop "0.1.11"] - [criterium "0.4.4"] - [day8.re-frame/http-fx "0.1.6"] - [funcool/struct "1.3.0"] - [java-jdbc/dsl "0.1.3"] - [luminus-immutant "0.2.4"] - [luminus-nrepl "0.1.4"] - [luminus/ring-ttl-session "0.3.2"] - [markdown-clj "1.0.2"] - [metosin/reitit "0.2.3"] - [metosin/ring-http-response "0.9.0"] - [metosin/ring-swagger-ui "3.17.2"] - [mount "0.1.12"] - [org.clojure/clojure "1.9.0"] - [org.clojure/clojurescript "1.10.339" :scope "provided"] - [org.clojure/core.async "0.4.474"] - [org.clojure/java.jdbc "0.7.7"] - [org.clojure/test.check "0.9.0"] - [org.clojure/tools.cli "0.3.7"] - [org.clojure/tools.logging "0.4.1"] - [org.clojure/tools.reader "1.3.0"] - [org.postgresql/postgresql "42.2.4"] - [phrase "0.3-SNAPSHOT"] - [re-com "2.2.0-SNAPSHOT"] - [re-frame "0.10.5"] - [re-pressed "0.2.2"] - [reagent "0.8.1"] - [reagent-utils "0.3.1"] - [ring/ring-core "1.6.3"] - [ring/ring-defaults "0.3.2"] - [selmer "1.11.8"] - [toucan "1.1.9"]] - - :min-lein-version "2.0.0" - - :source-paths ["src/clj" "src/cljc"] - :test-paths ["test/clj" "test/cljc"] - :resource-paths ["resources" "target/cljsbuild"] - :target-path "target/%s/" - :main ^:skip-aot flexblock.core - - :plugins [[lein-cljsbuild "1.1.5"] - [lein-immutant "2.1.0"] - [lein-cloverage "1.0.10"] - [jonase/eastwood "0.2.6"] - [lein-kibit "0.1.6"] - [lein-ancient "0.6.15"] - [nightlight/lein-nightlight "RELEASE"]] - :clean-targets ^{:protect false} - [:target-path [:cljsbuild :builds :app :compiler :output-dir] [:cljsbuild :builds :app :compiler :output-to]] - :figwheel - {:http-server-root "public" - :nrepl-port 7002 - :css-dirs ["resources/public/css"] - :nrepl-middleware - [cider.piggieback/wrap-cljs-repl cider.nrepl/cider-middleware]} - - - :profiles - {:uberjar {:omit-source true - :prep-tasks ["compile" ["cljsbuild" "once" "min"]] - :cljsbuild - {:builds - {:min - {:source-paths ["src/cljc" "src/cljs" "env/prod/cljs"] - :compiler - {:output-dir "target/cljsbuild/public/js" - :output-to "target/cljsbuild/public/js/app.js" - :source-map "target/cljsbuild/public/js/app.js.map" - :optimizations :advanced - :pretty-print false - :closure-warnings - {:externs-validation :off :non-standard-jsdoc :off} - :externs ["react/externs/react.js" - "resources/public/js/materialize.min.js" - "resources/public/js/masonry.pkgd.min.js"]}}}} - - - :aot :all - :uberjar-name "flexblock.jar" - :source-paths ["env/prod/clj"] - :resource-paths ["env/prod/resources"]} - - :dev [:project/dev :profiles/dev] - :test [:project/dev :project/test :profiles/test] - - :ci {:local-repo ".m2"} - - :project/dev {:jvm-opts ["-server" "-Dconf=dev-config.edn"] - :dependencies [[binaryage/devtools "0.9.10"] - [cider/piggieback "0.3.6"] - [doo "0.1.10"] - [figwheel-sidecar "0.5.16"] - [pjstadig/humane-test-output "0.8.3"] - [prone "1.6.0"] - [re-frisk "0.5.4"] - [ring/ring-devel "1.6.3"] - [ring/ring-mock "0.3.2"]] - :plugins [[com.jakemccrary/lein-test-refresh "0.19.0"] - [lein-doo "0.1.8"] - [lein-figwheel "0.5.14"] - [org.clojure/clojurescript "1.9.946"]] - :cljsbuild - {:builds - {:app - {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] - :figwheel {:on-jsload "flexblock.core/mount-components"} - :compiler - {:main "flexblock.app" - :asset-path "/js/out" - :output-to "target/cljsbuild/public/js/app.js" - :output-dir "target/cljsbuild/public/js/out" - :source-map true - :optimizations :none - :pretty-print true - :preloads [re-frisk.preload]}}}} - - - - :doo {:build "test"} - :source-paths ["env/dev/clj"] - :resource-paths ["env/dev/resources"] - :repl-options {:init-ns user} - :injections [(require 'pjstadig.humane-test-output) - (pjstadig.humane-test-output/activate!)]} - :project/test {:jvm-opts ["-server" "-Dconf=test-config.edn"] - :resource-paths ["env/test/resources"] - :cljsbuild - {:builds - {:test - {:source-paths ["src/cljc" - "src/cljs" - ;; Dont test cljc in cljs. - ;; "test/cljc" - "test/cljs"] - :compiler - {:output-to "target/test.js" - :main "flexblock.doo-runner" - :optimizations :whitespace - :pretty-print true}}}} - - } - :profiles/dev {} - :profiles/test {}}) diff --git a/shadow-cljs.edn b/shadow-cljs.edn new file mode 100644 index 0000000..21c62d5 --- /dev/null +++ b/shadow-cljs.edn @@ -0,0 +1,8 @@ +{:nrepl {:middleware [refactor-nrepl.middleware/wrap-refactor]} + :deps true + :builds {:app {:target :browser + :output-dir "resources/public/js" + :asset-path "/js" + :modules {:app {:entries [flexblock.core] + :init-fn flexblock.core/init!}} + :devtools {:after-load flexblock.core/reload!}}}} diff --git a/src/cljs/flexblock/components/material.cljs b/src/cljs/flexblock/components/material.cljs index 0e84972..e99f4b0 100644 --- a/src/cljs/flexblock/components/material.cljs +++ b/src/cljs/flexblock/components/material.cljs @@ -1,18 +1,21 @@ (ns flexblock.components.material + (:refer-clojure :exclude [List]) (:require [camel-snake-kebab.core :as case] [clojure.set :as set] - [material-ui] + ["@material-ui/core" :as material] + ["@material-ui/core/colors" :as material-colors] + ["@material-ui/core/styles" :as material-styles] [reagent.core :as r] [reagent.impl.template :as rtpl])) (defn color [color] - (aget js/MaterialUIColors (name color))) + (aget material-colors (name color))) (defn createMuiTheme [theme] - ((aget js/MaterialUIStyles "createMuiTheme") (clj->js theme))) + (material-styles/createMuiTheme (clj->js theme))) -(def MuiThemeProvider (-> js/MaterialUIStyles - (aget "MuiThemeProvider") + +(def MuiThemeProvider (-> material-styles/MuiThemeProvider (reagent.core/adapt-react-class))) (def ^:private input-component @@ -52,13 +55,13 @@ input-component)) rtpl/convert-prop-value)] (apply r/create-element - (aget js/MaterialUI "TextField") + material/TextField props (map r/as-element children)))) (defn material-component [key] - (r/adapt-react-class (aget js/MaterialUI (name key)))) + (r/adapt-react-class (aget material (name key)))) (def AppBar (material-component :AppBar)) (def Avatar (material-component :Avatar)) @@ -76,6 +79,7 @@ (def DialogContent (material-component :DialogContent)) (def DialogTitle (material-component :DialogTitle)) (def Drawer (material-component :Drawer)) +(def Fab (material-component :Fab)) (def FormControl (material-component :FormControl)) (def FormControlLabel (material-component :FormControlLabel)) (def Grid (material-component :Grid)) diff --git a/src/cljs/flexblock/core.cljs b/src/cljs/flexblock/core.cljs index acc20e7..2303aaf 100644 --- a/src/cljs/flexblock/core.cljs +++ b/src/cljs/flexblock/core.cljs @@ -36,13 +36,13 @@ :users #'users/page}) (defn page [] - [material/CssBaseline - [material/MuiThemeProvider - {:theme (material/createMuiTheme - {:typography {:useNextVariants true} - :palette - {:primary (material/color :purple) - :secondary (material/color :deepOrange)}})} + [material/MuiThemeProvider + {:theme (material/createMuiTheme + {:typography {:useNextVariants true} + :palette + {:primary (material/color :deepPurple) + :secondary (material/color :deepOrange)}})} + [material/CssBaseline [navbar/navbar] [login/modal] [(pages @(rf/subscribe [:page]))] @@ -62,3 +62,6 @@ (load-interceptors!) (keybinds/init-keybindings!) (mount-components)) + +(defn reload! [] + (mount-components)) diff --git a/src/cljs/flexblock/rooms/views.cljs b/src/cljs/flexblock/rooms/views.cljs index 0e20e8d..d697e8b 100644 --- a/src/cljs/flexblock/rooms/views.cljs +++ b/src/cljs/flexblock/rooms/views.cljs @@ -302,9 +302,8 @@ (not (str/blank? @(rf/subscribe [:login/token])))) [material/Zoom {:in true} - [material/Button - {:variant :fab - :color :secondary + [material/Fab + {:color :secondary :onClick #(rf/dispatch [:rooms/set-modal-open true]) :style {:position :fixed :right "2em" diff --git a/src/cljs/flexblock/users/subs.cljs b/src/cljs/flexblock/users/subs.cljs index 32e67b9..5ad706f 100644 --- a/src/cljs/flexblock/users/subs.cljs +++ b/src/cljs/flexblock/users/subs.cljs @@ -1,6 +1,6 @@ (ns flexblock.users.subs "This namespace contains the subscription handlers relating to students." - (:require [cljsjs.zxcvbn] + (:require ["zxcvbn" :as zxcvbn] [clojure.string :as str] [flexblock.interop :as interop] [flexblock.users :as users] @@ -100,8 +100,8 @@ :<- [:users/password] :<- [:users/password-dict] (fn [[password dict] _] - (let [results (js->clj (js/zxcvbn (str password) - (clj->js dict)) + (let [results (js->clj (zxcvbn (str password) + (clj->js dict)) :keywordize-keys true) score (* 10 (+ -3 (:guesses_log10 results) )) processed-score (cond diff --git a/src/cljs/flexblock/users/views.cljs b/src/cljs/flexblock/users/views.cljs index ad071e5..15a9dc7 100644 --- a/src/cljs/flexblock/users/views.cljs +++ b/src/cljs/flexblock/users/views.cljs @@ -233,7 +233,7 @@ {:value @tab :indicatorColor :primary :textColor :primary - :fullWidth true + :variant :fullWidth :onChange (fn [_ selected] (reset-fn) (reset! tab selected))} @@ -263,9 +263,8 @@ (not (str/blank? @(rf/subscribe [:login/token])))) [material/Zoom {:in true} - [material/Button - {:variant :fab - :color :secondary + [material/Fab + {:color :secondary :onClick #(rf/dispatch [:users/set-modal-open true]) :style {:position :fixed :right "2em" diff --git a/tests.edn b/tests.edn new file mode 100644 index 0000000..66f126d --- /dev/null +++ b/tests.edn @@ -0,0 +1,3 @@ +#kaocha/v1 +{:plugins [:kaocha.plugin/profiling + :kaocha.plugin/cloverage]}