diff --git a/.gitignore b/.gitignore index 02f42a1b6b..29825090aa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ node_modules yarn-error.log playbook/stats.json *.dec.* +.env diff --git a/.sops.yaml b/.sops.yaml index f315deb821..ff8b71d1dc 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -6,9 +6,13 @@ creation_rules: - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" role: "arn:aws:iam::205083374951:role/playbook-admins" + - arn: "arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c" + role: "arn:aws:iam::205083374951:role/forever-people" - key_groups: - kms: - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" role: "arn:aws:iam::205083374951:role/playbook-admins" + - arn: "arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41" + role: "arn:aws:iam::205083374951:role/forever-people" diff --git a/Jenkinsfile b/Jenkinsfile index 2dea7c0e79..233dcc2588 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,6 +25,9 @@ app.build( memory: '10Gi', ] ], + storageConfig: [ + size: '15Gi', + ], ]) } diff --git a/docker-compose.yml b/docker-compose.yml index 08164e4db4..04f7f9ce60 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,24 @@ services: - image-registry.powerapp.cloud/playbook/playbook:main volumes: - .:/home/app/src - - bundle:/usr/local/bundle + - bundle:/usr/local/rvm/gems ports: - "8089:3000" + depends_on: + db: + condition: service_healthy + db: + platform: linux/x86_64 + image: percona:8.0.36-28@sha256:1128d56e64711ed65cb0c57041048967ee5875a2167d708d327885fd1f995fa0 + init: true + ports: + - 3306 + environment: + MYSQL_ROOT_HOST: "%" + MYSQL_ROOT_PASSWORD: password + healthcheck: + test: ["CMD", "mysql", "-u", "root", "-ppassword", "-e", "SHOW DATABASES;"] + interval: 5s + timeout: 2s + retries: 5 + start_period: 20s diff --git a/milano.production.yml b/milano.production.yml index 66f7a0dc03..7141e513a1 100644 --- a/milano.production.yml +++ b/milano.production.yml @@ -3,11 +3,15 @@ dependencies: machine: directory: playbook-website/ + environment: + CLUSTER: app-prod-hq deploy: &deploy max_commits: # Automatically deploy all commits, no matter how many. + pre: + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/pre_deploy" override: - - ./bin/deployer bash -lc "cluster=app-prod-hq environment=${ENVIRONMENT} tag=${REVISION} deploy_url=${DEPLOY_URL} bin/deploy" + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/deploy" rollback: <<: *deploy diff --git a/milano.staging.yml b/milano.staging.yml index da46d2664c..eb9be7627e 100644 --- a/milano.staging.yml +++ b/milano.staging.yml @@ -3,11 +3,15 @@ dependencies: machine: directory: playbook-website/ + environment: + CLUSTER: app-beta-hq deploy: &deploy max_commits: # Automatically deploy all commits, no matter how many. + pre: + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/pre_deploy" override: - - ./bin/deployer bash -lc "cluster=app-beta-hq environment=${ENVIRONMENT} tag=${REVISION} deploy_url=${DEPLOY_URL} bin/deploy" + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/deploy" rollback: <<: *deploy diff --git a/milano.yml b/milano.yml index d1ebd7a604..7c94d7b562 100644 --- a/milano.yml +++ b/milano.yml @@ -6,8 +6,10 @@ machine: deploy: &deploy max_commits: # Automatically deploy all commits, no matter how many. + pre: + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/pre_deploy" override: - - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} deploy_url=${DEPLOY_URL} bin/deploy" + - ./bin/deployer bash -lc "cluster=${CLUSTER} environment=${ENVIRONMENT} tag=${REVISION} namespace=${GITHUB_REPO_NAME}-${ENVIRONMENT} deploy_url=${DEPLOY_URL} bin/deploy" rollback: <<: *deploy diff --git a/playbook-website/.gitignore b/playbook-website/.gitignore index 7589e0e431..651de6e0c6 100644 --- a/playbook-website/.gitignore +++ b/playbook-website/.gitignore @@ -35,4 +35,5 @@ yarn-debug.log* # Vite uses dotenv and suggests to ignore local-only env files. See # https://vitejs.dev/guide/env-and-mode.html#env-files *.local +.env diff --git a/playbook-website/Gemfile b/playbook-website/Gemfile index e74c01ade0..ee48ba3d67 100644 --- a/playbook-website/Gemfile +++ b/playbook-website/Gemfile @@ -7,6 +7,7 @@ ruby "3.3.0" gem "playbook_ui", path: "../playbook" +gem "mysql2", "0.5.6" gem "rails", "~> 7.0.8" gem "turbo-rails", "~> 1.4.0" gem "puma", "~> 6.3" diff --git a/playbook-website/Gemfile.lock b/playbook-website/Gemfile.lock index 3bc1bfb9fe..dde6ef761d 100644 --- a/playbook-website/Gemfile.lock +++ b/playbook-website/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../playbook specs: - playbook_ui (14.5.0) + playbook_ui (14.6.0) actionpack (>= 5.2.4.5) actionview (>= 5.2.4.5) activesupport (>= 5.2.4.5) @@ -140,6 +140,7 @@ GEM mini_mime (1.1.5) minitest (5.25.1) msgpack (1.7.2) + mysql2 (0.5.6) net-imap (0.4.15) date net-protocol @@ -332,6 +333,7 @@ DEPENDENCIES front_matter_parser (~> 1.0.1) health_check listen + mysql2 (= 0.5.6) playbook_ui! psych (< 4) puma (~> 6.3) diff --git a/playbook-website/app/javascript/components/AvailableProps/globalPropsValues.ts b/playbook-website/app/javascript/components/AvailableProps/globalPropsValues.ts index 949a2f54be..92a30ed8b8 100644 --- a/playbook-website/app/javascript/components/AvailableProps/globalPropsValues.ts +++ b/playbook-website/app/javascript/components/AvailableProps/globalPropsValues.ts @@ -78,6 +78,11 @@ const globalPropsValues = [ type: "union", values: '"wrap" | "nowrap" | "wrapReverse"' }, + { + prop: "htmlOptions", + type: "object", + values: "{ [key: string]: string | number | boolean | (() => void); }" + }, { prop: "id", type: "string", diff --git a/playbook-website/app/javascript/components/AvailableProps/index.tsx b/playbook-website/app/javascript/components/AvailableProps/index.tsx index f37ee20812..daea96b73f 100644 --- a/playbook-website/app/javascript/components/AvailableProps/index.tsx +++ b/playbook-website/app/javascript/components/AvailableProps/index.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react' import { Card, Nav, SectionSeparator, NavItem } from 'playbook-ui' import GlobalProps from './globalProps' +import GlobalPropsValues from './globalPropsValues' import KitProps from './kitProps' type AvailablePropsType = { @@ -10,8 +11,15 @@ type AvailablePropsType = { const AvailableProps = ({ availableProps, darkMode }: AvailablePropsType) => { const props = JSON.parse(availableProps) + const globalPropsNames = GlobalPropsValues.map(prop => prop.prop) const [showKitTab, setShowKitTab] = useState(true) + for(var propName in props) { + if(globalPropsNames.includes(propName)) { + delete props[propName] + } + } + return ( <> diff --git a/playbook-website/app/models/application_record.rb b/playbook-website/app/models/application_record.rb new file mode 100644 index 0000000000..08dc537989 --- /dev/null +++ b/playbook-website/app/models/application_record.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class +end diff --git a/playbook-website/bin/deploy b/playbook-website/bin/deploy index 06a9bebad7..32a13a5d2e 100755 --- a/playbook-website/bin/deploy +++ b/playbook-website/bin/deploy @@ -21,6 +21,11 @@ fi extraBindings="{\"ingress\":{\"hosts\":[\"${deploy_url}\"]}}" +# Priority resources +source "${workDir}/bin/priority_deploy" +priority_deploy + +# Application Deployment krane render \ --filenames=${workDir}/config/deploy/templates \ --bindings="@${workDir}/config/deploy/values.yaml" $environmentValuesFile $environmentSecretsFile "image_tag=${tag}" "environment=${environment}" $extraBindings \ diff --git a/playbook-website/bin/deployer b/playbook-website/bin/deployer index 55164ffd8e..03b6590d8d 100755 --- a/playbook-website/bin/deployer +++ b/playbook-website/bin/deployer @@ -7,7 +7,7 @@ if [ -t 0 ]; then AWS_CREDS_MOUNT="--mount type=bind,source=${HOME}/.aws/credentials,destination=/root/.aws/credentials,readonly" fi -DEPLOYER_IMAGE="image-registry.powerapp.cloud/app/deployer:master-467d8015ffc91fc62c347367db792bb6de0eeea8-1439" +DEPLOYER_IMAGE="image-registry.powerapp.cloud/app/deployer:main-559f8a815f07c3e04dc82bf416c403ef80f6eacb-42" DEPLOYER_MOUNTS="${AWS_CREDS_MOUNT} --mount type=bind,source=$(pwd),destination=/app --mount type=bind,source=${HOME}/.kube,destination=/root/.kube" RUN_DEPLOYER="docker run --tty ${INTERACTIVE} ${EXTRA_ARGS} --rm --env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY ${DEPLOYER_MOUNTS} ${DEPLOYER_IMAGE}" diff --git a/playbook-website/bin/deployment_helpers b/playbook-website/bin/deployment_helpers new file mode 100644 index 0000000000..b1d5769600 --- /dev/null +++ b/playbook-website/bin/deployment_helpers @@ -0,0 +1,87 @@ +#!/bin/bash + +set -eo pipefail + +: ${baseline:="review"} + +function setup_colors() { + if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then + NOFORMAT='\033[0m' + RED='\033[0;31m' + GREEN='\033[0;32m' + ORANGE='\033[0;33m' + BLUE='\033[0;34m' + PURPLE='\033[0;35m' + CYAN='\033[0;36m' + YELLOW='\033[1;33m' + else + NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' + fi +} +export -f setup_colors +setup_colors + +function begin { + local message=$1 + + echo "\ + +======================================= +BEGIN: $message +======================================= +" +} +export -f begin + +function succeed { + local message=$1 + + echo -e "\ +${GREEN} +======================================= +SUCCESS: $message +======================================= +${NOFORMAT}" +} +export -f succeed + +function fail { + local message=$1 + + echo -e "\ +#{RED} +======================================= +ERROR: $message +======================================= +#{NOFORMAT}" + exit 1 +} +export -f fail + +function instance_name { + local inst=$environment + echo "$inst" +} +export -f instance_name + +function log() { + echo >&2 -e "[$cluster][$namespace][$task_name] ${1-}" +} + +log "BEGIN: $task_name" +trap 'log "${RED:-}ERROR: $task_name - $cluster / $namespace${NOFORMAT:-}"' ERR + +function decrypt() { + local encrypted_file_path= + encrypted_file_path="$1" + + local decrypted_file_path= + decrypted_file_path="$2" + + secrets="$(sops --decrypt "$encrypted_file_path")" + if [ ! -e "$decrypted_file_path" ] + then + log "writing decrypted secrets for: $encrypted_file_path" + echo -n "$secrets" > "$decrypted_file_path" + fi +} diff --git a/playbook-website/bin/mysql/user-setup.sh b/playbook-website/bin/mysql/user-setup.sh new file mode 100644 index 0000000000..543339bfe9 --- /dev/null +++ b/playbook-website/bin/mysql/user-setup.sh @@ -0,0 +1,120 @@ +#!/bin/bash + + cluster="$1" + namespace="$2" + +ctrl="kubectl --context $cluster -n $namespace" + +mysql_pod=$($ctrl get pods --no-headers -o custom-columns=":metadata.name" | grep 'pxc-0$') +secret_po_name=$($ctrl get pxc -o jsonpath='{.items[0].spec.secretsName}') +root_pwd=$(eval "$ctrl get secrets $secret_po_name -o jsonpath='{.data.root}'" | base64 --decode ) + +if [ -z "$mysql_pod" ]; then + exit 0 +fi + +mysql_pod="$mysql_pod -c pxc" + +users=$($ctrl get configmap mysql-users-playbook -o=jsonpath='{.data.users}' | sed 's/^"//;s/"$//;s/\\\"/\"/g') + +custom_users=() +mysql_commands=() + +clean_up_users() { + local percona_users=("root" "clustercheck" "monitor" "operator" "pmmserver" "proxyadmin" "replication" "xtrabackup") + local target_users=("$@") + + if [ ${#target_users[@]} -gt 0 ]; then + for element in "${target_users[@]}"; do + percona_users+=("$element") + done + fi + + excluded_users_list=$(printf "'%s'," "${percona_users[@]}") + excluded_users_list=${excluded_users_list%,} + + command="SELECT CONCAT(\"'\", user, \"'@'\", host, \"'\") FROM mysql.user WHERE NOT (user LIKE 'mysql%' OR user IN ($excluded_users_list));" + + users_to_delete=$($ctrl exec $mysql_pod -- mysql -uroot -p$root_pwd -N -e"$command") + + echo "--- List of MySQL users to delete:" + echo $users_to_delete + # Loop through the list of users and execute DROP USER statements + # we can't use `delete from` statement as if it will be once deleted, + # we will be unable to recreate it with the same name/password/grants due to mysql security policy + for user in $users_to_delete; do + $($ctrl exec $mysql_pod -- mysql -uroot -p$root_pwd -e "DROP USER $user;") + done +} + +if [ -z "$users" ]; then + clean_up_users "${custom_users[@]}" + exit 0 +fi + +# Extract the count of user objects +count=$(echo "$users" | grep -o '{' | wc -l) + +if [ "$count" -eq 0 ]; then + clean_up_users "${custom_users[@]}" + exit 0 +fi + +for (( i=1; i<=$count; i++ )); do + # Extracting each user block + block=$(echo "$users" | awk -v RS='},' 'NR=='$i'') + + name=$(echo "$block" | grep -o '"name":"[^"]*"' | awk -F\" '{print $4}') + grant=$(echo "$block" | grep -o '"grant":"[^"]*"' | awk -F\" '{print $4}') + user_pwd=$(eval "$ctrl get secrets $secret_po_name -o jsonpath='{.data.$name}'" ) + + custom_users+=("$name") + + if [ -z "$user_pwd" ]; then + echo "No password in $secret_po_name for user $name" + exit 1 + else + user_pwd=$(echo "$user_pwd" | base64 --decode) + fi + + mysql_commands+=("CREATE USER IF NOT EXISTS '$name'@'%' IDENTIFIED BY '$user_pwd'") + + # Extract dbs and append '.*' to each one + dbs_raw=$(echo "$block" | grep -o '"dbs":\["[^]]*"' | sed 's/"dbs":\["//' | sed 's/"//g' | sed 's/,/, /g') + IFS=', ' read -ra dbs_array <<< "$dbs_raw" + for db in "${dbs_array[@]}"; do + mysql_commands+=("GRANT $grant ON ${db}.* TO '$name'@'%'") + done +done + +clean_up_users "${custom_users[@]}" + +# get list of users with 'mysql_native_password' plugin +users_w_deprecated_auth=$($ctrl exec $mysql_pod -- mysql -uroot -p$root_pwd -Bse "SELECT CONCAT(user, '@', host) FROM mysql.user WHERE plugin = 'mysql_native_password';") + +# alter authentication plugin +for user in $users_w_deprecated_auth; do + uname=$(echo "$user" | cut -d "@" -f 1) + user_pwd=$(eval "$ctrl get secrets $secret_po_name -o jsonpath='{.data.$uname}'" ) + if [ -n "$user_pwd" ]; then + user_pwd=$(echo "$user_pwd" | base64 --decode) + quoted_username=$(echo "$user" | sed "s/\(.*\)@\(.*\)/'\1'@'\2'/") + mysql_commands+=("ALTER USER $quoted_username IDENTIFIED WITH caching_sha2_password BY '$user_pwd'") + fi +done + +mysql_commands+=("FLUSH PRIVILEGES;") + +IFS=';' +joint_output="${mysql_commands[*]}" +unset IFS + +$($ctrl exec $mysql_pod -- mysql -uroot -p$root_pwd -e "$joint_output") + +if [ $? -eq 0 ]; then + echo "$joint_output" | sed "s/BY '[^']*'/BY '***'/g" | tr ';' '\n' + echo "--- Users CREATED with password and granted permissions." +else + echo "--- User creation and permission granting FAILED" + exit 1 +fi diff --git a/playbook-website/bin/pre_deploy b/playbook-website/bin/pre_deploy new file mode 100755 index 0000000000..d7576eeb99 --- /dev/null +++ b/playbook-website/bin/pre_deploy @@ -0,0 +1,19 @@ +#!/bin/bash + +task_name="Predeployment" +source "$(dirname $(realpath $0))/deployment_helpers" + +log ">>> Installing dependencies at $tag into $environment ($cluster # $namespace)" + +instance=$(environment=$environment instance_name) + +operatorResources=$(krane render \ + --filenames=/app/config/deploy/templates/operators \ + --current-sha=${tag}) || exit $? +echo "$operatorResources" | krane deploy ${namespace} ${cluster} \ + --selector="app.kubernetes.io/component=operator" \ + --verbose-log-prefix \ + --global-timeout=300s \ + --stdin + +log "${GREEN}SUCCESS: $task_name - $cluster / $namespace${NOFORMAT}" diff --git a/playbook-website/bin/priority_deploy b/playbook-website/bin/priority_deploy new file mode 100644 index 0000000000..0bdb807bbb --- /dev/null +++ b/playbook-website/bin/priority_deploy @@ -0,0 +1,15 @@ +#!/bin/bash + +priorityResources=$(krane render \ + --filenames="$(pwd)/config/deploy/templates/priority" \ + --bindings="@${workDir}/config/deploy/values.yaml" $environmentValuesFile $environmentSecretsFile "image_tag=${tag}" "environment=${environment}" $extraBindings \ + --current-sha=${tag}) || exit $? + +echo "$priorityResources" | krane deploy ${namespace} ${cluster} \ + --selector="app.kubernetes.io/name=playbook,app.kubernetes.io/part-of=priority-deploy" \ + --verbose-log-prefix \ + --global-timeout=10m \ + --stdin + +### run post priority scripts +"${workDir}/bin/mysql/user-setup.sh" "$cluster" "$namespace" diff --git a/playbook-website/bin/setup b/playbook-website/bin/setup index 1ac8ce0b7a..a8044c64ea 100755 --- a/playbook-website/bin/setup +++ b/playbook-website/bin/setup @@ -16,10 +16,17 @@ FileUtils.chdir APP_ROOT do puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') - # Install JavaScript dependencies system! 'bin/yarn' + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' diff --git a/playbook-website/config/application.rb b/playbook-website/config/application.rb index 786e521774..ccfb275613 100644 --- a/playbook-website/config/application.rb +++ b/playbook-website/config/application.rb @@ -6,7 +6,7 @@ # Pick the frameworks you want: require "active_model/railtie" require "active_job/railtie" -# require "active_record/railtie" +require "active_record/railtie" # require "active_storage/engine" require "action_controller/railtie" # require "action_mailer/railtie" @@ -38,6 +38,10 @@ class Application < Rails::Application # config.eager_load_paths << Rails.root.join("extras") # Don't generate system test files. + config.generators.system_tests = nil + + openai_api_key = ENV["OPENAI_API_KEY"] + Rails.application.config.openai_api_key = openai_api_key end end diff --git a/playbook-website/config/database.yml b/playbook-website/config/database.yml new file mode 100644 index 0000000000..a5273910b1 --- /dev/null +++ b/playbook-website/config/database.yml @@ -0,0 +1,27 @@ +base: &base + adapter: mysql2 + pool: <%= ENV['DATABASE_POOL'] || ENV['RAILS_MAX_THREADS'] || 5 %> + timeout: <%= ENV['DATABASE_TIMEOUT'] || 5000 %> + username: <%= ENV['DATABASE_USER'] || "root" %> + password: <%= ENV['DATABASE_PASSWORD'] || "password" %> + host: <%= ENV['DATABASE_HOST'] || "db" %> + database: <%= ENV['DATABASE_NAME'] || "playbook_website" %> + advisory_locks: <%= ENV['DATABASE_ADVISORY_LOCKS'] || false %> + +development: + <<: *base + database: playbook_website_dev + +test: + <<: *base + database: playbook_website_test + username: <%= ENV['DATABASE_USER'] || "root" %> + +review: + <<: *base + +staging: + <<: *base + +production: + <<: *base diff --git a/playbook-website/config/deploy/production/secrets.yaml b/playbook-website/config/deploy/production/secrets.yaml index 8d8b1dd44c..f11748c1aa 100644 --- a/playbook-website/config/deploy/production/secrets.yaml +++ b/playbook-website/config/deploy/production/secrets.yaml @@ -1,23 +1,42 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:utBa4kM3fNPxfLSDALiJvOz6KVlzSefm/K3xDLrKQYypDY2TbreTY5QpeOH6l49D9zMD5i8XGwn3p9f6OEDg4uwzWLSUw0c6U7EcQkrxmRpvOLiOKz2SX8B3xWv3rdLwNs50LHINvjQcjdJviAo9R/oxLxEs6Ds/snSxYXbKQcs=,iv:p0zTY4f3gXqTLJRSjdhX+WsO2/eA6d/rh3S38O4XvuE=,tag:QjENfqpSSZ4hlvvoAuzRJA==,type:str] - sentry_dsn: ENC[AES256_GCM,data:p1TU1BvZqZyqo3WPVhGQKqT5IB9zsdA/LkEltdp2zNir3Xub7+yKWg5PXVhjCkDii90MPXSEd08zZYUHZQsTdWI6A/f/jMj1DepJhhPq7H8hpQ==,iv:NHG0oirvwkU2dRh9K0eyEjxyA8WAXRXt9HCGyOmKkrg=,tag:kDJqiCE+n5XoSJMVYqbFBQ==,type:str] + secretKeyBase: ENC[AES256_GCM,data:pTc2zofEnukd275BCaIpHOM5LokYsaYdgyGPHH6tggclP8Fj2S3/s7UREfDDuNZdAWAelmfWj3AY6xhtmdNRXt0F1UDvrNw0aCDAUqQK/jfHvgCtPQ2ncdZdpoUeEzmTaHUu3JVCphrCBFWxYrSvQg5qfx/docSiGR/PtaWTgOA=,iv:NwUXrRidkh5gT0E90h8CH9qtddboQnd2uvQfPXVkfM0=,tag:vJ5vPuSFeiRc+kDX5tY3Jg==,type:str] + sentry_dsn: ENC[AES256_GCM,data:pnb9JEfwZ0wCe+eh1CChiKiYAyyMeRtMvJEy9HyY9HFyleFB336Q6nVFx1D74LSxgNbrZiBqg/aA1ZysYCIDRciN6LOMlFAn5AAbMhpYcdNbLQ==,iv:j8l3ISTxrLVF21VRTDkqcmxeMt8jPrZDzDW9rHZ84lc=,tag:eMnHnxYQ+uAIhba0bDNuAA==,type:str] + open_ai: ENC[AES256_GCM,data:He7ruscMUMlif05pZhA7f0/rjVt1oG8qzJKGXO6SxHVKZ5t062dD3covgJAGqjCh9aH4wcjCS2xyhBNJWbqjcLhrYsLHuZB4URLfA1CyhJa+maReOCOH88vTutFdIYY=,iv:cZBzbIWxtp+sq3mcE+YGeHeALlSADsU7uXgmKgbmQc8=,tag:a09iJDiJRQ5ImhndwPeYkA==,type:str] +database: + passwords: + root: ENC[AES256_GCM,data:nDfX3v48uA4GelHiENJK72WR4QaHfNM=,iv:1pQEajcbP6Se9TUIjM9FChsGS02DWSrObHsC1Y+srbI=,tag:FIcrACADrNiXWKW0LmKbaQ==,type:str] + proxyadmin: ENC[AES256_GCM,data:wJocaatR/RSxgVJkMSPvP1qNlOIAZBc=,iv:yDwiFOujQAUvu50A4AcZAhwIZ45oVbB+ov92+wzC2eI=,tag:h3YnYRlzmjjU7eJIewSh5A==,type:str] + xtrabackup: ENC[AES256_GCM,data:sHdJ8nIBvwDh+oSrTAg7DageobzjXhs=,iv:ITkA5erLjdD04ikNDiIHo+VNQ4C2qJ0ACwuf83LThQ0=,tag:Nt1w8xeDAgzlcuSzZTMV3Q==,type:str] + clustercheck: ENC[AES256_GCM,data:0HhHODIwTtarziG6UlNWPSmgSYXbYVk=,iv:LKJ1uypr3QBbDWoRKTpHS4cw16KtJDaLPT3YIvytIbo=,tag:yEIXVSQNpz6l5c4igqeTcA==,type:str] + monitor: ENC[AES256_GCM,data:FCgUZ+0LRGboG8RObAttvqaZ0I5r474=,iv:K0crq4vV4kgFJ13z6orMKai0v1nW4iblCgskgzsX1mQ=,tag:JyBPNRAgiySgss2AFQ6LYg==,type:str] + pmmserver: ENC[AES256_GCM,data:5uIvdwcHvc08j4nyI3Dz9uef+omXCjg=,iv:QWUPM/beeqmqo0Sdw44BTACnzVFOmSH7dfPVNLFdaqg=,tag:Iq5ZCD9Wafglanhwd1Vo6A==,type:str] + operator: ENC[AES256_GCM,data:VchIqmam5gyZh3a9QU15m7fJK6meJ1w=,iv:kk2UvLMiu7Hir0AfBuH0CA348LhWCmvnkvhJzMqqpJI=,tag:t9syBAwmnS4s95X97Ne/Ug==,type:str] + replication: ENC[AES256_GCM,data:RCkm/S9hsu8ot7GExCXD1JXOdPzUyis=,iv:2rFI0hjihBvbtId7jLXs4IqS/H/KttN+FeWux/bfnFY=,tag:BvCsay3WONt2l+ZGgrfhxA==,type:str] + webapp: ENC[AES256_GCM,data:AiSQqvWKYklNkIw9vxz2HMBsR6NIVXU=,iv:wJxILj6WcWIXvsmTm6QpfAMVhtcp83u9F8BUZs/t1TQ=,tag:pJGCBOTXuy3mfl2yDK5qBw==,type:str] + migrator: ENC[AES256_GCM,data:eEdeN7i0gH8tTSxVq4BLH0BvTy5s+Qs=,iv:Fowkiy9rc+3x6pjGUC2wsY44zNHIUQGkriP/0FDQkjs=,tag:pDtxnA1ymaQRCNi3hpCg2Q==,type:str] + webapp_readonly: ENC[AES256_GCM,data:sc3vdWirNtrBls+8gArufmYD+YxhoIY=,iv:KROKBT1qwZVf6zQ3a0N51I5mbL74g+gucSXJtqbgGDk=,tag:E8XRjcfsXcPbCyuc6MlmIA==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c - created_at: "2023-09-12T17:54:03Z" - enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgGYWcyxf9B5fC0BIXbcdcWHAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMTtG/4IC1DRbS0sO0AgEQgDtkAetmvS1Gc4T4SBgXkXzEtZ11EIGuPKbxG4IHpv/Lf1taPKmf02QDhxEiMWCc57jkeWwCSLapvSTIQQ== + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgE1qwTrUkCGbpiPQPZ2Wy+lAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM5uYzVUOAppdO0PKyAgEQgDtfwSqTGPBcAQB/DMjvu5ePLTHSR+Yb8KhkFICf598s5H4VonhIO7zQROhfUurkPSXK9omJ2WMGuULDiQ== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:54:03Z" - enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgEAsmw6mIADb67KveUG6MUfAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMkfv8wA9Ec5IztTyEAgEQgDuXn1EN5zG2dP7pJpU4nE/KiJ1krq+UizveI0mSylzpKmRz0BFPtpj6kNv/pANo+YwCNKm5uVOGSkBfRw== + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgEzvPVSKC0gdgIl7GsNe51uAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMJfl/u6aJQu7MHZ5XAgEQgDu7iEdG6TL87qMoOGrGyEK1jcRq12uyCzOyAGhqkcE7XaWFuqP6ukaU2J12ZHVqAGHQNyhdqGN3sGQSOw== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/405f40cd-a18a-40ed-bf2f-a0e7816a9a5c + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:47Z" + enc: AQICAHgujLkoWj/2YBLvu57MUrNOqZOY5uV5l5pH7MV/vzKMBgF0x/5dkX78lPXUwfrdoJ7JAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMX2NSksmyhVS1j4q5AgEQgDsx23n3n0AtKWp7logPrzOrVwBMD8zhzC3QJyV6iDk7ia4Wk0OubJd84MPo4thMo1HPd0Xjhy4oXBTjUw== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:35:17Z" - mac: ENC[AES256_GCM,data:GBtmDnvvc+KwYkBML0M8+e02FgpfbY68FfCGSHxMUuAvjPYLHLZVA99pMtsKsFWdSiwRsZLcU6xoJm3y9YRLSGSJ0/7NzdVM5386tuSSm+RdqdcYNYU9hrcf1XhmMDT/enyjYsUP9p5f6sAPorQgj9VHv/1yV6ppZ5SRfrNr8v4=,iv:l7Wt0uqtrEeIAa9CWB7HPpnPJPPbYoAYzD7jIzw3/ao=,tag:ooQCNoR0QdBYsv/tOiLRqA==,type:str] + lastmodified: "2024-10-09T16:27:54Z" + mac: ENC[AES256_GCM,data:XZUMDOE+3t8dqAGGi1kZa7JCeC4yHKtBLKMAptq9rDfVqu+j8jjQpN6GuoHarhCSNmo14gOeN619YwSDpowgqY1WTv4LSRMBy2/zZfyd+dIfAnDpvIOMAB2nFj6QoAzRRZjR7j54/oXLJUaySyVB6Wfe8gz4JXBwkaXS2qKDr6Q=,iv:Q05Ig42Jcdx8TOJImzv2OVIRhkKAloE+GSPCKP/OC0s=,tag:Ib3otL+28qidtoYGWqXxYA==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/playbook-website/config/deploy/prs/secrets.yaml b/playbook-website/config/deploy/prs/secrets.yaml index 291e953d37..44cb2f815e 100644 --- a/playbook-website/config/deploy/prs/secrets.yaml +++ b/playbook-website/config/deploy/prs/secrets.yaml @@ -1,23 +1,42 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:ydJ6vKcPVMp9SxRCxgKH3AgAX5JjeoGn7fDKf77XkhODoTZnRHpkyiAezdhFhakCajJFHmaOttWytEVJiMg9DoW111f4AWsNQ+awVztblsCUmT1ZPgZtAGH8H76RKzl3adRy466E3FFoBzQf2FZhP5fdDRSmhzlHWUZdtrfDt/g=,iv:fDmiR4EzqRJ72pl4xLjG5Lv3bw6ijE7hqDHY9HqBcF8=,tag:vzpbilZlEFKlKrt9Rl9zvA==,type:str] - sentry_dsn: ENC[AES256_GCM,data:ufQrClZMvpS72OLyRqlO4jisNfnC793ASma61YUOssmQ2gUb/H/U5loD731e4a1fI6dADN66cjQbEnBGwNz0tU58dPMFlR5wSf+bs+JH2Jgn+w==,iv:m3oKFJrByerC7/DPezR0j98Jsolqp8IOm67trL0zJwc=,tag:JAGhc5ts+QBO+DDttXgSVw==,type:str] + secretKeyBase: ENC[AES256_GCM,data:tX9TTqw+teUH3ugGOXIfCMSE1fofM8YBfei2cXLB8Wbmsxp+bsoIgByMtgAbyHzov73P2PaJhQWWo/fG2W7Zj8T0gQ5BcK9mVFUkbt/+SmQpbbprLeIpauN4Gar2QWzyzJPIVEUopf3gdEERx3a+2rbMotJ4rB8Xfvv9OO4sOy4=,iv:H7nM7+ERPsNpehyuaAXSotrG8YCMhL1VSNAmFggs+Q4=,tag:eE+MuzvnCuQ6XKAKMMkrsA==,type:str] + sentry_dsn: ENC[AES256_GCM,data:9k5OGZqUcLote0IAJt7GJBsDxN3iJxoVtfPn9RKYfV1KZzNNWyeTZKC/PmjdjKotNbXzLPUSIPbtV8bk2DmttOwQr/r0xbnaTcRT2CWh/4XwVA==,iv:qML16zZMGBSO9nz7kSihlvn5uLj1s+RDSSqOb1jivGw=,tag:XvsrAuIAgAboayg2ZN1KZQ==,type:str] + open_ai: ENC[AES256_GCM,data:ewKDrouP0ZuMNAr19DvqBIzYdRInruWH7GxPX2ZH79DVJkT53qLFqzQMIBScqai40WXQmFLNicLnnbHVK+Xcv0tq/xBHtS7BANIwa5XJnWo56jYR3mOdbAcw+ZNFrks=,iv:c5veWRGGydflJ38TFf56ei9GI0tCnE24TLxJGFHscdE=,tag:rGLKDJesBs5JTNt83dsmoA==,type:str] +database: + passwords: + root: ENC[AES256_GCM,data:xIIpSFhtwuTyIkKtIEn0zIC+IrfJZeU=,iv:7hSo3KUZ8ujfVk/T2Hnv8v0IaanH370w7klGd9HRG9Q=,tag:U58Pf57vVHDqCDlS0jIlvQ==,type:str] + proxyadmin: ENC[AES256_GCM,data:nmOiIeM3EqnsJXQStcYOLDNJy3FB7h8=,iv:8uORWJHsVKm9c/v4AKXsD5Iwau9QLw3TTp0OOlYwwck=,tag:+tBl/LPwhcnqBrHm1OKF/w==,type:str] + xtrabackup: ENC[AES256_GCM,data:xsRzRtnZtfesmQChZrF4qa7UEeBv2FY=,iv:OVwhRcAIfRAwDBhriq3l5rJxG+sBP34czF6PmA7TUgo=,tag:ThL2czkqs1cJkHF1+Ac6tw==,type:str] + clustercheck: ENC[AES256_GCM,data:QMXMALsTWZ3nhPUQCBrSZ8EjX/9R4vQ=,iv:05OTLP+H5Zv0g9v9L3vnGSs/j8jinP9nAeKL/DcvU3E=,tag:WnWUzbXe6EujA8bBbQaVLw==,type:str] + monitor: ENC[AES256_GCM,data:QNI7y7ZIiIbGhx2Ou6LGTZcevsT5Pew=,iv:uwzYfjGJEVaT+Uya/zYNiZd4j2iv2D6bE1uQpikAYis=,tag:uQ+QEN4Jg9k0z1GIiclQ6g==,type:str] + pmmserver: ENC[AES256_GCM,data:aw+6klR8I9Is2z/7HP4w6RvVOR0Mzmk=,iv:TOli7mz6QgEEzHkqfKJqSbmgr8JCtnAvIQzHzg4nXU8=,tag:brsgeMPlI+JD2RLCzZ8+zw==,type:str] + operator: ENC[AES256_GCM,data:IkRgcdpfnm7r7rZz2a5092xBmNOBLKw=,iv:SJSvFzK0LhcpZRdC1mtB62BMd9G7BMxE8y9W/mvbdU0=,tag:81UCk/KCNpqljoeXLzr6uA==,type:str] + replication: ENC[AES256_GCM,data:ica6B3ekPiaY2rc/Xs0K8D4v9NIUows=,iv:yiduzxuZJC28Ci96a6IgCxX8kG/xojEVLZfc6B+bcU4=,tag:BCvR+NANOM/2B5uyA5/5YQ==,type:str] + webapp: ENC[AES256_GCM,data:IAe84IbWb3c/QYI7doZ/bjQ3e9X6NXQ=,iv:Wp3mAAvSQ/Af9fP633Vr0EgCImGP7eAjN/AcIHsxS3w=,tag:UcECml6q4G0hyYbcOnIQsg==,type:str] + migrator: ENC[AES256_GCM,data:+manxHLfyLJwaiS/pLFBnTyhAUzna+0=,iv:jJPHbO0sBQP2mu55uomoxRt2DGkAOyeAl/KFKjEnA7g=,tag:8MAyL3lkQTsVwoBw/wNuVA==,type:str] + webapp_readonly: ENC[AES256_GCM,data:QtmYwMEZWWpGsG0GRtaY0ldS2L0vDYA=,iv:mZ/RtJETHVUZBjXWxa3Yrr0Sf5m3u3ao1FZDSHcNVpo=,tag:lgW4ckJy9wDsPEdt9nTK/Q==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 - created_at: "2023-09-12T17:53:37Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgHC1WOVOEbBuNn+YEN+OKiFAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMGQ04cBdxcLtN+PMRAgEQgDvbxveKjEU0mIfKAh7I3yjnnrUSDl1UymkOJYlz4MaKw+MNze2Q1zIX0FzkwSFLFcW/7NguN5/FQ1thZw== + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFd2W8ic6/TkyA8p8wcv5IGAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMPj5cEbt+ndJRHdEMAgEQgDuThOL+h28iSGmeB/TcKDZZ61OXEzoWxD3Io9pC63eWkHhJjcQJO/Cklequ8L43LZh9ISXUhWBdm7Nlbg== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:53:37Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgHs+bIDMZJ9K5YoWkyxqXpxAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMv1q+HlWt0cTzINo0AgEQgDuDojVpcg6NuTpVs8Q2ojw9MnIGfuQ4OI14tvSE78863Esk0Tvgno/oqxDjSfWqIV0kmV/h5hapIm/C3w== + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFTilLgQ0cpyV0/bCSqiidWAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMHJQrlVXmV3auRC97AgEQgDuvJdg6V6f/H15ui2BVgII+a7sUe4I3Suwcjy/Jl4o9+E7GKETkWm8RLpWdwI0yP65GmjLRkmoqnGERlg== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:23Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFNq+jUXbUie4tTLRqUg0niAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMBG8uY6UVCCS+9t0lAgEQgDsURce9Oc/zXJFhLivk3rJP4KZRsUrc5qB/6z43P0c3IXvNN3PtTLl+/yPGpZ1zmukv4aBBU0GuVOh0Bw== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:37:14Z" - mac: ENC[AES256_GCM,data:V4LLnTmHRV39R4VNTsQ5Qc2xjjsE0LuSGYJu2W+ERJkwwCcQe1B874xgSttSQs4aiXybjedqLiveHsr42NGuTvjKDXDxXChffp0bdp/eHB22Av8aONEz9Nbqwr4uBI8SvRPRWL0cT5oFVYR18/cVcxYUYhwreCR5hkg0x+xTF4o=,iv:KsKq/7TqDDCKeIUWW+NGJNNypdiBxth8yeIKMOLjq2k=,tag:i9++nJGiPipwXj7H1dimLQ==,type:str] + lastmodified: "2024-10-09T16:20:19Z" + mac: ENC[AES256_GCM,data:qG/8X0ZyqRkCAaQvxqghiYPmPsqhRoq7WAeh8MvkzfAdYWuEp3UIcOqG/CYs77A9770537CYearWkuskXRvYxDyTHdMasqyvayxxl3INwkZgqWWY9fcePLS76FL0nXq8jKz4rQg39148PgeYbYEQuz72wq6CmlXRLj6jg1cyAJw=,iv:24lVuaQcM+GyKg8A8+ek462zm9rXj/D7B5tli6hxJF4=,tag:mX/8OUwt6Mb7dn/Tmx8VkA==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/playbook-website/config/deploy/staging/secrets.yaml b/playbook-website/config/deploy/staging/secrets.yaml index ae5aa11956..b364148685 100644 --- a/playbook-website/config/deploy/staging/secrets.yaml +++ b/playbook-website/config/deploy/staging/secrets.yaml @@ -1,23 +1,42 @@ appConfig: - secretKeyBase: ENC[AES256_GCM,data:T9rr+yNvQIiOKYgGO8p2MM+iqDius270ooqlHTwNDMMXrApWsybNBLIy88tupU/FDm01/FI7ggUZXKbO7WJvBFyxnY/nAQFbw5Xv58lsEGjIrIWAfwANd96NuCOjahnG6PRnICyLbxenQfK7gjXzdxNzRWeRhGFHp/hj29JVx9k=,iv:wB2eCtbFsCXXrPgCOUj2abpBdgsq0iOxL8AiN7wL/Hs=,tag:B3ZaXCT2IlcLmKhR/XrQ4Q==,type:str] - sentry_dsn: ENC[AES256_GCM,data:mZ68vE4jCVVi+xrOBnhldUwvObAIdo4HzAXg/r7J+x0BdYTH7SMQWL70mNdVyjbW+KA6aYq6VY6kOIa4FgcUfgBP6cmNDoE4SwTSosUTBm0r3Q==,iv:vqtZKpp7ALuQJYrkDpgn+sjvmEMq9tzBVlc/2hcj8qE=,tag:JByaTyGLemz1Z0wuqzjVBg==,type:str] + secretKeyBase: ENC[AES256_GCM,data:kOsJ3LgAPCPZ4Vdm/EBFksqneV7tSeFqKrI965UEqkZ7EQ7n/STuE0vte90/oMF1C0eBYzwHEsnT/1kPirKn5/Rz4np2M1jQIvucTfk7QMd76fueIS2lXtLw155rYL8iEh5JyxviOJNfL93U3FiySo1Fa8+1FoOqvlnHa4+/Uoo=,iv:pjEQHF499TYjNEe+O95G3VgqpAfUyJPtVws8nl3zw4k=,tag:38L8NgqyGQu8ueIBS8NcmQ==,type:str] + sentry_dsn: ENC[AES256_GCM,data:8vuODktXtDvYOUKS5XR7JJ0t9sX0urMWdmIeu66wwvqfGNJogacXj097PqM4MHLIsl0DICJen/2xA+uvUSV+ZVqJH7gCqeY3fH4XwMhE8dKZZA==,iv:5NYs+MiDZiR41cuazIFPXC3IKJsJgbl4eoRvTym0dTo=,tag:XP1rGTjYcl5nMUScJ/3w/A==,type:str] + open_ai: ENC[AES256_GCM,data:XX6uvmMSXUvaZFcLR4o5zOmZaQDodlRP4tpZ9Od73pnk4sA6VEaPoqBk3AHr0Ret5KD0WhE2TLnMt2UVEv4+xeRcct0lJtN/n+xXODXsGXoRYRRF7hiYGTJJewT5glY=,iv:tOWyS9Ep+TmP4MtY6LEez9XcD/mWERVV2sbEXQU8rrc=,tag:v3sv3+FzybiKijYY7ncqYg==,type:str] +database: + passwords: + root: ENC[AES256_GCM,data:cRdz9hWx4RmdMmOir00dBX7J0TE3hBg=,iv:DZ5KDjhIzLWT8SzKtoawhVhv6zvK9z9zG/izg70X/04=,tag:Ap7n0oEIgu8NfmJkTJKXrQ==,type:str] + proxyadmin: ENC[AES256_GCM,data:dDeprOtOPGV6NapCoaOcl5L8sgcOSCE=,iv:wXWKJ8pArDVCPauU1kNvspaeLBiqbLjdEXXt0t5sAhg=,tag:sziTzyCsrVihiqH87obr+g==,type:str] + xtrabackup: ENC[AES256_GCM,data:YQSZCfvn2zM47DlJ11dMfXwKRNtbLCk=,iv:U9TvTWE2f6z1FB3xBlPSrJrgBeneZosDZI80/x19LzA=,tag:ZzkCs1nqbqjCCRy+KpNZmQ==,type:str] + clustercheck: ENC[AES256_GCM,data:UBCBw802YZnqtZk6Sck87RTNvYL8XWI=,iv:ZslmsPbCnhNcdhUEdhAAk0SCRQoatQNHCaw0D54u2D8=,tag:Xd/HfrDZMN6umy+HJb41tw==,type:str] + monitor: ENC[AES256_GCM,data:e6NjQgt9E8SHiXNGMAi+Z82WIKT1gqM=,iv:ynE1Jrz7oQqqmVNNzqo9Trkk52OrEd56HOQfpLBeY/M=,tag:DYyPpJiweD+z89kkgIDebA==,type:str] + pmmserver: ENC[AES256_GCM,data:92h/4RrrR8K7gXSXopZPA+qWdwoLCkc=,iv:C8tABUx7iJUKagazJt3O9Ow/MHRxgUE3xbbR8MlcT5I=,tag:4aDmKJKj+JJpYAASzeeLUw==,type:str] + operator: ENC[AES256_GCM,data:9Qb+nW2jEPHOyfHOjZQYvoCIRfs93rU=,iv:rOqCYY6nrEmYGsIHYtw+xRvPn6TRQCcNB/EUTw4WtGM=,tag:NfcN9ZhJ6gBXANxDPP3fnw==,type:str] + replication: ENC[AES256_GCM,data:tYyhKwh17Eh5Cl+OtcUhB12Isnew6vg=,iv:O84q03Yz3bLTmaMiBi9HLk2grbMmAIFehOjTn1Iyl1A=,tag:VIxzLLEAHNOnTDoUBtYJ2A==,type:str] + webapp: ENC[AES256_GCM,data:8xcWhzx5M8U/7CKJpwSJLUzTt1kFkaI=,iv:UK0Y9+rgRJlcSt0/Nsh+uYE2seO+XGl4+vHcpwd8wDw=,tag:7CimZ310KliXTI1D9aJB5Q==,type:str] + migrator: ENC[AES256_GCM,data:ns2KCG6ivLFldBuPfB0yO6odnBRSUfg=,iv:G3ZHzpb/3c+IuDD3NXXEwNozXMgqB4k1d3+nwl9FXtI=,tag:bIqe4CCswBLXhNnHOWCCBA==,type:str] + webapp_readonly: ENC[AES256_GCM,data:A+wxbSY08SYgzRpWBSsfSNyWkm/1NcQ=,iv:d6mTiGOPCAFhCftyYunIgQJqBKCN3fmI5Eiz8HPl528=,tag:hanVevwzajabijbgtqsPmw==,type:str] sops: kms: - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 - created_at: "2023-09-12T17:53:51Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgFAHaaoyZIhorapEiPc+qICAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMX8BKJ5TiL9Pi1CvvAgEQgDuKgTNkmLECrXQsOPMINqkAT5HzdOqKY/7QT3wS6+EcgVF/kVSD++0yceqYZY6urOFZs9AcPoffFghlGw== + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgF5FR6Zmbb2LmX/Ok1UTBQ1AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMGDGnVWMmvb8v8r8JAgEQgDt6so3JsL9WSkXPw1VsgKL0K1S5G0J1of4jczsf7uJp4lCWyzYzN3iK0aD/ZMDQfq/CHkAo3da9NodoZg== aws_profile: "" - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 role: arn:aws:iam::205083374951:role/playbook-admins - created_at: "2023-09-12T17:53:51Z" - enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgEkSuHel9uABP7kWsy3YHNXAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMjUBIc+aCDRrVDqTOAgEQgDu8ZcejevO0RMIsndJbpN47yZ/kCIhfsLGv7YQ2CamhWhAP5b4V3Bz7aY+bFkXEmsumaSbQB2j+e+vL5Q== + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgE7Au4PrldyBvUsxJu2XM0WAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM6DF1DaKLw/6qWGHAAgEQgDsjGaEv60fzlf3o7oHNGPanagVIHk7eNWBzpgjAH256rJoC7mXeSn2qFEnRjLYOA1/4LeLgXD3B0+bHAQ== + aws_profile: "" + - arn: arn:aws:kms:us-east-1:205083374951:key/d46e780d-ee0a-4a7d-b049-0443e1b91d41 + role: arn:aws:iam::205083374951:role/forever-people + created_at: "2024-10-09T14:15:36Z" + enc: AQICAHjD4+PGrfNOURZm5p39Rxa0oVLr81xX3U8gvQwj2QeLTgG3HMs993vA2J7ym2Gaf0jXAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMg1PVXgdv92RavP2NAgEQgDvx4LuwYEmnAGtCR1j8B0DhkL0sIowwUrJgQKcpf3453ixUzxs+vnILsUajTxm3RVpWxoPV1OjNxRfLew== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2023-09-12T19:36:52Z" - mac: ENC[AES256_GCM,data:7fTUBII1iCtdGe4QZFkXw5j+P2vUhRJF6Ny4CxeTzZctRi+upYME23maBPpzEFjKU0J9S1jLl1XFodOSZe5Sc3E1UEOBhs1/J7rvuEhWZC5QkKXVrq/UzNkDPO9eTQmE7XA6/w0TLx++xcy9iSHIsrLubidAU3EO5R4eaiaT3gw=,iv:zfDYlma2ltcRHWw47rn/kSJjJX3s95Xg5kvBM96Koz4=,tag:GiHRN/m9Q6GAITAFmPZobg==,type:str] + lastmodified: "2024-10-09T16:26:13Z" + mac: ENC[AES256_GCM,data:4oDOX4judp5IwSWIwz0TjModHhgYdIDLlij0m4S7qVeoluGaDSdn8Xx5bhaTOapYxzk1xIzrCuRhAeMFnieBhJ4l8V9XBGxRYDOobYPCJzXuWJFF80T+QoOe2JyjMB84LjtA6qUTn4cvul/tW7hMQ7ySKVr94oHpNW6lx24N2JM=,iv:EDoIKM6S044f764iz73LJBG+NRBqyy+ev6lCK+12cHg=,tag:pR8fl4g7ppG14Mym05P2uQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/playbook-website/config/deploy/templates/deployment.yaml.erb b/playbook-website/config/deploy/templates/deployment.yaml.erb index 343681a9bf..73a80b7732 100644 --- a/playbook-website/config/deploy/templates/deployment.yaml.erb +++ b/playbook-website/config/deploy/templates/deployment.yaml.erb @@ -1,3 +1,6 @@ +<% + database_user = database["application_user"] +%> apiVersion: apps/v1 kind: Deployment metadata: @@ -50,6 +53,20 @@ spec: secretKeyRef: name: playbook key: sentry-dsn + - name: OPEN_AI_KEY + valueFrom: + secretKeyRef: + name: playbook + key: open-ai + - name: DATABASE_HOST + value: database-haproxy + - name: DATABASE_USER + value: <%= database_user %> + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: database-secrets + key: <%= database_user %> livenessProbe: httpGet: path: /health_check/site @@ -72,4 +89,4 @@ spec: "memory" => "256Mi", "ephemeral-storage" => "100Mi" } - } %> + } %> diff --git a/playbook-website/config/deploy/templates/migration-hook.yaml.erb b/playbook-website/config/deploy/templates/migration-hook.yaml.erb new file mode 100644 index 0000000000..33bcae85c1 --- /dev/null +++ b/playbook-website/config/deploy/templates/migration-hook.yaml.erb @@ -0,0 +1,57 @@ +<% + migrate_user = database["allprivs_user"] +%> +apiVersion: v1 +kind: Pod +metadata: + name: "db-migrate-<%= deployment_id.downcase %>" + labels: + app: playbook + app.kubernetes.io/name: playbook + app.kubernetes.io/instance: playbook-<%= environment %> + app.kubernetes.io/version: <%= image_tag %> + app.kubernetes.io/component: db-migrate + app.kubernetes.io/part-of: playbook + app.kubernetes.io/managed-by: krane + annotations: + krane.shopify.io/timeout-override: 30m +spec: + restartPolicy: Never + shareProcessNamespace: true + containers: + - name: db-migrate + image: "image-registry.powerapp.cloud/playbook/playbook:<%= image_tag %>" + workingDir: /home/app/src/playbook-website + command: + - bin/rails + args: + - db:prepare + env: + - name: SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: playbook + key: secret-key-base + - name: RACK_ENV + value: <%= appConfig["rackEnv"] %> + - name: RAILS_ENV + value: <%= appConfig["railsEnv"] %> + - name: RAILS_LOG_TO_STDOUT + value: "true" + - name: DATABASE_HOST + value: database-haproxy + - name: DATABASE_USER + value: "<%= migrate_user %>" + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: database-secrets + key: "<%= migrate_user %>" + resources: + limits: + memory: 700Mi + ephemeral-storage: 100Mi + requests: + cpu: 0.1 + memory: 500Mi + ephemeral-storage: 100Mi diff --git a/playbook-website/config/deploy/templates/operators/pxc-operator.yml b/playbook-website/config/deploy/templates/operators/pxc-operator.yml new file mode 100644 index 0000000000..e94a395d52 --- /dev/null +++ b/playbook-website/config/deploy/templates/operators/pxc-operator.yml @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: percona-xtradb-cluster-operator + labels: + app.kubernetes.io/component: operator + app.kubernetes.io/instance: percona-xtradb-cluster-operator + app.kubernetes.io/name: percona-xtradb-cluster-operator + app.kubernetes.io/part-of: percona-xtradb-cluster-operator +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: operator + app.kubernetes.io/instance: percona-xtradb-cluster-operator + app.kubernetes.io/name: percona-xtradb-cluster-operator + app.kubernetes.io/part-of: percona-xtradb-cluster-operator + strategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/component: operator + app.kubernetes.io/instance: percona-xtradb-cluster-operator + app.kubernetes.io/name: percona-xtradb-cluster-operator + app.kubernetes.io/part-of: percona-xtradb-cluster-operator + spec: + serviceAccountName: percona-xtradb-cluster-operator + containers: + - name: percona-xtradb-cluster-operator + image: percona/percona-xtradb-cluster-operator:1.15.0@sha256:6f7d8d4e472b8c4d166573cc7bb714bbb0fdf1535142b6138c62fdecbf881df9 + ports: + - containerPort: 8080 + name: metrics + protocol: TCP + command: + - percona-xtradb-cluster-operator + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "percona-xtradb-cluster-operator" + livenessProbe: + failureThreshold: 3 + httpGet: + path: /metrics + port: metrics + scheme: HTTP + resources: + requests: + cpu: "200m" + memory: "256Mi" + ephemeral-storage: 100Mi + limits: + memory: "256Mi" + ephemeral-storage: 100Mi diff --git a/playbook-website/config/deploy/templates/priority/mysql.yaml.erb b/playbook-website/config/deploy/templates/priority/mysql.yaml.erb new file mode 100644 index 0000000000..da8a0ada7e --- /dev/null +++ b/playbook-website/config/deploy/templates/priority/mysql.yaml.erb @@ -0,0 +1,120 @@ +<% + pxc_cluster_size = 3 +%> +--- +apiVersion: v1 +kind: Secret +metadata: + name: database-secrets + labels: + app.kubernetes.io/name: playbook + app.kubernetes.io/instance: playbook-<%= environment %> + app.kubernetes.io/version: <%= image_tag %> + app.kubernetes.io/component: database + app.kubernetes.io/part-of: priority-deploy + app.kubernetes.io/managed-by: krane +type: Opaque +data: + root: <%= Base64.encode64(database.dig("passwords", "root")) %> + proxyadmin: <%= Base64.encode64(database.dig("passwords", "proxyadmin")) %> + xtrabackup: <%= Base64.encode64(database.dig("passwords", "xtrabackup")) %> + clustercheck: <%= Base64.encode64(database.dig("passwords", "clustercheck")) %> + monitor: <%= Base64.encode64(database.dig("passwords", "monitor")) %> + pmmserver: <%= Base64.encode64(database.dig("passwords", "pmmserver")) %> + operator: <%= Base64.encode64(database.dig("passwords", "operator")) %> + replication: <%= Base64.encode64(database.dig("passwords", "replication")) %> + webapp: <%= Base64.encode64(database.dig("passwords", "webapp")) %> + migrator: <%= Base64.encode64(database.dig("passwords", "migrator")) %> + webapp_readonly: <%= Base64.encode64(database.dig("passwords", "webapp_readonly")) %> +--- +apiVersion: pxc.percona.com/v1 +kind: PerconaXtraDBCluster +metadata: + name: database + labels: + app.kubernetes.io/name: playbook + app.kubernetes.io/instance: playbook-<%= environment %> + app.kubernetes.io/component: database + app.kubernetes.io/part-of: priority-deploy + app.kubernetes.io/managed-by: krane + annotations: + krane.shopify.io/timeout-override: 400s + finalizers: [] +spec: + crVersion: 1.15.0 + secretsName: database-secrets + allowUnsafeConfigurations: false + pause: false + updateStrategy: SmartUpdate + pxc: + size: <%= pxc_cluster_size %> + image: percona/percona-xtradb-cluster:8.0.36@sha256:b5cc4034ccfb0186d6a734cb749ae17f013b027e9e64746b2c876e8beef379b3 + autoRecovery: true + resources: + requests: + memory: 200M + cpu: "0.2" + ephemeral-storage: 500Mi + limits: + memory: 1G + ephemeral-storage: 500Mi + affinity: + antiAffinityTopologyKey: "kubernetes.io/hostname" + podDisruptionBudget: + maxUnavailable: 1 + volumeSpec: + persistentVolumeClaim: + storageClassName: <%= database["storageClass"] %> + accessModes: [ "ReadWriteOnce" ] + resources: + requests: + storage: <%= database["volumeSize"] %> + gracePeriod: 600 + configuration: | + [mysqld] + pxc_strict_mode=MASTER + log_bin_trust_function_creators=ON + haproxy: + enabled: true + size: 3 + image: percona/haproxy:2.8.5@sha256:941f3bd0977bff9145e904bf8f8298a1a024d7f03152edaea0cf65fd1c137340 + resources: + requests: + memory: 100M + cpu: 100m + ephemeral-storage: 500Mi + limits: + memory: 200M + ephemeral-storage: 500Mi + sidecarResources: + requests: + memory: 50M + cpu: 100m + ephemeral-storage: 250Mi + limits: + memory: 100M + ephemeral-storage: 250Mi + affinity: + antiAffinityTopologyKey: "kubernetes.io/hostname" + podDisruptionBudget: + maxUnavailable: 1 + gracePeriod: 30 + annotations: + fluentbit.io/exclude: "true" + pmm: + enabled: false +<% if database["users"] %> +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-users-playbook + labels: + app.kubernetes.io/component: database + app.kubernetes.io/name: playbook + app.kubernetes.io/instance: playbook-<%= environment %> + app.kubernetes.io/part-of: priority-deploy + app.kubernetes.io/managed-by: krane +data: + users: '<%= database["users"].to_json %>' +<% end %> diff --git a/playbook-website/config/deploy/templates/secret.yaml.erb b/playbook-website/config/deploy/templates/secret.yaml.erb index 77b3f039ca..1110c51c2f 100644 --- a/playbook-website/config/deploy/templates/secret.yaml.erb +++ b/playbook-website/config/deploy/templates/secret.yaml.erb @@ -9,3 +9,4 @@ type: Opaque data: secret-key-base: <%= Base64.encode64(appConfig.fetch("secretKeyBase")).gsub("\n", "").strip %> sentry-dsn: <%= Base64.encode64(appConfig.fetch("sentry_dsn")).gsub("\n", "").strip %> + open-ai: <%= Base64.encode64(appConfig.fetch("open_ai")).gsub("\n", "").strip %> diff --git a/playbook-website/config/deploy/templates/shell.yaml.erb b/playbook-website/config/deploy/templates/shell.yaml.erb index 7f8f4d4717..6c4344a74b 100644 --- a/playbook-website/config/deploy/templates/shell.yaml.erb +++ b/playbook-website/config/deploy/templates/shell.yaml.erb @@ -41,6 +41,11 @@ template: value: "true" - name: RAILS_LOG_TO_STDOUT value: "true" + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: playbook + key: open-ai resources: limits: cpu: 0.3 diff --git a/playbook-website/config/deploy/values.yaml b/playbook-website/config/deploy/values.yaml index 6eb52762f9..87d4b76bcc 100644 --- a/playbook-website/config/deploy/values.yaml +++ b/playbook-website/config/deploy/values.yaml @@ -1,3 +1,22 @@ appConfig: rackEnv: "production" railsEnv: "production" + +database: + users: + - name: webapp + dbs: + - playbook_website + grant: 'SELECT, INSERT, UPDATE, DELETE' + - name: migrator + dbs: + - playbook_website + grant: 'ALL' + - name: webapp_readonly + dbs: + - playbook_website + grant: 'SELECT' + allprivs_user: migrator + application_user: webapp + storageClass: staging-performance + volumeSize: 1Gi diff --git a/playbook-website/config/initializers/openai.rb b/playbook-website/config/initializers/openai.rb new file mode 100644 index 0000000000..457e6095b6 --- /dev/null +++ b/playbook-website/config/initializers/openai.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +Rails.application.config.openai_api_key = ENV["OPENAI_API_KEY"] diff --git a/playbook-website/db/schema.rb b/playbook-website/db/schema.rb new file mode 100644 index 0000000000..a3b0495167 --- /dev/null +++ b/playbook-website/db/schema.rb @@ -0,0 +1,14 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 0) do +end diff --git a/playbook/CHANGELOG.md b/playbook/CHANGELOG.md index 75c3509657..7080805df6 100644 --- a/playbook/CHANGELOG.md +++ b/playbook/CHANGELOG.md @@ -1,3 +1,40 @@ +# 🐝 Improved Usability, Flexibility, Customization and More! 🐝 +##### October 1, 2024 + +![1450](https://github.com/user-attachments/assets/04a54556-9148-4a9e-8ff2-4902c59aef18) + +We’re back! 14.5.0 is live in Nitro and is packed with improvements, bug fixes and more! +This release focuses on continued improvements to usability, customization, and visual consistency! + +[14.5.0](https://github.com/powerhome/playbook/tree/14.5.0) full list of changes: + +**Kit Enhancements:** +- Enable Kits Instead of Text for Radio Label - React Only [\#3691](https://github.com/powerhome/playbook/pull/3691) ([nickamantia](https://github.com/nickamantia)) +- Remove marginBottom from Typeahead Kit [\#3680](https://github.com/powerhome/playbook/pull/3680) ([elisashapiro](https://github.com/elisashapiro)) + + +**Fixed Bugs:** +- Fixing Minimizing the Dropdown When Clicking Outside [\#3688](https://github.com/powerhome/playbook/pull/3688) ([carloslimasd](https://github.com/carloslimasd)) +- Make Dialog Close 'x' Icons Same Size [\#3706](https://github.com/powerhome/playbook/pull/3706) ([kangaree](https://github.com/kangaree)) + + + +**Improvements:** +- Bump Highcharts Packages to Latest [\#3708](https://github.com/powerhome/playbook/pull/3708) ([kangaree](https://github.com/kangaree)) +- Remove FA from the Playbook Website [\#3687](https://github.com/powerhome/playbook/pull/3687) ([skduncan](https://github.com/skduncan)) +- Add Comment When Merge to Master for RC [\#3727](https://github.com/powerhome/playbook/pull/3727) ([markdoeswork](https://github.com/markdoeswork)) +- Fix Spacing on Radio kit Children Doc Example [\#3730](https://github.com/powerhome/playbook/pull/3730) ([elisashapiro](https://github.com/elisashapiro)) +- Add Multiple Pagination Components Example and Dynamic Key Copy [\#3715](https://github.com/powerhome/playbook/pull/3715) ([elisashapiro](https://github.com/elisashapiro)) +- Bump to ci-kubed v8.6.0 [no-cache] [\#3639](https://github.com/powerhome/playbook/pull/3639) ([TeamTeaTime](https://github.com/TeamTeaTime)) +- Add Comment Script for RCs [\#3717](https://github.com/powerhome/playbook/pull/3717) ([markdoeswork](https://github.com/markdoeswork)) +- Use New Icons [\#3648](https://github.com/powerhome/playbook/pull/3648) ([markdoeswork](https://github.com/markdoeswork)) + +**New Kits:** +- Drawer Beta Kit [\#3686](https://github.com/powerhome/playbook/pull/3686) ([markdoeswork](https://github.com/markdoeswork)) + + +[Full Changelog](https://github.com/powerhome/playbook/compare/14.4.0...14.5.0) + # Spot Everyone at a Glance: Meet the New Multiple Users Component! ##### September 18, 2024 diff --git a/playbook/Gemfile.lock b/playbook/Gemfile.lock index 5faece6284..98fa51d2da 100644 --- a/playbook/Gemfile.lock +++ b/playbook/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - playbook_ui (14.5.0) + playbook_ui (14.6.0) actionpack (>= 5.2.4.5) actionview (>= 5.2.4.5) activesupport (>= 5.2.4.5) diff --git a/playbook/app/entrypoints/playbook-rails.js b/playbook/app/entrypoints/playbook-rails.js index 6c11e2a8a8..7769cb5fb9 100644 --- a/playbook/app/entrypoints/playbook-rails.js +++ b/playbook/app/entrypoints/playbook-rails.js @@ -1,5 +1,7 @@ // Forms import 'kits/pb_form/pb_form_validation' +import formHelper from 'kits/pb_form/formHelper' +window.formHelper = formHelper // Date Picker import datePickerHelper from 'kits/pb_date_picker/date_picker_helper' diff --git a/playbook/app/javascript/plugins.js b/playbook/app/javascript/plugins.js index d27f3cec4a..0ecdf5e1e6 100644 --- a/playbook/app/javascript/plugins.js +++ b/playbook/app/javascript/plugins.js @@ -6,3 +6,5 @@ export { default as PbTextarea } from '../pb_kits/playbook/pb_textarea' export { default as PbTooltip } from '../pb_kits/playbook/pb_tooltip' export { default as PbTypeahead } from '../pb_kits/playbook/pb_typeahead' export { default as dialogHelper } from '../pb_kits/playbook/pb_dialog/dialogHelper' +export { default as formHelper } from '../pb_kits/playbook/pb_form/formHelper' + diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx b/playbook/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx index 154676418d..52b0c7fcf4 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx @@ -91,7 +91,7 @@ const AdvancedTable = (props: AdvancedTableProps) => { const columnHelper = createColumnHelper() //Create cells for first columns - const createCellFunction = (cellAccessors: string[]) => { + const createCellFunction = (cellAccessors: string[], customRenderer?: (row: Row, value: any) => JSX.Element) => { const columnCells = ({ row, getValue, @@ -101,6 +101,11 @@ const AdvancedTable = (props: AdvancedTableProps) => { }) => { const rowData = row.original + // Use customRenderer if provided, otherwise default rendering + if (customRenderer) { + return customRenderer(row, getValue()) + } + switch (row.depth) { case 0: { return ( @@ -134,18 +139,31 @@ const AdvancedTable = (props: AdvancedTableProps) => { //Create column array in format needed by Tanstack const columns = columnDefinitions && - columnDefinitions.map((column) => { + columnDefinitions.map((column, index) => { // Define the base column structure const columnStructure = { ...columnHelper.accessor(column.accessor, { header: column.label, }), } - if (column.cellAccessors) { - columnStructure.cell = createCellFunction(column.cellAccessors) - } - return columnStructure - }) + + // Use the custom renderer if provided, EXCEPT for the first column + if (index !== 0) { + if (column.cellAccessors || column.customRenderer) { + columnStructure.cell = createCellFunction( + column.cellAccessors, + column.customRenderer + ) + } + } else { + // For the first column, apply createCellFunction without customRenderer + if (column.cellAccessors) { + columnStructure.cell = createCellFunction(column.cellAccessors) + } + } + + return columnStructure +}) //Syntax for sorting Array if we want to manage state ourselves const sorting = [ diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx new file mode 100644 index 0000000000..b1c5bad58d --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx @@ -0,0 +1,72 @@ +import React from "react" +import { AdvancedTable, Pill, Body, Flex, Detail, Caption } from "playbook-ui" +import MOCK_DATA from "./advanced_table_mock_data.json" + +const AdvancedTableCustomCell = (props) => { + const columnDefinitions = [ + { + accessor: "year", + label: "Year", + cellAccessors: ["quarter", "month", "day"], + + }, + { + accessor: "newEnrollments", + label: "New Enrollments", + customRenderer: (row, value) => ( + + ), + }, + { + accessor: "scheduledMeetings", + label: "Scheduled Meetings", + customRenderer: (row, value) => {value}, + }, + { + accessor: "attendanceRate", + label: "Attendance Rate", + customRenderer: (row, value) => ( + + + {row.original.graduatedStudents} + + ), + }, + { + accessor: "completedClasses", + label: "Completed Classes", + }, + { + accessor: "classCompletionRate", + label: "Class Completion Rate", + }, + { + accessor: "graduatedStudents", + label: "Graduated Students", + }, + ] + + return ( +
+ + + + +
+ ) +} + +export default AdvancedTableCustomCell diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md new file mode 100644 index 0000000000..5ad9e1c219 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md @@ -0,0 +1,5 @@ +The Advanced Table also allows for rendering custom components within individual Cells. To achieve this, you can make use of the optional `customRenderer` item within each columnDefinition. This function gives you access to the current Cell's value if you just want to use that with a custom Kit, but it also gives you access to the entire `row` object. The row object provides all data for the current row. To access the data, use `row.original` which is the entire data object for the current row. See the code snippet below for 3 separate use cases and how they were acheived. + +See [here](https://playbook.powerapp.cloud/kits/advanced_table/react#columnDefinitions) for more indepth information on columnDefinitions are how to use them. + +See [here](https://github.com/powerhome/playbook/tree/master/playbook/app/pb_kits/playbook/pb_advanced_table#readme) for the structure of the tableData used. \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml index aec491e8e9..87fb7abf3d 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/example.yml @@ -3,6 +3,7 @@ examples: - advanced_table_beta: Default (Required Props) - advanced_table_beta_subrow_headers: SubRow Headers - advanced_table_beta_sort: Enable Sorting + react: - advanced_table_default: Default (Required Props) - advanced_table_loading: Loading State @@ -15,3 +16,4 @@ examples: - advanced_table_table_props: Table Props - advanced_table_inline_row_loading: Inline Row Loading - advanced_table_responsive: Responsive Tables + - advanced_table_custom_cell: Custom Components for Cells diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/index.js b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/index.js index c67f4b9bbd..d7992a808a 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/docs/index.js @@ -9,3 +9,4 @@ export { default as AdvancedTableTableOptions } from './_advanced_table_table_op export { default as AdvancedTableTableProps } from './_advanced_table_table_props.jsx' export { default as AdvancedTableInlineRowLoading } from './_advanced_table_inline_row_loading.jsx' export { default as AdvancedTableResponsive } from './_advanced_table_responsive.jsx' +export { default as AdvancedTableCustomCell } from './_advanced_table_custom_cell.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js index fa6e2523bf..ec9c1a71b5 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/index.js +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/index.js @@ -13,9 +13,20 @@ export default class PbAdvancedTable extends PbEnhancedElement { get target() { return document.querySelector(CONTENT_SELECTOR.replace("id", this.element.id)) } + + static expandedRows = new Set() + static isCollapsing = false connect() { this.element.addEventListener('click', () => { + if (!PbAdvancedTable.isCollapsing) { + const isExpanded = this.element.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + if (!isExpanded) { + PbAdvancedTable.expandedRows.add(this.element.id) + } else { + PbAdvancedTable.expandedRows.delete(this.element.id) + } + } this.toggleElement(this.target) }) } @@ -75,4 +86,53 @@ export default class PbAdvancedTable extends PbEnhancedElement { this.element.querySelector(UP_ARROW_SELECTOR).style.display = 'inline-block' this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = 'none' } + + static handleToggleAllHeaders(element) { + const table = element.closest('.pb_table') + const firstLevelButtons = table.querySelectorAll('.pb_advanced_table_body > .pb_table_tr [data-advanced-table]') + + const expandedRows = Array.from(firstLevelButtons).filter(button => + button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + ) + + if (expandedRows.length === firstLevelButtons.length) { + expandedRows.forEach(button => { + button.click() + }) + this.expandedRows.clear() + } else { + firstLevelButtons.forEach(button => { + if (!this.expandedRows.has(button.id)) { + button.click() + } + }) + } + } + static handleToggleAllSubRows(element, rowDepth) { + const parentElement = element.closest(".toggle-content") + const subrowButtons = parentElement.querySelectorAll('.depth-sub-row-' + rowDepth + ' [data-advanced-table]') + + const expandedSubRows = Array.from(subrowButtons).filter(button => + button.querySelector(UP_ARROW_SELECTOR).style.display === 'inline-block' + ) + + if (expandedSubRows.length === subrowButtons.length) { + expandedSubRows.forEach(button => { + button.click() + }) + } else { + subrowButtons.forEach(button => { + if (!this.expandedRows.has(button.id)) { + button.click() + } + }) + } + } +} + +window.expandAllRows = (element) => { + PbAdvancedTable.handleToggleAllHeaders(element) } +window.expandAllSubRows = (element, rowDepth) => { + PbAdvancedTable.handleToggleAllSubRows(element, rowDepth) +} \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb index f98b8bfda3..fa857e1926 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb @@ -13,12 +13,4 @@ <% end %> <% end %> <% end %> -<% end %> - - \ No newline at end of file +<% end %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb index 8870a1f9ea..8815af0bf0 100644 --- a/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +++ b/playbook/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb @@ -23,12 +23,4 @@ <% end %> <% end %> <% end %> -<% end %> - - \ No newline at end of file +<% end %> \ No newline at end of file diff --git a/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb b/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb index 5d8254f492..764f0d9559 100644 --- a/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb +++ b/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb @@ -1,13 +1,36 @@ <%= pb_rails("button", props: { text: "Open Dialog", data: {"open-dialog": "dialog-loading"} }) %> -<%= pb_rails("dialog", props: { - id:"dialog-loading", - size: "sm", - title: "Loading Exmaple", - text: "Make a loading request?", - cancel_button: "Cancel Button", +<%= pb_rails("dialog", props: { + id:"dialog-loading", + size: "sm", + title: "Loading Example", + text: "Make a loading request?", + cancel_button: "Cancel Button", cancel_button_id: "cancel-button-loading", - confirm_button: "Okay", + confirm_button: "Okay", confirm_button_id: "confirm-button-loading", loading: true, }) %> + + diff --git a/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md b/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md index ad7fbfab33..de0f291d1a 100644 --- a/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md +++ b/playbook/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md @@ -1,3 +1 @@ Pressing the "Okay" button will trigger a loading state where the button content is replaced by a spinner icon and both buttons are disabled. - -Currently, the loading state cannot be undone and will require a page refresh to reset the dialog. diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.scss b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.scss index a64e52f2aa..05b26b4b07 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.scss @@ -69,15 +69,21 @@ padding-bottom: $space_xs; cursor: pointer; &:hover { - background-color: $border_light; + background-color: $bg_light; } &[class*="_focused"] { - background-color: $border_light; + background-color: $bg_light; } &[class*="_list"] { border-bottom: 1px solid $border_light; + + &:hover, &:focus { + .pb_body_kit { + color: $primary; + } + } } &[class*="selected"] { background-color: $primary; @@ -87,7 +93,7 @@ color: $white !important; } &:hover { - background-color: $primary !important; + background-color: $product_1_background !important; } } } @@ -125,6 +131,81 @@ } } + &.separators_hidden { + .dropdown_wrapper { + .pb_dropdown_container { + + [class*="pb_dropdown_option"] { + border: none; + } + } + } + } + + &.subtle { + .dropdown_wrapper { + .pb_dropdown_container { + + [class*="pb_dropdown_option"]:first-child { + margin-top: $space_xs; + } + + [class*="pb_dropdown_option"]:last-child { + margin-bottom: $space_xs; + } + + [class*="pb_dropdown_option"] { + margin: $space_xs; + padding: $space_xs; + border-radius: $border_radius_md; + border-bottom: none; + position: relative; + + &::after { + content: ""; + position: absolute; + left: -$space_xs; + right: -$space_xs; + bottom: -4.5px; + height: 1px; + background: $border_light; + } + } + + [class*="pb_dropdown_option"]:last-child::after { + display: none; + } + } + } + + &.separators_hidden { + .dropdown_wrapper { + .pb_dropdown_container { + [class*="pb_dropdown_option"]:first-child { + margin-top: $space_xs; + } + + [class*="pb_dropdown_option"]:last-child { + margin-bottom: $space_xs; + } + + [class*="pb_dropdown_option"] { + padding: $space_xxs $space_xs; + margin: $space_xxs $space_xs; + + &::after { + height: 0px; + } + + &[class*="selected"] { + border-bottom: none; + } + } + } + } + } + } + &.dark { .dropdown_wrapper { [class*="dropdown_trigger_wrapper"] { diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx index 5940e22e29..28af6eb76a 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from "react"; import classnames from "classnames"; import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props"; import { globalProps } from "../utilities/globalProps"; @@ -35,10 +35,19 @@ type DropdownProps = { label?: string; onSelect?: (arg: GenericObject) => null; options: GenericObject; + separators: boolean; triggerRef?: any; + variant?: "default" | "subtle"; }; -const Dropdown = (props: DropdownProps) => { +interface DropdownComponent + extends React.ForwardRefExoticComponent> { + Option: typeof DropdownOption; + Trigger: typeof DropdownTrigger; + Container: typeof DropdownContainer; +} + +const Dropdown = forwardRef((props: DropdownProps, ref: any) => { const { aria = {}, autocomplete = false, @@ -55,15 +64,20 @@ const Dropdown = (props: DropdownProps) => { label, onSelect, options, - triggerRef + separators = true, + triggerRef, + variant = "default", } = props; const ariaProps = buildAriaProps(aria); const dataProps = buildDataProps(data); const htmlProps = buildHtmlProps(htmlOptions); + const separatorsClass = separators ? '' : 'separators_hidden' const classes = classnames( buildCss("pb_dropdown"), globalProps(props), + variant, + separatorsClass, className ); @@ -125,7 +139,7 @@ const Dropdown = (props: DropdownProps) => { const filteredOptions = optionsWithBlankSelection?.filter((option: GenericObject) => { const label = typeof option.label === 'string' ? option.label.toLowerCase() : option.label; return String(label).toLowerCase().includes(filterItem.toLowerCase()); - }); + }); // For keyboard accessibility: Set focus within dropdown to selected item if it exists useEffect(() => { @@ -175,6 +189,14 @@ const Dropdown = (props: DropdownProps) => { dark }); + useImperativeHandle(ref, () => ({ + clearSelected: () => { + setSelected({}); + setFilterItem(""); + setIsDropDownClosed(true); + onSelect && onSelect(null); + }, + })); return (
{
) -}; +}) as DropdownComponent +Dropdown.displayName = "Dropdown"; Dropdown.Option = DropdownOption; Dropdown.Trigger = DropdownTrigger; Dropdown.Container = DropdownContainer; diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx new file mode 100644 index 0000000000..eeece1ec52 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx @@ -0,0 +1,45 @@ +import React, { useRef } from 'react' +import { Button, Dropdown } from 'playbook-ui' + +const options = [ + { + label: "United States", + value: "United States", + }, + { + label: "Canada", + value: "Canada", + }, + { + label: "Pakistan", + value: "Pakistan", + } +] + +const DropdownClearSelection = (props) => { + const dropdownRef = useRef(null) + + const handleReset = () => { + if (dropdownRef.current) { + dropdownRef.current.clearSelected() + } + } + + return ( + <> + +