commit 284dc650c4a796149a8c873998e3472735a2a1ef Author: Sébastien Huss Date: Fri Jul 14 11:51:07 2023 +0200 first commit diff --git a/apps/code-server/configs.tf b/apps/code-server/configs.tf new file mode 100644 index 0000000..8206d63 --- /dev/null +++ b/apps/code-server/configs.tf @@ -0,0 +1,20 @@ +resource "kubectl_manifest" "code-server-config" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ConfigMap + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + data: + config.yml: | + auth: none + autostart.sh: | + #!/bin/bash + kubectl config set-cluster default --server=https://$${KUBERNETES_SERVICE_HOST}:$${KUBERNETES_SERVICE_PORT} --certificate-authority=/run/secrets/kubernetes.io/serviceaccount/ca.crt + kubectl config set-credentials default --token=$(cat /run/secrets/kubernetes.io/serviceaccount/token) + kubectl config set-context default --cluster=default --user=default + kubectl config use-context default + [ -e /home/coder/.bashrc ] || cp /etc/skel/.bashrc /home/coder/.bashrc + EOF +} diff --git a/apps/code-server/datas.tf b/apps/code-server/datas.tf new file mode 100644 index 0000000..ac5f6fe --- /dev/null +++ b/apps/code-server/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/apps/code-server/deploy.tf b/apps/code-server/deploy.tf new file mode 100644 index 0000000..9608a37 --- /dev/null +++ b/apps/code-server/deploy.tf @@ -0,0 +1,109 @@ +resource "kubectl_manifest" "deploy" { + yaml_body = <<-EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + replicas: 1 + hostname: "${var.component}-${var.instance}" + subdomain: "${var.domain-name}" + selector: + matchLabels: ${jsonencode(local.common-labels)} + template: + metadata: + labels: ${jsonencode(local.common-labels)} + spec: + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + capabilities: + add: + - SETGID + - SETUID + - SYS_CHROOT + hostname: "${var.component}-${var.instance}" + containers: + - name: code-server + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + privileged: true + env: + - name: TZ + value: "${var.timezone}" + - name: ENTRYPOINTD + value: /usr/local/startup + - name: PORT + value: "8080" + - name: CODE_SERVER_CONFIG + value: /etc/code-server/config.yml + image: "${var.images.codeserver.registry}/${var.images.codeserver.repository}:${var.images.codeserver.tag}" + imagePullPolicy: "${var.images.codeserver.pullPolicy}" + ports: + - containerPort: 8080 + name: http + protocol: TCP + livenessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + httpGet: + path: / + port: http + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + volumeMounts: + - name: config + mountPath: /etc/code-server/config.yml + subPath: config.yml + - name: startup + mountPath: /usr/local/startup/autostart.sh + subPath: autostart.sh + - name: home + mountPath: /home/coder + - name: run + mountPath: /run + restartPolicy: Always + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + serviceAccount: "${var.component}-${var.instance}" + serviceAccountName: "${var.component}-${var.instance}" + volumes: + - name: config + configMap: + defaultMode: 0420 + name: "${var.component}-${var.instance}" + items: + - key: config.yml + path: config.yml + - name: startup + configMap: + defaultMode: 0755 + name: "${var.component}-${var.instance}" + items: + - key: autostart.sh + path: autostart.sh + - name: home + persistentVolumeClaim: + claimName: "${var.component}-${var.instance}" + - name: run + emptyDir: {} + EOF +} diff --git a/apps/code-server/forward.tf b/apps/code-server/forward.tf new file mode 100644 index 0000000..39a0d80 --- /dev/null +++ b/apps/code-server/forward.tf @@ -0,0 +1,122 @@ +locals { + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + forward-outpost-providers = jsondecode(data.http.get_forward_outpost.response_body).results[0].providers + forward-outpost-pk = jsondecode(data.http.get_forward_outpost.response_body).results[0].pk + app-name = var.component == var.instance ? var.instance : format("%s-%s", var.component, var.instance) + app-icon = "dashboard/statics/icons/favicon-96x96.png" + main-group = format("%s-users", local.app-name) + sub-groups = [] + external-url = format("https://%s", local.dns-names[0]) + access-token-validity = "hours=10" // ;minutes=10 +} + + +data "authentik_flow" "default-authorization-flow" { + depends_on = [authentik_group.prj_users] + slug = "default-provider-authorization-implicit-consent" +} + +resource "authentik_provider_proxy" "prj_forward" { + name = local.app-name + external_host = local.external-url + authorization_flow = data.authentik_flow.default-authorization-flow.id + mode = "forward_single" + access_token_validity = local.access-token-validity +} + + +resource "authentik_application" "prj_application" { + name = local.app-name + slug = local.app-name + protocol_provider = authentik_provider_proxy.prj_forward.id + meta_launch_url = local.external-url + meta_icon = format("%s/%s", local.external-url, local.app-icon) +} + +resource "authentik_group" "prj_users" { + name = local.main-group +} + +resource "authentik_group" "subgroup" { + count = length(local.sub-groups) + name = format("%s-%s", local.app-name, local.sub-groups[count.index]) + parent = authentik_group.prj_users.id +} + +data "authentik_group" "vynil-admin" { + depends_on = [authentik_group.prj_users] # fake dependency so it is not evaluated at plan stage + name = "vynil-forward-admins" +} + +resource "authentik_policy_binding" "prj_access_users" { + target = authentik_application.prj_application.uuid + group = authentik_group.prj_users.id + order = 0 +} +resource "authentik_policy_binding" "prj_access_vynil" { + target = authentik_application.prj_application.uuid + group = data.authentik_group.vynil-admin.id + order = 1 +} + +data "http" "get_forward_outpost" { + depends_on = [authentik_provider_proxy.prj_forward] + url = "http://authentik.${var.domain}-auth.svc/api/v3/outposts/instances/?name__iexact=forward" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +provider "restapi" { + uri = "http://authentik.${var.domain}-auth.svc/api/v3/" + headers = local.request_headers + create_method = "PATCH" + update_method = "PATCH" + destroy_method = "PATCH" + write_returns_object = true + id_attribute = "name" +} + +resource "restapi_object" "forward_outpost_binding" { + path = "/outposts/instances/${local.forward-outpost-pk}/" + data = jsonencode({ + name = "forward" + providers = contains(local.forward-outpost-providers, authentik_provider_proxy.prj_forward.id) ? local.forward-outpost-providers : concat(local.forward-outpost-providers, [authentik_provider_proxy.prj_forward.id]) + }) +} + +resource "kubectl_manifest" "prj_middleware" { + yaml_body = <<-EOF + apiVersion: traefik.containo.us/v1alpha1 + kind: Middleware + metadata: + name: "forward-${local.app-name}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forwardAuth: + address: http://ak-outpost-forward.${var.domain}-auth.svc:9000/outpost.goauthentik.io/auth/traefik + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + # - X-authentik-groups + # - X-authentik-email + # - X-authentik-name + # - X-authentik-uid + # - X-authentik-jwt + # - X-authentik-meta-jwks + # - X-authentik-meta-outpost + # - X-authentik-meta-provider + # - X-authentik-meta-app + # - X-authentik-meta-version + EOF +} diff --git a/apps/code-server/index.yaml b/apps/code-server/index.yaml new file mode 100644 index 0000000..4fa6c69 --- /dev/null +++ b/apps/code-server/index.yaml @@ -0,0 +1,130 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: code-server + description: null +options: + sub-domain: + default: code + examples: + - code + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + admin: + default: + cluster: false + namespace: false + examples: + - cluster: false + namespace: false + properties: + cluster: + default: false + type: boolean + namespace: + default: false + type: boolean + type: object + ingress-class: + default: traefik + examples: + - traefik + type: string + images: + default: + codeserver: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/code-server + tag: 4.13 + examples: + - codeserver: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/code-server + tag: 4.13 + properties: + codeserver: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/code-server + tag: 4.13 + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: sebt3/code-server + type: string + tag: + default: 4.13 + type: number + type: object + type: object + domain: + default: your-company + examples: + - your-company + type: string + timezone: + default: Europe/Paris + examples: + - Europe/Paris + type: string + storage: + default: + accessMode: ReadWriteOnce + size: 20Gi + type: Filesystem + examples: + - accessMode: ReadWriteOnce + size: 20Gi + type: Filesystem + properties: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadOnlyMany + - ReadWriteMany + type: string + size: + default: 20Gi + type: string + type: + default: Filesystem + enum: + - Filesystem + - block + type: string + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string +dependencies: +- dist: null + category: share + component: authentik-forward +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true diff --git a/apps/code-server/ingress.tf b/apps/code-server/ingress.tf new file mode 100644 index 0000000..d765fe2 --- /dev/null +++ b/apps/code-server/ingress.tf @@ -0,0 +1,76 @@ + +locals { + dns-names = ["${var.instance}.${var.sub-domain}.${var.domain-name}"] + middlewares = ["${var.instance}-https", "forward-${local.app-name}"] + service = { + "name" = "${var.component}-${var.instance}" + "port" = { + "number" = 80 + } + } + rules = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + annotations: + "traefik.ingress.kubernetes.io/router.middlewares": "${join(",", [for m in local.middlewares : format("%s-%s@kubernetescrd", var.namespace, m)])}" + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/code-server/pvc.tf b/apps/code-server/pvc.tf new file mode 100644 index 0000000..1398514 --- /dev/null +++ b/apps/code-server/pvc.tf @@ -0,0 +1,17 @@ +resource "kubectl_manifest" "pvc" { + yaml_body = <<-EOF + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + accessModes: + - "${var.storage.accessMode}" + resources: + requests: + storage: "${var.storage.size}" + volumeMode: "${var.storage.type}" + EOF +} diff --git a/apps/code-server/rbac.tf b/apps/code-server/rbac.tf new file mode 100644 index 0000000..ab33bb3 --- /dev/null +++ b/apps/code-server/rbac.tf @@ -0,0 +1,78 @@ +resource "kubectl_manifest" "sa" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ServiceAccount + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + EOF +} + +resource "kubectl_manifest" "role" { + count = var.admin.namespace?1:0 + yaml_body = <<-EOF + apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + rules: + - apiGroups: ['*'] + resources: ['*'] + verbs: ['*'] + EOF +} +resource "kubectl_manifest" "rb" { + count = var.admin.namespace?1:0 + yaml_body = <<-EOF + apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + namespace: "${var.namespace}" + name: "${var.component}-${var.instance}" + subjects: + - kind: ServiceAccount + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + EOF +} +resource "kubectl_manifest" "clusterrole" { + count = var.admin.cluster?1:0 + yaml_body = <<-EOF + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + name: "${var.component}-${var.namespace}-${var.instance}" + labels: ${jsonencode(local.common-labels)} + rules: + - apiGroups: ['*'] + resources: ['*'] + verbs: ['*'] + EOF +} +resource "kubectl_manifest" "crb" { + count = var.admin.cluster?1:0 + yaml_body = <<-EOF + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: "${var.component}-${var.namespace}-${var.instance}" + labels: ${jsonencode(local.common-labels)} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: "${var.component}-${var.namespace}-${var.instance}" + subjects: + - kind: ServiceAccount + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + EOF +} diff --git a/apps/code-server/svc.tf b/apps/code-server/svc.tf new file mode 100644 index 0000000..c73fa48 --- /dev/null +++ b/apps/code-server/svc.tf @@ -0,0 +1,18 @@ +resource "kubectl_manifest" "service" { + yaml_body = <<-EOF + apiVersion: v1 + kind: Service + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http + selector: ${jsonencode(local.common-labels)} + EOF +} diff --git a/apps/dolibarr/application.tf b/apps/dolibarr/application.tf new file mode 100644 index 0000000..e180cc5 --- /dev/null +++ b/apps/dolibarr/application.tf @@ -0,0 +1,69 @@ +locals { + sorted-group-names = reverse(distinct(sort([ + for grp in var.user-groups: grp.name + ]))) + sorted-groups = flatten([ + for name in local.sorted-group-names: [ + for grp in var.user-groups: + grp if grp.name == name + ] + ]) +} + +data "authentik_group" "vynil-admin" { + name = "vynil-ldap-admins" +} + +resource "authentik_group" "groups" { + count = length(local.sorted-groups) + name = local.sorted-groups[count.index].name +} +data "authentik_group" "readed_groups" { + depends_on = [ authentik_group.groups ] + count = length(local.sorted-groups) + name = local.sorted-groups[count.index].name +} + +resource "authentik_application" "dolibarr_application_ldap" { + name = "${var.component}-${var.instance}-ldap" + slug = "${var.component}-${var.instance}-ldap" + protocol_provider = authentik_provider_ldap.dolibarr_provider_ldap.id + meta_launch_url = "blank://blank" +} + +resource "authentik_policy_binding" "dolibarr_ldap_access_users" { + count = length(local.sorted-groups) + target = authentik_application.dolibarr_application_ldap.uuid + group = authentik_group.groups[count.index].id + order = count.index +} +resource "authentik_policy_binding" "dolibarr_ldap_access_ldap" { + target = authentik_application.dolibarr_application_ldap.uuid + group = authentik_group.dolibarr_ldapsearch.id + order = length(local.sorted-groups)+1 +} +resource "authentik_policy_binding" "dolibarr_ldap_access_vynil" { + target = authentik_application.dolibarr_application_ldap.uuid + group = data.authentik_group.vynil-admin.id + order = length(local.sorted-groups)+2 +} + +resource "authentik_application" "dolibarr_application_saml" { + name = var.component==var.instance?var.component:"${var.component}-${var.instance}" + slug = "${var.component}-${var.instance}" + protocol_provider = authentik_provider_saml.dolibarr.id + meta_launch_url = format("https://%s.%s", var.sub-domain, var.domain-name) + meta_icon = format("https://%s.%s/%s", var.sub-domain, var.domain-name, "theme/dolibarr_256x256_color.png") +} + +resource "authentik_policy_binding" "dolibarr_saml_access_users" { + count = length(local.sorted-groups) + target = authentik_application.dolibarr_application_saml.uuid + group = authentik_group.groups[count.index].id + order = count.index +} +resource "authentik_policy_binding" "dolibarr_saml_access_vynil" { + target = authentik_application.dolibarr_application_saml.uuid + group = data.authentik_group.vynil-admin.id + order = length(local.sorted-groups)+1 +} diff --git a/apps/dolibarr/configmap.tf b/apps/dolibarr/configmap.tf new file mode 100644 index 0000000..c17f688 --- /dev/null +++ b/apps/dolibarr/configmap.tf @@ -0,0 +1,180 @@ +data "kubernetes_ingress_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} +locals { + authentik-metadata-url="${data.kubernetes_ingress_v1.authentik.spec[0].rule[0].host}/api/v3/providers/saml/${authentik_provider_saml.dolibarr.id}/metadata/?download" + module-list = [ + "user", + "ldap", + "syslog" + ] + json-config = { + groups = [ for index, g in local.sorted-groups: { + name = g.name + admin = contains([for k,v in g:k], "admin")?g.admin:false + users = data.authentik_group.readed_groups[index].users_obj + }] + parameters = merge(var.parameters, { + LDAP_FIELD_FULLNAME="sAMAccountName" + LDAP_FIELD_LOGIN_SAMBA="sAMAccountName" + LDAP_FIELD_MAIL="mail" + LDAP_FIELD_NAME="sn" + LDAP_GROUP_FIELD_DESCRIPTION="sAMAccountName" + LDAP_GROUP_FIELD_FULLNAME="cn" + LDAP_GROUP_FIELD_GROUPID="gidNumber" + LDAP_GROUP_FIELD_GROUPMEMBERS="member" + LDAP_GROUP_OBJECT_CLASS="group" + LDAP_KEY_GROUPS="cn" + LDAP_KEY_USERS="cn" + LDAP_PASSWORD_HASH_TYPE="md5" + LDAP_SERVER_HOST="ak-outpost-ldap.${var.domain}-auth.svc" + LDAP_SERVER_PORT="389" + LDAP_SERVER_PROTOCOLVERSION="3" + LDAP_SERVER_TYPE="openldap" + LDAP_SERVER_DN="${local.base-dn}" + LDAP_SERVER_USE_TLS="0" + LDAP_SYNCHRO_ACTIVE="2" + LDAP_USER_OBJECT_CLASS="person" + LDAP_USER_DN=local.base-user-dn + LDAP_GROUP_DN=local.base-group-dn + LDAP_GROUP_FILTER="&(&(objectClass=groupOfNames)(|${join("",[for g in local.sorted-groups: format("(cn=%s)",g.name)])}))" + LDAP_ADMIN_DN="cn=${var.instance}-${var.component}-ldapsearch,${local.base-user-dn}" + LDAP_FILTER_CONNECTION="&(&(objectClass=inetOrgPerson)(|${join("",[for g in local.sorted-groups: format("(memberof=cn=%s,%s)",g.name,local.base-group-dn)])}))" + SAMLCONNECTOR_CREATE_UNEXISTING_USER="1" + SAMLCONNECTOR_MAPPING_USER_EMAIL="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + SAMLCONNECTOR_MAPPING_USER_FIRSTNAME="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" + SAMLCONNECTOR_MAPPING_USER_LASTNAME="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" + SAMLCONNECTOR_UPDATE_USER_EVERYTIME="1" + SAMLCONNECTOR_DISABLE_IDP_DISCONNECTION="1" + SAMLCONNECTOR_IDP_DISPLAY_BUTTON="1" + SAMLCONNECTOR_IDP_METADATA_SOURCE="url" + SAMLCONNECTOR_MANAGE_MULTIPLE_IDP="0" + SAMLCONNECTOR_SP_CERT_PATH="/var/saml/tls.crt" + SAMLCONNECTOR_SP_PRIV_KEY_PATH="/var/saml/tls.key" + SAMLCONNECTOR_IDP_METADATA_URL=local.authentik-metadata-url + SAMLCONNECTOR_IDP_METADATA_XML_PATH=local.authentik-metadata-url + MAIN_MODULE_SAMLCONNECTOR="1" + MAIN_MODULE_SAMLCONNECTOR_CSS="[\"\\/samlconnector\\/css\\/samlconnector.css.php\"]" + MAIN_MODULE_SAMLCONNECTOR_HOOKS="[\"mainloginpage\",\"logout\",\"samlconnectorsetup\"]" + MAIN_MODULE_SAMLCONNECTOR_JS="[\"\\/samlconnector\\/js\\/samlconnector.js.php\"]" + MAIN_MODULE_SAMLCONNECTOR_LOGIN="1" + MAIN_MODULE_SAMLCONNECTOR_MODULEFOREXTERNAL="1" + MAIN_MODULE_SAMLCONNECTOR_SUBSTITUTIONS="1" + MAIN_MODULE_SAMLCONNECTOR_TRIGGERS="1" + SYSLOG_LEVEL="${var.log-level}" + SYSLOG_FILE="/var/logs/dolibarr.log" + SYSLOG_HANDLERS="[\"mod_syslog_file\"]" + }) + modules=join(",",[for i in concat(var.modules, local.module-list): format("MAIN_MODULE_%s",upper(i))]) + } +} + +resource "kubectl_manifest" "config-json" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ConfigMap + metadata: + name: "${var.instance}-json" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + data: + "docker-php-ext-redis.ini": |- + extension = redis.so + session.save_handler = redis + session.save_path = "tcp://${var.instance}-${var.component}-redis.${var.namespace}.svc:6379/?prefix=SESSION_${var.component}_${var.instance}:" + "vynil-configurator.sh": |- + #!/bin/ash + pgsqlRun() { PGPASSWORD="$${DOLI_DB_PASSWORD:="dolibarr"}" psql -h "$${DOLI_DB_HOST:="postgres"}" -p "$${DOLI_DB_PORT}" -U "$${DOLI_DB_USER}" -w "$DOLI_DB_NAME" "$@"; } + setDBconf() { pgsqlRun -c "insert into llx_const(entity, name, type, value, visible) VALUES (1, '$1', 'chaine', '$2', 0) ON CONFLICT(entity, name) DO UPDATE SET value='$2';"; } + createUser() { pgsqlRun -c "INSERT INTO llx_user(entity, admin, employee, fk_establishment, datec, login, lastname, email, statut, fk_barcode_type, nb_holiday) VALUES (1, 0, 1, 0, NOW(), '$1', '$2', '$3', 1, 0, 0) ON CONFLICT(entity, login) DO UPDATE SET admin=0;"; } + createAdmin() { pgsqlRun -c "INSERT INTO llx_user(entity, admin, employee, fk_establishment, datec, login, lastname, email, statut, fk_barcode_type, nb_holiday) VALUES (1, 1, 0, 0, NOW(), '$1', '$2', '$3', 1, 0, 0) ON CONFLICT(entity, login) DO UPDATE SET admin=1;"; } + createGroup() { pgsqlRun -c "INSERT INTO llx_usergroup(entity, nom, datec) VALUES (1, '$1', NOW()) ON CONFLICT(entity, nom) DO NOTHING;"; } + setGroupPerm() { pgsqlRun -c "insert into llx_usergroup_rights(fk_id,fk_usergroup,entity) select d.id as fk_id, g.rowid as fk_usergroup, 1 as entity from llx_rights_def d, llx_usergroup g where d.id is not null and d.module<>'user' and g.nom='$1' ON CONFLICT(fk_id,fk_usergroup,entity) DO NOTHING;"; } + setGroupUser() { pgsqlRun -c "insert into llx_usergroup_user(entity, fk_user, fk_usergroup) select 1, u.rowid, g.rowid from llx_usergroup g, llx_user u where g.nom='$1' and u.login='$2' ON CONFLICT(entity, fk_user, fk_usergroup) DO NOTHING;"; } + configquery() { jq -r "$1" /var/logs/dolibarr.log + "config.json": |- + ${jsonencode(local.json-config)} + EOF +} + +resource "kubectl_manifest" "config" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ConfigMap + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + data: + DOLI_DB_HOST: "${var.instance}-${var.component}.${var.namespace}.svc" + DOLI_DB_USER: "${var.component}" + DOLI_DB_NAME: "${var.component}" + DOLI_DB_PORT: "5432" + DOLI_DB_TYPE: "pgsql" + DOLI_ADMIN_LOGIN: "admin_${var.instance}" + DOLI_MODULES: "modSociete,modBlockedLog,modSamlConnector,modLdap" + DOLI_AUTH: "dolibarr" + DOLI_URL_ROOT: "https://${var.sub-domain}.${var.domain-name}" + DOLI_LDAP_PORT: "389" + DOLI_LDAP_VERSION: "3" + DOLI_LDAP_SERVERTYPE: "openldap" + DOLI_LDAP_LOGIN_ATTRIBUTE: "sAMAccountName" + DOLI_LDAP_FILTER: "(&(|${join("",[for g in local.sorted-groups: format("(memberof=cn=%s,%s)",g.name,local.base-group-dn)])})(|(uid=%1%)(mail=%1%)))" + DOLI_LDAP_ADMIN_LOGIN: "cn=${var.instance}-${var.component}-ldapsearch,${local.base-user-dn}" + DOLI_LDAP_DN: "${local.base-dn}" + DOLI_LDAP_HOST: "ak-outpost-ldap.${var.domain}-auth.svc" + EOF +} diff --git a/apps/dolibarr/deploy.tf b/apps/dolibarr/deploy.tf new file mode 100644 index 0000000..1469c97 --- /dev/null +++ b/apps/dolibarr/deploy.tf @@ -0,0 +1,185 @@ +locals { + deploy-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "dolibarr" + }) +} +resource "kubectl_manifest" "hpa" { + yaml_body = <<-EOF +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: ${var.instance} + namespace: ${var.namespace} + labels: ${jsonencode(local.deploy-labels)} +spec: + minReplicas: ${var.hpa.min-replicas} + maxReplicas: ${var.hpa.max-replicas} + metrics: + - resource: + name: cpu + target: + averageUtilization: ${var.hpa.avg-cpu} + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: ${var.instance} + EOF +} + +resource "kubectl_manifest" "deploy" { + yaml_body = <<-EOF +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ${var.instance} + namespace: ${var.namespace} + labels: ${jsonencode(local.deploy-labels)} +spec: + selector: + matchLabels: ${jsonencode(local.deploy-labels)} + template: + metadata: + labels: ${jsonencode(local.deploy-labels)} + spec: + securityContext: + runAsGroup: 82 + runAsUser: 82 + fsGroup: 82 + volumes: + - name: config-json + configMap: + name: ${kubectl_manifest.config-json.name} + - name: documents + persistentVolumeClaim: + claimName: ${kubectl_manifest.pvc.name} + - name: nginx-run + emptyDir: {} + - name: nginx-cache + emptyDir: {} + - name: shared-files + emptyDir: {} + - name: shared-logs + emptyDir: {} + - name: nginx-config + configMap: + name: ${kubectl_manifest.nginx-config.name} + - name: saml-cert + secret: + secretName: "${kubectl_manifest.saml_certificate.name}" + initContainers: + - name: configure + args: + - echo + - SUCCESS + image: "${var.images.dolibarr.registry}/${var.images.dolibarr.repository}:${var.images.dolibarr.tag}" + imagePullPolicy: "${var.images.dolibarr.pullPolicy}" + volumeMounts: + - name: shared-files + mountPath: /var/www/ + - name: shared-logs + mountPath: /var/logs/ + - name: documents + mountPath: /var/documents + - name: config-json + mountPath: /etc/config/config.json + subPath: config.json + - name: config-json + mountPath: /docker-entrypoint.d/vynil-configurator.sh + subPath: vynil-configurator.sh + securityContext: + runAsNonRoot: true + env: + - name: DOLI_DB_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: "${var.component}.${var.instance}-${var.component}.credentials.postgresql.acid.zalan.do" + envFrom: + - configMapRef: + name: "${kubectl_manifest.config.name}" + - secretRef: + name: "${kubectl_manifest.dolibarr_ldap.name}" + containers: + - name: dolibarr + command: + - "/usr/local/sbin/php-fpm" + image: "${var.images.dolibarr.registry}/${var.images.dolibarr.repository}:${var.images.dolibarr.tag}" + imagePullPolicy: "${var.images.dolibarr.pullPolicy}" + resources: ${jsonencode(var.resources)} + readinessProbe: + httpGet: + path: /index.php + port: 3000 + scheme: HTTP + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + livenessProbe: + httpGet: + path: /index.php + port: 3000 + scheme: HTTP + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + volumeMounts: + - name: shared-logs + mountPath: /var/logs/ + - name: shared-files + mountPath: /var/www/ + - name: documents + mountPath: /var/documents + - name: saml-cert + mountPath: /var/saml + - name: config-json + mountPath: /usr/local/etc/php/conf.d/docker-php-ext-redis.ini + subPath: docker-php-ext-redis.ini + securityContext: + runAsNonRoot: true + env: + - name: DOLI_DB_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: "${var.component}.${var.instance}-${var.component}.credentials.postgresql.acid.zalan.do" + envFrom: + - configMapRef: + name: "${kubectl_manifest.config.name}" + - secretRef: + name: "${kubectl_manifest.dolibarr_ldap.name}" + - name: dolibarr-logs + command: + - "tail" + - "-f" + - "/var/logs/dolibarr.log" + image: "${var.images.dolibarr.registry}/${var.images.dolibarr.repository}:${var.images.dolibarr.tag}" + imagePullPolicy: "${var.images.dolibarr.pullPolicy}" + volumeMounts: + - name: shared-logs + mountPath: /var/logs/ + - name: nginx + image: "${var.images.nginx.registry}/${var.images.nginx.repository}:${var.images.nginx.tag}" + imagePullPolicy: "${var.images.nginx.pullPolicy}" + securityContext: + runAsNonRoot: true + readOnlyRootFilesystem: true + ports: + - name: http + containerPort: 3000 + protocol: TCP + volumeMounts: + - name: nginx-run + mountPath: /var/run + - name: nginx-cache + mountPath: /var/cache/nginx + - name: shared-files + mountPath: /var/www/ + - name: nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + EOF +} diff --git a/apps/dolibarr/index.yaml b/apps/dolibarr/index.yaml new file mode 100644 index 0000000..9d9bde0 --- /dev/null +++ b/apps/dolibarr/index.yaml @@ -0,0 +1,300 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: dolibarr + description: null +options: + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + redis: + default: + exporter: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + image: quay.io/opstree/redis:v7.0.5 + storage: 2Gi + examples: + - exporter: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + image: quay.io/opstree/redis:v7.0.5 + storage: 2Gi + properties: + exporter: + default: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + properties: + enabled: + default: true + type: boolean + image: + default: quay.io/opstree/redis-exporter:v1.44.0 + type: string + type: object + image: + default: quay.io/opstree/redis:v7.0.5 + type: string + storage: + default: 2Gi + type: string + type: object + domain: + default: your-company + examples: + - your-company + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + parameters: + default: + MAIN_LANG_DEFAULT: auto + examples: + - MAIN_LANG_DEFAULT: auto + properties: + MAIN_LANG_DEFAULT: + default: auto + type: string + type: object + storage: + default: + accessMode: ReadWriteOnce + size: 10Gi + type: Filesystem + examples: + - accessMode: ReadWriteOnce + size: 10Gi + type: Filesystem + properties: + accessMode: + default: ReadWriteOnce + enum: + - ReadWriteOnce + - ReadOnlyMany + - ReadWriteMany + type: string + size: + default: 10Gi + type: string + type: + default: Filesystem + enum: + - Filesystem + - block + type: string + type: object + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + modules: + default: + - societe + examples: + - - societe + items: + type: string + type: array + sub-domain: + default: erp + examples: + - erp + type: string + user-groups: + default: + - admin: true + name: dolibarr-admin + examples: + - - admin: true + name: dolibarr-admin + items: + properties: + admin: + type: boolean + name: + type: string + type: object + type: array + hpa: + default: + avg-cpu: 50 + max-replicas: 5 + min-replicas: 1 + examples: + - avg-cpu: 50 + max-replicas: 5 + min-replicas: 1 + properties: + avg-cpu: + default: 50 + type: integer + max-replicas: + default: 5 + type: integer + min-replicas: + default: 1 + type: integer + type: object + images: + default: + dolibarr: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/dolibarr + tag: 17.0.1 + nginx: + pullPolicy: IfNotPresent + registry: docker.io + repository: nginx + tag: alpine + examples: + - dolibarr: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/dolibarr + tag: 17.0.1 + nginx: + pullPolicy: IfNotPresent + registry: docker.io + repository: nginx + tag: alpine + properties: + dolibarr: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: sebt3/dolibarr + tag: 17.0.1 + properties: + pullPolicy: + default: IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: sebt3/dolibarr + type: string + tag: + default: 17.0.1 + type: string + type: object + nginx: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: nginx + tag: alpine + properties: + pullPolicy: + default: IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: nginx + type: string + tag: + default: alpine + type: string + type: object + type: object + log-level: + default: 5 + examples: + - 5 + type: integer + postgres: + default: + replicas: 1 + storage: 5Gi + version: '14' + examples: + - replicas: 1 + storage: 5Gi + version: '14' + properties: + replicas: + default: 1 + type: integer + storage: + default: 5Gi + type: string + version: + default: '14' + type: string + type: object + resources: + default: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 50m + memory: 100Mi + examples: + - limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 50m + memory: 100Mi + properties: + limits: + default: + cpu: 200m + memory: 256Mi + properties: + cpu: + default: 200m + type: string + memory: + default: 256Mi + type: string + type: object + requests: + default: + cpu: 50m + memory: 100Mi + properties: + cpu: + default: 50m + type: string + memory: + default: 100Mi + type: string + type: object + type: object +dependencies: +- dist: null + category: share + component: authentik-ldap +- dist: null + category: core + component: secret-generator +- dist: null + category: core + component: cert-manager-self-sign +- dist: null + category: dbo + component: postgresql +- dist: null + category: dbo + component: redis +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true diff --git a/apps/dolibarr/ingress.tf b/apps/dolibarr/ingress.tf new file mode 100644 index 0000000..cc84f10 --- /dev/null +++ b/apps/dolibarr/ingress.tf @@ -0,0 +1,75 @@ +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = ["${var.instance}-https"] + service = { + "name" = "${var.instance}" + "port" = { + "number" = 80 + } + } + rules = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + annotations: + "traefik.ingress.kubernetes.io/router.middlewares": "${join(",", [for m in local.middlewares : format("%s-%s@kubernetescrd", var.namespace, m)])}" + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/dolibarr/ldap.tf b/apps/dolibarr/ldap.tf new file mode 100644 index 0000000..2990f10 --- /dev/null +++ b/apps/dolibarr/ldap.tf @@ -0,0 +1,115 @@ +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} +locals { + base-dn = format("dc=%s", join(",dc=", split(".", format("%s.%s", var.sub-domain, var.domain-name)))) + base-group-dn = format("ou=groups,%s", local.base-dn) + base-user-dn = format("ou=users,%s", local.base-dn) + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + ldap-outpost-providers = jsondecode(data.http.get_ldap_outpost.response_body).results[0].providers + ldap-outpost-pk = jsondecode(data.http.get_ldap_outpost.response_body).results[0].pk +} + +resource "kubectl_manifest" "dolibarr_ldap" { + ignore_fields = ["metadata.annotations"] + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forceRegenerate: false + fields: + - fieldName: "DOLI_LDAP_ADMIN_PASS" + length: "32" + - fieldName: "DOLI_ADMIN_PASSWORD" + length: "32" + - fieldName: "DOLI_COOKIE_CRYPTKEY" + length: "32" + EOF +} +data "kubernetes_secret_v1" "dolibarr_ldap_password" { + depends_on = [kubectl_manifest.dolibarr_ldap] + metadata { + name = kubectl_manifest.dolibarr_ldap.name + namespace = var.namespace + } +} + +resource "authentik_user" "dolibarr_ldapsearch" { + username = "${var.instance}-${var.component}-ldapsearch" + name = "${var.instance}-${var.component}-ldapsearch" +} + +resource "authentik_group" "dolibarr_ldapsearch" { + name = "${var.instance}-${var.component}-ldapsearch" + users = [authentik_user.dolibarr_ldapsearch.id] + is_superuser = true +} + + +data "http" "dolibarr_ldapsearch_password" { + url = "http://authentik.${var.domain}-auth.svc/api/v3/core/users/${authentik_user.dolibarr_ldapsearch.id}/set_password/" + method = "POST" + request_headers = local.request_headers + request_body = jsonencode({password=data.kubernetes_secret_v1.dolibarr_ldap_password.data["DOLI_LDAP_ADMIN_PASS"]}) + lifecycle { + postcondition { + condition = contains([201, 204], self.status_code) + error_message = "Status code invalid" + } + } +} + + +data "authentik_flow" "ldap-authentication-flow" { + slug = "ldap-authentication-flow" +} + +resource "authentik_provider_ldap" "dolibarr_provider_ldap" { + name = "dolibarr-${var.instance}-ldap" + base_dn = local.base-dn + search_group = authentik_group.dolibarr_ldapsearch.id + bind_flow = data.authentik_flow.ldap-authentication-flow.id +} + +data "http" "get_ldap_outpost" { + depends_on = [authentik_policy_binding.dolibarr_ldap_access_users] # fake dependency so it is not evaluated at plan stage + url = "http://authentik.${var.domain}-auth.svc/api/v3/outposts/instances/?name__iexact=ldap" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +provider "restapi" { + uri = "http://authentik.${var.domain}-auth.svc/api/v3/" + headers = local.request_headers + create_method = "PATCH" + update_method = "PATCH" + destroy_method = "PATCH" + write_returns_object = true + id_attribute = "name" +} + +resource "restapi_object" "ldap_outpost_binding" { + path = "/outposts/instances/${local.ldap-outpost-pk}/" + data = jsonencode({ + name = "ldap" + providers = contains(local.ldap-outpost-providers, authentik_provider_ldap.dolibarr_provider_ldap.id) ? local.ldap-outpost-providers : concat(local.ldap-outpost-providers, [authentik_provider_ldap.dolibarr_provider_ldap.id]) + }) +} + diff --git a/apps/dolibarr/nginx-config.tf b/apps/dolibarr/nginx-config.tf new file mode 100644 index 0000000..9ab14dd --- /dev/null +++ b/apps/dolibarr/nginx-config.tf @@ -0,0 +1,44 @@ +resource "kubectl_manifest" "nginx-config" { + yaml_body = <<-EOF + +kind: ConfigMap +apiVersion: v1 +metadata: + name: ${var.instance}-nginx + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} +data: + nginx.conf: | + worker_processes 5; + events { + } + http { + include /etc/nginx/mime.types; + server { + listen 3000; + server_name $${NGINX_HOST}; + root /var/www/htdocs; + index index.php; + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + location ~ [^/]\.php(/|$) { + # try_files $uri =404; + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + } + location / { + try_files $uri $uri/ index.php; + } + location /api { + if ( !-e $request_filename) { + rewrite ^.* /api/index.php last; + } + } + } + } + EOF +} diff --git a/apps/dolibarr/postgresql.tf b/apps/dolibarr/postgresql.tf new file mode 100644 index 0000000..eb9c3a2 --- /dev/null +++ b/apps/dolibarr/postgresql.tf @@ -0,0 +1,31 @@ +locals { + pg-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "postgresql" + }) +} +resource "kubectl_manifest" "dolibarr_postgresql" { + yaml_body = <<-EOF + apiVersion: "acid.zalan.do/v1" + kind: "postgresql" + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + spec: + databases: + ${var.component}: "${var.component}" + numberOfInstances: ${var.postgres.replicas} + podAnnotations: + "k8up.io/backupcommand": "pg_dump -U postgres -d ${var.component} --clean" + "k8up.io/file-extension": ".sql" + postgresql: + version: "${var.postgres.version}" + teamId: "${var.instance}" + users: + ${var.component}: + - "superuser" + - "createdb" + volume: + size: "${var.postgres.storage}" + EOF +} diff --git a/apps/dolibarr/pvc.tf b/apps/dolibarr/pvc.tf new file mode 100644 index 0000000..377f31b --- /dev/null +++ b/apps/dolibarr/pvc.tf @@ -0,0 +1,19 @@ +resource "kubectl_manifest" "pvc" { + yaml_body = <<-EOF + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: ${var.instance} + namespace: "${var.namespace}" + annotations: + k8up.io/backup: "true" + labels: ${jsonencode(local.common-labels)} + spec: + accessModes: + - "${var.storage.accessMode}" + resources: + requests: + storage: "${var.storage.size}" + volumeMode: "${var.storage.type}" + EOF +} diff --git a/apps/dolibarr/redis.tf b/apps/dolibarr/redis.tf new file mode 100644 index 0000000..648115f --- /dev/null +++ b/apps/dolibarr/redis.tf @@ -0,0 +1,32 @@ +locals { + redis-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "redis" + }) +} +resource "kubectl_manifest" "dolibarr_redis" { + yaml_body = <<-EOF + apiVersion: "redis.redis.opstreelabs.in/v1beta1" + kind: "Redis" + metadata: + name: "${var.instance}-${var.component}-redis" + namespace: "${var.namespace}" + labels: ${jsonencode(local.redis-labels)} + spec: + kubernetesConfig: + image: "${var.redis.image}" + imagePullPolicy: "IfNotPresent" + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: "${var.redis.storage}" + redisExporter: + enabled: ${var.redis.exporter.enabled} + image: "${var.redis.exporter.image}" + securityContext: + runAsUser: 1000 + fsGroup: 1000 + EOF +} diff --git a/apps/dolibarr/saml.tf b/apps/dolibarr/saml.tf new file mode 100644 index 0000000..908e338 --- /dev/null +++ b/apps/dolibarr/saml.tf @@ -0,0 +1,56 @@ +data "authentik_flow" "default-authorization-flow" { + slug = "default-provider-authorization-implicit-consent" +} +data "authentik_flow" "default-authentication-flow" { + slug = "default-authentication-flow" +} + +data "authentik_property_mapping_saml" "saml_maps" { + managed_list = [ + "goauthentik.io/providers/saml/email", + "goauthentik.io/providers/saml/groups", + "goauthentik.io/providers/saml/name", + "goauthentik.io/providers/saml/upn", + "goauthentik.io/providers/saml/uid", + "goauthentik.io/providers/saml/username", + "goauthentik.io/providers/saml/ms-windowsaccountname", + ] +} + +data "authentik_property_mapping_saml" "saml_name" { + managed = "goauthentik.io/providers/saml/username" +} + +data "authentik_certificate_key_pair" "generated" { + name = "authentik Self-signed Certificate" +} + +resource "kubectl_manifest" "saml_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}-${var.component}-saml" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-${var.component}-saml" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "self-sign" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "authentik_provider_saml" "dolibarr" { + name = "dolibarr-${var.instance}-saml" + authentication_flow = data.authentik_flow.default-authentication-flow.id + authorization_flow = data.authentik_flow.default-authorization-flow.id + acs_url = "https://${var.sub-domain}.${var.domain-name}/custom/samlconnector/acs.php?entity=1&fk_idp=0" + property_mappings = data.authentik_property_mapping_saml.saml_maps.ids + name_id_mapping = data.authentik_property_mapping_saml.saml_name.id + signing_kp = data.authentik_certificate_key_pair.generated.id + sp_binding = "post" +} + diff --git a/apps/dolibarr/svc.tf b/apps/dolibarr/svc.tf new file mode 100644 index 0000000..fa6dc9a --- /dev/null +++ b/apps/dolibarr/svc.tf @@ -0,0 +1,18 @@ +resource "kubectl_manifest" "svc" { + yaml_body = <<-EOF +apiVersion: v1 +kind: Service +metadata: + name: ${var.instance} + namespace: "${var.namespace}" + labels: ${jsonencode(local.deploy-labels)} +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 3000 + selector: ${jsonencode(local.deploy-labels)} + type: ClusterIP + EOF +} diff --git a/apps/gitea/apps_v1_Deployment_gitea-memcached.yaml b/apps/gitea/apps_v1_Deployment_gitea-memcached.yaml new file mode 100644 index 0000000..b69e2c8 --- /dev/null +++ b/apps/gitea/apps_v1_Deployment_gitea-memcached.yaml @@ -0,0 +1,89 @@ +# Source: gitea/charts/memcached/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gitea-memcached + namespace: vynil-ci + labels: + app.kubernetes.io/name: memcached + helm.sh/chart: memcached-6.3.14 + app.kubernetes.io/instance: gitea + app.kubernetes.io/managed-by: Helm +spec: + selector: + matchLabels: + app.kubernetes.io/name: memcached + app.kubernetes.io/instance: gitea + replicas: 1 + strategy: + rollingUpdate: {} + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/name: memcached + helm.sh/chart: memcached-6.3.14 + app.kubernetes.io/instance: gitea + app.kubernetes.io/managed-by: Helm + annotations: + spec: + + affinity: + podAffinity: + + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: memcached + app.kubernetes.io/instance: gitea + topologyKey: kubernetes.io/hostname + weight: 1 + nodeAffinity: + + securityContext: + fsGroup: 1001 + serviceAccountName: default + containers: + - name: memcached + image: docker.io/bitnami/memcached:1.6.19-debian-11-r7 + imagePullPolicy: "IfNotPresent" + securityContext: + runAsNonRoot: true + runAsUser: 1001 + env: + - name: BITNAMI_DEBUG + value: "false" + - name: MEMCACHED_PORT_NUMBER + value: "11211" + ports: + - name: memcache + containerPort: 11211 + livenessProbe: + failureThreshold: 6 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + tcpSocket: + port: memcache + readinessProbe: + failureThreshold: 6 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + tcpSocket: + port: memcache + resources: + limits: {} + requests: + cpu: 250m + memory: 256Mi + volumeMounts: + - name: tmp + mountPath: /tmp + volumes: + - name: tmp + emptyDir: {} \ No newline at end of file diff --git a/apps/gitea/apps_v1_StatefulSet_gitea.yaml b/apps/gitea/apps_v1_StatefulSet_gitea.yaml new file mode 100644 index 0000000..6d0a21b --- /dev/null +++ b/apps/gitea/apps_v1_StatefulSet_gitea.yaml @@ -0,0 +1,247 @@ +# Source: gitea/templates/gitea/statefulset.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: gitea + annotations: + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + serviceName: gitea + template: + metadata: + annotations: + checksum/config: 27af0e4460a4b6fa0279e60d04c3d82609060dda7af59dd2051139acc1cdb203 + checksum/ldap_0: 9356e28431e375c7fc7d624460a9f41c243f14c3f9765c40aa2b13cf46203eaf + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm + spec: + + securityContext: + fsGroup: 1000 + initContainers: + - name: init-directories + image: "gitea/gitea:1.19.3" + imagePullPolicy: Always + command: ["/usr/sbin/init_directory_structure.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + - name: TZ + value: Europe/Paris + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + + securityContext: + capabilities: + add: + - SYS_CHROOT + resources: + limits: {} + requests: + cpu: 100m + memory: 128Mi + - name: init-app-ini + image: "gitea/gitea:1.19.3" + imagePullPolicy: Always + command: ["/usr/sbin/config_environment.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + - name: TZ + value: Europe/Paris + - name: ENV_TO_INI__DATABASE__LOG_SQL + value: "false" + - name: ENV_TO_INI__LOG__LEVEL + value: Debug + volumeMounts: + - name: config + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + - name: inline-config-sources + mountPath: /env-to-ini-mounts/inlines/ + + securityContext: + capabilities: + add: + - SYS_CHROOT + resources: + limits: {} + requests: + cpu: 100m + memory: 128Mi + - name: configure-gitea + image: "gitea/gitea:1.19.3" + command: ["/usr/sbin/configure_gitea.sh"] + imagePullPolicy: Always + securityContext: + capabilities: + add: + - SYS_CHROOT + runAsUser: 1000 + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + - name: GITEA_LDAP_BIND_DN_0 + valueFrom: + secretKeyRef: + key: bindDn + name: gitea-ldap + - name: GITEA_LDAP_PASSWORD_0 + valueFrom: + secretKeyRef: + key: bindPassword + name: gitea-ldap + - name: GITEA_ADMIN_USERNAME + valueFrom: + secretKeyRef: + key: username + name: gitea-admin-user + - name: GITEA_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: gitea-admin-user + - name: TZ + value: Europe/Paris + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + + resources: + limits: {} + requests: + cpu: 100m + memory: 128Mi + terminationGracePeriodSeconds: 60 + containers: + - name: gitea + image: "gitea/gitea:1.19.3" + imagePullPolicy: Always + env: + # SSH Port values have to be set here as well for openssh configuration + - name: SSH_LISTEN_PORT + value: "2222" + - name: SSH_PORT + value: "2222" + - name: SSH_LOG_LEVEL + value: "INFO" + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + - name: TMPDIR + value: /tmp/gitea + - name: TZ + value: Europe/Paris + ports: + - name: ssh + containerPort: 2222 + - name: http + containerPort: 3000 + livenessProbe: + failureThreshold: 10 + initialDelaySeconds: 200 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: http + timeoutSeconds: 1 + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: http + timeoutSeconds: 1 + resources: + {} + securityContext: + capabilities: + add: + - SYS_CHROOT + volumeMounts: + - name: temp + mountPath: /tmp + - name: data + mountPath: /data + - mountPath: /data/gitea/public/css + name: gitea-themes + volumes: + - name: init + secret: + secretName: gitea-init + defaultMode: 110 + - name: config + secret: + secretName: gitea + defaultMode: 110 + - configMap: + name: gitea-themes + name: gitea-themes + - name: inline-config-sources + secret: + secretName: gitea-inline-config + - name: temp + emptyDir: {} + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: "10Gi" \ No newline at end of file diff --git a/apps/gitea/datas.tf b/apps/gitea/datas.tf new file mode 100644 index 0000000..2b7dad6 --- /dev/null +++ b/apps/gitea/datas.tf @@ -0,0 +1,136 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/instance" = var.instance + } + removePatch = <<-EOF + - op: remove + path: /spec/loadBalancerIP + EOF + modifyPatch = <<-EOF + - op: replace + path: /spec/loadBalancerIP + value: "${var.load-balancer.ip}" + EOF +} + +data "kubernetes_secret_v1" "postgresql_password" { + depends_on = [kubectl_manifest.gitea_postgresql] + metadata { + name = "${var.component}.${var.instance}-${var.component}.credentials.postgresql.acid.zalan.do" + namespace = var.namespace + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} + +data "kustomization_overlay" "data" { + common_labels = local.common-labels + namespace = var.namespace + resources = [for file in fileset(path.module, "*.yaml"): file if ! contains(["index.yaml", "v1_ConfigMap_gitea-themes.yaml"], file)] + images { + name = "docker.io/bitnami/memcached" + new_name = "${var.images.memcached.registry}/${var.images.memcached.repository}" + new_tag = "${var.images.memcached.tag}" + } + patches { + target { + kind = "StatefulSet" + name = "gitea" + } + patch = <<-EOF + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: gitea + spec: + replicas: ${var.replicas} + template: + spec: + initContainers: + - name: init-directories + image: "${var.images.gitea.registry}/${var.images.gitea.repository}:${var.images.gitea.tag}" + imagePullPolicy: "${var.images.gitea.pullPolicy}" + - name: init-app-ini + image: "${var.images.gitea.registry}/${var.images.gitea.repository}:${var.images.gitea.tag}" + imagePullPolicy: IfNotPresent + - name: configure-gitea + image: "${var.images.gitea.registry}/${var.images.gitea.repository}:${var.images.gitea.tag}" + imagePullPolicy: IfNotPresent + env: + - name: LDAP_USER_SEARCH_BASE + valueFrom: + secretKeyRef: + key: user-search-base + name: gitea-ldap + - name: LDAP_USER_FILTER + valueFrom: + secretKeyRef: + key: user-filter + name: gitea-ldap + - name: LDAP_ADMIN_FILTER + valueFrom: + secretKeyRef: + key: admin-filter + name: gitea-ldap + - name: LDAP_HOST + valueFrom: + secretKeyRef: + key: endpoint + name: gitea-ldap + - name: TZ + value: ${var.timezone} + containers: + - name: gitea + image: "${var.images.gitea.registry}/${var.images.gitea.repository}:${var.images.gitea.tag}" + imagePullPolicy: IfNotPresent + env: + - name: SSH_LISTEN_PORT + value: "2222" + - name: SSH_PORT + value: "${var.ssh-port}" + - name: SSH_LOG_LEVEL + value: "INFO" + - name: TZ + value: ${var.timezone} + volumeClaimTemplates: + - metadata: + name: data + annotations: + k8up.io/backup: "true" + spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: "${var.volume.size}" + EOF + } + patches { + target { + kind = "Service" + name = "gitea-ssh" + } + patch = <<-EOF + - op: replace + path: /spec/ports/0/port + value: ${var.ssh-port} + EOF + } + patches { + target { + kind = "Service" + name = "gitea-ssh" + } + patch = var.load-balancer.ip==""?local.removePatch:local.modifyPatch + } +} diff --git a/apps/gitea/index.rhai b/apps/gitea/index.rhai new file mode 100644 index 0000000..04ad77f --- /dev/null +++ b/apps/gitea/index.rhai @@ -0,0 +1,18 @@ + +const NS=config.namespace; +const SRC=src; +const DEST=dest; +fn pre_pack() { + shell("helm repo add gitea-charts https://dl.gitea.io/charts/"); + shell(`helm template gitea gitea-charts/gitea --namespace=vynil-ci --values values.yml >${global::SRC}/chart.yaml`); +} +fn post_pack() { + shell(`rm -f ${global::DEST}/v1_Pod_gitea-test-connection.yaml`); + shell(`rm -f ${global::DEST}/v1_Secret_gitea-inline-config.yaml`); + let regex = "'\\\\\\${\\(LDAP_[A-Z_]*\\)}'"; + let final = "\\\"\\${\\1}\\\""; + shell(`sed -i "s/${regex}/${final}/g" ${global::DEST}/v1_Secret_gitea-init.yaml`); +} +fn pre_install() { + shell(`kubectl create -n ${global::NS} -f ${global::SRC}/v1_ConfigMap_gitea-themes.yaml || :`); +} diff --git a/apps/gitea/index.yaml b/apps/gitea/index.yaml new file mode 100644 index 0000000..7029348 --- /dev/null +++ b/apps/gitea/index.yaml @@ -0,0 +1,241 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: gitea + description: |- + Git with a cup of tea + A painless self-hosted Git service. + Gitea is a community managed lightweight code hosting solution written in Go. It is published under the MIT license. +options: + images: + default: + gitea: + pullPolicy: IfNotPresent + registry: docker.io + repository: gitea/gitea + tag: 1.19.3 + memcached: + registry: docker.io + repository: bitnami/memcached + tag: 1.6.19-debian-11-r7 + examples: + - gitea: + pullPolicy: IfNotPresent + registry: docker.io + repository: gitea/gitea + tag: 1.19.3 + memcached: + registry: docker.io + repository: bitnami/memcached + tag: 1.6.19-debian-11-r7 + properties: + gitea: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: gitea/gitea + tag: 1.19.3 + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: gitea/gitea + type: string + tag: + default: 1.19.3 + type: string + type: object + memcached: + default: + registry: docker.io + repository: bitnami/memcached + tag: 1.6.19-debian-11-r7 + properties: + registry: + default: docker.io + type: string + repository: + default: bitnami/memcached + type: string + tag: + default: 1.6.19-debian-11-r7 + type: string + type: object + type: object + load-balancer: + default: + ip: '' + examples: + - ip: '' + properties: + ip: + default: '' + type: string + type: object + webhook: + default: + allowed-hosts: private + skip-tls-verify: false + examples: + - allowed-hosts: private + skip-tls-verify: false + properties: + allowed-hosts: + default: private + type: string + skip-tls-verify: + default: false + type: boolean + type: object + release: + default: 8.3.0 + examples: + - 8.3.0 + type: string + push-create: + default: + org: 'true' + private: 'false' + user: 'true' + examples: + - org: 'true' + private: 'false' + user: 'true' + properties: + org: + default: 'true' + type: string + private: + default: 'false' + type: string + user: + default: 'true' + type: string + type: object + volume: + default: + size: 10Gi + examples: + - size: 10Gi + properties: + size: + default: 10Gi + type: string + type: object + default-branch: + default: main + examples: + - main + type: string + postgres: + default: + replicas: 1 + storage: 10Gi + version: '14' + examples: + - replicas: 1 + storage: 10Gi + version: '14' + properties: + replicas: + default: 1 + type: integer + storage: + default: 10Gi + type: string + version: + default: '14' + type: string + type: object + ingress-class: + default: traefik + examples: + - traefik + type: string + ssh-port: + default: 2222 + examples: + - 2222 + type: integer + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + domain: + default: your-company + examples: + - your-company + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + disable-registration: + default: true + examples: + - true + type: boolean + replicas: + default: 1 + examples: + - 1 + type: integer + sub-domain: + default: git + examples: + - git + type: string + admin: + default: + email: git-admin@git.your_company.com + name: gitea_admin + examples: + - email: git-admin@git.your_company.com + name: gitea_admin + properties: + email: + default: git-admin@git.your_company.com + type: string + name: + default: gitea_admin + type: string + type: object + timezone: + default: Europe/Paris + examples: + - Europe/Paris + type: string + theme: + default: gitea-modern + examples: + - gitea-modern + type: string +dependencies: +- dist: null + category: share + component: authentik-ldap +- dist: null + category: core + component: secret-generator +- dist: null + category: dbo + component: postgresql +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true diff --git a/apps/gitea/ingress.tf b/apps/gitea/ingress.tf new file mode 100644 index 0000000..0ded2c5 --- /dev/null +++ b/apps/gitea/ingress.tf @@ -0,0 +1,69 @@ + +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = [{"name" = "${var.instance}-https"}] + services = [{ + "kind" = "Service" + "name" = "gitea-http" + "namespace" = var.namespace + "port" = 3000 + }] + routes = [ for v in local.dns-names : { + "kind" = "Rule" + "match" = "Host(`${v}`)" + "middlewares" = local.middlewares + "services" = local.services + }] +} + +resource "kubectl_manifest" "gitea_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "gitea_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "gitea_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "IngressRoute" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + # annotations: + # "kubernetes.io/ingress.class": "${var.ingress-class}" + spec: + entryPoints: ["web","websecure"] + routes: ${jsonencode(local.routes)} + tls: + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/gitea/inline-config.tf b/apps/gitea/inline-config.tf new file mode 100644 index 0000000..5788e26 --- /dev/null +++ b/apps/gitea/inline-config.tf @@ -0,0 +1,54 @@ +resource "kubernetes_secret_v1" "gitea_inline_config" { + metadata { + name = "gitea-inline-config" + namespace = var.namespace + labels = local.common-labels + } + + data = { + "_generals_" = "" + metrics = "ENABLED=true" + security = "INSTALL_LOCK=true" + service = "DISABLE_REGISTRATION=${var.disable-registration}" + cache = <<-EOF +ADAPTER=memcache +ENABLED=true +HOST=gitea-memcached.${var.namespace}.svc:11211 + EOF + database = <<-EOF +DB_TYPE=postgres +HOST=${var.instance}-${var.component}.${var.namespace}.svc:5432 +NAME=${var.component} +PASSWD=${data.kubernetes_secret_v1.postgresql_password.data["password"]} +USER=${data.kubernetes_secret_v1.postgresql_password.data["username"]} +SSL_MODE=require + EOF + repository = <<-EOF +DEFAULT_BRANCH=${var.default-branch} +DEFAULT_PUSH_CREATE_PRIVATE=${var.push-create.private} +ENABLE_PUSH_CREATE_ORG=${var.push-create.org} +ENABLE_PUSH_CREATE_USER=${var.push-create.user} +ROOT=/data/git/gitea-repositories + EOF + server = <<-EOF +APP_DATA_PATH=/data +DOMAIN=${var.sub-domain}.${var.domain-name} +ENABLE_PPROF=false +HTTP_PORT=3000 +PROTOCOL=http +ROOT_URL=https://${var.sub-domain}.${var.domain-name} +SSH_DOMAIN=${var.sub-domain}.${var.domain-name} +SSH_LISTEN_PORT=${var.ssh-port} +SSH_PORT=${var.ssh-port} + EOF + ui = <<-EOF +DEFAULT_THEME=${var.theme} +SHOW_USER_EMAIL=false +THEMES=auto,gitea,arc-green,edge-auto,edge-dark,edge-light,everforest-auto,everforest-dark,everforest-light,gitea-modern,gruvbox-auto,gruvbox-dark,gruvbox-light,gruvbox-material-auto,gruvbox-material-dark,gruvbox-material-light,palenight,soft-era,sonokai-andromeda,sonokai-atlantis,sonokai-espresso,sonokai-maia,sonokai-shusia,sonokai,theme-nord + EOF + webhook = <<-EOF +ALLOWED_HOST_LIST=${var.webhook.allowed-hosts} +SKIP_TLS_VERIFY=${var.webhook.skip-tls-verify} + EOF + } +} diff --git a/apps/gitea/ldap.tf b/apps/gitea/ldap.tf new file mode 100644 index 0000000..7036002 --- /dev/null +++ b/apps/gitea/ldap.tf @@ -0,0 +1,149 @@ + +locals { + base-dn = format("dc=%s", join(",dc=", split(".", format("%s.%s", var.sub-domain, var.domain-name)))) + base-group-dn = format("ou=groups,%s", local.base-dn) + base-user-dn = format("ou=users,%s", local.base-dn) + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + ldap-outpost-prividers = jsondecode(data.http.get_ldap_outpost.response_body).results[0].providers + ldap-outpost-pk = jsondecode(data.http.get_ldap_outpost.response_body).results[0].pk +} +resource "kubectl_manifest" "gitea_ldap" { + ignore_fields = ["metadata.annotations"] + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "${var.component}-ldap" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forceRegenerate: false + data: + bindDn: "cn=${var.component}-ldapsearch,${local.base-user-dn}" + user-search-base: "${local.base-user-dn}" + user-filter: "(&(|(memberof=cn=gitea_admin,${local.base-group-dn})(memberof=cn=gitea_users,${local.base-group-dn}))(|(cn=%[1]s)(mail=%[1]s)))" + admin-filter: "(memberof=cn=gitea_admin,${local.base-group-dn})" + endpoint: "ak-outpost-ldap.${var.domain}-auth.svc" + fields: + - fieldName: "bindPassword" + length: "32" + EOF +} +data "kubernetes_secret_v1" "gitea_ldap_password" { + depends_on = [kubectl_manifest.gitea_ldap] + metadata { + name = kubectl_manifest.gitea_ldap.name + namespace = var.namespace + } +} + +resource "authentik_user" "gitea_ldapsearch" { + username = "${var.component}-ldapsearch" + name = "${var.component}-ldapsearch" +} + +resource "authentik_group" "gitea_ldapsearch" { + name = "${var.component}-ldapsearch" + users = [authentik_user.gitea_ldapsearch.id] + is_superuser = true +} + + +data "http" "gitea_ldapsearch_password" { + url = "http://authentik.${var.domain}-auth.svc/api/v3/core/users/${authentik_user.gitea_ldapsearch.id}/set_password/" + method = "POST" + request_headers = local.request_headers + request_body = jsonencode({password=data.kubernetes_secret_v1.gitea_ldap_password.data["bindPassword"]}) + lifecycle { + postcondition { + condition = contains([201, 204], self.status_code) + error_message = "Status code invalid" + } + } +} + +data "authentik_flow" "ldap-authentication-flow" { + depends_on = [authentik_user.gitea_ldapsearch] # fake dependency so it is not evaluated at plan stage + slug = "ldap-authentication-flow" +} + +resource "authentik_provider_ldap" "gitea_provider_ldap" { + name = "gitea-ldap-provider" + base_dn = local.base-dn + search_group = authentik_group.gitea_ldapsearch.id + bind_flow = data.authentik_flow.ldap-authentication-flow.id +} + +resource "authentik_application" "gitea_application" { + name = "gitea" + slug = "gitea" + protocol_provider = authentik_provider_ldap.gitea_provider_ldap.id + meta_launch_url = format("https://%s.%s", var.sub-domain, var.domain-name) + meta_icon = format("https://%s.%s/%s", var.sub-domain, var.domain-name, "assets/img/logo.svg") +} + +resource "authentik_group" "gitea_users" { + name = "gitea_users" +} + +data "authentik_group" "vynil-admin" { + depends_on = [authentik_group.gitea_users] # fake dependency so it is not evaluated at plan stage + name = "vynil-ldap-admins" +} + +resource "authentik_group" "gitea_admin" { + name = "gitea_admin" + parent = authentik_group.gitea_users.id +} + +resource "authentik_policy_binding" "gitea_access_users" { + target = authentik_application.gitea_application.uuid + group = authentik_group.gitea_users.id + order = 0 +} +resource "authentik_policy_binding" "gitea_access_vynil" { + target = authentik_application.gitea_application.uuid + group = data.authentik_group.vynil-admin.id + order = 1 +} +resource "authentik_policy_binding" "gitea_access_ldap" { + target = authentik_application.gitea_application.uuid + group = authentik_group.gitea_ldapsearch.id + order = 2 +} + +data "http" "get_ldap_outpost" { + depends_on = [authentik_group.gitea_users] # fake dependency so it is not evaluated at plan stage + url = "http://authentik.${var.domain}-auth.svc/api/v3/outposts/instances/?name__iexact=ldap" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +provider "restapi" { + uri = "http://authentik.${var.domain}-auth.svc/api/v3/" + headers = local.request_headers + create_method = "PATCH" + update_method = "PATCH" + destroy_method = "PATCH" + write_returns_object = true + id_attribute = "name" +} + +resource "restapi_object" "ldap_outpost_binding" { + path = "/outposts/instances/${local.ldap-outpost-pk}/" + data = jsonencode({ + name = "ldap" + providers = contains(local.ldap-outpost-prividers, authentik_provider_ldap.gitea_provider_ldap.id) ? local.ldap-outpost-prividers : concat(local.ldap-outpost-prividers, [authentik_provider_ldap.gitea_provider_ldap.id]) + }) +} + diff --git a/apps/gitea/postgresql.tf b/apps/gitea/postgresql.tf new file mode 100644 index 0000000..da30ad4 --- /dev/null +++ b/apps/gitea/postgresql.tf @@ -0,0 +1,31 @@ +locals { + pg-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "postgresql" + }) +} +resource "kubectl_manifest" "gitea_postgresql" { + yaml_body = <<-EOF + apiVersion: "acid.zalan.do/v1" + kind: "postgresql" + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + spec: + databases: + ${var.component}: "${var.component}" + numberOfInstances: ${var.postgres.replicas} + podAnnotations: + "k8up.io/backupcommand": "pg_dump -U postgres -d ${var.component} --clean" + "k8up.io/file-extension": ".sql" + postgresql: + version: "${var.postgres.version}" + teamId: "${var.instance}" + users: + ${var.component}: + - "superuser" + - "createdb" + volume: + size: "${var.postgres.storage}" + EOF +} diff --git a/apps/gitea/secret.tf b/apps/gitea/secret.tf new file mode 100644 index 0000000..a3db92e --- /dev/null +++ b/apps/gitea/secret.tf @@ -0,0 +1,19 @@ + +resource "kubectl_manifest" "gitea_secret" { + ignore_fields = ["metadata.annotations"] + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "gitea-admin-user" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forceRegenerate: false + data: + username: "${var.admin.name}" + fields: + - fieldName: "password" + length: "32" + EOF +} diff --git a/apps/gitea/v1_ConfigMap_gitea-themes.yaml b/apps/gitea/v1_ConfigMap_gitea-themes.yaml new file mode 100644 index 0000000..3ffbf5a --- /dev/null +++ b/apps/gitea/v1_ConfigMap_gitea-themes.yaml @@ -0,0 +1,2030 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: gitea-themes +data: + theme-edge-auto.css: '@media (prefers-color-scheme:dark){:root{--is-dark-theme:true;--color-primary:#6cb6eb;--color-primary-contrast:#2b2d37;--color-primary-dark-1:#6cb6eb;--color-primary-dark-2:#7bbae8;--color-primary-dark-3:#8abee5;--color-primary-dark-4:#99c2e2;--color-primary-dark-5:#a7c5df;--color-primary-dark-6:#b6c9dc;--color-primary-dark-7:#c5cdd9;--color-primary-light-1:#6cb6eb;--color-primary-light-2:#63a1d0;--color-primary-light-3:#5a8db5;--color-primary-light-4:#51789a;--color-primary-light-5:#48637f;--color-primary-light-6:#3f4f64;--color-primary-light-7:#363a49;--color-primary-alpha-10:rgba(108,182,235,0.1);--color-primary-alpha-20:rgba(108,182,235,0.2);--color-primary-alpha-30:rgba(108,182,235,0.3);--color-primary-alpha-40:rgba(108,182,235,0.4);--color-primary-alpha-50:rgba(108,182,235,0.5);--color-primary-alpha-60:rgba(108,182,235,0.6);--color-primary-alpha-70:rgba(108,182,235,0.7);--color-primary-alpha-80:rgba(108,182,235,0.8);--color-primary-alpha-90:rgba(108,182,235,0.9);--color-secondary:#404455;--color-secondary-dark-1:#404455;--color-secondary-dark-2:#4b4f60;--color-secondary-dark-3:#565b6b;--color-secondary-dark-4:#616676;--color-secondary-dark-5:#6c7281;--color-secondary-dark-6:#777d8c;--color-secondary-dark-7:#838997;--color-secondary-dark-8:#8e94a2;--color-secondary-dark-9:#999fad;--color-secondary-dark-10:#a4abb8;--color-secondary-dark-11:#afb6c3;--color-secondary-dark-12:#bac2ce;--color-secondary-dark-13:#c5cdd9;--color-secondary-light-1:#404455;--color-secondary-light-2:#3a3e4e;--color-secondary-light-3:#363a49;--color-secondary-light-4:#333644;--color-secondary-alpha-10:rgba(64,68,85,0.1);--color-secondary-alpha-20:rgba(64,68,85,0.2);--color-secondary-alpha-30:rgba(64,68,85,0.3);--color-secondary-alpha-40:rgba(64,68,85,0.4);--color-secondary-alpha-50:rgba(64,68,85,0.5);--color-secondary-alpha-60:rgba(64,68,85,0.6);--color-secondary-alpha-70:rgba(64,68,85,0.7);--color-secondary-alpha-80:rgba(64,68,85,0.8);--color-secondary-alpha-90:rgba(64,68,85,0.9);--color-red:#ec7279;--color-orange:#deb974;--color-yellow:#deb974;--color-olive:#a0c980;--color-green:#a0c980;--color-teal:#5dbbc1;--color-blue:#6cb6eb;--color-violet:#d38aea;--color-purple:#d38aea;--color-pink:#d38aea;--color-brown:#deb974;--color-grey:#758094;--color-red-light:#ec7279;--color-orange-light:#deb974;--color-yellow-light:#deb974;--color-olive-light:#a0c980;--color-green-light:#a0c980;--color-teal-light:#5dbbc1;--color-blue-light:#6cb6eb;--color-violet-light:#d38aea;--color-purple-light:#d38aea;--color-pink-light:#d38aea;--color-brown-light:#deb974;--color-grey-light:#758094;--color-black:#363a49;--color-gold:#deb974;--color-white:#2b2d37;--color-diff-removed-word-bg:#824a4f;--color-diff-added-word-bg:#586d4b;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a1565b;--color-diff-moved-row-border:#517ca1;--color-diff-added-row-border:#6d885a;--color-diff-inactive:#363a49;--color-error-border:#a1565b;--color-error-bg:#55393d;--color-error-text:#ec7279;--color-success-border:#6d885a;--color-success-bg:#394634;--color-success-text:#a0c980;--color-warning-border:#967e52;--color-warning-bg:#4e432f;--color-warning-text:#deb974;--color-info-border:#517ca1;--color-info-bg:#354157;--color-info-text:#6cb6eb;--color-body:#2b2d37;--color-box-header:#3a3e4e;--color-box-body:#333644;--color-box-body-highlight:#363a49;--color-text-dark:#d38aea;--color-text:#c5cdd9;--color-text-hover:rgba(197,205,217,0.8);--color-text-light:#a0c980;--color-text-light-1:#758094;--color-text-light-2:#c5cdd9;--color-text-light-3:#758094;--color-footer:#333644;--color-timeline:#404455;--color-input-text:#c5cdd9;--color-input-background:#2b2d37;--color-input-toggle-background:#333644;--color-input-border:#404455;--color-input-border-hover:#5b6275;--color-navbar:#333644;--color-navbar-transparent:#333644;--color-light:#333644;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#404455;--color-hover:rgba(64,68,85,0.5);--color-active:rgba(64,68,85,0.5);--color-menu:#333644;--color-card:#333644;--color-markup-table-row:#3a3e4e;--color-markup-code-block:#2b2d37;--color-button:#3a3e4e;--color-code-bg:#2b2d37;--color-code-sidebar-bg:#333644;--color-shadow:#00000060;--color-secondary-bg:#333644;--color-text-focus:#fff;--color-expand-button:#363a49;--color-placeholder-text:#758094;--color-editor-line-highlight:#333644;--color-project-board-bg:#2b2d37;--color-project-board-light-label:#6cb6eb;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#333644;--color-label-active-bg:#404455;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#333644;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#c5cdd9}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#758094}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#ec7279}.chroma + .o,.chroma .ow{color:#d38aea}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#a0c980}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#a0c980}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#deb974}.chroma .nf,.chroma .nb,.chroma .na{color:#6cb6eb}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#5dbbc1}.ui.primary.button,.ui.primary.buttons + .button{background:#6cb6eb;background-color:#6cb6eb !important;color:#2b2d37}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(108,182,235,0.8);background-color:rgba(108,182,235,0.8) + !important;color:#2b2d37}.ui.green.buttons .button,.ui.green.button{background:#a0c980;background-color:#a0c980;color:#2b2d37}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(160,201,128,0.8);background-color:rgba(160,201,128,0.8);color:#2b2d37}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#d38aea}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#d38aea}.repo-title{color:#d38aea}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#6cb6eb}.ui.basic.labels + .label,.ui.basic.label{color:#6cb6eb}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2b2d37}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2b2d37}i.grey.icon.icon.icon.icon{color:#758094}*{scrollbar-color:#d38aea + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #d38aea !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #d38aea !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #d38aea !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ec7279;background-color:#ec7279;color:#2b2d37}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(236,114,121,0.8);background-color:rgba(236,114,121,0.8);color:#2b2d37}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2b2d37}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2b2d37}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#deb974;background-color:#deb974;color:#2b2d37}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(222,185,116,0.8);background-color:rgba(222,185,116,0.8);color:#2b2d37}}@media + (prefers-color-scheme:light){:root{--is-dark-theme:true;--color-primary:#5079be;--color-primary-contrast:#fafafa;--color-primary-dark-1:#5079be;--color-primary-dark-2:#4f72ad;--color-primary-dark-3:#4e6b9d;--color-primary-dark-4:#4e658d;--color-primary-dark-5:#4d5e7c;--color-primary-dark-6:#4c576c;--color-primary-dark-7:#4b505b;--color-primary-light-1:#5079be;--color-primary-light-2:#698cc6;--color-primary-light-3:#839fcf;--color-primary-light-4:#9cb2d7;--color-primary-light-5:#b5c5df;--color-primary-light-6:#cfd8e8;--color-primary-light-7:#e8ebf0;--color-primary-alpha-10:rgba(80,121,190,0.1);--color-primary-alpha-20:rgba(80,121,190,0.2);--color-primary-alpha-30:rgba(80,121,190,0.3);--color-primary-alpha-40:rgba(80,121,190,0.4);--color-primary-alpha-50:rgba(80,121,190,0.5);--color-primary-alpha-60:rgba(80,121,190,0.6);--color-primary-alpha-70:rgba(80,121,190,0.7);--color-primary-alpha-80:rgba(80,121,190,0.8);--color-primary-alpha-90:rgba(80,121,190,0.9);--color-secondary:#dde2e7;--color-secondary-dark-1:#dde2e7;--color-secondary-dark-2:#d1d6db;--color-secondary-dark-3:#c5cad0;--color-secondary-dark-4:#b9bec4;--color-secondary-dark-5:#acb1b8;--color-secondary-dark-6:#a0a5ad;--color-secondary-dark-7:#9499a1;--color-secondary-dark-8:#888d95;--color-secondary-dark-9:#7c818a;--color-secondary-dark-10:#70757e;--color-secondary-dark-11:#636872;--color-secondary-dark-12:#575c67;--color-secondary-dark-13:#4b505b;--color-secondary-light-1:#dde2e7;--color-secondary-light-2:#e8ebf0;--color-secondary-light-3:#e8ebf0;--color-secondary-light-4:#eef1f4;--color-secondary-alpha-10:rgba(221,226,231,0.1);--color-secondary-alpha-20:rgba(221,226,231,0.2);--color-secondary-alpha-30:rgba(221,226,231,0.3);--color-secondary-alpha-40:rgba(221,226,231,0.4);--color-secondary-alpha-50:rgba(221,226,231,0.5);--color-secondary-alpha-60:rgba(221,226,231,0.6);--color-secondary-alpha-70:rgba(221,226,231,0.7);--color-secondary-alpha-80:rgba(221,226,231,0.8);--color-secondary-alpha-90:rgba(221,226,231,0.9);--color-red:#d05858;--color-orange:#be7e05;--color-yellow:#be7e05;--color-olive:#608e32;--color-green:#608e32;--color-teal:#3a8b84;--color-blue:#5079be;--color-violet:#b05ccc;--color-purple:#b05ccc;--color-pink:#b05ccc;--color-brown:#be7e05;--color-grey:#8790a0;--color-red-light:#d05858;--color-orange-light:#be7e05;--color-yellow-light:#be7e05;--color-olive-light:#608e32;--color-green-light:#608e32;--color-teal-light:#3a8b84;--color-blue-light:#5079be;--color-violet-light:#b05ccc;--color-purple-light:#b05ccc;--color-pink-light:#b05ccc;--color-brown-light:#be7e05;--color-grey-light:#8790a0;--color-black:#e8ebf0;--color-gold:#be7e05;--color-white:#fafafa;--color-diff-removed-word-bg:#ebbaba;--color-diff-added-word-bg:#bdd1af;--color-diff-removed-row-bg:#f6e4e4;--color-diff-moved-row-bg:#e3eaf6;--color-diff-added-row-bg:#e5eee4;--color-diff-removed-row-border:#e39e9e;--color-diff-moved-row-border:#9ab2da;--color-diff-added-row-border:#a3be8b;--color-diff-inactive:#e8ebf0;--color-error-border:#e39e9e;--color-error-bg:#f6e4e4;--color-error-text:#d05858;--color-success-border:#a3be8b;--color-success-bg:#e5eee4;--color-success-text:#608e32;--color-warning-border:#d7b574;--color-warning-bg:#f0ece2;--color-warning-text:#be7e05;--color-info-border:#9ab2da;--color-info-bg:#e3eaf6;--color-info-text:#5079be;--color-body:#fafafa;--color-box-header:#e8ebf0;--color-box-body:#eef1f4;--color-box-body-highlight:#e8ebf0;--color-text-dark:#b05ccc;--color-text:#4b505b;--color-text-hover:rgba(75,80,91,0.8);--color-text-light:#608e32;--color-text-light-1:#8790a0;--color-text-light-2:#4b505b;--color-text-light-3:#8790a0;--color-footer:#eef1f4;--color-timeline:#dde2e7;--color-input-text:#4b505b;--color-input-background:#fafafa;--color-input-toggle-background:#eef1f4;--color-input-border:#dde2e7;--color-input-border-hover:#b2b9c4;--color-navbar:#eef1f4;--color-navbar-transparent:#eef1f4;--color-light:#eef1f4;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#dde2e7;--color-hover:rgba(221,226,231,0.5);--color-active:rgba(221,226,231,0.5);--color-menu:#eef1f4;--color-card:#eef1f4;--color-markup-table-row:#e8ebf0;--color-markup-code-block:#fafafa;--color-button:#e8ebf0;--color-code-bg:#fafafa;--color-code-sidebar-bg:#eef1f4;--color-shadow:#00000060;--color-secondary-bg:#eef1f4;--color-text-focus:#fff;--color-expand-button:#e8ebf0;--color-placeholder-text:#8790a0;--color-editor-line-highlight:#eef1f4;--color-project-board-bg:#fafafa;--color-project-board-light-label:#5079be;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#eef1f4;--color-label-active-bg:#dde2e7;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#eef1f4;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#4b505b}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#8790a0}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#d05858}.chroma + .o,.chroma .ow{color:#b05ccc}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#608e32}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#608e32}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#be7e05}.chroma .nf,.chroma .nb,.chroma .na{color:#5079be}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#3a8b84}.ui.primary.button,.ui.primary.buttons + .button{background:#6996e0;background-color:#6996e0 !important;color:#fafafa}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(105,150,224,0.8);background-color:rgba(105,150,224,0.8) + !important;color:#fafafa}.ui.green.buttons .button,.ui.green.button{background:#76af6f;background-color:#76af6f;color:#fafafa}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(118,175,111,0.8);background-color:rgba(118,175,111,0.8);color:#fafafa}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#b05ccc}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#b05ccc}.repo-title{color:#b05ccc}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#5079be}.ui.basic.labels + .label,.ui.basic.label{color:#5079be}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fafafa}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fafafa}i.grey.icon.icon.icon.icon{color:#8790a0}*{scrollbar-color:#b05ccc + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #b05ccc !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #b05ccc !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #b05ccc !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e17373;background-color:#e17373;color:#fafafa}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(225,115,115,0.8);background-color:rgba(225,115,115,0.8);color:#fafafa}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fafafa}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fafafa}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#be7e05;background-color:#be7e05;color:#fafafa}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(190,126,5,0.8);background-color:rgba(190,126,5,0.8);color:#fafafa}}' + theme-edge-dark.css: ':root{--is-dark-theme:true;--color-primary:#6cb6eb;--color-primary-contrast:#2b2d37;--color-primary-dark-1:#6cb6eb;--color-primary-dark-2:#7bbae8;--color-primary-dark-3:#8abee5;--color-primary-dark-4:#99c2e2;--color-primary-dark-5:#a7c5df;--color-primary-dark-6:#b6c9dc;--color-primary-dark-7:#c5cdd9;--color-primary-light-1:#6cb6eb;--color-primary-light-2:#63a1d0;--color-primary-light-3:#5a8db5;--color-primary-light-4:#51789a;--color-primary-light-5:#48637f;--color-primary-light-6:#3f4f64;--color-primary-light-7:#363a49;--color-primary-alpha-10:rgba(108,182,235,0.1);--color-primary-alpha-20:rgba(108,182,235,0.2);--color-primary-alpha-30:rgba(108,182,235,0.3);--color-primary-alpha-40:rgba(108,182,235,0.4);--color-primary-alpha-50:rgba(108,182,235,0.5);--color-primary-alpha-60:rgba(108,182,235,0.6);--color-primary-alpha-70:rgba(108,182,235,0.7);--color-primary-alpha-80:rgba(108,182,235,0.8);--color-primary-alpha-90:rgba(108,182,235,0.9);--color-secondary:#404455;--color-secondary-dark-1:#404455;--color-secondary-dark-2:#4b4f60;--color-secondary-dark-3:#565b6b;--color-secondary-dark-4:#616676;--color-secondary-dark-5:#6c7281;--color-secondary-dark-6:#777d8c;--color-secondary-dark-7:#838997;--color-secondary-dark-8:#8e94a2;--color-secondary-dark-9:#999fad;--color-secondary-dark-10:#a4abb8;--color-secondary-dark-11:#afb6c3;--color-secondary-dark-12:#bac2ce;--color-secondary-dark-13:#c5cdd9;--color-secondary-light-1:#404455;--color-secondary-light-2:#3a3e4e;--color-secondary-light-3:#363a49;--color-secondary-light-4:#333644;--color-secondary-alpha-10:rgba(64,68,85,0.1);--color-secondary-alpha-20:rgba(64,68,85,0.2);--color-secondary-alpha-30:rgba(64,68,85,0.3);--color-secondary-alpha-40:rgba(64,68,85,0.4);--color-secondary-alpha-50:rgba(64,68,85,0.5);--color-secondary-alpha-60:rgba(64,68,85,0.6);--color-secondary-alpha-70:rgba(64,68,85,0.7);--color-secondary-alpha-80:rgba(64,68,85,0.8);--color-secondary-alpha-90:rgba(64,68,85,0.9);--color-red:#ec7279;--color-orange:#deb974;--color-yellow:#deb974;--color-olive:#a0c980;--color-green:#a0c980;--color-teal:#5dbbc1;--color-blue:#6cb6eb;--color-violet:#d38aea;--color-purple:#d38aea;--color-pink:#d38aea;--color-brown:#deb974;--color-grey:#758094;--color-red-light:#ec7279;--color-orange-light:#deb974;--color-yellow-light:#deb974;--color-olive-light:#a0c980;--color-green-light:#a0c980;--color-teal-light:#5dbbc1;--color-blue-light:#6cb6eb;--color-violet-light:#d38aea;--color-purple-light:#d38aea;--color-pink-light:#d38aea;--color-brown-light:#deb974;--color-grey-light:#758094;--color-black:#363a49;--color-gold:#deb974;--color-white:#2b2d37;--color-diff-removed-word-bg:#824a4f;--color-diff-added-word-bg:#586d4b;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a1565b;--color-diff-moved-row-border:#517ca1;--color-diff-added-row-border:#6d885a;--color-diff-inactive:#363a49;--color-error-border:#a1565b;--color-error-bg:#55393d;--color-error-text:#ec7279;--color-success-border:#6d885a;--color-success-bg:#394634;--color-success-text:#a0c980;--color-warning-border:#967e52;--color-warning-bg:#4e432f;--color-warning-text:#deb974;--color-info-border:#517ca1;--color-info-bg:#354157;--color-info-text:#6cb6eb;--color-body:#2b2d37;--color-box-header:#3a3e4e;--color-box-body:#333644;--color-box-body-highlight:#363a49;--color-text-dark:#d38aea;--color-text:#c5cdd9;--color-text-hover:rgba(197,205,217,0.8);--color-text-light:#a0c980;--color-text-light-1:#758094;--color-text-light-2:#c5cdd9;--color-text-light-3:#758094;--color-footer:#333644;--color-timeline:#404455;--color-input-text:#c5cdd9;--color-input-background:#2b2d37;--color-input-toggle-background:#333644;--color-input-border:#404455;--color-input-border-hover:#5b6275;--color-navbar:#333644;--color-navbar-transparent:#333644;--color-light:#333644;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#404455;--color-hover:rgba(64,68,85,0.5);--color-active:rgba(64,68,85,0.5);--color-menu:#333644;--color-card:#333644;--color-markup-table-row:#3a3e4e;--color-markup-code-block:#2b2d37;--color-button:#3a3e4e;--color-code-bg:#2b2d37;--color-code-sidebar-bg:#333644;--color-shadow:#00000060;--color-secondary-bg:#333644;--color-text-focus:#fff;--color-expand-button:#363a49;--color-placeholder-text:#758094;--color-editor-line-highlight:#333644;--color-project-board-bg:#2b2d37;--color-project-board-light-label:#6cb6eb;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#333644;--color-label-active-bg:#404455;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#333644;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#c5cdd9}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#758094}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#ec7279}.chroma + .o,.chroma .ow{color:#d38aea}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#a0c980}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#a0c980}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#deb974}.chroma .nf,.chroma .nb,.chroma .na{color:#6cb6eb}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#5dbbc1}.ui.primary.button,.ui.primary.buttons + .button{background:#6cb6eb;background-color:#6cb6eb !important;color:#2b2d37}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(108,182,235,0.8);background-color:rgba(108,182,235,0.8) + !important;color:#2b2d37}.ui.green.buttons .button,.ui.green.button{background:#a0c980;background-color:#a0c980;color:#2b2d37}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(160,201,128,0.8);background-color:rgba(160,201,128,0.8);color:#2b2d37}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#d38aea}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#d38aea}.repo-title{color:#d38aea}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#6cb6eb}.ui.basic.labels + .label,.ui.basic.label{color:#6cb6eb}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2b2d37}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2b2d37}i.grey.icon.icon.icon.icon{color:#758094}*{scrollbar-color:#d38aea + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #d38aea !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #d38aea !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #d38aea !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ec7279;background-color:#ec7279;color:#2b2d37}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(236,114,121,0.8);background-color:rgba(236,114,121,0.8);color:#2b2d37}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2b2d37}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2b2d37}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#deb974;background-color:#deb974;color:#2b2d37}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(222,185,116,0.8);background-color:rgba(222,185,116,0.8);color:#2b2d37}' + theme-edge-light.css: ':root{--is-dark-theme:true;--color-primary:#5079be;--color-primary-contrast:#fafafa;--color-primary-dark-1:#5079be;--color-primary-dark-2:#4f72ad;--color-primary-dark-3:#4e6b9d;--color-primary-dark-4:#4e658d;--color-primary-dark-5:#4d5e7c;--color-primary-dark-6:#4c576c;--color-primary-dark-7:#4b505b;--color-primary-light-1:#5079be;--color-primary-light-2:#698cc6;--color-primary-light-3:#839fcf;--color-primary-light-4:#9cb2d7;--color-primary-light-5:#b5c5df;--color-primary-light-6:#cfd8e8;--color-primary-light-7:#e8ebf0;--color-primary-alpha-10:rgba(80,121,190,0.1);--color-primary-alpha-20:rgba(80,121,190,0.2);--color-primary-alpha-30:rgba(80,121,190,0.3);--color-primary-alpha-40:rgba(80,121,190,0.4);--color-primary-alpha-50:rgba(80,121,190,0.5);--color-primary-alpha-60:rgba(80,121,190,0.6);--color-primary-alpha-70:rgba(80,121,190,0.7);--color-primary-alpha-80:rgba(80,121,190,0.8);--color-primary-alpha-90:rgba(80,121,190,0.9);--color-secondary:#dde2e7;--color-secondary-dark-1:#dde2e7;--color-secondary-dark-2:#d1d6db;--color-secondary-dark-3:#c5cad0;--color-secondary-dark-4:#b9bec4;--color-secondary-dark-5:#acb1b8;--color-secondary-dark-6:#a0a5ad;--color-secondary-dark-7:#9499a1;--color-secondary-dark-8:#888d95;--color-secondary-dark-9:#7c818a;--color-secondary-dark-10:#70757e;--color-secondary-dark-11:#636872;--color-secondary-dark-12:#575c67;--color-secondary-dark-13:#4b505b;--color-secondary-light-1:#dde2e7;--color-secondary-light-2:#e8ebf0;--color-secondary-light-3:#e8ebf0;--color-secondary-light-4:#eef1f4;--color-secondary-alpha-10:rgba(221,226,231,0.1);--color-secondary-alpha-20:rgba(221,226,231,0.2);--color-secondary-alpha-30:rgba(221,226,231,0.3);--color-secondary-alpha-40:rgba(221,226,231,0.4);--color-secondary-alpha-50:rgba(221,226,231,0.5);--color-secondary-alpha-60:rgba(221,226,231,0.6);--color-secondary-alpha-70:rgba(221,226,231,0.7);--color-secondary-alpha-80:rgba(221,226,231,0.8);--color-secondary-alpha-90:rgba(221,226,231,0.9);--color-red:#d05858;--color-orange:#be7e05;--color-yellow:#be7e05;--color-olive:#608e32;--color-green:#608e32;--color-teal:#3a8b84;--color-blue:#5079be;--color-violet:#b05ccc;--color-purple:#b05ccc;--color-pink:#b05ccc;--color-brown:#be7e05;--color-grey:#8790a0;--color-red-light:#d05858;--color-orange-light:#be7e05;--color-yellow-light:#be7e05;--color-olive-light:#608e32;--color-green-light:#608e32;--color-teal-light:#3a8b84;--color-blue-light:#5079be;--color-violet-light:#b05ccc;--color-purple-light:#b05ccc;--color-pink-light:#b05ccc;--color-brown-light:#be7e05;--color-grey-light:#8790a0;--color-black:#e8ebf0;--color-gold:#be7e05;--color-white:#fafafa;--color-diff-removed-word-bg:#ebbaba;--color-diff-added-word-bg:#bdd1af;--color-diff-removed-row-bg:#f6e4e4;--color-diff-moved-row-bg:#e3eaf6;--color-diff-added-row-bg:#e5eee4;--color-diff-removed-row-border:#e39e9e;--color-diff-moved-row-border:#9ab2da;--color-diff-added-row-border:#a3be8b;--color-diff-inactive:#e8ebf0;--color-error-border:#e39e9e;--color-error-bg:#f6e4e4;--color-error-text:#d05858;--color-success-border:#a3be8b;--color-success-bg:#e5eee4;--color-success-text:#608e32;--color-warning-border:#d7b574;--color-warning-bg:#f0ece2;--color-warning-text:#be7e05;--color-info-border:#9ab2da;--color-info-bg:#e3eaf6;--color-info-text:#5079be;--color-body:#fafafa;--color-box-header:#e8ebf0;--color-box-body:#eef1f4;--color-box-body-highlight:#e8ebf0;--color-text-dark:#b05ccc;--color-text:#4b505b;--color-text-hover:rgba(75,80,91,0.8);--color-text-light:#608e32;--color-text-light-1:#8790a0;--color-text-light-2:#4b505b;--color-text-light-3:#8790a0;--color-footer:#eef1f4;--color-timeline:#dde2e7;--color-input-text:#4b505b;--color-input-background:#fafafa;--color-input-toggle-background:#eef1f4;--color-input-border:#dde2e7;--color-input-border-hover:#b2b9c4;--color-navbar:#eef1f4;--color-navbar-transparent:#eef1f4;--color-light:#eef1f4;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#dde2e7;--color-hover:rgba(221,226,231,0.5);--color-active:rgba(221,226,231,0.5);--color-menu:#eef1f4;--color-card:#eef1f4;--color-markup-table-row:#e8ebf0;--color-markup-code-block:#fafafa;--color-button:#e8ebf0;--color-code-bg:#fafafa;--color-code-sidebar-bg:#eef1f4;--color-shadow:#00000060;--color-secondary-bg:#eef1f4;--color-text-focus:#fff;--color-expand-button:#e8ebf0;--color-placeholder-text:#8790a0;--color-editor-line-highlight:#eef1f4;--color-project-board-bg:#fafafa;--color-project-board-light-label:#5079be;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#eef1f4;--color-label-active-bg:#dde2e7;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#eef1f4;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#4b505b}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#8790a0}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#d05858}.chroma + .o,.chroma .ow{color:#b05ccc}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#608e32}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#608e32}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#be7e05}.chroma .nf,.chroma .nb,.chroma .na{color:#5079be}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#3a8b84}.ui.primary.button,.ui.primary.buttons + .button{background:#6996e0;background-color:#6996e0 !important;color:#fafafa}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(105,150,224,0.8);background-color:rgba(105,150,224,0.8) + !important;color:#fafafa}.ui.green.buttons .button,.ui.green.button{background:#76af6f;background-color:#76af6f;color:#fafafa}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(118,175,111,0.8);background-color:rgba(118,175,111,0.8);color:#fafafa}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#b05ccc}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#b05ccc}.repo-title{color:#b05ccc}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#5079be}.ui.basic.labels + .label,.ui.basic.label{color:#5079be}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fafafa}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fafafa}i.grey.icon.icon.icon.icon{color:#8790a0}*{scrollbar-color:#b05ccc + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #b05ccc !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #b05ccc !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #b05ccc !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e17373;background-color:#e17373;color:#fafafa}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(225,115,115,0.8);background-color:rgba(225,115,115,0.8);color:#fafafa}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fafafa}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fafafa}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#be7e05;background-color:#be7e05;color:#fafafa}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(190,126,5,0.8);background-color:rgba(190,126,5,0.8);color:#fafafa}' + theme-everforest-auto.css: '@media (prefers-color-scheme:dark){:root{--is-dark-theme:true;--color-primary:#a7c080;--color-primary-contrast:#272e33;--color-primary-dark-1:#a7c080;--color-primary-dark-2:#aec187;--color-primary-dark-3:#b6c28e;--color-primary-dark-4:#bdc395;--color-primary-dark-5:#c4c49c;--color-primary-dark-6:#ccc5a3;--color-primary-dark-7:#d3c6aa;--color-primary-light-1:#a7c080;--color-primary-light-2:#94aa76;--color-primary-light-3:#81956c;--color-primary-light-4:#6e8062;--color-primary-light-5:#5a6a58;--color-primary-light-6:#47554e;--color-primary-light-7:#343f44;--color-primary-alpha-10:rgba(167,192,128,0.1);--color-primary-alpha-20:rgba(167,192,128,0.2);--color-primary-alpha-30:rgba(167,192,128,0.3);--color-primary-alpha-40:rgba(167,192,128,0.4);--color-primary-alpha-50:rgba(167,192,128,0.5);--color-primary-alpha-60:rgba(167,192,128,0.6);--color-primary-alpha-70:rgba(167,192,128,0.7);--color-primary-alpha-80:rgba(167,192,128,0.8);--color-primary-alpha-90:rgba(167,192,128,0.9);--color-secondary:#475258;--color-secondary-dark-1:#475258;--color-secondary-dark-2:#535c5f;--color-secondary-dark-3:#5e6566;--color-secondary-dark-4:#6a6f6d;--color-secondary-dark-5:#767973;--color-secondary-dark-6:#81827a;--color-secondary-dark-7:#8d8c81;--color-secondary-dark-8:#999688;--color-secondary-dark-9:#a49f8f;--color-secondary-dark-10:#b0a996;--color-secondary-dark-11:#bcb39c;--color-secondary-dark-12:#c7bca3;--color-secondary-dark-13:#d3c6aa;--color-secondary-light-1:#475258;--color-secondary-light-2:#3d484d;--color-secondary-light-3:#343f44;--color-secondary-light-4:#2d353b;--color-secondary-alpha-10:rgba(71,82,88,0.1);--color-secondary-alpha-20:rgba(71,82,88,0.2);--color-secondary-alpha-30:rgba(71,82,88,0.3);--color-secondary-alpha-40:rgba(71,82,88,0.4);--color-secondary-alpha-50:rgba(71,82,88,0.5);--color-secondary-alpha-60:rgba(71,82,88,0.6);--color-secondary-alpha-70:rgba(71,82,88,0.7);--color-secondary-alpha-80:rgba(71,82,88,0.8);--color-secondary-alpha-90:rgba(71,82,88,0.9);--color-red:#e67e80;--color-orange:#e69875;--color-yellow:#dbbc7f;--color-olive:#a7c080;--color-green:#a7c080;--color-teal:#83c092;--color-blue:#7fbbb3;--color-violet:#d699b6;--color-purple:#d699b6;--color-pink:#d699b6;--color-brown:#e69875;--color-grey:#859289;--color-red-light:#e67e80;--color-orange-light:#e69875;--color-yellow-light:#dbbc7f;--color-olive-light:#a7c080;--color-green-light:#a7c080;--color-teal-light:#83c092;--color-blue-light:#7fbbb3;--color-violet-light:#d699b6;--color-purple-light:#d699b6;--color-pink-light:#d699b6;--color-brown-light:#e69875;--color-grey-light:#859289;--color-black:#343f44;--color-gold:#e69875;--color-white:#272e33;--color-diff-removed-word-bg:#7e5357;--color-diff-added-word-bg:#607258;--color-diff-removed-row-bg:#514045;--color-diff-moved-row-bg:#3a515d;--color-diff-added-row-bg:#425047;--color-diff-removed-row-border:#9c5f63;--color-diff-moved-row-border:#5d8688;--color-diff-added-row-border:#758864;--color-diff-inactive:#343f44;--color-error-border:#9c5f63;--color-error-bg:#514045;--color-error-text:#e67e80;--color-success-border:#758864;--color-success-bg:#425047;--color-success-text:#a7c080;--color-warning-border:#948461;--color-warning-bg:#4d4c43;--color-warning-text:#dbbc7f;--color-info-border:#5d8688;--color-info-bg:#3a515d;--color-info-text:#7fbbb3;--color-body:#272e33;--color-box-header:#3d484d;--color-box-body:#2d353b;--color-box-body-highlight:#343f44;--color-text-dark:#e69875;--color-text:#d3c6aa;--color-text-hover:rgba(211,198,170,0.8);--color-text-light:#dbbc7f;--color-text-light-1:#859289;--color-text-light-2:#d3c6aa;--color-text-light-3:#859289;--color-footer:#2d353b;--color-timeline:#475258;--color-input-text:#d3c6aa;--color-input-background:#272e33;--color-input-toggle-background:#2d353b;--color-input-border:#475258;--color-input-border-hover:#667271;--color-navbar:#2d353b;--color-navbar-transparent:#2d353b;--color-light:#2d353b;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#475258;--color-hover:rgba(71,82,88,0.5);--color-active:rgba(71,82,88,0.5);--color-menu:#2d353b;--color-card:#2d353b;--color-markup-table-row:#3d484d;--color-markup-code-block:#272e33;--color-button:#3d484d;--color-code-bg:#272e33;--color-code-sidebar-bg:#2d353b;--color-shadow:#00000060;--color-secondary-bg:#2d353b;--color-text-focus:#fff;--color-expand-button:#343f44;--color-placeholder-text:#859289;--color-editor-line-highlight:#2d353b;--color-project-board-bg:#272e33;--color-project-board-light-label:#a7c080;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#2d353b;--color-label-active-bg:#475258;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#2d353b;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#d3c6aa}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#859289}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#e67e80}.chroma + .o,.chroma .ow{color:#e69875}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#83c092}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d699b6}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#dbbc7f}.chroma .nf,.chroma .nb,.chroma .na{color:#a7c080}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#7fbbb3}.ui.primary.button,.ui.primary.buttons + .button{background:#a7c080;background-color:#a7c080 !important;color:#272e33}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(167,192,128,0.8);background-color:rgba(167,192,128,0.8) + !important;color:#272e33}.ui.green.buttons .button,.ui.green.button{background:#a7c080;background-color:#a7c080;color:#272e33}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(167,192,128,0.8);background-color:rgba(167,192,128,0.8);color:#272e33}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#e69875}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#e69875}.repo-title{color:#e69875}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#a7c080}.ui.basic.labels + .label,.ui.basic.label{color:#a7c080}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#272e33}.ui.green.labels .label,.ui.ui.ui.green.label{color:#272e33}i.grey.icon.icon.icon.icon{color:#859289}*{scrollbar-color:#e69875 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #e69875 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #e69875 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #e69875 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e67e80;background-color:#e67e80;color:#272e33}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(230,126,128,0.8);background-color:rgba(230,126,128,0.8);color:#272e33}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#272e33}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#272e33}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#e69875;background-color:#e69875;color:#272e33}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(230,152,117,0.8);background-color:rgba(230,152,117,0.8);color:#272e33}}@media + (prefers-color-scheme:light){:root{--is-dark-theme:true;--color-primary:#8da101;--color-primary-contrast:#fdf6e3;--color-primary-dark-1:#8da101;--color-primary-dark-2:#859814;--color-primary-dark-3:#7d8f27;--color-primary-dark-4:#75863a;--color-primary-dark-5:#6c7c4c;--color-primary-dark-6:#64735f;--color-primary-dark-7:#5c6a72;--color-primary-light-1:#8da101;--color-primary-light-2:#9dad24;--color-primary-light-3:#aeba47;--color-primary-light-4:#bec66b;--color-primary-light-5:#ced28e;--color-primary-light-6:#dfdfb1;--color-primary-light-7:#efebd4;--color-primary-alpha-10:rgba(141,161,1,0.1);--color-primary-alpha-20:rgba(141,161,1,0.2);--color-primary-alpha-30:rgba(141,161,1,0.3);--color-primary-alpha-40:rgba(141,161,1,0.4);--color-primary-alpha-50:rgba(141,161,1,0.5);--color-primary-alpha-60:rgba(141,161,1,0.6);--color-primary-alpha-70:rgba(141,161,1,0.7);--color-primary-alpha-80:rgba(141,161,1,0.8);--color-primary-alpha-90:rgba(141,161,1,0.9);--color-secondary:#e0dcc7;--color-secondary-dark-1:#e0dcc7;--color-secondary-dark-2:#d5d3c0;--color-secondary-dark-3:#cac9b9;--color-secondary-dark-4:#bfc0b2;--color-secondary-dark-5:#b4b6ab;--color-secondary-dark-6:#a9aca4;--color-secondary-dark-7:#9ea39d;--color-secondary-dark-8:#939a95;--color-secondary-dark-9:#88908e;--color-secondary-dark-10:#7d8787;--color-secondary-dark-11:#727d80;--color-secondary-dark-12:#677379;--color-secondary-dark-13:#5c6a72;--color-secondary-light-1:#e0dcc7;--color-secondary-light-2:#e6e2cc;--color-secondary-light-3:#efebd4;--color-secondary-light-4:#f4f0d9;--color-secondary-alpha-10:rgba(224,220,199,0.1);--color-secondary-alpha-20:rgba(224,220,199,0.2);--color-secondary-alpha-30:rgba(224,220,199,0.3);--color-secondary-alpha-40:rgba(224,220,199,0.4);--color-secondary-alpha-50:rgba(224,220,199,0.5);--color-secondary-alpha-60:rgba(224,220,199,0.6);--color-secondary-alpha-70:rgba(224,220,199,0.7);--color-secondary-alpha-80:rgba(224,220,199,0.8);--color-secondary-alpha-90:rgba(224,220,199,0.9);--color-red:#f85552;--color-orange:#f57d26;--color-yellow:#dfa000;--color-olive:#8da101;--color-green:#8da101;--color-teal:#35a77c;--color-blue:#3a94c5;--color-violet:#df69ba;--color-purple:#df69ba;--color-pink:#df69ba;--color-brown:#f57d26;--color-grey:#939f91;--color-red-light:#f85552;--color-orange-light:#f57d26;--color-yellow-light:#dfa000;--color-olive-light:#8da101;--color-green-light:#8da101;--color-teal-light:#35a77c;--color-blue-light:#3a94c5;--color-violet-light:#df69ba;--color-purple-light:#df69ba;--color-pink-light:#df69ba;--color-brown-light:#f57d26;--color-grey-light:#939f91;--color-black:#efebd4;--color-gold:#f57d26;--color-white:#fdf6e3;--color-diff-removed-word-bg:#fab8b1;--color-diff-added-word-bg:#d2d993;--color-diff-removed-row-bg:#fbe3da;--color-diff-moved-row-bg:#e9f0e9;--color-diff-added-row-bg:#f0f1d2;--color-diff-removed-row-border:#fa9c96;--color-diff-moved-row-border:#92c2d7;--color-diff-added-row-border:#bfc96a;--color-diff-inactive:#efebd4;--color-error-border:#fa9c96;--color-error-bg:#fbe3da;--color-error-text:#f85552;--color-success-border:#bfc96a;--color-success-bg:#f0f1d2;--color-success-text:#8da101;--color-warning-border:#edc767;--color-warning-bg:#faedcd;--color-warning-text:#dfa000;--color-info-border:#92c2d7;--color-info-bg:#e9f0e9;--color-info-text:#3a94c5;--color-body:#fdf6e3;--color-box-header:#e6e2cc;--color-box-body:#f4f0d9;--color-box-body-highlight:#efebd4;--color-text-dark:#f57d26;--color-text:#5c6a72;--color-text-hover:rgba(92,106,114,0.8);--color-text-light:#dfa000;--color-text-light-1:#939f91;--color-text-light-2:#5c6a72;--color-text-light-3:#939f91;--color-footer:#f4f0d9;--color-timeline:#e0dcc7;--color-input-text:#5c6a72;--color-input-background:#fdf6e3;--color-input-toggle-background:#f4f0d9;--color-input-border:#e0dcc7;--color-input-border-hover:#babeac;--color-navbar:#f4f0d9;--color-navbar-transparent:#f4f0d9;--color-light:#f4f0d9;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e0dcc7;--color-hover:rgba(224,220,199,0.5);--color-active:rgba(224,220,199,0.5);--color-menu:#f4f0d9;--color-card:#f4f0d9;--color-markup-table-row:#e6e2cc;--color-markup-code-block:#fdf6e3;--color-button:#e6e2cc;--color-code-bg:#fdf6e3;--color-code-sidebar-bg:#f4f0d9;--color-shadow:#00000060;--color-secondary-bg:#f4f0d9;--color-text-focus:#fff;--color-expand-button:#efebd4;--color-placeholder-text:#939f91;--color-editor-line-highlight:#f4f0d9;--color-project-board-bg:#fdf6e3;--color-project-board-light-label:#8da101;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4f0d9;--color-label-active-bg:#e0dcc7;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4f0d9;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#5c6a72}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#939f91}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f85552}.chroma + .o,.chroma .ow{color:#f57d26}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#35a77c}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#df69ba}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#dfa000}.chroma .nf,.chroma .nb,.chroma .na{color:#8da101}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#3a94c5}.ui.primary.button,.ui.primary.buttons + .button{background:#93b259;background-color:#93b259 !important;color:#fdf6e3}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(147,178,89,0.8);background-color:rgba(147,178,89,0.8) + !important;color:#fdf6e3}.ui.green.buttons .button,.ui.green.button{background:#93b259;background-color:#93b259;color:#fdf6e3}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(147,178,89,0.8);background-color:rgba(147,178,89,0.8);color:#fdf6e3}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#f57d26}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#f57d26}.repo-title{color:#f57d26}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#8da101}.ui.basic.labels + .label,.ui.basic.label{color:#8da101}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fdf6e3}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fdf6e3}i.grey.icon.icon.icon.icon{color:#939f91}*{scrollbar-color:#f57d26 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #f57d26 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #f57d26 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #f57d26 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e66868;background-color:#e66868;color:#fdf6e3}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(230,104,104,0.8);background-color:rgba(230,104,104,0.8);color:#fdf6e3}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fdf6e3}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fdf6e3}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f57d26;background-color:#f57d26;color:#fdf6e3}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(245,125,38,0.8);background-color:rgba(245,125,38,0.8);color:#fdf6e3}}' + theme-everforest-dark.css: ':root{--is-dark-theme:true;--color-primary:#a7c080;--color-primary-contrast:#272e33;--color-primary-dark-1:#a7c080;--color-primary-dark-2:#aec187;--color-primary-dark-3:#b6c28e;--color-primary-dark-4:#bdc395;--color-primary-dark-5:#c4c49c;--color-primary-dark-6:#ccc5a3;--color-primary-dark-7:#d3c6aa;--color-primary-light-1:#a7c080;--color-primary-light-2:#94aa76;--color-primary-light-3:#81956c;--color-primary-light-4:#6e8062;--color-primary-light-5:#5a6a58;--color-primary-light-6:#47554e;--color-primary-light-7:#343f44;--color-primary-alpha-10:rgba(167,192,128,0.1);--color-primary-alpha-20:rgba(167,192,128,0.2);--color-primary-alpha-30:rgba(167,192,128,0.3);--color-primary-alpha-40:rgba(167,192,128,0.4);--color-primary-alpha-50:rgba(167,192,128,0.5);--color-primary-alpha-60:rgba(167,192,128,0.6);--color-primary-alpha-70:rgba(167,192,128,0.7);--color-primary-alpha-80:rgba(167,192,128,0.8);--color-primary-alpha-90:rgba(167,192,128,0.9);--color-secondary:#475258;--color-secondary-dark-1:#475258;--color-secondary-dark-2:#535c5f;--color-secondary-dark-3:#5e6566;--color-secondary-dark-4:#6a6f6d;--color-secondary-dark-5:#767973;--color-secondary-dark-6:#81827a;--color-secondary-dark-7:#8d8c81;--color-secondary-dark-8:#999688;--color-secondary-dark-9:#a49f8f;--color-secondary-dark-10:#b0a996;--color-secondary-dark-11:#bcb39c;--color-secondary-dark-12:#c7bca3;--color-secondary-dark-13:#d3c6aa;--color-secondary-light-1:#475258;--color-secondary-light-2:#3d484d;--color-secondary-light-3:#343f44;--color-secondary-light-4:#2d353b;--color-secondary-alpha-10:rgba(71,82,88,0.1);--color-secondary-alpha-20:rgba(71,82,88,0.2);--color-secondary-alpha-30:rgba(71,82,88,0.3);--color-secondary-alpha-40:rgba(71,82,88,0.4);--color-secondary-alpha-50:rgba(71,82,88,0.5);--color-secondary-alpha-60:rgba(71,82,88,0.6);--color-secondary-alpha-70:rgba(71,82,88,0.7);--color-secondary-alpha-80:rgba(71,82,88,0.8);--color-secondary-alpha-90:rgba(71,82,88,0.9);--color-red:#e67e80;--color-orange:#e69875;--color-yellow:#dbbc7f;--color-olive:#a7c080;--color-green:#a7c080;--color-teal:#83c092;--color-blue:#7fbbb3;--color-violet:#d699b6;--color-purple:#d699b6;--color-pink:#d699b6;--color-brown:#e69875;--color-grey:#859289;--color-red-light:#e67e80;--color-orange-light:#e69875;--color-yellow-light:#dbbc7f;--color-olive-light:#a7c080;--color-green-light:#a7c080;--color-teal-light:#83c092;--color-blue-light:#7fbbb3;--color-violet-light:#d699b6;--color-purple-light:#d699b6;--color-pink-light:#d699b6;--color-brown-light:#e69875;--color-grey-light:#859289;--color-black:#343f44;--color-gold:#e69875;--color-white:#272e33;--color-diff-removed-word-bg:#7e5357;--color-diff-added-word-bg:#607258;--color-diff-removed-row-bg:#514045;--color-diff-moved-row-bg:#3a515d;--color-diff-added-row-bg:#425047;--color-diff-removed-row-border:#9c5f63;--color-diff-moved-row-border:#5d8688;--color-diff-added-row-border:#758864;--color-diff-inactive:#343f44;--color-error-border:#9c5f63;--color-error-bg:#514045;--color-error-text:#e67e80;--color-success-border:#758864;--color-success-bg:#425047;--color-success-text:#a7c080;--color-warning-border:#948461;--color-warning-bg:#4d4c43;--color-warning-text:#dbbc7f;--color-info-border:#5d8688;--color-info-bg:#3a515d;--color-info-text:#7fbbb3;--color-body:#272e33;--color-box-header:#3d484d;--color-box-body:#2d353b;--color-box-body-highlight:#343f44;--color-text-dark:#e69875;--color-text:#d3c6aa;--color-text-hover:rgba(211,198,170,0.8);--color-text-light:#dbbc7f;--color-text-light-1:#859289;--color-text-light-2:#d3c6aa;--color-text-light-3:#859289;--color-footer:#2d353b;--color-timeline:#475258;--color-input-text:#d3c6aa;--color-input-background:#272e33;--color-input-toggle-background:#2d353b;--color-input-border:#475258;--color-input-border-hover:#667271;--color-navbar:#2d353b;--color-navbar-transparent:#2d353b;--color-light:#2d353b;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#475258;--color-hover:rgba(71,82,88,0.5);--color-active:rgba(71,82,88,0.5);--color-menu:#2d353b;--color-card:#2d353b;--color-markup-table-row:#3d484d;--color-markup-code-block:#272e33;--color-button:#3d484d;--color-code-bg:#272e33;--color-code-sidebar-bg:#2d353b;--color-shadow:#00000060;--color-secondary-bg:#2d353b;--color-text-focus:#fff;--color-expand-button:#343f44;--color-placeholder-text:#859289;--color-editor-line-highlight:#2d353b;--color-project-board-bg:#272e33;--color-project-board-light-label:#a7c080;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#2d353b;--color-label-active-bg:#475258;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#2d353b;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#d3c6aa}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#859289}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#e67e80}.chroma + .o,.chroma .ow{color:#e69875}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#83c092}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d699b6}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#dbbc7f}.chroma .nf,.chroma .nb,.chroma .na{color:#a7c080}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#7fbbb3}.ui.primary.button,.ui.primary.buttons + .button{background:#a7c080;background-color:#a7c080 !important;color:#272e33}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(167,192,128,0.8);background-color:rgba(167,192,128,0.8) + !important;color:#272e33}.ui.green.buttons .button,.ui.green.button{background:#a7c080;background-color:#a7c080;color:#272e33}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(167,192,128,0.8);background-color:rgba(167,192,128,0.8);color:#272e33}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#e69875}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#e69875}.repo-title{color:#e69875}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#a7c080}.ui.basic.labels + .label,.ui.basic.label{color:#a7c080}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#272e33}.ui.green.labels .label,.ui.ui.ui.green.label{color:#272e33}i.grey.icon.icon.icon.icon{color:#859289}*{scrollbar-color:#e69875 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #e69875 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #e69875 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #e69875 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e67e80;background-color:#e67e80;color:#272e33}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(230,126,128,0.8);background-color:rgba(230,126,128,0.8);color:#272e33}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#272e33}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#272e33}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#e69875;background-color:#e69875;color:#272e33}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(230,152,117,0.8);background-color:rgba(230,152,117,0.8);color:#272e33}' + theme-everforest-light.css: ':root{--is-dark-theme:true;--color-primary:#8da101;--color-primary-contrast:#fdf6e3;--color-primary-dark-1:#8da101;--color-primary-dark-2:#859814;--color-primary-dark-3:#7d8f27;--color-primary-dark-4:#75863a;--color-primary-dark-5:#6c7c4c;--color-primary-dark-6:#64735f;--color-primary-dark-7:#5c6a72;--color-primary-light-1:#8da101;--color-primary-light-2:#9dad24;--color-primary-light-3:#aeba47;--color-primary-light-4:#bec66b;--color-primary-light-5:#ced28e;--color-primary-light-6:#dfdfb1;--color-primary-light-7:#efebd4;--color-primary-alpha-10:rgba(141,161,1,0.1);--color-primary-alpha-20:rgba(141,161,1,0.2);--color-primary-alpha-30:rgba(141,161,1,0.3);--color-primary-alpha-40:rgba(141,161,1,0.4);--color-primary-alpha-50:rgba(141,161,1,0.5);--color-primary-alpha-60:rgba(141,161,1,0.6);--color-primary-alpha-70:rgba(141,161,1,0.7);--color-primary-alpha-80:rgba(141,161,1,0.8);--color-primary-alpha-90:rgba(141,161,1,0.9);--color-secondary:#e0dcc7;--color-secondary-dark-1:#e0dcc7;--color-secondary-dark-2:#d5d3c0;--color-secondary-dark-3:#cac9b9;--color-secondary-dark-4:#bfc0b2;--color-secondary-dark-5:#b4b6ab;--color-secondary-dark-6:#a9aca4;--color-secondary-dark-7:#9ea39d;--color-secondary-dark-8:#939a95;--color-secondary-dark-9:#88908e;--color-secondary-dark-10:#7d8787;--color-secondary-dark-11:#727d80;--color-secondary-dark-12:#677379;--color-secondary-dark-13:#5c6a72;--color-secondary-light-1:#e0dcc7;--color-secondary-light-2:#e6e2cc;--color-secondary-light-3:#efebd4;--color-secondary-light-4:#f4f0d9;--color-secondary-alpha-10:rgba(224,220,199,0.1);--color-secondary-alpha-20:rgba(224,220,199,0.2);--color-secondary-alpha-30:rgba(224,220,199,0.3);--color-secondary-alpha-40:rgba(224,220,199,0.4);--color-secondary-alpha-50:rgba(224,220,199,0.5);--color-secondary-alpha-60:rgba(224,220,199,0.6);--color-secondary-alpha-70:rgba(224,220,199,0.7);--color-secondary-alpha-80:rgba(224,220,199,0.8);--color-secondary-alpha-90:rgba(224,220,199,0.9);--color-red:#f85552;--color-orange:#f57d26;--color-yellow:#dfa000;--color-olive:#8da101;--color-green:#8da101;--color-teal:#35a77c;--color-blue:#3a94c5;--color-violet:#df69ba;--color-purple:#df69ba;--color-pink:#df69ba;--color-brown:#f57d26;--color-grey:#939f91;--color-red-light:#f85552;--color-orange-light:#f57d26;--color-yellow-light:#dfa000;--color-olive-light:#8da101;--color-green-light:#8da101;--color-teal-light:#35a77c;--color-blue-light:#3a94c5;--color-violet-light:#df69ba;--color-purple-light:#df69ba;--color-pink-light:#df69ba;--color-brown-light:#f57d26;--color-grey-light:#939f91;--color-black:#efebd4;--color-gold:#f57d26;--color-white:#fdf6e3;--color-diff-removed-word-bg:#fab8b1;--color-diff-added-word-bg:#d2d993;--color-diff-removed-row-bg:#fbe3da;--color-diff-moved-row-bg:#e9f0e9;--color-diff-added-row-bg:#f0f1d2;--color-diff-removed-row-border:#fa9c96;--color-diff-moved-row-border:#92c2d7;--color-diff-added-row-border:#bfc96a;--color-diff-inactive:#efebd4;--color-error-border:#fa9c96;--color-error-bg:#fbe3da;--color-error-text:#f85552;--color-success-border:#bfc96a;--color-success-bg:#f0f1d2;--color-success-text:#8da101;--color-warning-border:#edc767;--color-warning-bg:#faedcd;--color-warning-text:#dfa000;--color-info-border:#92c2d7;--color-info-bg:#e9f0e9;--color-info-text:#3a94c5;--color-body:#fdf6e3;--color-box-header:#e6e2cc;--color-box-body:#f4f0d9;--color-box-body-highlight:#efebd4;--color-text-dark:#f57d26;--color-text:#5c6a72;--color-text-hover:rgba(92,106,114,0.8);--color-text-light:#dfa000;--color-text-light-1:#939f91;--color-text-light-2:#5c6a72;--color-text-light-3:#939f91;--color-footer:#f4f0d9;--color-timeline:#e0dcc7;--color-input-text:#5c6a72;--color-input-background:#fdf6e3;--color-input-toggle-background:#f4f0d9;--color-input-border:#e0dcc7;--color-input-border-hover:#babeac;--color-navbar:#f4f0d9;--color-navbar-transparent:#f4f0d9;--color-light:#f4f0d9;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e0dcc7;--color-hover:rgba(224,220,199,0.5);--color-active:rgba(224,220,199,0.5);--color-menu:#f4f0d9;--color-card:#f4f0d9;--color-markup-table-row:#e6e2cc;--color-markup-code-block:#fdf6e3;--color-button:#e6e2cc;--color-code-bg:#fdf6e3;--color-code-sidebar-bg:#f4f0d9;--color-shadow:#00000060;--color-secondary-bg:#f4f0d9;--color-text-focus:#fff;--color-expand-button:#efebd4;--color-placeholder-text:#939f91;--color-editor-line-highlight:#f4f0d9;--color-project-board-bg:#fdf6e3;--color-project-board-light-label:#8da101;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4f0d9;--color-label-active-bg:#e0dcc7;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4f0d9;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#5c6a72}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#939f91}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f85552}.chroma + .o,.chroma .ow{color:#f57d26}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#35a77c}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#df69ba}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#dfa000}.chroma .nf,.chroma .nb,.chroma .na{color:#8da101}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#3a94c5}.ui.primary.button,.ui.primary.buttons + .button{background:#93b259;background-color:#93b259 !important;color:#fdf6e3}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(147,178,89,0.8);background-color:rgba(147,178,89,0.8) + !important;color:#fdf6e3}.ui.green.buttons .button,.ui.green.button{background:#93b259;background-color:#93b259;color:#fdf6e3}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(147,178,89,0.8);background-color:rgba(147,178,89,0.8);color:#fdf6e3}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#f57d26}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#f57d26}.repo-title{color:#f57d26}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#8da101}.ui.basic.labels + .label,.ui.basic.label{color:#8da101}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fdf6e3}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fdf6e3}i.grey.icon.icon.icon.icon{color:#939f91}*{scrollbar-color:#f57d26 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #f57d26 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #f57d26 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #f57d26 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#e66868;background-color:#e66868;color:#fdf6e3}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(230,104,104,0.8);background-color:rgba(230,104,104,0.8);color:#fdf6e3}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fdf6e3}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fdf6e3}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f57d26;background-color:#f57d26;color:#fdf6e3}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(245,125,38,0.8);background-color:rgba(245,125,38,0.8);color:#fdf6e3}' + theme-gitea-modern.css: ':root{--base-border-radius:6px}html.theme-gitea,html.theme-arc-green{--fonts-override:var(--fonts-regular);accent-color:var(--color-primary)}a[rel*="nofollow"]:not(.ui):hover,.link:not(.ui):hover,a[rel*="nofollow"]:not(.ui):focus,.link:not(.ui):focus{text-decoration:underline;text-underline-offset:5px}*:not(input){outline:0 + dashed var(--color-primary);outline-offset:-3px;transition:outline-color .5s,outline-offset + .5s}:focus-visible:not(input){box-shadow:0 0 5px 0 var(--color-primary) !important;border-radius:2px;outline:2px + solid var(--color-primary) !important;outline-offset:0}.ui.buttons .ui.basic.button{border-radius:0}.ui.button,.ui.basic.button,.ui.menu,.input.action.fluid,.ui.label,.ui.form + input:not([type]),.ui.form input[type="date"],.ui.form input[type="datetime-local"],.ui.form + input[type="email"],.ui.form input[type="file"],.ui.form input[type="number"],.ui.form + input[type="password"],.ui.form input[type="search"],.ui.form input[type="tel"],.ui.form + input[type="text"],.ui.form input[type="time"],.ui.form input[type="url"],.ui.table,.tab-size-8{border-radius:var(--base-border-radius)}.ui.menu:not(.secondary) + > .item:first-child,.ui.buttons .button:first-child,.ui.buttons > .ui.basic.button:first-child{border-radius:var(--base-border-radius) + 0 0 var(--base-border-radius)}.ui.compact.menu:not(.secondary) .item:last-child,.ui.buttons + .button:last-child,.ui.action.input > .button:last-child,.ui.buttons > .ui.basic.button:last-child{border-radius:0 + var(--base-border-radius) var(--base-border-radius) 0}.ui.top.attached.header{border-radius:var(--base-border-radius) + var(--base-border-radius) 0 0}.ui.avatar{border-radius:var(--base-border-radius) + !important}.button{text-overflow:ellipsis}.ui.compact.button{padding-top:.3em;padding-bottom:.3em;min-height:2.4em;display:inline-flex;align-items:center}.ui.compact.button + svg{margin-bottom:-1px !important}.ui.basic.button,.ui.basic.label{background:none}.input.action.fluid{border:1px + solid var(--color-secondary) !important;height:auto;background-color:var(--color-input-background)}.input.action.fluid + input{border:0}.input.action.fluid button{margin:2px !important;margin-left:0 + !important;padding-top:0 !important;padding-bottom:0 !important;box-sizing:border-box;border-radius:var(--base-border-radius) + !important}.input.action.fluid .icon{border:0 !important}.input.action.fluid:focus-within{border-color:var(--color-primary) + !important}.ui.form + .ui.divider{display:none}.three.column{margin-left:0;margin-right:0}.three.column + .column{width:auto !important;align-items:center !important;display:flex !important}.three.column + .column .small:not(.modal),.three.column .column a{max-height:40px !important;min-height:unset;display:flex;align-items:center}.three.column + .column form{width:100%}.three.column .column:first-child{padding-left:0}.three.column + .column:last-child{padding-right:0}.three.column .center{flex-grow:100;padding-left:0 + !important;padding-right:0 !important}.popup.tiny.inverted{background:var(--color-body);color:var(--color-text);box-shadow:var(--lt-shadowDefault) + !important;border:1px solid var(--color-secondary);pointer-events:none;z-index:9999999}.popup.tiny.inverted::before{background:var(--color-body) + !important;z-index:-99999 !important;border-color:var(--color-secondary) !important}.ui.left.popup::before{border:0;border-top:1px + solid;border-right:1px solid}.ui.right.popup::before{border:0;border-left:1px + solid;border-bottom:1px solid}.ui.top.popup::before{border:0;border-right:1px + solid;border-bottom:1px solid}.ui.bottom.popup::before{border:0;border-top:1px + solid;border-left:1px solid}.settings > .ui.container{max-width:1150px !important}.ui.two.column.stackable.grid + > .column{width:auto !important;flex-grow:1}.ui.compact.tiny.menu{flex-grow:1}.ui.compact.tiny.menu + a{flex-grow:1;justify-content:center}.page-content:not(.home) > .middle{padding:calc(4vw + + 1rem) 4vw !important;width:100% !important}.page-content:not(.home) > .middle + > .column{padding:0 !important}.full.height > span[style="display: inline !important;"]:first-child{margin:auto;line-height:40px;padding:30px;text-align:center}.full.height + > span[style="display: inline !important;"]:first-child::before{content:"";position:absolute;top:0;left:0;right:0;height:40px;background:var(--color-navbar);border-bottom:1px + solid var(--color-secondary);z-index:-1}.full.height > span[style="display: inline + !important;"]:first-child ~ .page-content #app,.full.height > span[style="display: + inline !important;"]:first-child ~ .page-content #dashboard-repo-list,.full.height + > span[style="display: inline !important;"]:first-child ~ .page-content #user-heatmap,.full.height + > span[style="display: inline !important;"]:first-child ~ .page-content #user-heatmap + + .divider{display:none !important}.full.height > span[style="display: inline + !important;"]:first-child ~ .page-content .dashboard-navbar{bottom:unset !important;top:10px + !important;align-self:flex-start !important;padding-top:10px}#user-heatmap{overflow:auto + hidden;justify-content:flex-start;max-width:900px;margin:auto;align-items:stretch}#user-heatmap + .vch__wrapper{min-width:600px}.repo-title a:nth-last-child(2){font-weight:600 + !important}.label:not(.basic){border-radius:100px !important;padding:4px 8px !important;font-weight:700;align-items:center;justify-content:center;min-width:24px + !important;display:inline-flex}.label:not(.basic).sha > .detail{margin-left:6px + !important}.label:not(.basic) svg{margin-right:4px}.alert-primary.p-10{border:0 + !important;padding:20px !important}.alert-primary.p-10 .close span{font-size:1.7em;font-weight:200;padding-right:6px}.alert-primary.p-10 + h4{margin-right:1.7em;font-weight:600}.ui.tabular.menu,.ui.tight.menu{position:relative;flex-direction:row + !important;flex-wrap:nowrap !important;overflow:auto hidden !important;scrollbar-width:thin;padding-bottom:2px;scrollbar-color:var(--color-primary) + var(--color-secondary);border-bottom:1px solid var(--color-secondary) !important}.ui.tabular.menu.ui.tight.menu + > .item,.ui.tight.menu.ui.tight.menu > .item{width:auto !important;border-bottom-width:0 + !important;position:relative !important;padding:.85714286em 1.14285714em}.ui.tabular.menu.ui.tight.menu + > .item::before,.ui.tight.menu.ui.tight.menu > .item::before{content:"" !important;position:absolute + !important;bottom:0 !important;left:20px !important;right:20px !important;top:unset + !important;width:unset !important;height:2px !important;opacity:.7;border-radius:4px + 4px 0 0;background:none;display:flex !important;transition:left 2s}.ui.tabular.menu.ui.tight.menu + > .item.active::before,.ui.tight.menu.ui.tight.menu > .item.active::before{background-color:var(--color-primary) + !important;left:0 !important;right:0 !important}.ui.tabular.menu{border-bottom:0 + !important;position:relative}.ui.tabular.menu::before{content:"";position:absolute;bottom:1px;left:0;right:0;z-index:-1;border-top:1px + solid var(--color-secondary)}.ui.tabular.menu:not(.borderless) > .item{width:auto + !important;padding:16px 24px;min-width:70px;justify-content:center;position:relative}.ui.tabular.menu:not(.borderless) + > .item svg{margin-right:10px !important}.ui.tabular.menu:not(.borderless) > .item.active{border-radius:10px + 10px 0 0 !important;z-index:1}.ui.tabular.menu:not(.borderless) > .item.active::after{content:"" + !important;position:absolute;width:6px;height:6px;bottom:1px;right:-7px;border-bottom-left-radius:100px;box-shadow:-1px + 1px 0 0 var(--color-secondary),-3px 3px 0 2px var(--color-body)}.ui.tabular.menu:not(.borderless) + > .item.active::before{all:unset;content:"" !important;position:absolute;width:6px;height:6px;bottom:1px;left:-7px;border-bottom-right-radius:100px;box-shadow:1px + 1px 0 0 var(--color-secondary),3px 3px 0 2px var(--color-body);display:unset !important}.ui.tabular.menu:not(.borderless) + > .item.active:first-child::before{content:none !important}.ui.secondary.pointing.menu{background:none + !important}.ui.secondary.pointing.menu .new-menu-inner{width:1150px;padding:0 + 20px}.ui.secondary.pointing.menu .item{border:none}.ui.secondary.pointing.menu + .active{position:relative}.ui.secondary.pointing.menu .active::before{all:unset;content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background-color:var(--color-primary) + !important;display:flex !important;opacity:.7}.ui.secondary.pointing.menu .item:last-child{padding-right:1.14286em + !important}.ui.secondary.pointing.menu::after{content:unset !important}.repository + .filter.menu .menu{max-height:80vh;width:max-content !important}.repository .filter.menu + .menu .info{width:100% !important;padding:.8em !important;line-height:1.2em;white-space:normal + !important}html{scroll-behavior:smooth;scroll-snap-type:y mandatory;scrollbar-width:auto + !important}.full.height{padding-bottom:30px}.following.bar{--min-height:60px;background:none + !important;border-bottom:1px solid var(--color-secondary) !important;background-color:var(--color-navbar) + !important;position:relative;min-height:var(--min-height);align-items:center}.following.bar + #navbar{padding:0 calc(18px + 1%) !important;background:none !important;max-width:100rem;transition:padding + 1s;min-height:var(--min-height) !important;margin:auto}.following.bar #navbar + > .item:not(.brand),.following.bar #navbar > .right > .item{color:var(--color-text-dark) + !important;font-size:1rem !important;font-weight:600;padding:0 calc(0px + 1%);height:calc(var(--min-height) + + 1px) !important;margin:0 !important;border-radius:0;border-top:1px solid transparent;border-bottom:1px + solid transparent;margin-bottom:-1px !important;background:none !important;box-sizing:border-box;transition:border + .5s,padding .7s}.following.bar #navbar > .item:not(.brand) > span,.following.bar + #navbar > .right > .item > span{height:100%;display:flex;align-items:center}.following.bar + #navbar > .item:not(.brand):hover,.following.bar #navbar > .right > .item:hover,.following.bar + #navbar > .item:not(.brand).active,.following.bar #navbar > .right > .item.active,.following.bar + #navbar > .item:not(.brand):focus,.following.bar #navbar > .right > .item:focus{border-bottom:1px + solid var(--color-primary);transition:border .1s;outline:none}.following.bar #navbar + .item.brand{padding:0 !important;min-height:var(--min-height) !important}.following.bar + #navbar .item.brand a{width:35px;height:35px;margin-right:20px;background-position:center}.following.bar + #navbar .item.brand a img{width:100%;height:100%}.following.bar #navbar .right{position:sticky;right:1%;background-color:var(--color-navbar);box-shadow:100px + 0 0 var(--color-navbar);width:100%;min-width:max-content;justify-content:flex-end}.following.bar + #navbar .right > .item{padding:0 10px}.following.bar #navbar .right > .item > + .text{display:flex;align-items:center}.following.bar #navbar .right > .item > + .text span:not(.fitted){margin-left:10px}.following.bar #navbar .right > .item + > .text img ~ .fitted{margin-left:4px}@media only screen and (min-width:850px){.page-content.repository:not(.issues.repository.milestones){display:grid;grid-row-gap:10px;grid-template-columns:100px + calc(100% - 100px);padding:0 20px;padding-top:20px;width:1367px;max-width:100%;margin:auto}.page-content.repository:not(.issues.repository.milestones) + > .middle{grid-column:1/span 2}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper{display:contents}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper > .ui.container:first-child{grid-row:1;grid-column:1/span 2;width:100%;padding-left:100px;border:0 + !important;box-shadow:none !important}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper > .ui.container:first-child .avatar,.page-content.repository:not(.issues.repository.milestones) + .header-wrapper > .ui.container:first-child .repo-icon{width:50px !important;height:50px + !important;object-fit:contain;margin:0 10px;margin-left:-90px;margin-right:39px + !important;margin-bottom:-10px;display:flex;justify-content:center;align-items:center;border-radius:8px + !important;z-index:2}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper > .ui.container:first-child .repo-title-wrap{display:flex;flex-direction:row + !important;align-items:center}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper > .ui.container:first-child .fork-flag{margin:0 !important;margin-left:6px + !important;padding:2px 8px;font-size:12px;border-radius:var(--base-border-radius);border:1px + solid var(--color-secondary)}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper .tabs{order:-1;width:auto !important;margin:0 !important;margin-top:60px + !important;grid-column:1;grid-row:2/span 5;height:min-content;position:sticky;bottom:20px;margin-bottom:-60px + !important;align-self:flex-end;align-content:flex-start;min-height:calc(100vh + - 40px)}.page-content.repository:not(.issues.repository.milestones) .header-wrapper + .tabs::before{content:"";position:absolute;bottom:0;left:20px;right:50px;top:-10px;background-color:var(--color-body);box-shadow:0 + 0 20px 20px var(--color-body);opacity:.8}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper .tabs .tabular{flex-direction:column !important;padding-right:30px;overflow:visible + !important;padding-left:0;border-bottom:0 !important}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper .tabs .tabular::before{content:unset !important}.page-content.repository:not(.issues.repository.milestones) + .header-wrapper .divider{display:none}.page-content.repository:not(.issues.repository.milestones) + > .ui.container{width:100%}#project-board{width:calc(100vw + 10px);max-width:unset;justify-self:center;margin-left:-100px;margin-top:-10px;margin-bottom:calc(-100vh + + 0px);overflow-x:auto;scrollbar-color:var(--color-primary) transparent;position:static;height:calc(100vh + - 175px);scroll-snap-align:end}#project-board > .board{overflow:visible;margin:0 + auto !important;padding-left:100px;box-sizing:content-box;width:1227px;height:100% + !important}#project-board > .board::after{content:"";display:flex;min-width:100px}#project-board + > .board .board-column{height:100% !important;box-sizing:border-box;box-shadow:20px + 0 0 0 var(--color-body)}#project-board > .board .card{box-sizing:border-box}}.page-content.repository:not(.milestones){width:1300px;height:min-content;max-width:100%;margin:auto;margin-bottom:-80px}.page-content.repository:not(.milestones) + .header-wrapper{padding-top:40px !important;border-bottom:0 !important;background:none}.page-content.repository:not(.milestones) + .header-wrapper .repo-title{flex-wrap:wrap;line-height:1.5em;justify-content:center}.page-content.repository:not(.milestones) + .header-wrapper .repo-buttons{display:flex;flex-wrap:wrap;justify-content:center}.page-content.repository:not(.milestones) + .header-wrapper .repo-buttons > *{margin-top:4px}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container{margin-inline:0 !important;width:100%;margin-bottom:.7em}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular{padding-inline:3rem;border-bottom:0 !important}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular::before{content:unset !important}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .item{display:flex;flex-direction:column;text-align:center;padding-top:12px;padding-bottom:18px;width:90px + !important;max-width:100% !important;flex-grow:1;position:relative;background:none;color:var(--color-text);opacity:.7;border-radius:12px + !important}.page-content.repository:not(.milestones) .header-wrapper .tabs.container + .tabular .item svg{margin:0;width:22px;height:22px;max-width:unset !important;margin-bottom:10px;margin-right:0 + !important;overflow:visible;order:-2}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .item .label{margin:0 !important;margin-top:-3px + !important;margin-bottom:6px !important;order:-1;position:relative}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .item.active{border:none;opacity:1}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .item.active::before{content:unset !important}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .item.active::after{all:unset;content:"";position:absolute;top:2px;left:-5px;right:-5px;bottom:5px;background:var(--color-primary);opacity:.1;border-radius:12px + !important;z-index:-1}.page-content.repository:not(.milestones) .header-wrapper + .tabs.container .tabular .item:hover{opacity:.9 !important}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .right.menu{display:contents}.page-content.repository:not(.milestones) + .header-wrapper .tabs.container .tabular .right.menu .item{justify-content:center}.page-content.repository:not(.milestones) + .header-wrapper .divider{display:none !important}.page-content.repository:not(.milestones) + > .ui.container{height:min-content;position:relative}.page-content.repository:not(.milestones) + > .ui.container > .three.column.stackable.grid:focus-within{position:relative;z-index:3;background-color:var(--color-body)}.page-content.repository:not(.milestones) + > .ui.container > .three.column.stackable.grid + .ui.divider + .two.column.stackable.grid{position:absolute;z-index:2;background-color:var(--color-body);inset:0;margin:0 + !important;height:39px}.page-content.repository:not(.milestones) > .ui.container + > .three.column.stackable.grid + .ui.divider + .two.column.stackable.grid > .column{padding:0 + !important}.page-content.repository:not(.milestones) > .ui.container > .three.column.stackable.grid + + .ui.divider + .two.column.stackable.grid > .column .item{padding-block:0 !important}.page-content.repository:not(.milestones) + > .ui.container > div:not(.attached){height:min-content;margin-top:0 !important;margin-bottom:.7rem + !important}.page-content.repository:not(.milestones) > .ui.container > .column + + .divider:not(.header){border:0;margin:-22px 0 !important}.page-content.repository:not(.milestones) + > .ui.container > .column:first-child > .column{padding-top:0}.page-content.repository:not(.milestones) + > .ui.container #repo-desc{font-size:1.14rem;line-height:1.6rem;margin-bottom:0 + !important}.page-content.repository:not(.milestones) > .ui.container #repo-desc:not(:empty){margin-bottom:.3rem + !important}.page-content.repository:not(.milestones) > .ui.container #repo-topics{margin:0 + -4px;margin-bottom:0 !important;margin-top:-.5rem !important;align-items:flex-start + !important}.page-content.repository:not(.milestones) > .ui.container #repo-topics + > *:last-child{margin:2px !important;height:22px !important;margin-bottom:.7rem + !important}.page-content.repository:not(.milestones) > .ui.container .repo-topic{border-radius:100px;padding:4px + 10px;color:var(--color-primary);font-size:.9rem;position:relative}.page-content.repository:not(.milestones) + > .ui.container .repo-topic:not(:hover){background:none}.page-content.repository:not(.milestones) + > .ui.container .repo-topic::before{content:"";position:absolute;inset:0;border-radius:100px;background:var(--color-primary);opacity:.07;z-index:-1}.page-content.repository:not(.milestones) + > .ui.container .form .ui.tabular.menu{margin-bottom:-2px;z-index:1}.page-content.repository:not(.milestones) + > .ui.container #comment-form .markdown{padding-top:15px}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats{border:0}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .language-stats-details{padding:0 + !important}.page-content.repository:not(.milestones) > .ui.container .repository-summary-language-stats + .item{position:relative;width:auto;flex-grow:1;padding:0 !important;min-width:min-content}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item.ac{padding:12px !important}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item .ui,.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item .ac{display:block;line-height:1.4em;width:100%;padding:8px}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item .ui svg,.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item .ac svg{vertical-align:top;margin-right:.2em}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .item::after{all:unset;content:"";inset:0;position:absolute;background-color:var(--color-primary);border-radius:var(--base-border-radius);opacity:.1;pointer-events:none}.page-content.repository:not(.milestones) + > .ui.container .repository-summary-language-stats .language-stats{border-radius:100px;overflow:hidden;margin-top:.7em}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins{margin:10px -.35714286em;flex-wrap:wrap;flex-direction:row + !important;grid-row-gap:5px}.page-content.repository:not(.milestones) > .ui.container + .mobile--no-negative-margins .mx-0,.page-content.repository:not(.milestones) > + .ui.container .mobile--no-negative-margins .mr-1{width:auto !important}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins > .item:nth-last-child(+2){flex-grow:1}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins > .item:last-child{flex:3 1 100px;margin-left:4px}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins > .item:first-child .floating.filter.dropdown + > .basic.small.compact.button .text{font-size:0;display:flex;align-items:center}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins > .item:first-child .floating.filter.dropdown + > .basic.small.compact.button .text strong{font-size:1rem;margin-left:8px;margin-right:4px;font-weight:500}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins .breadcrumb{margin:0 10px}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins button,.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins .button{align-content:center;display:flex;align-items:center;height:32px + !important;padding-left:16px !important;padding-right:16px !important;box-sizing:border-box}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins button.icon,.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins .button.icon{padding-left:8px !important;padding-right:8px + !important}.page-content.repository:not(.milestones) > .ui.container .mobile--no-negative-margins + #file-buttons{margin:0 4px !important}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins #clone-panel{width:300px !important;box-sizing:border-box;flex-grow:1;border:1px + solid var(--color-secondary);padding:2px;height:auto;border-radius:var(--base-border-radius)}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins #clone-panel input{width:0 !important;min-width:unset;flex-grow:1;border:0;background:none;border-radius:var(--base-border-radius) + !important}.page-content.repository:not(.milestones) > .ui.container .mobile--no-negative-margins + #clone-panel button{border-radius:var(--base-border-radius) !important;margin-right:2px;background:none;border:0;height:28px + !important}.page-content.repository:not(.milestones) > .ui.container .mobile--no-negative-margins + #clone-panel button:hover{background:var(--color-light)}.page-content.repository:not(.milestones) + > .ui.container .mobile--no-negative-margins #clone-panel button:last-child{margin-right:0}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table{margin-top:0;background:none !important;display:flex;flex-direction:column}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead{position:relative}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead::after{content:"";position:absolute;inset:0;background:var(--color-primary);opacity:.05;z-index:-1}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead tr{background:none !important}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead th{padding:12px 12px;background:none !important;max-width:100%}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead th:first-child{width:0;flex-grow:1;display:flex + !important;align-items:center;flex-wrap:wrap}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead th:first-child > a{min-width:max-content + !important;max-width:100px}.page-content.repository:not(.milestones) > .ui.container + #repo-files-table thead th:last-child{padding-block:0 !important}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead th a{color:var(--color-text)}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead th > .avatar{width:20px;height:20px;margin:4px;margin-right:8px + !important}.page-content.repository:not(.milestones) > .ui.container #repo-files-table + thead .avatar{margin-right:4px}.page-content.repository:not(.milestones) > .ui.container + #repo-files-table thead .avatar + a{display:inline-flex !important}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead .commit-summary{display:contents}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead .message-wrapper{display:inline-flex;width:100px;flex-grow:1}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead .message-wrapper a{overflow:hidden;text-overflow:ellipsis;max-width:100%}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table thead .commit-body{padding:6px;padding-right:0;width:100%}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table tr{position:relative;width:100%;display:flex + !important;align-items:center}.page-content.repository:not(.milestones) > .ui.container + #repo-files-table tr td:first-child{width:250px;max-width:50%}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table tr td:nth-child(2){width:0 !important;max-width:unset;flex-grow:1}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table td::before{content:"";position:absolute !important;inset:0;pointer-events:none + !important;z-index:-100 !important;background:var(--color-primary);opacity:0}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table tr:hover td:nth-child(2)::before,.page-content.repository:not(.milestones) + > .ui.container #repo-files-table tr:focus-within td:nth-child(2)::before{opacity:.1}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate{overflow:visible;display:inline-flex}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate a{margin:-8px 0;overflow:hidden;text-overflow:ellipsis;max-width:100%;color:var(--color-text)}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate a:hover,.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate a:focus{color:var(--color-primary)}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate a:last-child{flex-grow:1}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .truncate svg{margin:0 10px;margin-top:2px}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .name{padding-left:8px}.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .message,.page-content.repository:not(.milestones) + > .ui.container #repo-files-table .right:not(.popup){opacity:.8}.page-content.repository:not(.milestones) + > .ui.container .file-header{padding:8px 16px !important;font-size:1rem}.page-content.repository:not(.milestones) + > .ui.container .file-header .octicon-book{color:var(--color-text-light-2) !important;margin-right:10px + !important}.page-content.repository:not(.milestones) > .ui.container .file-header + strong{line-height:35px}.page-content.repository:not(.milestones) > .ui.container + .issue.list{margin-top:-8px !important}.page-content.repository:not(.milestones) + > .ui.container .diff-box.sticky{margin:0 -1px;border-bottom:0}.page-content.repository:not(.milestones) + > .ui.container .sticky-2nd-row{box-shadow:0 -2px 0 2px var(--color-body)}.page-content.repository:not(.milestones) + > .ui.container .ui.timeline{margin-left:-16px}.page-content.repository:not(.milestones) + > .ui.container .timeline-item.comment{display:flex}.page-content.repository:not(.milestones) + > .ui.container .timeline-item.comment .content{flex-grow:1}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .timeline-avatar{position:relative;left:0;width:0;margin-top:.7em;z-index:2}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .timeline-avatar img,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .timeline-avatar svg{margin:0;height:1.6em !important;min-width:1.6em;width:1.6em + !important}.page-content.repository:not(.milestones) > .ui.container .timeline-item.form + .timeline-avatar{display:none !important}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .comment-header::after,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .form::after,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .no-header::after,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .comment-header::before,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .form::before,.page-content.repository:not(.milestones) + > .ui.container .timeline-item .no-header::before{content:unset !important}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .header{padding-left:calc(1.7em + 22px) !important}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .reactions{border:0 !important;padding:0 1em;margin-top:-.4em + !important;padding-bottom:.4em !important}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .reactions > a{padding:4px 8px !important;margin-right:.5em;margin-bottom:.5em;border:1px + solid var(--color-secondary);border-radius:calc(var(--base-border-radius) * 2)}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .reactions > a .reaction{font-size:1em}.page-content.repository:not(.milestones) + > .ui.container .timeline-item .reactions .select-reaction{padding:0 !important}.page-content.repository:not(.milestones) + > .ui.container .ui.grid > .eleven{margin-left:1rem}.page-content.repository:not(.milestones) + > .ui.container .ui.grid > .four > .metas{position:sticky;min-height:calc(100vh + - 40px);bottom:20px;top:unset;align-self:flex-end}@media only screen and (min-width:1000px){.page-content.repository:not(.milestones).file + > .ui.container,.page-content.repository:not(.milestones).commits > .ui.container,.page-content.repository:not(.milestones).branches + > .ui.container{margin:0 !important;grid-column:2;width:auto !important;max-width:100% + !important;display:grid;grid-template-columns:calc(100% - 100px) 100px;grid-template-rows:min-content + !important;align-items:flex-start}.page-content.repository:not(.milestones).file + > .ui.container > *,.page-content.repository:not(.milestones).commits > .ui.container + > *,.page-content.repository:not(.milestones).branches > .ui.container > *{grid-column:1}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"],.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"],.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"]{grid-column:2;grid-row:1/span 50;border:0;margin-top:12px + !important;margin-left:30px;height:min-content;position:sticky;width:70px;bottom:20px;top:unset;align-self:flex-end;background:none + !important;min-height:calc(100vh - 40px);margin-bottom:-60px !important;overflow:visible + !important;display:flex;flex-direction:column;align-items:center}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .segment,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .segment,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .segment,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .segment,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .segment,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .segment{background:none}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .language-stats-details,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .language-stats-details,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats-details,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details{display:flex !important;padding:0;border-top:1px + solid rgba(0,0,0,0.1);overflow:hidden;background:none !important}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .language-stats-details .item,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .language-stats-details .item,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats-details .item,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item{flex-direction:column}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .language-stats-details .item > *,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .language-stats-details .item > *,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats-details .item > *,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item > *,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item > *,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item > *{margin:0 !important}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .language-stats-details .item i,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .language-stats-details .item i,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats-details .item i,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item i,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item i,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details .item i{margin-bottom:8px + !important}.page-content.repository:not(.milestones).file > .ui.container .repository-summary + .repository-menu,.page-content.repository:not(.milestones).commits > .ui.container + .repository-summary .repository-menu,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .repository-menu,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .repository-menu,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .repository-menu,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .repository-menu{display:block !important;height:unset + !important}.page-content.repository:not(.milestones).file > .ui.container .repository-summary + .language-stats-details,.page-content.repository:not(.milestones).commits > .ui.container + .repository-summary .language-stats-details,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats-details,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats-details{height:unset !important;padding:0 + !important;margin:0 !important;order:2;border:0 !important}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .language-stats,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .language-stats,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .language-stats,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .language-stats{border-radius:var(--base-border-radius) + !important;overflow:hidden !important;pointer-events:none;margin-top:0}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .horizontal.list:not(.two),.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .horizontal.list:not(.two),.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .horizontal.list:not(.two),.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .horizontal.list:not(.two),.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .horizontal.list:not(.two),.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .horizontal.list:not(.two){padding-top:1rem}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal{flex-direction:column;background:none + !important;justify-content:flex-start}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment{border-radius:12px + !important;padding:12px 0 !important;padding-bottom:18px !important;flex-grow:0;position:relative;margin-right:0 + !important;border:none}.page-content.repository:not(.milestones).file > .ui.container + .repository-summary .list .item a,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item a,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item a,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item a,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment a,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment a,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment a,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment a{padding:0 !important}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item .bold,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item .bold,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item .bold,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item .bold,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item .bold,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item .bold,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item .bold,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item .bold,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item .bold,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item .bold,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item .bold,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item .bold,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment .bold,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment .bold,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment .bold,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment .bold,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment .bold,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment .bold,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment .bold,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item b,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item b,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item b,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item b,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item b,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item b,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item b,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item b,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item b,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item b,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item b,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item b,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment b,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment b,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment b,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment b,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment b,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment b,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment b,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment b,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment b,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment b,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment b,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment b{font-weight:500 + !important}.page-content.repository:not(.milestones).file > .ui.container .repository-summary + .list .item.active,.page-content.repository:not(.milestones).commits > .ui.container + .repository-summary .list .item.active,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item.active,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item.active,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item.active,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item.active,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment.active,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment.active,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment.active,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item:target,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item:target,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item:target,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item:target,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item:target,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item:target,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment:target,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment:target,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment:target,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment:target,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment:target,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment:target,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target{background:none + !important}.page-content.repository:not(.milestones).file > .ui.container .repository-summary + .list .item.active::before,.page-content.repository:not(.milestones).commits > + .ui.container .repository-summary .list .item.active::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item.active::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item.active::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item.active::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item.active::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item.active::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item.active::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment.active::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment.active::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item:target::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item:target::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item:target::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item:target::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item:target::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item:target::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item:target::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item:target::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment:target::before,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment:target::before,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment:target::before,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment:target::before,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target::before,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target::before,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment:target::before{content:"";position:absolute;top:0;left:-5px;right:-5px;bottom:5px;background:var(--color-primary);opacity:.1;z-index:-1;border-radius:12px}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list .item::after,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list .item::after,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list .item::after,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item::after,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item::after,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list .item::after,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal .item::after,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal .item::after,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal .item::after,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item::after,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item::after,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal .item::after,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list > a.attached.segment::after,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list > a.attached.segment::after,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list > a.attached.segment::after,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment::after,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment::after,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list > a.attached.segment::after,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal > a.attached.segment::after,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal > a.attached.segment::after,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal > a.attached.segment::after,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment::after,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment::after,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal > a.attached.segment::after{content:unset + !important}.page-content.repository:not(.milestones).file > .ui.container .repository-summary + .list.list a,.page-content.repository:not(.milestones).commits > .ui.container + .repository-summary .list.list a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list.list a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal.list a,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal.list a,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal.list a,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list span,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list span,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list span,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal span,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal span,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal span,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span{display:flex;flex-direction:column;padding:0 + 10px;justify-content:center}.page-content.repository:not(.milestones).file > .ui.container + .repository-summary .list.list a > *,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list.list a > *,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list.list a > *,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a > *,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a > *,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a > *,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal.list a > *,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal.list a > *,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal.list a > *,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a > *,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a > *,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a > *,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list span > *,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list span > *,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list span > *,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span > *,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span > *,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span > *,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal span > *,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal span > *,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal span > *,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span > *,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span > *,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span > *{margin-bottom:5px}.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list.list a svg,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list.list a svg,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list.list a svg,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a svg,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a svg,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list.list a svg,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal.list a svg,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal.list a svg,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal.list a svg,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a svg,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a svg,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal.list a svg,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary .list span svg,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary .list span svg,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary .list span svg,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span svg,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span svg,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"] .list span svg,.page-content.repository:not(.milestones).file + > .ui.container .repository-summary.horizontal span svg,.page-content.repository:not(.milestones).commits + > .ui.container .repository-summary.horizontal span svg,.page-content.repository:not(.milestones).branches + > .ui.container .repository-summary.horizontal span svg,.page-content.repository:not(.milestones).file + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span svg,.page-content.repository:not(.milestones).commits + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span svg,.page-content.repository:not(.milestones).branches + > .ui.container [class="ui attached segment two column grid"] + [class="ui attached + segment horizontal segments"].horizontal span svg{width:100%;height:20px;margin-bottom:10px + !important}}.issue.list,.milestone.list{border:1px solid var(--color-secondary);border-radius:var(--base-border-radius);background:var(--color-menu);padding:0 + !important;margin-top:1rem}.issue.list.milestone.list li,.milestone.list.milestone.list + li{border-bottom-style:solid !important;padding:10px 15px !important}.issue.list.milestone.list + li:last-child,.milestone.list.milestone.list li:last-child{border-bottom:0}.issue.list.milestone.list + li > a,.milestone.list.milestone.list li > a{font-size:1.4em}.issue.list li,.milestone.list + li{padding:8px 10px !important;position:relative}.issue.list li:hover:before,.milestone.list + li:hover:before,.issue.list li:focus-within:before,.milestone.list li:focus-within:before{content:"";position:absolute;inset:0;background-color:var(--color-primary);z-index:0;opacity:.07;pointer-events:none}.issue.list + li > *,.milestone.list li > *{z-index:1}.issue.list li .issue-item-left,.milestone.list + li .issue-item-left{padding-top:2px}.issue.list li .issue-item-left svg,.milestone.list + li .issue-item-left svg{margin-right:8px}.issue.list li .issue-item-top-row,.milestone.list + li .issue-item-top-row{display:flex;flex-wrap:wrap}.issue.list li .issue-item-top-row + a,.milestone.list li .issue-item-top-row a{vertical-align:unset !important;margin-bottom:8px}.issue.list + li .issue-item-top-row > a,.milestone.list li .issue-item-top-row > a{margin-right:4px}.issue.list + li .issue-item-top-row .label,.milestone.list li .issue-item-top-row .label{line-height:1em;font-weight:500;text-transform:capitalize;filter:saturate(0.8)}.issue.list + li .issue-item-bottom-row,.milestone.list li .issue-item-bottom-row{margin-top:-6px + !important}.issue.list li .issue-item-bottom-row svg,.milestone.list li .issue-item-bottom-row + svg{margin-top:3px}.issue.list li .issue-item-bottom-row .checklist,.milestone.list + li .issue-item-bottom-row .checklist{margin-left:auto}.issue.list li .content,.milestone.list + li .content{padding-bottom:2px;padding-top:2px !important}.issue.list li .issue-item-icons-right,.milestone.list + li .issue-item-icons-right{width:5em;justify-content:flex-end}.issue.list li .issue-item-icons-right + .text,.milestone.list li .issue-item-icons-right .text{min-width:unset;margin-left:0}.issue.list + li .issue-item-icons-right a,.milestone.list li .issue-item-icons-right a{display:flex;align-items:center}.issue.list + .label,.milestone.list .label{margin-top:-1px;position:relative}#release-list{padding-left:0}#release-list + .four.wide.column{width:150px !important}.explore,.page-content.user,.profile{display:flex;flex-wrap:wrap;justify-content:center}.explore.explore + > .ui.container,.page-content.user.explore > .ui.container,.profile.explore > + .ui.container{max-width:calc(100vw - 40px);width:3000px !important;padding:0 calc(10vw + - 80px)}.explore.organization > .ui.container:first-child,.page-content.user.organization + > .ui.container:first-child,.profile.organization > .ui.container:first-child{padding-top:30px;padding-bottom:30px}.explore.organization + > .ui.container:first-child #org-info,.page-content.user.organization > .ui.container:first-child + #org-info,.profile.organization > .ui.container:first-child #org-info{display:flex;flex-direction:column;margin:0 + 10px;justify-content:center}.explore.organization > .ui.container:first-child + #org-info > p,.page-content.user.organization > .ui.container:first-child #org-info + > p,.profile.organization > .ui.container:first-child #org-info > p{margin:0 !important;margin-bottom:.2rem + !important}.explore.organization > .ui.container:first-child #org-info .item,.page-content.user.organization + > .ui.container:first-child #org-info .item,.profile.organization > .ui.container:first-child + #org-info .item{display:flex;align-items:center}.explore.organization > .ui.container:first-child + #org-info .item a,.page-content.user.organization > .ui.container:first-child + #org-info .item a,.profile.organization > .ui.container:first-child #org-info + .item a{margin-left:4px}.explore.organization .ui.eleven.wide.column::before,.page-content.user.organization + .ui.eleven.wide.column::before,.profile.organization .ui.eleven.wide.column::before{content:unset + !important}.explore > .ui.container,.page-content.user > .ui.container,.profile + > .ui.container{max-width:1350px;width:100% !important;padding:0 20px}.explore + > .ui.container[class="ui container"] .five.wide.column,.page-content.user > .ui.container[class="ui + container"] .five.wide.column,.profile > .ui.container[class="ui container"] .five.wide.column{width:320px + !important;flex:1 0 320px !important;position:sticky;bottom:10px;align-self:flex-end;min-height:100vh;line-height:1.5em}.explore + > .ui.container[class="ui container"] .five.wide.column .card,.page-content.user + > .ui.container[class="ui container"] .five.wide.column .card,.profile > .ui.container[class="ui + container"] .five.wide.column .card{width:100%;border:0;background:none !important}.explore + > .ui.container[class="ui container"] .five.wide.column .card #profile-avatar,.page-content.user + > .ui.container[class="ui container"] .five.wide.column .card #profile-avatar,.profile + > .ui.container[class="ui container"] .five.wide.column .card #profile-avatar{padding-left:.7rem;padding-right:2rem + !important;padding-top:.5rem}.explore > .ui.container[class="ui container"] .five.wide.column + .card #profile-avatar .avatar,.page-content.user > .ui.container[class="ui container"] + .five.wide.column .card #profile-avatar .avatar,.profile > .ui.container[class="ui + container"] .five.wide.column .card #profile-avatar .avatar{border-radius:calc(var(--base-border-radius) + * 2) !important;box-shadow:0 4px 12px rgba(0,0,0,0.1);border:1px solid var(--color-secondary);background:var(--color-body);height:100% + !important;max-height:80vh}.explore > .ui.container[class="ui container"] .five.wide.column + .card .profile-avatar-name,.page-content.user > .ui.container[class="ui container"] + .five.wide.column .card .profile-avatar-name,.profile > .ui.container[class="ui + container"] .five.wide.column .card .profile-avatar-name{font-size:2rem;display:contents}.explore + > .ui.container[class="ui container"] .five.wide.column .card .profile-avatar-name + span,.page-content.user > .ui.container[class="ui container"] .five.wide.column + .card .profile-avatar-name span,.profile > .ui.container[class="ui container"] + .five.wide.column .card .profile-avatar-name span{text-align:left;font-weight:600;padding-left:.7rem;margin:1.5rem + 0}.explore > .ui.container[class="ui container"] .five.wide.column .card .profile-avatar-name + span.header,.page-content.user > .ui.container[class="ui container"] .five.wide.column + .card .profile-avatar-name span.header,.profile > .ui.container[class="ui container"] + .five.wide.column .card .profile-avatar-name span.header{font-weight:500;letter-spacing:.1em;height:39px;line-height:40px;margin-bottom:-1em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:0;background:var(--color-body);position:sticky;top:0;z-index:3;box-shadow:0 + -5px 5px var(--color-body)}.explore > .ui.container[class="ui container"] .five.wide.column + .card .profile-avatar-name span.username,.page-content.user > .ui.container[class="ui + container"] .five.wide.column .card .profile-avatar-name span.username,.profile + > .ui.container[class="ui container"] .five.wide.column .card .profile-avatar-name + span.username{z-index:2}.explore > .ui.container[class="ui container"] .five.wide.column + .card .extra,.page-content.user > .ui.container[class="ui container"] .five.wide.column + .card .extra,.profile > .ui.container[class="ui container"] .five.wide.column + .card .extra{border:0 !important}.explore > .ui.container[class="ui container"] + .five.wide.column .card .extra li,.page-content.user > .ui.container[class="ui + container"] .five.wide.column .card .extra li,.profile > .ui.container[class="ui + container"] .five.wide.column .card .extra li{border:0 !important;padding:.3rem + .7rem;display:flex;align-items:center}.explore > .ui.container[class="ui container"] + .five.wide.column .card .extra li svg,.page-content.user > .ui.container[class="ui + container"] .five.wide.column .card .extra li svg,.profile > .ui.container[class="ui + container"] .five.wide.column .card .extra li svg{margin-right:8px}.explore > + .ui.container[class="ui container"] .five.wide.column .card .extra li .user-orgs,.page-content.user + > .ui.container[class="ui container"] .five.wide.column .card .extra li .user-orgs,.profile + > .ui.container[class="ui container"] .five.wide.column .card .extra li .user-orgs{justify-content:center;margin-top:1rem + !important}.explore > .ui.container[class="ui container"] .five.wide.column .card + .extra li .user-orgs li,.page-content.user > .ui.container[class="ui container"] + .five.wide.column .card .extra li .user-orgs li,.profile > .ui.container[class="ui + container"] .five.wide.column .card .extra li .user-orgs li{flex-grow:1 !important}.explore + > .ui.container[class="ui container"] .five.wide.column .card .extra li .user-orgs + li a,.page-content.user > .ui.container[class="ui container"] .five.wide.column + .card .extra li .user-orgs li a,.profile > .ui.container[class="ui container"] + .five.wide.column .card .extra li .user-orgs li a{width:100%}.explore > .ui.container[class="ui + container"] .five.wide.column .card .extra li .user-orgs li a img,.page-content.user + > .ui.container[class="ui container"] .five.wide.column .card .extra li .user-orgs + li a img,.profile > .ui.container[class="ui container"] .five.wide.column .card + .extra li .user-orgs li a img{width:35px;height:35px}.explore > .ui.container[class="ui + container"] .five.wide.column .card .extra li .render-content,.page-content.user + > .ui.container[class="ui container"] .five.wide.column .card .extra li .render-content,.profile + > .ui.container[class="ui container"] .five.wide.column .card .extra li .render-content{padding:.6rem + 0;padding-bottom:.7rem !important}.explore > .ui.container[class="ui container"] + .five.wide.column .card .extra li .render-content p,.page-content.user > .ui.container[class="ui + container"] .five.wide.column .card .extra li .render-content p,.profile > .ui.container[class="ui + container"] .five.wide.column .card .extra li .render-content p{word-break:normal + !important;line-height:1.5em}.explore > .ui.container[class="ui container"] .five.wide.column + .card .extra .follow form,.page-content.user > .ui.container[class="ui container"] + .five.wide.column .card .extra .follow form,.profile > .ui.container[class="ui + container"] .five.wide.column .card .extra .follow form{margin-top:1rem;width:100%}.explore + > .ui.container .eleven.wide.column,.page-content.user > .ui.container .eleven.wide.column,.profile + > .ui.container .eleven.wide.column{flex-grow:100;width:500px !important;max-width:100%}.explore + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu,.page-content.user + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu,.profile + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu{margin:0 + -2px;margin:0 -20px;margin-left:-40px;margin-bottom:1.5rem;position:sticky;top:-2px + !important;margin-top:-42px;height:42px;z-index:99;background-color:var(--color-body)}.explore + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::before,.page-content.user + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::before,.profile + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::before{content:"";min-width:40px;display:block;position:sticky;left:0;background:linear-gradient(to + right,var(--color-body),transparent);z-index:1}@supports (backdrop-filter:blur(10px)){.explore + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu,.page-content.user + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu,.profile + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu{backdrop-filter:blur(10px);background-color:transparent}.explore + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::after,.page-content.user + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::after,.profile + > .ui.container .eleven.wide.column .ui.secondary.stackable.pointing.tight.menu::after{all:unset;z-index:-1;content:"" + !important;position:absolute;inset:0;background-color:var(--color-body);opacity:.8}}.explore + > .ui.container .eleven.wide.column::before,.page-content.user > .ui.container + .eleven.wide.column::before,.profile > .ui.container .eleven.wide.column::before{content:"";position:sticky;margin:0 + !important;margin-left:-100vw !important;margin-top:42px !important;width:200vw;display:block;border-bottom:1px + solid var(--color-secondary);top:39px;z-index:-1}.explore > .ui.container .item + .meta,.page-content.user > .ui.container .item .meta,.profile > .ui.container + .item .meta{display:flex;align-items:center}.explore > .ui.container .item .meta + svg,.page-content.user > .ui.container .item .meta svg,.profile > .ui.container + .item .meta svg{margin-right:4px}.explore > .ui.container .item .meta a,.page-content.user + > .ui.container .item .meta a,.profile > .ui.container .item .meta a{max-width:calc(100% + - 20px);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ui.container + > .news,.feeds > .news{border:1px solid var(--color-secondary) !important;border-bottom:none + !important;background:var(--color-box-body);border-radius:var(--base-border-radius) + var(--base-border-radius) 0 0;padding:12px 12px !important;position:relative;overflow:hidden}.ui.container + > .news + .news,.feeds > .news + .news{border-radius:0 !important}.ui.container + > .news:last-child,.feeds > .news:last-child{border-radius:0 0 var(--base-border-radius) + var(--base-border-radius) !important;border-bottom:1px solid var(--color-secondary) + !important}.ui.container > .news::before,.feeds > .news::before{content:"";position:absolute;inset:0;background:var(--color-primary);opacity:0;transition:opacity + .2s}.ui.container > .news:hover::before,.feeds > .news:hover::before,.ui.container + > .news:focus-within::before,.feeds > .news:focus-within::before{opacity:.05}.ui.container + > .news .ui.left,.feeds > .news .ui.left{width:42px}.ui.container > .news .ui.left + img,.feeds > .news .ui.left img{margin:0 !important;margin-right:18px !important;border-radius:var(--base-border-radius)}.ui.container + > .news .ui.grid,.feeds > .news .ui.grid{margin:0 !important;padding:0 !important;padding-top:1px + !important;min-height:30px}.ui.container > .news .ui.grid > .column,.feeds > .news + .ui.grid > .column{padding:0 !important;width:100% !important}.ui.container > + .news .ui.grid > .column > div,.feeds > .news .ui.grid > .column > div{display:flex;flex-wrap:wrap;align-items:center;min-height:100%}.ui.container + > .news .ui.grid > .column > div p,.feeds > .news .ui.grid > .column > div p{height:max-content}.ui.container + > .news .ui.grid > .column > div > *:first-child,.feeds > .news .ui.grid > .column + > div > *:first-child{min-width:calc(100% - 200px);margin-bottom:0 !important}.ui.container + > .news .ui.grid > .column > div > :not(:first-child):not(:last-child),.feeds + > .news .ui.grid > .column > div > :not(:first-child):not(:last-child){min-width:calc(100% + - 200px);margin-top:.3em}.ui.container > .news .ui.grid > .column > div > *:last-child,.feeds + > .news .ui.grid > .column > div > *:last-child{margin-left:auto;margin-top:auto}.ui.container + > .news .ui.grid .content,.feeds > .news .ui.grid .content{max-width:100%}.ui.container + > .news .ui.grid > .column > div > p:first-child,.feeds > .news .ui.grid > .column + > div > p:first-child{margin-bottom:.4rem;font-weight:600}.ui.container > .news + .ui.grid > .right.column,.feeds > .news .ui.grid > .right.column{position:absolute;top:32px;left:0;width:53px + !important;text-align:center;z-index:2}.ui.container > .news .ui.grid > .right.column + svg,.feeds > .news .ui.grid > .right.column svg{width:26px;height:26px;padding:3px;fill:#000 + !important;background-color:var(--color-box-body);border-radius:100px;border:1px + solid var(--color-secondary)}.ui.container > .news .ui.grid > .right.column svg + path,.feeds > .news .ui.grid > .right.column svg path{fill:var(--color-primary)}.ui.container + > .news .ui.grid .title,.feeds > .news .ui.grid .title,.ui.container > .news .ui.grid + p,.feeds > .news .ui.grid p,.ui.container > .news .ui.grid ul,.feeds > .news .ui.grid + ul{margin-bottom:0}.ui.container > .news .ui.grid ul,.feeds > .news .ui.grid ul{margin-top:0;border-left:4px + solid var(--color-secondary)}.ui.container > .news .ui.grid ul li .mr-2,.feeds + > .news .ui.grid ul li .mr-2{margin-top:.1em}.ui.container > .news .divider,.feeds + > .news .divider{display:none}.ui.repository.list,.ui.user.list{margin-top:16px + !important;display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));grid-gap:16px}.ui.repository.list + .item,.ui.user.list .item{width:100%;max-width:calc(100vw - 40px);min-height:unset + !important;padding:14px 16px !important;border:1px solid var(--color-secondary);background:var(--color-box-body);border-radius:8px;flex-grow:1;display:grid + !important;position:relative;grid-template-columns:max-content 1fr;align-content:flex-start}.ui.repository.list + .item:hover,.ui.user.list .item:hover,.ui.repository.list .item:focus-within,.ui.user.list + .item:focus-within{box-shadow:var(--lt-shadowDefault)}.ui.repository.list .item:focus-within,.ui.user.list + .item:focus-within{border-color:var(--color-primary) !important}.ui.repository.list + .item .header,.ui.user.list .item .header{display:contents !important}.ui.repository.list + .item .header .repo-title,.ui.user.list .item .header .repo-title{display:contents;position:relative;font-size:1.2rem;line-height:1.4em}.ui.repository.list + .item .header .repo-title a,.ui.user.list .item .header .repo-title a{white-space:normal + !important;word-break:normal !important;text-decoration:none !important}.ui.repository.list + .item .header .repo-title > *,.ui.user.list .item .header .repo-title > *{grid-column:2/span + 4}.ui.repository.list .item .header .repo-title .labels,.ui.user.list .item .header + .repo-title .labels{grid-column:3;grid-row:2;margin-left:0 !important;white-space:nowrap}.ui.repository.list + .item .header .repo-title a::before,.ui.user.list .item .header .repo-title a::before{content:"";position:absolute;top:0;left:0;height:80px;width:100%}.ui.repository.list + .item .header .avatar,.ui.user.list .item .header .avatar{height:50px !important;width:50px + !important;margin-right:12px !important;margin-left:-2px;grid-column:1;grid-row:1/span + 2;border-radius:12px}.ui.repository.list .item .header .metas,.ui.user.list .item + .header .metas{grid-column:2;grid-row:2;width:100%}.ui.repository.list .item .description,.ui.user.list + .item .description{display:contents}.ui.repository.list .item .description > p:first-child,.ui.user.list + .item .description > p:first-child{padding-top:4px;margin-bottom:0}.ui.repository.list + .item .description > *,.ui.user.list .item .description > *{grid-column:1/span + 4}.ui.repository.list .item .description > *:nth-last-child(2),.ui.user.list .item + .description > *:nth-last-child(2){margin-bottom:0 !important}.ui.repository.list + .item .description .time,.ui.user.list .item .description .time{grid-row:2;grid-column:4;margin-left:10px;text-align:right;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;z-index:3;height:max-content;margin-block:auto + !important}.ui.repository.list .item .description .tags,.ui.user.list .item .description + .tags{display:flex;overflow-x:auto;scrollbar-width:none;margin-top:8px;border-radius:100px}.ui.repository.list + .item .description .tags::-webkit-scrollbar,.ui.user.list .item .description .tags::-webkit-scrollbar{display:none}.ui.repository.list + .item .description .tags a > div,.ui.user.list .item .description .tags a > div{width:max-content + !important}.ui.repository.list .item .description .tags a:last-child,.ui.user.list + .item .description .tags a:last-child{padding-right:100px}.ui.repository.list + .item .description .tags::after,.ui.user.list .item .description .tags::after{content:"";min-width:100px;margin-left:-100px;height:100%;position:sticky;display:flex;right:0;background:linear-gradient(to + right,transparent,var(--color-box-body));pointer-events:none}.ui.repository.list + .item .content,.ui.user.list .item .content{padding-left:12px;margin-bottom:-.4em}.ui.repository.list + .item .content .header a,.ui.user.list .item .content .header a{font-weight:600;text-transform:capitalize}.ui.repository.list + .item .content .description,.ui.user.list .item .content .description{display:flex;flex-direction:column;padding-left:22px;line-height:1.8em;margin-top:.5em}.ui.repository.list + .item .content .description svg,.ui.user.list .item .content .description svg{margin-bottom:-1.45em + !important;margin-left:-22px !important}.dashboard{display:flex;flex-wrap:wrap;margin:auto;max-width:100%;padding:30px + 0;width:1400px;margin-bottom:-45px;gap:-50px}.dashboard .dashboard-navbar{width:auto;position:sticky;bottom:10px;align-self:flex-end;height:min-content;z-index:2;flex:1 + 1 80px;max-width:calc(100% - 60px);margin:0 auto;margin-left:10px !important;margin-bottom:40px;background-color:var(--color-bg)}.dashboard + .dashboard-navbar > .menu{padding-top:0 !important;padding-right:0 !important}.dashboard + .dashboard-navbar .button{white-space:nowrap;max-width:100% !important;padding-inline:10px + !important;overflow:hidden}.dashboard .dashboard-navbar > .ui{display:flex;flex-wrap:wrap;flex-direction:row + !important;padding-top:20px}.dashboard .dashboard-navbar > .ui .right.right{display:contents}.dashboard + .dashboard-navbar > .ui > .item,.dashboard .dashboard-navbar > .ui > .right > + .item{flex-direction:column;margin:0 !important;text-align:center;flex-grow:1;display:flex + !important;align-content:center;align-items:center;padding-bottom:20px;max-width:100%;width:80px + !important;text-indent:-.35em !important}.dashboard .dashboard-navbar > .ui > + .item > .dropdown,.dashboard .dashboard-navbar > .ui > .right > .item > .dropdown,.dashboard + .dashboard-navbar > .ui > .item .button,.dashboard .dashboard-navbar > .ui > .right + > .item .button{text-indent:0 !important}.dashboard .dashboard-navbar > .ui > + .item span,.dashboard .dashboard-navbar > .ui > .right > .item span{text-align:center}.dashboard + .dashboard-navbar > .ui > .item .label,.dashboard .dashboard-navbar > .ui > .right + > .item .label{margin:0 !important}.dashboard .dashboard-navbar > .ui > .item + svg:not(.octicon-plus),.dashboard .dashboard-navbar > .ui > .right > .item svg:not(.octicon-plus){width:100%;height:22px;margin:0 + !important;margin-bottom:10px !important}.dashboard .dashboard-navbar > .ui > + .item .dropdown > span,.dashboard .dashboard-navbar > .ui > .right > .item .dropdown + > span{flex-direction:column}.dashboard .dashboard-navbar > .ui > .item .dropdown + > span img,.dashboard .dashboard-navbar > .ui > .right > .item .dropdown > span + img{margin:0 auto;width:40px;height:40px !important;max-height:unset;margin-bottom:10px}.dashboard + .dashboard-navbar > .ui > .item .dropdown > span .icon,.dashboard .dashboard-navbar + > .ui > .right > .item .dropdown > span .icon{margin-bottom:0 !important}.dashboard + .dashboard-navbar > .ui > .item .dropdown > span .avatar,.dashboard .dashboard-navbar + > .ui > .right > .item .dropdown > span .avatar{border-radius:var(--base-border-radius)}.dashboard + .dashboard-navbar > .ui > .item .dropdown > span span,.dashboard .dashboard-navbar + > .ui > .right > .item .dropdown > span span{margin-bottom:-4px}.dashboard > .ui.container{margin:0 + auto !important;display:flex;flex-wrap:wrap-reverse !important;flex:9999 1 900px;margin-top:-31px + !important;padding-top:30px;border-top:1px solid var(--color-secondary);--container-padding-x:calc(2.5% + + 0px)}.dashboard > .ui.container > .ui.stackable.grid{width:100%;justify-content:center;margin:0}.dashboard + > .ui.container > .ui.stackable.grid > .ten.column{z-index:1;padding:15px var(--container-padding-x) + !important;padding-bottom:var(--container-padding-x) !important;margin:0 auto + !important}.dashboard > .ui.container > .ui.stackable.grid > .column{max-width:calc(100vw + - 40px);margin:0 auto;flex-grow:1}.dashboard > .ui.container > .ui.stackable.grid + > .column #user-heatmap + .divider{border-bottom:0}.dashboard > .ui.container + > .ui.stackable.grid > .column .ui.secondary.menu,.dashboard > .ui.container > + .ui.stackable.grid > .column .ui.stackable.grid{margin-left:0 !important;margin-right:0 + !important}.dashboard > .ui.container > .ui.stackable.grid .four.wide.column{min-width:230px}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column + div{flex-grow:9999}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column > .menu{display:flex;flex-direction:row;flex-wrap:wrap;margin:0 + -6px !important;background:none !important}.dashboard > .ui.container > .ui.stackable.grid + .four.wide.column > .menu a{display:flex;flex:1 0 auto;min-width:200px;max-width:calc(100% + - 12px);border:1px solid var(--color-secondary);margin:3px 6px}.dashboard > .ui.container + > .ui.stackable.grid .four.wide.column > .menu a .right{margin-left:auto}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column > .menu a span{width:100%}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column > .menu a .label{margin-left:8px}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column > .menu a:hover{background-color:var(--color-light)}.dashboard + > .ui.container > .ui.stackable.grid .four.wide.column > .menu .ui.divider{width:100%;margin:12px + 6px}.dashboard #app,.dashboard #dashboard-repo-list{height:min-content;position:sticky;bottom:0;align-self:flex-end;margin:0 + auto !important;margin-top:0 !important;margin-bottom:-81px !important;padding:0 + var(--container-padding-x) !important;padding-top:10px !important;padding-bottom:0 + !important;z-index:0;max-width:100% !important;flex-shrink:9999}.dashboard #app + > div,.dashboard #dashboard-repo-list > div{height:100%;display:flex;flex-direction:column;min-height:calc(100vh + - 30px)}.dashboard #app::before,.dashboard #dashboard-repo-list::before{content:"";position:absolute;bottom:-15px;width:200vw;height:calc(100vh + + 25px);right:calc(100% - 15px);right:100%;z-index:-100;background:var(--color-light);box-shadow:200vw + 0 var(--color-body),inset 0 -8px 8px rgba(0,0,0,0.07)}.dashboard #app .tabable,.dashboard + #dashboard-repo-list .tabable{background:none;margin-right:0 !important;box-sizing:border-box;width:unset;margin-bottom:0}.dashboard + #app .dashboard-repos,.dashboard #dashboard-repo-list .dashboard-repos,.dashboard + #app .dashboard-orgs,.dashboard #dashboard-repo-list .dashboard-orgs{margin:0 + -10px !important;padding:0 10px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;margin-bottom:0;min-height:200px;height:0;flex-grow:1;padding-top:15px;scrollbar-color:var(--color-secondary) + transparent}.dashboard #app .dashboard-repos .repos-search .input,.dashboard #dashboard-repo-list + .dashboard-repos .repos-search .input,.dashboard #app .dashboard-orgs .repos-search + .input,.dashboard #dashboard-repo-list .dashboard-orgs .repos-search .input{margin-bottom:8px}.dashboard + #app .dashboard-repos > .ui:last-child,.dashboard #dashboard-repo-list .dashboard-repos + > .ui:last-child,.dashboard #app .dashboard-orgs > .ui:last-child,.dashboard #dashboard-repo-list + .dashboard-orgs > .ui:last-child{margin-bottom:78px}.dashboard #app .dashboard-repos + > *,.dashboard #dashboard-repo-list .dashboard-repos > *,.dashboard #app .dashboard-orgs + > *,.dashboard #dashboard-repo-list .dashboard-orgs > *{border:0;background:none}.dashboard + #app .dashboard-repos .label,.dashboard #dashboard-repo-list .dashboard-repos + .label,.dashboard #app .dashboard-orgs .label,.dashboard #dashboard-repo-list + .dashboard-orgs .label{padding:0 .5em !important;height:1.4em;min-height:unset;font-size:.9em;min-width:2em + !important;position:relative;background:none;margin-left:.5em}.dashboard #app + .dashboard-repos .label::before,.dashboard #dashboard-repo-list .dashboard-repos + .label::before,.dashboard #app .dashboard-orgs .label::before,.dashboard #dashboard-repo-list + .dashboard-orgs .label::before{content:"";position:absolute;inset:0;background-color:var(--color-primary);opacity:.9;z-index:-1;border-radius:100px}.dashboard + #app .dashboard-repos .header,.dashboard #dashboard-repo-list .dashboard-repos + .header,.dashboard #app .dashboard-orgs .header,.dashboard #dashboard-repo-list + .dashboard-orgs .header{font-weight:600}.dashboard #app .dashboard-repos .header + a,.dashboard #dashboard-repo-list .dashboard-repos .header a,.dashboard #app .dashboard-orgs + .header a,.dashboard #dashboard-repo-list .dashboard-orgs .header a{display:flex;align-items:center}.dashboard + #app .dashboard-repos .header a svg,.dashboard #dashboard-repo-list .dashboard-repos + .header a svg,.dashboard #app .dashboard-orgs .header a svg,.dashboard #dashboard-repo-list + .dashboard-orgs .header a svg{width:18px;height:18px}.dashboard #app .dashboard-repos + .header,.dashboard #dashboard-repo-list .dashboard-repos .header,.dashboard #app + .dashboard-orgs .header,.dashboard #dashboard-repo-list .dashboard-orgs .header,.dashboard + #app .dashboard-repos .repos-search,.dashboard #dashboard-repo-list .dashboard-repos + .repos-search,.dashboard #app .dashboard-orgs .repos-search,.dashboard #dashboard-repo-list + .dashboard-orgs .repos-search{padding-left:5px;padding-right:5px}.dashboard #app + .dashboard-repos .repos-search,.dashboard #dashboard-repo-list .dashboard-repos + .repos-search,.dashboard #app .dashboard-orgs .repos-search,.dashboard #dashboard-repo-list + .dashboard-orgs .repos-search{padding-top:0}.dashboard #app .dashboard-repos .repos-search + input,.dashboard #dashboard-repo-list .dashboard-repos .repos-search input,.dashboard + #app .dashboard-orgs .repos-search input,.dashboard #dashboard-repo-list .dashboard-orgs + .repos-search input,.dashboard #app .dashboard-repos .repos-search .button,.dashboard + #dashboard-repo-list .dashboard-repos .repos-search .button,.dashboard #app .dashboard-orgs + .repos-search .button,.dashboard #dashboard-repo-list .dashboard-orgs .repos-search + .button{background:none !important}.dashboard #app .dashboard-repos .repos-filter,.dashboard + #dashboard-repo-list .dashboard-repos .repos-filter,.dashboard #app .dashboard-orgs + .repos-filter,.dashboard #dashboard-repo-list .dashboard-orgs .repos-filter{justify-content:flex-start}.dashboard + #app .dashboard-repos .repos-filter .item,.dashboard #dashboard-repo-list .dashboard-repos + .repos-filter .item,.dashboard #app .dashboard-orgs .repos-filter .item,.dashboard + #dashboard-repo-list .dashboard-orgs .repos-filter .item{padding:.85714286em 1.14285714em}.dashboard + #app .dashboard-repos .repos-filter .active,.dashboard #dashboard-repo-list .dashboard-repos + .repos-filter .active,.dashboard #app .dashboard-orgs .repos-filter .active,.dashboard + #dashboard-repo-list .dashboard-orgs .repos-filter .active{font-weight:600}.dashboard + #app .dashboard-repos .repos-filter .active::before,.dashboard #dashboard-repo-list + .dashboard-repos .repos-filter .active::before,.dashboard #app .dashboard-orgs + .repos-filter .active::before,.dashboard #dashboard-repo-list .dashboard-orgs + .repos-filter .active::before{content:unset !important}.dashboard #app .dashboard-repos + .repos-filter .active .label,.dashboard #dashboard-repo-list .dashboard-repos + .repos-filter .active .label,.dashboard #app .dashboard-orgs .repos-filter .active + .label,.dashboard #dashboard-repo-list .dashboard-orgs .repos-filter .active .label{font-size:1em}.dashboard + #app .dashboard-repos .table,.dashboard #dashboard-repo-list .dashboard-repos + .table,.dashboard #app .dashboard-orgs .table,.dashboard #dashboard-repo-list + .dashboard-orgs .table{margin-top:4px}.dashboard #app .dashboard-repos li,.dashboard + #dashboard-repo-list .dashboard-repos li,.dashboard #app .dashboard-orgs li,.dashboard + #dashboard-repo-list .dashboard-orgs li{background:none;font-weight:700;border:0}.dashboard + #app .dashboard-repos li a,.dashboard #dashboard-repo-list .dashboard-repos li + a,.dashboard #app .dashboard-orgs li a,.dashboard #dashboard-repo-list .dashboard-orgs + li a{padding:5px 5px;position:relative}.dashboard #app .dashboard-repos li a:hover:before,.dashboard + #dashboard-repo-list .dashboard-repos li a:hover:before,.dashboard #app .dashboard-orgs + li a:hover:before,.dashboard #dashboard-repo-list .dashboard-orgs li a:hover:before{content:"";position:absolute;inset:-2px + -6px;background-color:var(--color-primary);border-radius:var(--base-border-radius);z-index:-1;opacity:.1}.dashboard + #app .dashboard-repos li a .item-name,.dashboard #dashboard-repo-list .dashboard-repos + li a .item-name,.dashboard #app .dashboard-orgs li a .item-name,.dashboard #dashboard-repo-list + .dashboard-orgs li a .item-name{display:flex;align-items:center;max-width:unset;margin-right:10px}.dashboard + #app .dashboard-repos li a .item-name svg,.dashboard #dashboard-repo-list .dashboard-repos + li a .item-name svg,.dashboard #app .dashboard-orgs li a .item-name svg,.dashboard + #dashboard-repo-list .dashboard-orgs li a .item-name svg{margin-right:8px !important;min-width:16px}.dashboard + #app .dashboard-repos li a strong,.dashboard #dashboard-repo-list .dashboard-repos + li a strong,.dashboard #app .dashboard-orgs li a strong,.dashboard #dashboard-repo-list + .dashboard-orgs li a strong{font-weight:700;text-overflow:ellipsis;overflow:hidden}footer{border:0;padding:20px;box-sizing:border-box;width:auto + !important;border-top:1px solid var(--color-secondary);background:var(--color-body);z-index:2}footer + .container{max-width:1390px !important;padding:0 40px;box-sizing:border-box;width:auto + !important}@media only screen and (max-width:767px){.page-content{margin-bottom:0 + !important}.ui{--mobile-padding:18px}.ui.ui.ui.container:not(.fluid){margin-inline:var(--mobile-padding) + !important}.ui.ui.ui.container:not(.fluid) > #repo-files-table,.ui.ui.ui.container:not(.fluid) + > .tab-size-8,.ui.ui.ui.container:not(.fluid) .issue.list,.ui.ui.ui.container:not(.fluid) + .comment-list,.ui.ui.ui.container:not(.fluid) .comment{margin-inline:calc(0px + - var(--mobile-padding)) !important;width:calc(100% + (var(--mobile-padding) * + 2)) !important;border-inline:none !important;border-radius:0}.ui.ui.ui.container:not(.fluid) + > #repo-files-table.comment-list,.ui.ui.ui.container:not(.fluid) > .tab-size-8.comment-list,.ui.ui.ui.container:not(.fluid) + .issue.list.comment-list,.ui.ui.ui.container:not(.fluid) .comment-list.comment-list,.ui.ui.ui.container:not(.fluid) + .comment.comment-list{margin-left:0 !important}.tabular,.attached.header,.issue.list + .item,.ui.ui.ui.ui.table:not(.unstackable) > tr,.ui.ui.ui.ui.table:not(.unstackable) + > thead > tr,.ui.ui.ui.ui.table:not(.unstackable) > tbody > tr,.ui.ui.ui.ui.table:not(.unstackable) + > tfoot > tr{padding-inline:var(--mobile-padding) !important}.five.wide.column{min-height:unset + !important}.ui.button:not(.icon.detail):not(.labeled){min-height:45px !important;padding-left:25px + !important;padding-right:25px !important;max-width:unset !important}.ui.menu.mobile--margin-between-items + > .fitted.item{width:auto !important}.repo-header{flex-direction:column}.repo-header + .repo-title{flex-direction:column}.repo-header .repo-title .repo-icon,.repo-header + .repo-title .avatar{width:120px !important;height:120px !important;margin-bottom:.3em;margin-right:0 + !important;display:flex;align-items:center;justify-content:center}.repo-header + .repo-title .repo-icon svg,.repo-header .repo-title .avatar svg{width:70%;height:70%}.repo-header + .repo-title .mx-2{display:none}.repo-header .repo-title .labels{margin-left:0 + !important;margin-top:.2em}.repo-header .repo-buttons{margin-top:1em}.repo-header + .repo-buttons [data-position]{width:100%;justify-content:center}.dashboard > .ui.container + #app{margin-bottom:0 !important}.dashboard > .ui.container #app::before{content:unset + !important}.dashboard > .ui.container #app > div{min-height:unset !important}.dashboard + > .ui.container #app > div .dashboard-repos{height:auto}footer .links > *{display:block;padding:0 + !important;margin:0 !important;border:0 !important}}@-moz-keyframes slideInY{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@-webkit-keyframes + slideInY{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@-o-keyframes + slideInY{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@keyframes + slideInY{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}@-moz-keyframes + slideOutY{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}@-webkit-keyframes + slideOutY{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}@-o-keyframes + slideOutY{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}@keyframes + slideOutY{from{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}' + theme-gruvbox-auto.css: '@media (prefers-color-scheme:dark){:root{--is-dark-theme:true;--color-primary:#b8bb26;--color-primary-contrast:#282828;--color-primary-dark-1:#b8bb26;--color-primary-dark-2:#c1c03d;--color-primary-dark-3:#c9c655;--color-primary-dark-4:#d2cb6c;--color-primary-dark-5:#dad083;--color-primary-dark-6:#e2d69b;--color-primary-dark-7:#ebdbb2;--color-primary-light-1:#b8bb26;--color-primary-light-2:#a3a529;--color-primary-light-3:#8e8f2b;--color-primary-light-4:#79792e;--color-primary-light-5:#646330;--color-primary-light-6:#4f4d32;--color-primary-light-7:#3a3735;--color-primary-alpha-10:rgba(184,187,38,0.1);--color-primary-alpha-20:rgba(184,187,38,0.2);--color-primary-alpha-30:rgba(184,187,38,0.3);--color-primary-alpha-40:rgba(184,187,38,0.4);--color-primary-alpha-50:rgba(184,187,38,0.5);--color-primary-alpha-60:rgba(184,187,38,0.6);--color-primary-alpha-70:rgba(184,187,38,0.7);--color-primary-alpha-80:rgba(184,187,38,0.8);--color-primary-alpha-90:rgba(184,187,38,0.9);--color-secondary:#504945;--color-secondary-dark-1:#504945;--color-secondary-dark-2:#5d554e;--color-secondary-dark-3:#6a6157;--color-secondary-dark-4:#776e60;--color-secondary-dark-5:#847a69;--color-secondary-dark-6:#918672;--color-secondary-dark-7:#9e927c;--color-secondary-dark-8:#aa9e85;--color-secondary-dark-9:#b7aa8e;--color-secondary-dark-10:#c4b797;--color-secondary-dark-11:#d1c3a0;--color-secondary-dark-12:#decfa9;--color-secondary-dark-13:#ebdbb2;--color-secondary-light-1:#504945;--color-secondary-light-2:#45403d;--color-secondary-light-3:#3a3735;--color-secondary-light-4:#32302f;--color-secondary-alpha-10:rgba(80,73,69,0.1);--color-secondary-alpha-20:rgba(80,73,69,0.2);--color-secondary-alpha-30:rgba(80,73,69,0.3);--color-secondary-alpha-40:rgba(80,73,69,0.4);--color-secondary-alpha-50:rgba(80,73,69,0.5);--color-secondary-alpha-60:rgba(80,73,69,0.6);--color-secondary-alpha-70:rgba(80,73,69,0.7);--color-secondary-alpha-80:rgba(80,73,69,0.8);--color-secondary-alpha-90:rgba(80,73,69,0.9);--color-red:#fb4934;--color-orange:#fe8019;--color-yellow:#fabd2f;--color-olive:#b8bb26;--color-green:#b8bb26;--color-teal:#8ec07c;--color-blue:#83a598;--color-violet:#d3869b;--color-purple:#d3869b;--color-pink:#d3869b;--color-brown:#fe8019;--color-grey:#928374;--color-red-light:#fb4934;--color-orange-light:#fe8019;--color-yellow-light:#fabd2f;--color-olive-light:#b8bb26;--color-green-light:#b8bb26;--color-teal-light:#8ec07c;--color-blue-light:#83a598;--color-violet-light:#d3869b;--color-purple-light:#d3869b;--color-pink-light:#d3869b;--color-brown-light:#fe8019;--color-grey-light:#928374;--color-black:#3a3735;--color-gold:#fe8019;--color-white:#282828;--color-diff-removed-word-bg:#813a33;--color-diff-added-word-bg:#616833;--color-diff-removed-row-bg:#4c3432;--color-diff-moved-row-bg:#374141;--color-diff-added-row-bg:#3b4439;--color-diff-removed-row-border:#a43f33;--color-diff-moved-row-border:#5d736d;--color-diff-added-row-border:#7a8030;--color-diff-inactive:#3a3735;--color-error-border:#a43f33;--color-error-bg:#4c3432;--color-error-text:#fb4934;--color-success-border:#7a8030;--color-success-bg:#3b4439;--color-success-text:#b8bb26;--color-warning-border:#a5802f;--color-warning-bg:#4f422e;--color-warning-text:#fabd2f;--color-info-border:#5d736d;--color-info-bg:#374141;--color-info-text:#83a598;--color-body:#282828;--color-box-header:#45403d;--color-box-body:#32302f;--color-box-body-highlight:#3a3735;--color-text-dark:#fe8019;--color-text:#ebdbb2;--color-text-hover:rgba(235,219,178,0.8);--color-text-light:#fabd2f;--color-text-light-1:#928374;--color-text-light-2:#ebdbb2;--color-text-light-3:#928374;--color-footer:#32302f;--color-timeline:#504945;--color-input-text:#ebdbb2;--color-input-background:#282828;--color-input-toggle-background:#32302f;--color-input-border:#504945;--color-input-border-hover:#71665d;--color-navbar:#32302f;--color-navbar-transparent:#32302f;--color-light:#32302f;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#504945;--color-hover:rgba(80,73,69,0.5);--color-active:rgba(80,73,69,0.5);--color-menu:#32302f;--color-card:#32302f;--color-markup-table-row:#45403d;--color-markup-code-block:#282828;--color-button:#45403d;--color-code-bg:#282828;--color-code-sidebar-bg:#32302f;--color-shadow:#00000060;--color-secondary-bg:#32302f;--color-text-focus:#fff;--color-expand-button:#3a3735;--color-placeholder-text:#928374;--color-editor-line-highlight:#32302f;--color-project-board-bg:#282828;--color-project-board-light-label:#b8bb26;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#32302f;--color-label-active-bg:#504945;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#32302f;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#ebdbb2}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#fb4934}.chroma + .o,.chroma .ow{color:#fe8019}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#8ec07c}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d3869b}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#fabd2f}.chroma .nf,.chroma .nb,.chroma .na{color:#b8bb26}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#83a598}.ui.primary.button,.ui.primary.buttons + .button{background:#b8bb26;background-color:#b8bb26 !important;color:#282828}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(184,187,38,0.8);background-color:rgba(184,187,38,0.8) + !important;color:#282828}.ui.green.buttons .button,.ui.green.button{background:#a89984;background-color:#a89984;color:#282828}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(168,153,132,0.8);background-color:rgba(168,153,132,0.8);color:#282828}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#fe8019}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#fe8019}.repo-title{color:#fe8019}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#b8bb26}.ui.basic.labels + .label,.ui.basic.label{color:#b8bb26}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#282828}.ui.green.labels .label,.ui.ui.ui.green.label{color:#282828}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#fe8019 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #fe8019 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #fe8019 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #fe8019 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#fb4934;background-color:#fb4934;color:#282828}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(251,73,52,0.8);background-color:rgba(251,73,52,0.8);color:#282828}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#282828}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#282828}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#fe8019;background-color:#fe8019;color:#282828}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(254,128,25,0.8);background-color:rgba(254,128,25,0.8);color:#282828}}@media + (prefers-color-scheme:light){:root{--is-dark-theme:true;--color-primary:#79740e;--color-primary-contrast:#fbf1c7;--color-primary-dark-1:#79740e;--color-primary-dark-2:#6f6a15;--color-primary-dark-3:#65601b;--color-primary-dark-4:#5b5622;--color-primary-dark-5:#504c29;--color-primary-dark-6:#46422f;--color-primary-dark-7:#3c3836;--color-primary-light-1:#79740e;--color-primary-light-2:#8d872b;--color-primary-light-3:#a19a48;--color-primary-light-4:#b6ad65;--color-primary-light-5:#cabf82;--color-primary-light-6:#ded29f;--color-primary-light-7:#f2e5bc;--color-primary-alpha-10:rgba(121,116,14,0.1);--color-primary-alpha-20:rgba(121,116,14,0.2);--color-primary-alpha-30:rgba(121,116,14,0.3);--color-primary-alpha-40:rgba(121,116,14,0.4);--color-primary-alpha-50:rgba(121,116,14,0.5);--color-primary-alpha-60:rgba(121,116,14,0.6);--color-primary-alpha-70:rgba(121,116,14,0.7);--color-primary-alpha-80:rgba(121,116,14,0.8);--color-primary-alpha-90:rgba(121,116,14,0.9);--color-secondary:#e5d5ad;--color-secondary-dark-1:#e5d5ad;--color-secondary-dark-2:#d7c8a3;--color-secondary-dark-3:#c9bb99;--color-secondary-dark-4:#bbae8f;--color-secondary-dark-5:#ada185;--color-secondary-dark-6:#9f947b;--color-secondary-dark-7:#918772;--color-secondary-dark-8:#827968;--color-secondary-dark-9:#746c5e;--color-secondary-dark-10:#665f54;--color-secondary-dark-11:#58524a;--color-secondary-dark-12:#4a4540;--color-secondary-dark-13:#3c3836;--color-secondary-light-1:#e5d5ad;--color-secondary-light-2:#eee0b7;--color-secondary-light-3:#f2e5bc;--color-secondary-light-4:#f4e8be;--color-secondary-alpha-10:rgba(229,213,173,0.1);--color-secondary-alpha-20:rgba(229,213,173,0.2);--color-secondary-alpha-30:rgba(229,213,173,0.3);--color-secondary-alpha-40:rgba(229,213,173,0.4);--color-secondary-alpha-50:rgba(229,213,173,0.5);--color-secondary-alpha-60:rgba(229,213,173,0.6);--color-secondary-alpha-70:rgba(229,213,173,0.7);--color-secondary-alpha-80:rgba(229,213,173,0.8);--color-secondary-alpha-90:rgba(229,213,173,0.9);--color-red:#9d0006;--color-orange:#af3a03;--color-yellow:#b57614;--color-olive:#79740e;--color-green:#79740e;--color-teal:#427b58;--color-blue:#076678;--color-violet:#8f3f71;--color-purple:#8f3f71;--color-pink:#8f3f71;--color-brown:#af3a03;--color-grey:#928374;--color-red-light:#9d0006;--color-orange-light:#af3a03;--color-yellow-light:#b57614;--color-olive-light:#79740e;--color-green-light:#79740e;--color-teal-light:#427b58;--color-blue-light:#076678;--color-violet-light:#8f3f71;--color-purple-light:#8f3f71;--color-pink-light:#8f3f71;--color-brown-light:#af3a03;--color-grey-light:#928374;--color-black:#f2e5bc;--color-gold:#af3a03;--color-white:#fbf1c7;--color-diff-removed-word-bg:#d89881;--color-diff-added-word-bg:#c0c184;--color-diff-removed-row-bg:#f1d9b5;--color-diff-moved-row-bg:#dadec0;--color-diff-added-row-bg:#dee2b6;--color-diff-removed-row-border:#c76d5e;--color-diff-moved-row-border:#71a29c;--color-diff-added-row-border:#acab62;--color-diff-inactive:#f2e5bc;--color-error-border:#c76d5e;--color-error-bg:#f1d9b5;--color-error-text:#9d0006;--color-success-border:#acab62;--color-success-bg:#dee2b6;--color-success-text:#79740e;--color-warning-border:#d8af64;--color-warning-bg:#fae7b3;--color-warning-text:#b57614;--color-info-border:#71a29c;--color-info-bg:#dadec0;--color-info-text:#076678;--color-body:#fbf1c7;--color-box-header:#eee0b7;--color-box-body:#f4e8be;--color-box-body-highlight:#f2e5bc;--color-text-dark:#af3a03;--color-text:#3c3836;--color-text-hover:rgba(60,56,54,0.8);--color-text-light:#b57614;--color-text-light-1:#928374;--color-text-light-2:#3c3836;--color-text-light-3:#928374;--color-footer:#f4e8be;--color-timeline:#e5d5ad;--color-input-text:#3c3836;--color-input-background:#fbf1c7;--color-input-toggle-background:#f4e8be;--color-input-border:#e5d5ad;--color-input-border-hover:#bcac91;--color-navbar:#f4e8be;--color-navbar-transparent:#f4e8be;--color-light:#f4e8be;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e5d5ad;--color-hover:rgba(229,213,173,0.5);--color-active:rgba(229,213,173,0.5);--color-menu:#f4e8be;--color-card:#f4e8be;--color-markup-table-row:#eee0b7;--color-markup-code-block:#fbf1c7;--color-button:#eee0b7;--color-code-bg:#fbf1c7;--color-code-sidebar-bg:#f4e8be;--color-shadow:#00000060;--color-secondary-bg:#f4e8be;--color-text-focus:#fff;--color-expand-button:#f2e5bc;--color-placeholder-text:#928374;--color-editor-line-highlight:#f4e8be;--color-project-board-bg:#fbf1c7;--color-project-board-light-label:#79740e;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4e8be;--color-label-active-bg:#e5d5ad;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4e8be;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#3c3836}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#9d0006}.chroma + .o,.chroma .ow{color:#af3a03}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#427b58}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#8f3f71}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#b57614}.chroma .nf,.chroma .nb,.chroma .na{color:#79740e}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#076678}.ui.primary.button,.ui.primary.buttons + .button{background:#79740e;background-color:#79740e !important;color:#fbf1c7}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(121,116,14,0.8);background-color:rgba(121,116,14,0.8) + !important;color:#fbf1c7}.ui.green.buttons .button,.ui.green.button{background:#7c6f64;background-color:#7c6f64;color:#fbf1c7}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(124,111,100,0.8);background-color:rgba(124,111,100,0.8);color:#fbf1c7}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#af3a03}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#af3a03}.repo-title{color:#af3a03}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#79740e}.ui.basic.labels + .label,.ui.basic.label{color:#79740e}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fbf1c7}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fbf1c7}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#af3a03 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #af3a03 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #af3a03 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #af3a03 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#9d0006;background-color:#9d0006;color:#fbf1c7}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(157,0,6,0.8);background-color:rgba(157,0,6,0.8);color:#fbf1c7}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fbf1c7}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fbf1c7}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#af3a03;background-color:#af3a03;color:#fbf1c7}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(175,58,3,0.8);background-color:rgba(175,58,3,0.8);color:#fbf1c7}}' + theme-gruvbox-dark.css: ':root{--is-dark-theme:true;--color-primary:#b8bb26;--color-primary-contrast:#282828;--color-primary-dark-1:#b8bb26;--color-primary-dark-2:#c1c03d;--color-primary-dark-3:#c9c655;--color-primary-dark-4:#d2cb6c;--color-primary-dark-5:#dad083;--color-primary-dark-6:#e2d69b;--color-primary-dark-7:#ebdbb2;--color-primary-light-1:#b8bb26;--color-primary-light-2:#a3a529;--color-primary-light-3:#8e8f2b;--color-primary-light-4:#79792e;--color-primary-light-5:#646330;--color-primary-light-6:#4f4d32;--color-primary-light-7:#3a3735;--color-primary-alpha-10:rgba(184,187,38,0.1);--color-primary-alpha-20:rgba(184,187,38,0.2);--color-primary-alpha-30:rgba(184,187,38,0.3);--color-primary-alpha-40:rgba(184,187,38,0.4);--color-primary-alpha-50:rgba(184,187,38,0.5);--color-primary-alpha-60:rgba(184,187,38,0.6);--color-primary-alpha-70:rgba(184,187,38,0.7);--color-primary-alpha-80:rgba(184,187,38,0.8);--color-primary-alpha-90:rgba(184,187,38,0.9);--color-secondary:#504945;--color-secondary-dark-1:#504945;--color-secondary-dark-2:#5d554e;--color-secondary-dark-3:#6a6157;--color-secondary-dark-4:#776e60;--color-secondary-dark-5:#847a69;--color-secondary-dark-6:#918672;--color-secondary-dark-7:#9e927c;--color-secondary-dark-8:#aa9e85;--color-secondary-dark-9:#b7aa8e;--color-secondary-dark-10:#c4b797;--color-secondary-dark-11:#d1c3a0;--color-secondary-dark-12:#decfa9;--color-secondary-dark-13:#ebdbb2;--color-secondary-light-1:#504945;--color-secondary-light-2:#45403d;--color-secondary-light-3:#3a3735;--color-secondary-light-4:#32302f;--color-secondary-alpha-10:rgba(80,73,69,0.1);--color-secondary-alpha-20:rgba(80,73,69,0.2);--color-secondary-alpha-30:rgba(80,73,69,0.3);--color-secondary-alpha-40:rgba(80,73,69,0.4);--color-secondary-alpha-50:rgba(80,73,69,0.5);--color-secondary-alpha-60:rgba(80,73,69,0.6);--color-secondary-alpha-70:rgba(80,73,69,0.7);--color-secondary-alpha-80:rgba(80,73,69,0.8);--color-secondary-alpha-90:rgba(80,73,69,0.9);--color-red:#fb4934;--color-orange:#fe8019;--color-yellow:#fabd2f;--color-olive:#b8bb26;--color-green:#b8bb26;--color-teal:#8ec07c;--color-blue:#83a598;--color-violet:#d3869b;--color-purple:#d3869b;--color-pink:#d3869b;--color-brown:#fe8019;--color-grey:#928374;--color-red-light:#fb4934;--color-orange-light:#fe8019;--color-yellow-light:#fabd2f;--color-olive-light:#b8bb26;--color-green-light:#b8bb26;--color-teal-light:#8ec07c;--color-blue-light:#83a598;--color-violet-light:#d3869b;--color-purple-light:#d3869b;--color-pink-light:#d3869b;--color-brown-light:#fe8019;--color-grey-light:#928374;--color-black:#3a3735;--color-gold:#fe8019;--color-white:#282828;--color-diff-removed-word-bg:#813a33;--color-diff-added-word-bg:#616833;--color-diff-removed-row-bg:#4c3432;--color-diff-moved-row-bg:#374141;--color-diff-added-row-bg:#3b4439;--color-diff-removed-row-border:#a43f33;--color-diff-moved-row-border:#5d736d;--color-diff-added-row-border:#7a8030;--color-diff-inactive:#3a3735;--color-error-border:#a43f33;--color-error-bg:#4c3432;--color-error-text:#fb4934;--color-success-border:#7a8030;--color-success-bg:#3b4439;--color-success-text:#b8bb26;--color-warning-border:#a5802f;--color-warning-bg:#4f422e;--color-warning-text:#fabd2f;--color-info-border:#5d736d;--color-info-bg:#374141;--color-info-text:#83a598;--color-body:#282828;--color-box-header:#45403d;--color-box-body:#32302f;--color-box-body-highlight:#3a3735;--color-text-dark:#fe8019;--color-text:#ebdbb2;--color-text-hover:rgba(235,219,178,0.8);--color-text-light:#fabd2f;--color-text-light-1:#928374;--color-text-light-2:#ebdbb2;--color-text-light-3:#928374;--color-footer:#32302f;--color-timeline:#504945;--color-input-text:#ebdbb2;--color-input-background:#282828;--color-input-toggle-background:#32302f;--color-input-border:#504945;--color-input-border-hover:#71665d;--color-navbar:#32302f;--color-navbar-transparent:#32302f;--color-light:#32302f;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#504945;--color-hover:rgba(80,73,69,0.5);--color-active:rgba(80,73,69,0.5);--color-menu:#32302f;--color-card:#32302f;--color-markup-table-row:#45403d;--color-markup-code-block:#282828;--color-button:#45403d;--color-code-bg:#282828;--color-code-sidebar-bg:#32302f;--color-shadow:#00000060;--color-secondary-bg:#32302f;--color-text-focus:#fff;--color-expand-button:#3a3735;--color-placeholder-text:#928374;--color-editor-line-highlight:#32302f;--color-project-board-bg:#282828;--color-project-board-light-label:#b8bb26;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#32302f;--color-label-active-bg:#504945;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#32302f;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#ebdbb2}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#fb4934}.chroma + .o,.chroma .ow{color:#fe8019}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#8ec07c}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d3869b}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#fabd2f}.chroma .nf,.chroma .nb,.chroma .na{color:#b8bb26}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#83a598}.ui.primary.button,.ui.primary.buttons + .button{background:#b8bb26;background-color:#b8bb26 !important;color:#282828}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(184,187,38,0.8);background-color:rgba(184,187,38,0.8) + !important;color:#282828}.ui.green.buttons .button,.ui.green.button{background:#a89984;background-color:#a89984;color:#282828}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(168,153,132,0.8);background-color:rgba(168,153,132,0.8);color:#282828}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#fe8019}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#fe8019}.repo-title{color:#fe8019}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#b8bb26}.ui.basic.labels + .label,.ui.basic.label{color:#b8bb26}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#282828}.ui.green.labels .label,.ui.ui.ui.green.label{color:#282828}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#fe8019 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #fe8019 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #fe8019 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #fe8019 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#fb4934;background-color:#fb4934;color:#282828}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(251,73,52,0.8);background-color:rgba(251,73,52,0.8);color:#282828}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#282828}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#282828}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#fe8019;background-color:#fe8019;color:#282828}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(254,128,25,0.8);background-color:rgba(254,128,25,0.8);color:#282828}' + theme-gruvbox-light.css: ':root{--is-dark-theme:true;--color-primary:#79740e;--color-primary-contrast:#fbf1c7;--color-primary-dark-1:#79740e;--color-primary-dark-2:#6f6a15;--color-primary-dark-3:#65601b;--color-primary-dark-4:#5b5622;--color-primary-dark-5:#504c29;--color-primary-dark-6:#46422f;--color-primary-dark-7:#3c3836;--color-primary-light-1:#79740e;--color-primary-light-2:#8d872b;--color-primary-light-3:#a19a48;--color-primary-light-4:#b6ad65;--color-primary-light-5:#cabf82;--color-primary-light-6:#ded29f;--color-primary-light-7:#f2e5bc;--color-primary-alpha-10:rgba(121,116,14,0.1);--color-primary-alpha-20:rgba(121,116,14,0.2);--color-primary-alpha-30:rgba(121,116,14,0.3);--color-primary-alpha-40:rgba(121,116,14,0.4);--color-primary-alpha-50:rgba(121,116,14,0.5);--color-primary-alpha-60:rgba(121,116,14,0.6);--color-primary-alpha-70:rgba(121,116,14,0.7);--color-primary-alpha-80:rgba(121,116,14,0.8);--color-primary-alpha-90:rgba(121,116,14,0.9);--color-secondary:#e5d5ad;--color-secondary-dark-1:#e5d5ad;--color-secondary-dark-2:#d7c8a3;--color-secondary-dark-3:#c9bb99;--color-secondary-dark-4:#bbae8f;--color-secondary-dark-5:#ada185;--color-secondary-dark-6:#9f947b;--color-secondary-dark-7:#918772;--color-secondary-dark-8:#827968;--color-secondary-dark-9:#746c5e;--color-secondary-dark-10:#665f54;--color-secondary-dark-11:#58524a;--color-secondary-dark-12:#4a4540;--color-secondary-dark-13:#3c3836;--color-secondary-light-1:#e5d5ad;--color-secondary-light-2:#eee0b7;--color-secondary-light-3:#f2e5bc;--color-secondary-light-4:#f4e8be;--color-secondary-alpha-10:rgba(229,213,173,0.1);--color-secondary-alpha-20:rgba(229,213,173,0.2);--color-secondary-alpha-30:rgba(229,213,173,0.3);--color-secondary-alpha-40:rgba(229,213,173,0.4);--color-secondary-alpha-50:rgba(229,213,173,0.5);--color-secondary-alpha-60:rgba(229,213,173,0.6);--color-secondary-alpha-70:rgba(229,213,173,0.7);--color-secondary-alpha-80:rgba(229,213,173,0.8);--color-secondary-alpha-90:rgba(229,213,173,0.9);--color-red:#9d0006;--color-orange:#af3a03;--color-yellow:#b57614;--color-olive:#79740e;--color-green:#79740e;--color-teal:#427b58;--color-blue:#076678;--color-violet:#8f3f71;--color-purple:#8f3f71;--color-pink:#8f3f71;--color-brown:#af3a03;--color-grey:#928374;--color-red-light:#9d0006;--color-orange-light:#af3a03;--color-yellow-light:#b57614;--color-olive-light:#79740e;--color-green-light:#79740e;--color-teal-light:#427b58;--color-blue-light:#076678;--color-violet-light:#8f3f71;--color-purple-light:#8f3f71;--color-pink-light:#8f3f71;--color-brown-light:#af3a03;--color-grey-light:#928374;--color-black:#f2e5bc;--color-gold:#af3a03;--color-white:#fbf1c7;--color-diff-removed-word-bg:#d89881;--color-diff-added-word-bg:#c0c184;--color-diff-removed-row-bg:#f1d9b5;--color-diff-moved-row-bg:#dadec0;--color-diff-added-row-bg:#dee2b6;--color-diff-removed-row-border:#c76d5e;--color-diff-moved-row-border:#71a29c;--color-diff-added-row-border:#acab62;--color-diff-inactive:#f2e5bc;--color-error-border:#c76d5e;--color-error-bg:#f1d9b5;--color-error-text:#9d0006;--color-success-border:#acab62;--color-success-bg:#dee2b6;--color-success-text:#79740e;--color-warning-border:#d8af64;--color-warning-bg:#fae7b3;--color-warning-text:#b57614;--color-info-border:#71a29c;--color-info-bg:#dadec0;--color-info-text:#076678;--color-body:#fbf1c7;--color-box-header:#eee0b7;--color-box-body:#f4e8be;--color-box-body-highlight:#f2e5bc;--color-text-dark:#af3a03;--color-text:#3c3836;--color-text-hover:rgba(60,56,54,0.8);--color-text-light:#b57614;--color-text-light-1:#928374;--color-text-light-2:#3c3836;--color-text-light-3:#928374;--color-footer:#f4e8be;--color-timeline:#e5d5ad;--color-input-text:#3c3836;--color-input-background:#fbf1c7;--color-input-toggle-background:#f4e8be;--color-input-border:#e5d5ad;--color-input-border-hover:#bcac91;--color-navbar:#f4e8be;--color-navbar-transparent:#f4e8be;--color-light:#f4e8be;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e5d5ad;--color-hover:rgba(229,213,173,0.5);--color-active:rgba(229,213,173,0.5);--color-menu:#f4e8be;--color-card:#f4e8be;--color-markup-table-row:#eee0b7;--color-markup-code-block:#fbf1c7;--color-button:#eee0b7;--color-code-bg:#fbf1c7;--color-code-sidebar-bg:#f4e8be;--color-shadow:#00000060;--color-secondary-bg:#f4e8be;--color-text-focus:#fff;--color-expand-button:#f2e5bc;--color-placeholder-text:#928374;--color-editor-line-highlight:#f4e8be;--color-project-board-bg:#fbf1c7;--color-project-board-light-label:#79740e;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4e8be;--color-label-active-bg:#e5d5ad;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4e8be;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#3c3836}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#9d0006}.chroma + .o,.chroma .ow{color:#af3a03}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#427b58}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#8f3f71}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#b57614}.chroma .nf,.chroma .nb,.chroma .na{color:#79740e}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#076678}.ui.primary.button,.ui.primary.buttons + .button{background:#79740e;background-color:#79740e !important;color:#fbf1c7}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(121,116,14,0.8);background-color:rgba(121,116,14,0.8) + !important;color:#fbf1c7}.ui.green.buttons .button,.ui.green.button{background:#7c6f64;background-color:#7c6f64;color:#fbf1c7}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(124,111,100,0.8);background-color:rgba(124,111,100,0.8);color:#fbf1c7}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#af3a03}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#af3a03}.repo-title{color:#af3a03}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#79740e}.ui.basic.labels + .label,.ui.basic.label{color:#79740e}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fbf1c7}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fbf1c7}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#af3a03 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #af3a03 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #af3a03 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #af3a03 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#9d0006;background-color:#9d0006;color:#fbf1c7}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(157,0,6,0.8);background-color:rgba(157,0,6,0.8);color:#fbf1c7}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fbf1c7}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fbf1c7}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#af3a03;background-color:#af3a03;color:#fbf1c7}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(175,58,3,0.8);background-color:rgba(175,58,3,0.8);color:#fbf1c7}' + theme-gruvbox-material-auto.css: '@media (prefers-color-scheme:dark){:root{--is-dark-theme:true;--color-primary:#a9b665;--color-primary-contrast:#282828;--color-primary-dark-1:#a9b665;--color-primary-dark-2:#b2b96f;--color-primary-dark-3:#babc79;--color-primary-dark-4:#c3bf83;--color-primary-dark-5:#ccc18d;--color-primary-dark-6:#d4c497;--color-primary-dark-7:#ddc7a1;--color-primary-light-1:#a9b665;--color-primary-light-2:#96a15d;--color-primary-light-3:#848c55;--color-primary-light-4:#72774d;--color-primary-light-5:#5f6145;--color-primary-light-6:#4d4c3d;--color-primary-light-7:#3a3735;--color-primary-alpha-10:rgba(169,182,101,0.1);--color-primary-alpha-20:rgba(169,182,101,0.2);--color-primary-alpha-30:rgba(169,182,101,0.3);--color-primary-alpha-40:rgba(169,182,101,0.4);--color-primary-alpha-50:rgba(169,182,101,0.5);--color-primary-alpha-60:rgba(169,182,101,0.6);--color-primary-alpha-70:rgba(169,182,101,0.7);--color-primary-alpha-80:rgba(169,182,101,0.8);--color-primary-alpha-90:rgba(169,182,101,0.9);--color-secondary:#504945;--color-secondary-dark-1:#504945;--color-secondary-dark-2:#5c534d;--color-secondary-dark-3:#685e54;--color-secondary-dark-4:#73695c;--color-secondary-dark-5:#7f7364;--color-secondary-dark-6:#8b7e6b;--color-secondary-dark-7:#978873;--color-secondary-dark-8:#a2927b;--color-secondary-dark-9:#ae9d82;--color-secondary-dark-10:#baa88a;--color-secondary-dark-11:#c5b292;--color-secondary-dark-12:#d1bd99;--color-secondary-dark-13:#ddc7a1;--color-secondary-light-1:#504945;--color-secondary-light-2:#45403d;--color-secondary-light-3:#3a3735;--color-secondary-light-4:#32302f;--color-secondary-alpha-10:rgba(80,73,69,0.1);--color-secondary-alpha-20:rgba(80,73,69,0.2);--color-secondary-alpha-30:rgba(80,73,69,0.3);--color-secondary-alpha-40:rgba(80,73,69,0.4);--color-secondary-alpha-50:rgba(80,73,69,0.5);--color-secondary-alpha-60:rgba(80,73,69,0.6);--color-secondary-alpha-70:rgba(80,73,69,0.7);--color-secondary-alpha-80:rgba(80,73,69,0.8);--color-secondary-alpha-90:rgba(80,73,69,0.9);--color-red:#ea6962;--color-orange:#e78a4e;--color-yellow:#d8a657;--color-olive:#a9b665;--color-green:#a9b665;--color-teal:#89b482;--color-blue:#7daea3;--color-violet:#d3869b;--color-purple:#d3869b;--color-pink:#d3869b;--color-brown:#e78a4e;--color-grey:#928374;--color-red-light:#ea6962;--color-orange-light:#e78a4e;--color-yellow-light:#d8a657;--color-olive-light:#a9b665;--color-green-light:#a9b665;--color-teal-light:#89b482;--color-blue-light:#7daea3;--color-violet-light:#d3869b;--color-purple-light:#d3869b;--color-pink-light:#d3869b;--color-brown-light:#e78a4e;--color-grey-light:#928374;--color-black:#3a3735;--color-gold:#e78a4e;--color-white:#282828;--color-diff-removed-word-bg:#7b4440;--color-diff-added-word-bg:#5c6646;--color-diff-removed-row-bg:#4c3432;--color-diff-moved-row-bg:#374141;--color-diff-added-row-bg:#3b4439;--color-diff-removed-row-border:#9b4f4a;--color-diff-moved-row-border:#5a7872;--color-diff-added-row-border:#727d4f;--color-diff-inactive:#3a3735;--color-error-border:#9b4f4a;--color-error-bg:#4c3432;--color-error-text:#ea6962;--color-success-border:#727d4f;--color-success-bg:#3b4439;--color-success-text:#a9b665;--color-warning-border:#947443;--color-warning-bg:#4f422e;--color-warning-text:#d8a657;--color-info-border:#5a7872;--color-info-bg:#374141;--color-info-text:#7daea3;--color-body:#282828;--color-box-header:#45403d;--color-box-body:#32302f;--color-box-body-highlight:#3a3735;--color-text-dark:#e78a4e;--color-text:#ddc7a1;--color-text-hover:rgba(221,199,161,0.8);--color-text-light:#d8a657;--color-text-light-1:#928374;--color-text-light-2:#ddc7a1;--color-text-light-3:#928374;--color-footer:#32302f;--color-timeline:#504945;--color-input-text:#ddc7a1;--color-input-background:#282828;--color-input-toggle-background:#32302f;--color-input-border:#504945;--color-input-border-hover:#71665d;--color-navbar:#32302f;--color-navbar-transparent:#32302f;--color-light:#32302f;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#504945;--color-hover:rgba(80,73,69,0.5);--color-active:rgba(80,73,69,0.5);--color-menu:#32302f;--color-card:#32302f;--color-markup-table-row:#45403d;--color-markup-code-block:#282828;--color-button:#45403d;--color-code-bg:#282828;--color-code-sidebar-bg:#32302f;--color-shadow:#00000060;--color-secondary-bg:#32302f;--color-text-focus:#fff;--color-expand-button:#3a3735;--color-placeholder-text:#928374;--color-editor-line-highlight:#32302f;--color-project-board-bg:#282828;--color-project-board-light-label:#a9b665;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#32302f;--color-label-active-bg:#504945;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#32302f;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#ddc7a1}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#ea6962}.chroma + .o,.chroma .ow{color:#e78a4e}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#89b482}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d3869b}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#d8a657}.chroma .nf,.chroma .nb,.chroma .na{color:#a9b665}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#7daea3}.ui.primary.button,.ui.primary.buttons + .button{background:#a9b665;background-color:#a9b665 !important;color:#282828}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(169,182,101,0.8);background-color:rgba(169,182,101,0.8) + !important;color:#282828}.ui.green.buttons .button,.ui.green.button{background:#a89984;background-color:#a89984;color:#282828}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(168,153,132,0.8);background-color:rgba(168,153,132,0.8);color:#282828}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#e78a4e}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#e78a4e}.repo-title{color:#e78a4e}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#a9b665}.ui.basic.labels + .label,.ui.basic.label{color:#a9b665}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#282828}.ui.green.labels .label,.ui.ui.ui.green.label{color:#282828}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#e78a4e + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #e78a4e !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #e78a4e !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #e78a4e !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ea6962;background-color:#ea6962;color:#282828}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(234,105,98,0.8);background-color:rgba(234,105,98,0.8);color:#282828}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#282828}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#282828}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#e78a4e;background-color:#e78a4e;color:#282828}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(231,138,78,0.8);background-color:rgba(231,138,78,0.8);color:#282828}}@media + (prefers-color-scheme:light){:root{--is-dark-theme:true;--color-primary:#6c782e;--color-primary-contrast:#fbf1c7;--color-primary-dark-1:#6c782e;--color-primary-dark-2:#6b702f;--color-primary-dark-3:#6a6830;--color-primary-dark-4:#696032;--color-primary-dark-5:#675733;--color-primary-dark-6:#664f34;--color-primary-dark-7:#654735;--color-primary-light-1:#6c782e;--color-primary-light-2:#828a46;--color-primary-light-3:#999c5d;--color-primary-light-4:#afaf75;--color-primary-light-5:#c5c18d;--color-primary-light-6:#dcd3a4;--color-primary-light-7:#f2e5bc;--color-primary-alpha-10:rgba(108,120,46,0.1);--color-primary-alpha-20:rgba(108,120,46,0.2);--color-primary-alpha-30:rgba(108,120,46,0.3);--color-primary-alpha-40:rgba(108,120,46,0.4);--color-primary-alpha-50:rgba(108,120,46,0.5);--color-primary-alpha-60:rgba(108,120,46,0.6);--color-primary-alpha-70:rgba(108,120,46,0.7);--color-primary-alpha-80:rgba(108,120,46,0.8);--color-primary-alpha-90:rgba(108,120,46,0.9);--color-secondary:#e5d5ad;--color-secondary-dark-1:#e5d5ad;--color-secondary-dark-2:#dac9a3;--color-secondary-dark-3:#d0bd99;--color-secondary-dark-4:#c5b28f;--color-secondary-dark-5:#baa685;--color-secondary-dark-6:#b09a7b;--color-secondary-dark-7:#a58e71;--color-secondary-dark-8:#9a8267;--color-secondary-dark-9:#90765d;--color-secondary-dark-10:#856b53;--color-secondary-dark-11:#7a5f49;--color-secondary-dark-12:#70533f;--color-secondary-dark-13:#654735;--color-secondary-light-1:#e5d5ad;--color-secondary-light-2:#eee0b7;--color-secondary-light-3:#f2e5bc;--color-secondary-light-4:#f4e8be;--color-secondary-alpha-10:rgba(229,213,173,0.1);--color-secondary-alpha-20:rgba(229,213,173,0.2);--color-secondary-alpha-30:rgba(229,213,173,0.3);--color-secondary-alpha-40:rgba(229,213,173,0.4);--color-secondary-alpha-50:rgba(229,213,173,0.5);--color-secondary-alpha-60:rgba(229,213,173,0.6);--color-secondary-alpha-70:rgba(229,213,173,0.7);--color-secondary-alpha-80:rgba(229,213,173,0.8);--color-secondary-alpha-90:rgba(229,213,173,0.9);--color-red:#c14a4a;--color-orange:#c35e0a;--color-yellow:#b47109;--color-olive:#6c782e;--color-green:#6c782e;--color-teal:#4c7a5d;--color-blue:#45707a;--color-violet:#945e80;--color-purple:#945e80;--color-pink:#945e80;--color-brown:#c35e0a;--color-grey:#928374;--color-red-light:#c14a4a;--color-orange-light:#c35e0a;--color-yellow-light:#b47109;--color-olive-light:#6c782e;--color-green-light:#6c782e;--color-teal-light:#4c7a5d;--color-blue-light:#45707a;--color-violet-light:#945e80;--color-purple-light:#945e80;--color-pink-light:#945e80;--color-brown-light:#c35e0a;--color-grey-light:#928374;--color-black:#f2e5bc;--color-gold:#c35e0a;--color-white:#fbf1c7;--color-diff-removed-word-bg:#e3ae95;--color-diff-added-word-bg:#bcc28d;--color-diff-removed-row-bg:#f1d9b5;--color-diff-moved-row-bg:#dadec0;--color-diff-added-row-bg:#dee2b6;--color-diff-removed-row-border:#d99280;--color-diff-moved-row-border:#90a79d;--color-diff-added-row-border:#a5ad72;--color-diff-inactive:#f2e5bc;--color-error-border:#d99280;--color-error-bg:#f1d9b5;--color-error-text:#c14a4a;--color-success-border:#a5ad72;--color-success-bg:#dee2b6;--color-success-text:#6c782e;--color-warning-border:#d7ac5e;--color-warning-bg:#fae7b3;--color-warning-text:#b47109;--color-info-border:#90a79d;--color-info-bg:#dadec0;--color-info-text:#45707a;--color-body:#fbf1c7;--color-box-header:#eee0b7;--color-box-body:#f4e8be;--color-box-body-highlight:#f2e5bc;--color-text-dark:#c35e0a;--color-text:#654735;--color-text-hover:rgba(101,71,53,0.8);--color-text-light:#b47109;--color-text-light-1:#928374;--color-text-light-2:#654735;--color-text-light-3:#928374;--color-footer:#f4e8be;--color-timeline:#e5d5ad;--color-input-text:#654735;--color-input-background:#fbf1c7;--color-input-toggle-background:#f4e8be;--color-input-border:#e5d5ad;--color-input-border-hover:#bcac91;--color-navbar:#f4e8be;--color-navbar-transparent:#f4e8be;--color-light:#f4e8be;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e5d5ad;--color-hover:rgba(229,213,173,0.5);--color-active:rgba(229,213,173,0.5);--color-menu:#f4e8be;--color-card:#f4e8be;--color-markup-table-row:#eee0b7;--color-markup-code-block:#fbf1c7;--color-button:#eee0b7;--color-code-bg:#fbf1c7;--color-code-sidebar-bg:#f4e8be;--color-shadow:#00000060;--color-secondary-bg:#f4e8be;--color-text-focus:#fff;--color-expand-button:#f2e5bc;--color-placeholder-text:#928374;--color-editor-line-highlight:#f4e8be;--color-project-board-bg:#fbf1c7;--color-project-board-light-label:#6c782e;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4e8be;--color-label-active-bg:#e5d5ad;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4e8be;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#654735}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#c14a4a}.chroma + .o,.chroma .ow{color:#c35e0a}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#4c7a5d}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#945e80}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#b47109}.chroma .nf,.chroma .nb,.chroma .na{color:#6c782e}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#45707a}.ui.primary.button,.ui.primary.buttons + .button{background:#6f8352;background-color:#6f8352 !important;color:#fbf1c7}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(111,131,82,0.8);background-color:rgba(111,131,82,0.8) + !important;color:#fbf1c7}.ui.green.buttons .button,.ui.green.button{background:#7c6f64;background-color:#7c6f64;color:#fbf1c7}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(124,111,100,0.8);background-color:rgba(124,111,100,0.8);color:#fbf1c7}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#c35e0a}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#c35e0a}.repo-title{color:#c35e0a}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#6c782e}.ui.basic.labels + .label,.ui.basic.label{color:#6c782e}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fbf1c7}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fbf1c7}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#c35e0a + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #c35e0a !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #c35e0a !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #c35e0a !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ae5858;background-color:#ae5858;color:#fbf1c7}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(174,88,88,0.8);background-color:rgba(174,88,88,0.8);color:#fbf1c7}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fbf1c7}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fbf1c7}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#c35e0a;background-color:#c35e0a;color:#fbf1c7}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(195,94,10,0.8);background-color:rgba(195,94,10,0.8);color:#fbf1c7}}' + theme-gruvbox-material-dark.css: ':root{--is-dark-theme:true;--color-primary:#a9b665;--color-primary-contrast:#282828;--color-primary-dark-1:#a9b665;--color-primary-dark-2:#b2b96f;--color-primary-dark-3:#babc79;--color-primary-dark-4:#c3bf83;--color-primary-dark-5:#ccc18d;--color-primary-dark-6:#d4c497;--color-primary-dark-7:#ddc7a1;--color-primary-light-1:#a9b665;--color-primary-light-2:#96a15d;--color-primary-light-3:#848c55;--color-primary-light-4:#72774d;--color-primary-light-5:#5f6145;--color-primary-light-6:#4d4c3d;--color-primary-light-7:#3a3735;--color-primary-alpha-10:rgba(169,182,101,0.1);--color-primary-alpha-20:rgba(169,182,101,0.2);--color-primary-alpha-30:rgba(169,182,101,0.3);--color-primary-alpha-40:rgba(169,182,101,0.4);--color-primary-alpha-50:rgba(169,182,101,0.5);--color-primary-alpha-60:rgba(169,182,101,0.6);--color-primary-alpha-70:rgba(169,182,101,0.7);--color-primary-alpha-80:rgba(169,182,101,0.8);--color-primary-alpha-90:rgba(169,182,101,0.9);--color-secondary:#504945;--color-secondary-dark-1:#504945;--color-secondary-dark-2:#5c534d;--color-secondary-dark-3:#685e54;--color-secondary-dark-4:#73695c;--color-secondary-dark-5:#7f7364;--color-secondary-dark-6:#8b7e6b;--color-secondary-dark-7:#978873;--color-secondary-dark-8:#a2927b;--color-secondary-dark-9:#ae9d82;--color-secondary-dark-10:#baa88a;--color-secondary-dark-11:#c5b292;--color-secondary-dark-12:#d1bd99;--color-secondary-dark-13:#ddc7a1;--color-secondary-light-1:#504945;--color-secondary-light-2:#45403d;--color-secondary-light-3:#3a3735;--color-secondary-light-4:#32302f;--color-secondary-alpha-10:rgba(80,73,69,0.1);--color-secondary-alpha-20:rgba(80,73,69,0.2);--color-secondary-alpha-30:rgba(80,73,69,0.3);--color-secondary-alpha-40:rgba(80,73,69,0.4);--color-secondary-alpha-50:rgba(80,73,69,0.5);--color-secondary-alpha-60:rgba(80,73,69,0.6);--color-secondary-alpha-70:rgba(80,73,69,0.7);--color-secondary-alpha-80:rgba(80,73,69,0.8);--color-secondary-alpha-90:rgba(80,73,69,0.9);--color-red:#ea6962;--color-orange:#e78a4e;--color-yellow:#d8a657;--color-olive:#a9b665;--color-green:#a9b665;--color-teal:#89b482;--color-blue:#7daea3;--color-violet:#d3869b;--color-purple:#d3869b;--color-pink:#d3869b;--color-brown:#e78a4e;--color-grey:#928374;--color-red-light:#ea6962;--color-orange-light:#e78a4e;--color-yellow-light:#d8a657;--color-olive-light:#a9b665;--color-green-light:#a9b665;--color-teal-light:#89b482;--color-blue-light:#7daea3;--color-violet-light:#d3869b;--color-purple-light:#d3869b;--color-pink-light:#d3869b;--color-brown-light:#e78a4e;--color-grey-light:#928374;--color-black:#3a3735;--color-gold:#e78a4e;--color-white:#282828;--color-diff-removed-word-bg:#7b4440;--color-diff-added-word-bg:#5c6646;--color-diff-removed-row-bg:#4c3432;--color-diff-moved-row-bg:#374141;--color-diff-added-row-bg:#3b4439;--color-diff-removed-row-border:#9b4f4a;--color-diff-moved-row-border:#5a7872;--color-diff-added-row-border:#727d4f;--color-diff-inactive:#3a3735;--color-error-border:#9b4f4a;--color-error-bg:#4c3432;--color-error-text:#ea6962;--color-success-border:#727d4f;--color-success-bg:#3b4439;--color-success-text:#a9b665;--color-warning-border:#947443;--color-warning-bg:#4f422e;--color-warning-text:#d8a657;--color-info-border:#5a7872;--color-info-bg:#374141;--color-info-text:#7daea3;--color-body:#282828;--color-box-header:#45403d;--color-box-body:#32302f;--color-box-body-highlight:#3a3735;--color-text-dark:#e78a4e;--color-text:#ddc7a1;--color-text-hover:rgba(221,199,161,0.8);--color-text-light:#d8a657;--color-text-light-1:#928374;--color-text-light-2:#ddc7a1;--color-text-light-3:#928374;--color-footer:#32302f;--color-timeline:#504945;--color-input-text:#ddc7a1;--color-input-background:#282828;--color-input-toggle-background:#32302f;--color-input-border:#504945;--color-input-border-hover:#71665d;--color-navbar:#32302f;--color-navbar-transparent:#32302f;--color-light:#32302f;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#504945;--color-hover:rgba(80,73,69,0.5);--color-active:rgba(80,73,69,0.5);--color-menu:#32302f;--color-card:#32302f;--color-markup-table-row:#45403d;--color-markup-code-block:#282828;--color-button:#45403d;--color-code-bg:#282828;--color-code-sidebar-bg:#32302f;--color-shadow:#00000060;--color-secondary-bg:#32302f;--color-text-focus:#fff;--color-expand-button:#3a3735;--color-placeholder-text:#928374;--color-editor-line-highlight:#32302f;--color-project-board-bg:#282828;--color-project-board-light-label:#a9b665;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#32302f;--color-label-active-bg:#504945;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#32302f;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#ddc7a1}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#ea6962}.chroma + .o,.chroma .ow{color:#e78a4e}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#89b482}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#d3869b}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#d8a657}.chroma .nf,.chroma .nb,.chroma .na{color:#a9b665}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#7daea3}.ui.primary.button,.ui.primary.buttons + .button{background:#a9b665;background-color:#a9b665 !important;color:#282828}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(169,182,101,0.8);background-color:rgba(169,182,101,0.8) + !important;color:#282828}.ui.green.buttons .button,.ui.green.button{background:#a89984;background-color:#a89984;color:#282828}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(168,153,132,0.8);background-color:rgba(168,153,132,0.8);color:#282828}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#e78a4e}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#e78a4e}.repo-title{color:#e78a4e}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#a9b665}.ui.basic.labels + .label,.ui.basic.label{color:#a9b665}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#282828}.ui.green.labels .label,.ui.ui.ui.green.label{color:#282828}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#e78a4e + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #e78a4e !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #e78a4e !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #e78a4e !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ea6962;background-color:#ea6962;color:#282828}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(234,105,98,0.8);background-color:rgba(234,105,98,0.8);color:#282828}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#282828}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#282828}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#e78a4e;background-color:#e78a4e;color:#282828}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(231,138,78,0.8);background-color:rgba(231,138,78,0.8);color:#282828}' + theme-gruvbox-material-light.css: ':root{--is-dark-theme:true;--color-primary:#6c782e;--color-primary-contrast:#fbf1c7;--color-primary-dark-1:#6c782e;--color-primary-dark-2:#6b702f;--color-primary-dark-3:#6a6830;--color-primary-dark-4:#696032;--color-primary-dark-5:#675733;--color-primary-dark-6:#664f34;--color-primary-dark-7:#654735;--color-primary-light-1:#6c782e;--color-primary-light-2:#828a46;--color-primary-light-3:#999c5d;--color-primary-light-4:#afaf75;--color-primary-light-5:#c5c18d;--color-primary-light-6:#dcd3a4;--color-primary-light-7:#f2e5bc;--color-primary-alpha-10:rgba(108,120,46,0.1);--color-primary-alpha-20:rgba(108,120,46,0.2);--color-primary-alpha-30:rgba(108,120,46,0.3);--color-primary-alpha-40:rgba(108,120,46,0.4);--color-primary-alpha-50:rgba(108,120,46,0.5);--color-primary-alpha-60:rgba(108,120,46,0.6);--color-primary-alpha-70:rgba(108,120,46,0.7);--color-primary-alpha-80:rgba(108,120,46,0.8);--color-primary-alpha-90:rgba(108,120,46,0.9);--color-secondary:#e5d5ad;--color-secondary-dark-1:#e5d5ad;--color-secondary-dark-2:#dac9a3;--color-secondary-dark-3:#d0bd99;--color-secondary-dark-4:#c5b28f;--color-secondary-dark-5:#baa685;--color-secondary-dark-6:#b09a7b;--color-secondary-dark-7:#a58e71;--color-secondary-dark-8:#9a8267;--color-secondary-dark-9:#90765d;--color-secondary-dark-10:#856b53;--color-secondary-dark-11:#7a5f49;--color-secondary-dark-12:#70533f;--color-secondary-dark-13:#654735;--color-secondary-light-1:#e5d5ad;--color-secondary-light-2:#eee0b7;--color-secondary-light-3:#f2e5bc;--color-secondary-light-4:#f4e8be;--color-secondary-alpha-10:rgba(229,213,173,0.1);--color-secondary-alpha-20:rgba(229,213,173,0.2);--color-secondary-alpha-30:rgba(229,213,173,0.3);--color-secondary-alpha-40:rgba(229,213,173,0.4);--color-secondary-alpha-50:rgba(229,213,173,0.5);--color-secondary-alpha-60:rgba(229,213,173,0.6);--color-secondary-alpha-70:rgba(229,213,173,0.7);--color-secondary-alpha-80:rgba(229,213,173,0.8);--color-secondary-alpha-90:rgba(229,213,173,0.9);--color-red:#c14a4a;--color-orange:#c35e0a;--color-yellow:#b47109;--color-olive:#6c782e;--color-green:#6c782e;--color-teal:#4c7a5d;--color-blue:#45707a;--color-violet:#945e80;--color-purple:#945e80;--color-pink:#945e80;--color-brown:#c35e0a;--color-grey:#928374;--color-red-light:#c14a4a;--color-orange-light:#c35e0a;--color-yellow-light:#b47109;--color-olive-light:#6c782e;--color-green-light:#6c782e;--color-teal-light:#4c7a5d;--color-blue-light:#45707a;--color-violet-light:#945e80;--color-purple-light:#945e80;--color-pink-light:#945e80;--color-brown-light:#c35e0a;--color-grey-light:#928374;--color-black:#f2e5bc;--color-gold:#c35e0a;--color-white:#fbf1c7;--color-diff-removed-word-bg:#e3ae95;--color-diff-added-word-bg:#bcc28d;--color-diff-removed-row-bg:#f1d9b5;--color-diff-moved-row-bg:#dadec0;--color-diff-added-row-bg:#dee2b6;--color-diff-removed-row-border:#d99280;--color-diff-moved-row-border:#90a79d;--color-diff-added-row-border:#a5ad72;--color-diff-inactive:#f2e5bc;--color-error-border:#d99280;--color-error-bg:#f1d9b5;--color-error-text:#c14a4a;--color-success-border:#a5ad72;--color-success-bg:#dee2b6;--color-success-text:#6c782e;--color-warning-border:#d7ac5e;--color-warning-bg:#fae7b3;--color-warning-text:#b47109;--color-info-border:#90a79d;--color-info-bg:#dadec0;--color-info-text:#45707a;--color-body:#fbf1c7;--color-box-header:#eee0b7;--color-box-body:#f4e8be;--color-box-body-highlight:#f2e5bc;--color-text-dark:#c35e0a;--color-text:#654735;--color-text-hover:rgba(101,71,53,0.8);--color-text-light:#b47109;--color-text-light-1:#928374;--color-text-light-2:#654735;--color-text-light-3:#928374;--color-footer:#f4e8be;--color-timeline:#e5d5ad;--color-input-text:#654735;--color-input-background:#fbf1c7;--color-input-toggle-background:#f4e8be;--color-input-border:#e5d5ad;--color-input-border-hover:#bcac91;--color-navbar:#f4e8be;--color-navbar-transparent:#f4e8be;--color-light:#f4e8be;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#e5d5ad;--color-hover:rgba(229,213,173,0.5);--color-active:rgba(229,213,173,0.5);--color-menu:#f4e8be;--color-card:#f4e8be;--color-markup-table-row:#eee0b7;--color-markup-code-block:#fbf1c7;--color-button:#eee0b7;--color-code-bg:#fbf1c7;--color-code-sidebar-bg:#f4e8be;--color-shadow:#00000060;--color-secondary-bg:#f4e8be;--color-text-focus:#fff;--color-expand-button:#f2e5bc;--color-placeholder-text:#928374;--color-editor-line-highlight:#f4e8be;--color-project-board-bg:#fbf1c7;--color-project-board-light-label:#6c782e;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4e8be;--color-label-active-bg:#e5d5ad;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4e8be;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#654735}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#928374}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#c14a4a}.chroma + .o,.chroma .ow{color:#c35e0a}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#4c7a5d}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#945e80}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#b47109}.chroma .nf,.chroma .nb,.chroma .na{color:#6c782e}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#45707a}.ui.primary.button,.ui.primary.buttons + .button{background:#6f8352;background-color:#6f8352 !important;color:#fbf1c7}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(111,131,82,0.8);background-color:rgba(111,131,82,0.8) + !important;color:#fbf1c7}.ui.green.buttons .button,.ui.green.button{background:#7c6f64;background-color:#7c6f64;color:#fbf1c7}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(124,111,100,0.8);background-color:rgba(124,111,100,0.8);color:#fbf1c7}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#c35e0a}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#c35e0a}.repo-title{color:#c35e0a}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#6c782e}.ui.basic.labels + .label,.ui.basic.label{color:#6c782e}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#fbf1c7}.ui.green.labels .label,.ui.ui.ui.green.label{color:#fbf1c7}i.grey.icon.icon.icon.icon{color:#928374}*{scrollbar-color:#c35e0a + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #c35e0a !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #c35e0a !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #c35e0a !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ae5858;background-color:#ae5858;color:#fbf1c7}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(174,88,88,0.8);background-color:rgba(174,88,88,0.8);color:#fbf1c7}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#fbf1c7}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#fbf1c7}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#c35e0a;background-color:#c35e0a;color:#fbf1c7}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(195,94,10,0.8);background-color:rgba(195,94,10,0.8);color:#fbf1c7}' + theme-palenight.css: ':root{--is-dark-theme:true;--color-primary:#89DDFF;--color-primary-contrast:#292D3E;--color-primary-dark-1:#89DDFF;--color-primary-dark-2:#92d9f8;--color-primary-dark-3:#9bd6f1;--color-primary-dark-4:#a4d2ea;--color-primary-dark-5:#adcee3;--color-primary-dark-6:#b6cbdc;--color-primary-dark-7:#BFC7D5;--color-primary-light-1:#89DDFF;--color-primary-light-2:#7ac1e0;--color-primary-light-3:#6ba5c2;--color-primary-light-4:#5c88a3;--color-primary-light-5:#4d6c84;--color-primary-light-6:#3e5066;--color-primary-light-7:#2f3447;--color-primary-alpha-10:rgba(137,221,255,0.1);--color-primary-alpha-20:rgba(137,221,255,0.2);--color-primary-alpha-30:rgba(137,221,255,0.3);--color-primary-alpha-40:rgba(137,221,255,0.4);--color-primary-alpha-50:rgba(137,221,255,0.5);--color-primary-alpha-60:rgba(137,221,255,0.6);--color-primary-alpha-70:rgba(137,221,255,0.7);--color-primary-alpha-80:rgba(137,221,255,0.8);--color-primary-alpha-90:rgba(137,221,255,0.9);--color-secondary:#353a50;--color-secondary-dark-1:#353a50;--color-secondary-dark-2:#40465b;--color-secondary-dark-3:#4c5266;--color-secondary-dark-4:#585d71;--color-secondary-dark-5:#63697c;--color-secondary-dark-6:#6f7587;--color-secondary-dark-7:#7a8193;--color-secondary-dark-8:#858c9e;--color-secondary-dark-9:#9198a9;--color-secondary-dark-10:#9da4b4;--color-secondary-dark-11:#a8afbf;--color-secondary-dark-12:#b4bbca;--color-secondary-dark-13:#BFC7D5;--color-secondary-light-1:#353a50;--color-secondary-light-2:#32374c;--color-secondary-light-3:#2f3447;--color-secondary-light-4:#2c3043;--color-secondary-alpha-10:rgba(53,58,80,0.1);--color-secondary-alpha-20:rgba(53,58,80,0.2);--color-secondary-alpha-30:rgba(53,58,80,0.3);--color-secondary-alpha-40:rgba(53,58,80,0.4);--color-secondary-alpha-50:rgba(53,58,80,0.5);--color-secondary-alpha-60:rgba(53,58,80,0.6);--color-secondary-alpha-70:rgba(53,58,80,0.7);--color-secondary-alpha-80:rgba(53,58,80,0.8);--color-secondary-alpha-90:rgba(53,58,80,0.9);--color-red:#FF5572;--color-orange:#FFCB6B;--color-yellow:#F78C6C;--color-olive:#C3E88D;--color-green:#C3E88D;--color-teal:#89DDFF;--color-blue:#82AAFF;--color-violet:#C792EA;--color-purple:#C792EA;--color-pink:#C792EA;--color-brown:#FFCB6B;--color-grey:#697098;--color-red-light:#FF5572;--color-orange-light:#FFCB6B;--color-yellow-light:#F78C6C;--color-olive-light:#C3E88D;--color-green-light:#C3E88D;--color-teal-light:#89DDFF;--color-blue-light:#82AAFF;--color-violet-light:#C792EA;--color-purple-light:#C792EA;--color-pink-light:#C792EA;--color-brown-light:#FFCB6B;--color-grey-light:#697098;--color-black:#2f3447;--color-gold:#FFCB6B;--color-white:#292D3E;--color-diff-removed-word-bg:#88414d;--color-diff-added-word-bg:#62774f;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#aa4758;--color-diff-moved-row-border:#5c76ab;--color-diff-added-row-border:#7e9761;--color-diff-inactive:#2f3447;--color-error-border:#aa4758;--color-error-bg:#55393d;--color-error-text:#FF5572;--color-success-border:#7e9761;--color-success-bg:#394634;--color-success-text:#C3E88D;--color-warning-border:#a3684e;--color-warning-bg:#4e432f;--color-warning-text:#F78C6C;--color-info-border:#5c76ab;--color-info-bg:#354157;--color-info-text:#82AAFF;--color-body:#292D3E;--color-box-header:#32374c;--color-box-body:#2c3043;--color-box-body-highlight:#2f3447;--color-text-dark:#C792EA;--color-text:#BFC7D5;--color-text-hover:rgba(191,199,213,0.8);--color-text-light:#82AAFF;--color-text-light-1:#697098;--color-text-light-2:#BFC7D5;--color-text-light-3:#697098;--color-footer:#2c3043;--color-timeline:#353a50;--color-input-text:#BFC7D5;--color-input-background:#292D3E;--color-input-toggle-background:#2c3043;--color-input-border:#353a50;--color-input-border-hover:#4f5574;--color-navbar:#2c3043;--color-navbar-transparent:#2c3043;--color-light:#2c3043;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#353a50;--color-hover:rgba(53,58,80,0.5);--color-active:rgba(53,58,80,0.5);--color-menu:#2c3043;--color-card:#2c3043;--color-markup-table-row:#32374c;--color-markup-code-block:#292D3E;--color-button:#32374c;--color-code-bg:#292D3E;--color-code-sidebar-bg:#2c3043;--color-shadow:#00000060;--color-secondary-bg:#2c3043;--color-text-focus:#fff;--color-expand-button:#2f3447;--color-placeholder-text:#697098;--color-editor-line-highlight:#2c3043;--color-project-board-bg:#292D3E;--color-project-board-light-label:#89DDFF;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#2c3043;--color-label-active-bg:#353a50;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#2c3043;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#BFC7D5}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#697098}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#C792EA}.chroma + .o,.chroma .ow{color:#82AAFF}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#C3E88D}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#FF5572}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#F78C6C}.chroma .nf,.chroma .nb,.chroma .na{color:#89DDFF}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#FFCB6B}.ui.primary.button,.ui.primary.buttons + .button{background:#82AAFF;background-color:#82AAFF !important;color:#292D3E}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(130,170,255,0.8);background-color:rgba(130,170,255,0.8) + !important;color:#292D3E}.ui.green.buttons .button,.ui.green.button{background:#C3E88D;background-color:#C3E88D;color:#292D3E}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(195,232,141,0.8);background-color:rgba(195,232,141,0.8);color:#292D3E}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#C792EA}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#C792EA}.repo-title{color:#C792EA}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#89DDFF}.ui.basic.labels + .label,.ui.basic.label{color:#89DDFF}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#292D3E}.ui.green.labels .label,.ui.ui.ui.green.label{color:#292D3E}i.grey.icon.icon.icon.icon{color:#697098}*{scrollbar-color:#C792EA + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #C792EA !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #C792EA !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #C792EA !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#FF5572;background-color:#FF5572;color:#292D3E}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,85,114,0.8);background-color:rgba(255,85,114,0.8);color:#292D3E}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#292D3E}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#292D3E}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#FFCB6B;background-color:#FFCB6B;color:#292D3E}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(255,203,107,0.8);background-color:rgba(255,203,107,0.8);color:#292D3E}' + theme-soft-era.css: ':root{--is-dark-theme:true;--color-primary:#aea6e1;--color-primary-contrast:#f9f5f5;--color-primary-dark-1:#aea6e1;--color-primary-dark-2:#b1a4d5;--color-primary-dark-3:#b3a1c9;--color-primary-dark-4:#b69fbd;--color-primary-dark-5:#b99db0;--color-primary-dark-6:#bb9aa4;--color-primary-dark-7:#be9898;--color-primary-light-1:#aea6e1;--color-primary-light-2:#b9b2e3;--color-primary-light-3:#c5bee5;--color-primary-light-4:#d0cae7;--color-primary-light-5:#dbd5e8;--color-primary-light-6:#e7e1ea;--color-primary-light-7:#f2edec;--color-primary-alpha-10:rgba(174,166,225,0.1);--color-primary-alpha-20:rgba(174,166,225,0.2);--color-primary-alpha-30:rgba(174,166,225,0.3);--color-primary-alpha-40:rgba(174,166,225,0.4);--color-primary-alpha-50:rgba(174,166,225,0.5);--color-primary-alpha-60:rgba(174,166,225,0.6);--color-primary-alpha-70:rgba(174,166,225,0.7);--color-primary-alpha-80:rgba(174,166,225,0.8);--color-primary-alpha-90:rgba(174,166,225,0.9);--color-secondary:#ebe6e4;--color-secondary-dark-1:#ebe6e4;--color-secondary-dark-2:#e7e0de;--color-secondary-dark-3:#e3d9d7;--color-secondary-dark-4:#e0d3d1;--color-secondary-dark-5:#dccccb;--color-secondary-dark-6:#d8c5c4;--color-secondary-dark-7:#d5bfbe;--color-secondary-dark-8:#d1b9b8;--color-secondary-dark-9:#cdb2b1;--color-secondary-dark-10:#c9acab;--color-secondary-dark-11:#c6a5a5;--color-secondary-dark-12:#c29e9e;--color-secondary-dark-13:#be9898;--color-secondary-light-1:#ebe6e4;--color-secondary-light-2:#efeae9;--color-secondary-light-3:#f2edec;--color-secondary-light-4:#f4f0f0;--color-secondary-alpha-10:rgba(235,230,228,0.1);--color-secondary-alpha-20:rgba(235,230,228,0.2);--color-secondary-alpha-30:rgba(235,230,228,0.3);--color-secondary-alpha-40:rgba(235,230,228,0.4);--color-secondary-alpha-50:rgba(235,230,228,0.5);--color-secondary-alpha-60:rgba(235,230,228,0.6);--color-secondary-alpha-70:rgba(235,230,228,0.7);--color-secondary-alpha-80:rgba(235,230,228,0.8);--color-secondary-alpha-90:rgba(235,230,228,0.9);--color-red:#f85552;--color-orange:#ec9157;--color-yellow:#dfa000;--color-olive:#96ad01;--color-green:#96ad01;--color-teal:#25b7b8;--color-blue:#75a9d9;--color-violet:#aea6e1;--color-purple:#aea6e1;--color-pink:#aea6e1;--color-brown:#ec9157;--color-grey:#dfc5c5;--color-red-light:#f85552;--color-orange-light:#ec9157;--color-yellow-light:#dfa000;--color-olive-light:#96ad01;--color-green-light:#96ad01;--color-teal-light:#25b7b8;--color-blue-light:#75a9d9;--color-violet-light:#aea6e1;--color-purple-light:#aea6e1;--color-pink-light:#aea6e1;--color-brown-light:#ec9157;--color-grey-light:#dfc5c5;--color-black:#f2edec;--color-gold:#ec9157;--color-white:#f9f5f5;--color-diff-removed-word-bg:#f9b7af;--color-diff-added-word-bg:#d1de91;--color-diff-removed-row-bg:#fae1d7;--color-diff-moved-row-bg:#e0f0f3;--color-diff-added-row-bg:#eaf3ce;--color-diff-removed-row-border:#f99b95;--color-diff-moved-row-border:#abcde6;--color-diff-added-row-border:#c0d068;--color-diff-inactive:#f2edec;--color-error-border:#f99b95;--color-error-bg:#fae1d7;--color-error-text:#f85552;--color-success-border:#c0d068;--color-success-bg:#eaf3ce;--color-success-text:#96ad01;--color-warning-border:#ebc568;--color-warning-bg:#f6ead0;--color-warning-text:#dfa000;--color-info-border:#abcde6;--color-info-bg:#e0f0f3;--color-info-text:#75a9d9;--color-body:#f9f5f5;--color-box-header:#efeae9;--color-box-body:#f4f0f0;--color-box-body-highlight:#f2edec;--color-text-dark:#75a9d9;--color-text:#be9898;--color-text-hover:rgba(190,152,152,0.8);--color-text-light:#96ad01;--color-text-light-1:#dfc5c5;--color-text-light-2:#be9898;--color-text-light-3:#dfc5c5;--color-footer:#f4f0f0;--color-timeline:#ebe6e4;--color-input-text:#be9898;--color-input-background:#f9f5f5;--color-input-toggle-background:#f4f0f0;--color-input-border:#ebe6e4;--color-input-border-hover:#e5d6d5;--color-navbar:#f4f0f0;--color-navbar-transparent:#f4f0f0;--color-light:#f4f0f0;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#ebe6e4;--color-hover:rgba(235,230,228,0.5);--color-active:rgba(235,230,228,0.5);--color-menu:#f4f0f0;--color-card:#f4f0f0;--color-markup-table-row:#efeae9;--color-markup-code-block:#f9f5f5;--color-button:#efeae9;--color-code-bg:#f9f5f5;--color-code-sidebar-bg:#f4f0f0;--color-shadow:#00000060;--color-secondary-bg:#f4f0f0;--color-text-focus:#fff;--color-expand-button:#f2edec;--color-placeholder-text:#dfc5c5;--color-editor-line-highlight:#f4f0f0;--color-project-board-bg:#f9f5f5;--color-project-board-light-label:#aea6e1;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#f4f0f0;--color-label-active-bg:#ebe6e4;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#f4f0f0;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#be9898}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#dfc5c5}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f165bd}.chroma + .o,.chroma .ow{color:#aea6e1}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#96ad01}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#96ad01}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#ec9157}.chroma .nf,.chroma .nb,.chroma .na{color:#75a9d9}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#25b7b8}.ui.primary.button,.ui.primary.buttons + .button{background:#aea6e1;background-color:#aea6e1 !important;color:#f9f5f5}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(174,166,225,0.8);background-color:rgba(174,166,225,0.8) + !important;color:#f9f5f5}.ui.green.buttons .button,.ui.green.button{background:#75a9d9;background-color:#75a9d9;color:#f9f5f5}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(117,169,217,0.8);background-color:rgba(117,169,217,0.8);color:#f9f5f5}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#75a9d9}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#75a9d9}.repo-title{color:#75a9d9}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#aea6e1}.ui.basic.labels + .label,.ui.basic.label{color:#aea6e1}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#f9f5f5}.ui.green.labels .label,.ui.ui.ui.green.label{color:#f9f5f5}i.grey.icon.icon.icon.icon{color:#dfc5c5}*{scrollbar-color:#75a9d9 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #75a9d9 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #75a9d9 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #75a9d9 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#f85552;background-color:#f85552;color:#f9f5f5}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(248,85,82,0.8);background-color:rgba(248,85,82,0.8);color:#f9f5f5}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#f9f5f5}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#f9f5f5}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#ec9157;background-color:#ec9157;color:#f9f5f5}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(236,145,87,0.8);background-color:rgba(236,145,87,0.8);color:#f9f5f5}' + theme-sonokai-andromeda.css: ':root{--is-dark-theme:true;--color-primary:#6dcae8;--color-primary-contrast:#2b2d3a;--color-primary-dark-1:#6dcae8;--color-primary-dark-2:#80cee7;--color-primary-dark-3:#94d2e7;--color-primary-dark-4:#a7d7e6;--color-primary-dark-5:#badbe5;--color-primary-dark-6:#cedfe5;--color-primary-dark-7:#e1e3e4;--color-primary-light-1:#6dcae8;--color-primary-light-2:#64b2ce;--color-primary-light-3:#5b9ab5;--color-primary-light-4:#52829b;--color-primary-light-5:#486a81;--color-primary-light-6:#3f5268;--color-primary-light-7:#363a4e;--color-primary-alpha-10:rgba(109,202,232,0.1);--color-primary-alpha-20:rgba(109,202,232,0.2);--color-primary-alpha-30:rgba(109,202,232,0.3);--color-primary-alpha-40:rgba(109,202,232,0.4);--color-primary-alpha-50:rgba(109,202,232,0.5);--color-primary-alpha-60:rgba(109,202,232,0.6);--color-primary-alpha-70:rgba(109,202,232,0.7);--color-primary-alpha-80:rgba(109,202,232,0.8);--color-primary-alpha-90:rgba(109,202,232,0.9);--color-secondary:#3f445b;--color-secondary-dark-1:#3f445b;--color-secondary-dark-2:#4c5166;--color-secondary-dark-3:#5a5f72;--color-secondary-dark-4:#686c7d;--color-secondary-dark-5:#757989;--color-secondary-dark-6:#838694;--color-secondary-dark-7:#9094a0;--color-secondary-dark-8:#9da1ab;--color-secondary-dark-9:#abaeb6;--color-secondary-dark-10:#b9bbc2;--color-secondary-dark-11:#c6c8cd;--color-secondary-dark-12:#d4d6d9;--color-secondary-dark-13:#e1e3e4;--color-secondary-light-1:#3f445b;--color-secondary-light-2:#393e53;--color-secondary-light-3:#363a4e;--color-secondary-light-4:#333648;--color-secondary-alpha-10:rgba(63,68,91,0.1);--color-secondary-alpha-20:rgba(63,68,91,0.2);--color-secondary-alpha-30:rgba(63,68,91,0.3);--color-secondary-alpha-40:rgba(63,68,91,0.4);--color-secondary-alpha-50:rgba(63,68,91,0.5);--color-secondary-alpha-60:rgba(63,68,91,0.6);--color-secondary-alpha-70:rgba(63,68,91,0.7);--color-secondary-alpha-80:rgba(63,68,91,0.8);--color-secondary-alpha-90:rgba(63,68,91,0.9);--color-red:#fb617e;--color-orange:#f89860;--color-yellow:#edc763;--color-olive:#9ed06c;--color-green:#9ed06c;--color-teal:#6dcae8;--color-blue:#6dcae8;--color-violet:#bb97ee;--color-purple:#bb97ee;--color-pink:#bb97ee;--color-brown:#f89860;--color-grey:#7e8294;--color-red-light:#fb617e;--color-orange-light:#f89860;--color-yellow-light:#edc763;--color-olive-light:#9ed06c;--color-green-light:#9ed06c;--color-teal-light:#6dcae8;--color-blue-light:#6dcae8;--color-violet-light:#bb97ee;--color-purple-light:#bb97ee;--color-pink-light:#bb97ee;--color-brown-light:#f89860;--color-grey-light:#7e8294;--color-black:#363a4e;--color-gold:#f89860;--color-white:#2b2d3a;--color-diff-removed-word-bg:#874551;--color-diff-added-word-bg:#576f45;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a84d5e;--color-diff-moved-row-border:#5186a0;--color-diff-added-row-border:#6c8b50;--color-diff-inactive:#363a4e;--color-error-border:#a84d5e;--color-error-bg:#55393d;--color-error-text:#fb617e;--color-success-border:#6c8b50;--color-success-bg:#394634;--color-success-text:#9ed06c;--color-warning-border:#9e8549;--color-warning-bg:#4e432f;--color-warning-text:#edc763;--color-info-border:#5186a0;--color-info-bg:#354157;--color-info-text:#6dcae8;--color-body:#2b2d3a;--color-box-header:#393e53;--color-box-body:#333648;--color-box-body-highlight:#363a4e;--color-text-dark:#fb617e;--color-text:#e1e3e4;--color-text-hover:rgba(225,227,228,0.8);--color-text-light:#9ed06c;--color-text-light-1:#7e8294;--color-text-light-2:#e1e3e4;--color-text-light-3:#7e8294;--color-footer:#333648;--color-timeline:#3f445b;--color-input-text:#e1e3e4;--color-input-background:#2b2d3a;--color-input-toggle-background:#333648;--color-input-border:#3f445b;--color-input-border-hover:#5f6378;--color-navbar:#333648;--color-navbar-transparent:#333648;--color-light:#333648;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#3f445b;--color-hover:rgba(63,68,91,0.5);--color-active:rgba(63,68,91,0.5);--color-menu:#333648;--color-card:#333648;--color-markup-table-row:#393e53;--color-markup-code-block:#2b2d3a;--color-button:#393e53;--color-code-bg:#2b2d3a;--color-code-sidebar-bg:#333648;--color-shadow:#00000060;--color-secondary-bg:#333648;--color-text-focus:#fff;--color-expand-button:#363a4e;--color-placeholder-text:#7e8294;--color-editor-line-highlight:#333648;--color-project-board-bg:#2b2d3a;--color-project-board-light-label:#6dcae8;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#333648;--color-label-active-bg:#3f445b;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#333648;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e1e3e4}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#7e8294}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#fb617e}.chroma + .o,.chroma .ow{color:#f89860}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#edc763}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#bb97ee}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#6dcae8}.chroma .nf,.chroma .nb,.chroma .na{color:#9ed06c}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#f89860}.ui.primary.button,.ui.primary.buttons + .button{background:#77d5f0;background-color:#77d5f0 !important;color:#2b2d3a}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(119,213,240,0.8);background-color:rgba(119,213,240,0.8) + !important;color:#2b2d3a}.ui.green.buttons .button,.ui.green.button{background:#a9dc76;background-color:#a9dc76;color:#2b2d3a}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(169,220,118,0.8);background-color:rgba(169,220,118,0.8);color:#2b2d3a}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#fb617e}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#fb617e}.repo-title{color:#fb617e}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#6dcae8}.ui.basic.labels + .label,.ui.basic.label{color:#6dcae8}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2b2d3a}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2b2d3a}i.grey.icon.icon.icon.icon{color:#7e8294}*{scrollbar-color:#fb617e + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #fb617e !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #fb617e !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #fb617e !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ff6188;background-color:#ff6188;color:#2b2d3a}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,97,136,0.8);background-color:rgba(255,97,136,0.8);color:#2b2d3a}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2b2d3a}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2b2d3a}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f89860;background-color:#f89860;color:#2b2d3a}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(248,152,96,0.8);background-color:rgba(248,152,96,0.8);color:#2b2d3a}' + theme-sonokai-atlantis.css: ':root{--is-dark-theme:true;--color-primary:#72cce8;--color-primary-contrast:#2a2f38;--color-primary-dark-1:#72cce8;--color-primary-dark-2:#85d0e7;--color-primary-dark-3:#97d4e7;--color-primary-dark-4:#aad8e6;--color-primary-dark-5:#bcdbe5;--color-primary-dark-6:#cedfe5;--color-primary-dark-7:#e1e3e4;--color-primary-light-1:#72cce8;--color-primary-light-2:#68b4ce;--color-primary-light-3:#5e9cb4;--color-primary-light-4:#55849a;--color-primary-light-5:#4b6c7f;--color-primary-light-6:#415465;--color-primary-light-7:#373c4b;--color-primary-alpha-10:rgba(114,204,232,0.1);--color-primary-alpha-20:rgba(114,204,232,0.2);--color-primary-alpha-30:rgba(114,204,232,0.3);--color-primary-alpha-40:rgba(114,204,232,0.4);--color-primary-alpha-50:rgba(114,204,232,0.5);--color-primary-alpha-60:rgba(114,204,232,0.6);--color-primary-alpha-70:rgba(114,204,232,0.7);--color-primary-alpha-80:rgba(114,204,232,0.8);--color-primary-alpha-90:rgba(114,204,232,0.9);--color-secondary:#424b5b;--color-secondary-dark-1:#424b5b;--color-secondary-dark-2:#4f5866;--color-secondary-dark-3:#5d6472;--color-secondary-dark-4:#6a717d;--color-secondary-dark-5:#777e89;--color-secondary-dark-6:#848a94;--color-secondary-dark-7:#9297a0;--color-secondary-dark-8:#9fa4ab;--color-secondary-dark-9:#acb0b6;--color-secondary-dark-10:#b9bdc2;--color-secondary-dark-11:#c6cacd;--color-secondary-dark-12:#d4d6d9;--color-secondary-dark-13:#e1e3e4;--color-secondary-light-1:#424b5b;--color-secondary-light-2:#3d4455;--color-secondary-light-3:#373c4b;--color-secondary-light-4:#333846;--color-secondary-alpha-10:rgba(66,75,91,0.1);--color-secondary-alpha-20:rgba(66,75,91,0.2);--color-secondary-alpha-30:rgba(66,75,91,0.3);--color-secondary-alpha-40:rgba(66,75,91,0.4);--color-secondary-alpha-50:rgba(66,75,91,0.5);--color-secondary-alpha-60:rgba(66,75,91,0.6);--color-secondary-alpha-70:rgba(66,75,91,0.7);--color-secondary-alpha-80:rgba(66,75,91,0.8);--color-secondary-alpha-90:rgba(66,75,91,0.9);--color-red:#ff6578;--color-orange:#f69c5e;--color-yellow:#eacb64;--color-olive:#9dd274;--color-green:#9dd274;--color-teal:#72cce8;--color-blue:#72cce8;--color-violet:#ba9cf3;--color-purple:#ba9cf3;--color-pink:#ba9cf3;--color-brown:#f69c5e;--color-grey:#828a9a;--color-red-light:#ff6578;--color-orange-light:#f69c5e;--color-yellow-light:#eacb64;--color-olive-light:#9dd274;--color-green-light:#9dd274;--color-teal-light:#72cce8;--color-blue-light:#72cce8;--color-violet-light:#ba9cf3;--color-purple-light:#ba9cf3;--color-pink-light:#ba9cf3;--color-brown-light:#f69c5e;--color-grey-light:#828a9a;--color-black:#373c4b;--color-gold:#f69c5e;--color-white:#2a2f38;--color-diff-removed-word-bg:#88464f;--color-diff-added-word-bg:#577047;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#aa4f5b;--color-diff-moved-row-border:#5487a0;--color-diff-added-row-border:#6b8c54;--color-diff-inactive:#373c4b;--color-error-border:#aa4f5b;--color-error-bg:#55393d;--color-error-text:#ff6578;--color-success-border:#6b8c54;--color-success-bg:#394634;--color-success-text:#9dd274;--color-warning-border:#9c874a;--color-warning-bg:#4e432f;--color-warning-text:#eacb64;--color-info-border:#5487a0;--color-info-bg:#354157;--color-info-text:#72cce8;--color-body:#2a2f38;--color-box-header:#3d4455;--color-box-body:#333846;--color-box-body-highlight:#373c4b;--color-text-dark:#ff6578;--color-text:#e1e3e4;--color-text-hover:rgba(225,227,228,0.8);--color-text-light:#9dd274;--color-text-light-1:#828a9a;--color-text-light-2:#e1e3e4;--color-text-light-3:#828a9a;--color-footer:#333846;--color-timeline:#424b5b;--color-input-text:#e1e3e4;--color-input-background:#2a2f38;--color-input-toggle-background:#333846;--color-input-border:#424b5b;--color-input-border-hover:#626b7b;--color-navbar:#333846;--color-navbar-transparent:#333846;--color-light:#333846;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#424b5b;--color-hover:rgba(66,75,91,0.5);--color-active:rgba(66,75,91,0.5);--color-menu:#333846;--color-card:#333846;--color-markup-table-row:#3d4455;--color-markup-code-block:#2a2f38;--color-button:#3d4455;--color-code-bg:#2a2f38;--color-code-sidebar-bg:#333846;--color-shadow:#00000060;--color-secondary-bg:#333846;--color-text-focus:#fff;--color-expand-button:#373c4b;--color-placeholder-text:#828a9a;--color-editor-line-highlight:#333846;--color-project-board-bg:#2a2f38;--color-project-board-light-label:#72cce8;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#333846;--color-label-active-bg:#424b5b;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#333846;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e1e3e4}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#828a9a}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#ff6578}.chroma + .o,.chroma .ow{color:#f69c5e}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#eacb64}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#ba9cf3}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#72cce8}.chroma .nf,.chroma .nb,.chroma .na{color:#9dd274}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#f69c5e}.ui.primary.button,.ui.primary.buttons + .button{background:#7ad5f1;background-color:#7ad5f1 !important;color:#2a2f38}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(122,213,241,0.8);background-color:rgba(122,213,241,0.8) + !important;color:#2a2f38}.ui.green.buttons .button,.ui.green.button{background:#a5e179;background-color:#a5e179;color:#2a2f38}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(165,225,121,0.8);background-color:rgba(165,225,121,0.8);color:#2a2f38}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#ff6578}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#ff6578}.repo-title{color:#ff6578}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#72cce8}.ui.basic.labels + .label,.ui.basic.label{color:#72cce8}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2a2f38}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2a2f38}i.grey.icon.icon.icon.icon{color:#828a9a}*{scrollbar-color:#ff6578 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #ff6578 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #ff6578 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #ff6578 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ff6d7e;background-color:#ff6d7e;color:#2a2f38}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,109,126,0.8);background-color:rgba(255,109,126,0.8);color:#2a2f38}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2a2f38}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2a2f38}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f69c5e;background-color:#f69c5e;color:#2a2f38}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(246,156,94,0.8);background-color:rgba(246,156,94,0.8);color:#2a2f38}' + theme-sonokai-espresso.css: ':root{--is-dark-theme:true;--color-primary:#81d0c9;--color-primary-contrast:#312c2b;--color-primary-dark-1:#81d0c9;--color-primary-dark-2:#92d3cd;--color-primary-dark-3:#a2d6d1;--color-primary-dark-4:#b3dad5;--color-primary-dark-5:#c3ddd9;--color-primary-dark-6:#d3e0dd;--color-primary-dark-7:#e4e3e1;--color-primary-light-1:#81d0c9;--color-primary-light-2:#76b7b1;--color-primary-light-3:#6c9e98;--color-primary-light-4:#618580;--color-primary-light-5:#566b68;--color-primary-light-6:#4c524f;--color-primary-light-7:#413937;--color-primary-alpha-10:rgba(129,208,201,0.1);--color-primary-alpha-20:rgba(129,208,201,0.2);--color-primary-alpha-30:rgba(129,208,201,0.3);--color-primary-alpha-40:rgba(129,208,201,0.4);--color-primary-alpha-50:rgba(129,208,201,0.5);--color-primary-alpha-60:rgba(129,208,201,0.6);--color-primary-alpha-70:rgba(129,208,201,0.7);--color-primary-alpha-80:rgba(129,208,201,0.8);--color-primary-alpha-90:rgba(129,208,201,0.9);--color-secondary:#4e433f;--color-secondary-dark-1:#4e433f;--color-secondary-dark-2:#5a504c;--color-secondary-dark-3:#675e5a;--color-secondary-dark-4:#746b68;--color-secondary-dark-5:#807875;--color-secondary-dark-6:#8d8683;--color-secondary-dark-7:#999390;--color-secondary-dark-8:#a5a09d;--color-secondary-dark-9:#b2aeab;--color-secondary-dark-10:#bfbbb9;--color-secondary-dark-11:#cbc8c6;--color-secondary-dark-12:#d8d6d4;--color-secondary-dark-13:#e4e3e1;--color-secondary-light-1:#4e433f;--color-secondary-light-2:#49403c;--color-secondary-light-3:#413937;--color-secondary-light-4:#393230;--color-secondary-alpha-10:rgba(78,67,63,0.1);--color-secondary-alpha-20:rgba(78,67,63,0.2);--color-secondary-alpha-30:rgba(78,67,63,0.3);--color-secondary-alpha-40:rgba(78,67,63,0.4);--color-secondary-alpha-50:rgba(78,67,63,0.5);--color-secondary-alpha-60:rgba(78,67,63,0.6);--color-secondary-alpha-70:rgba(78,67,63,0.7);--color-secondary-alpha-80:rgba(78,67,63,0.8);--color-secondary-alpha-90:rgba(78,67,63,0.9);--color-red:#f86882;--color-orange:#f08d71;--color-yellow:#f0c66f;--color-olive:#a6cd77;--color-green:#a6cd77;--color-teal:#81d0c9;--color-blue:#81d0c9;--color-violet:#9fa0e1;--color-purple:#9fa0e1;--color-pink:#9fa0e1;--color-brown:#f08d71;--color-grey:#90817b;--color-red-light:#f86882;--color-orange-light:#f08d71;--color-yellow-light:#f0c66f;--color-olive-light:#a6cd77;--color-green-light:#a6cd77;--color-teal-light:#81d0c9;--color-blue-light:#81d0c9;--color-violet-light:#9fa0e1;--color-purple-light:#9fa0e1;--color-pink-light:#9fa0e1;--color-brown-light:#f08d71;--color-grey-light:#90817b;--color-black:#413937;--color-gold:#f08d71;--color-white:#312c2b;--color-diff-removed-word-bg:#864752;--color-diff-added-word-bg:#5a6f48;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a75160;--color-diff-moved-row-border:#5b8990;--color-diff-added-row-border:#708a56;--color-diff-inactive:#413937;--color-error-border:#a75160;--color-error-bg:#55393d;--color-error-text:#f86882;--color-success-border:#708a56;--color-success-bg:#394634;--color-success-text:#a6cd77;--color-warning-border:#9f854f;--color-warning-bg:#4e432f;--color-warning-text:#f0c66f;--color-info-border:#5b8990;--color-info-bg:#354157;--color-info-text:#81d0c9;--color-body:#312c2b;--color-box-header:#49403c;--color-box-body:#393230;--color-box-body-highlight:#413937;--color-text-dark:#f86882;--color-text:#e4e3e1;--color-text-hover:rgba(228,227,225,0.8);--color-text-light:#a6cd77;--color-text-light-1:#90817b;--color-text-light-2:#e4e3e1;--color-text-light-3:#90817b;--color-footer:#393230;--color-timeline:#4e433f;--color-input-text:#e4e3e1;--color-input-background:#312c2b;--color-input-toggle-background:#393230;--color-input-border:#4e433f;--color-input-border-hover:#6f625d;--color-navbar:#393230;--color-navbar-transparent:#393230;--color-light:#393230;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#4e433f;--color-hover:rgba(78,67,63,0.5);--color-active:rgba(78,67,63,0.5);--color-menu:#393230;--color-card:#393230;--color-markup-table-row:#49403c;--color-markup-code-block:#312c2b;--color-button:#49403c;--color-code-bg:#312c2b;--color-code-sidebar-bg:#393230;--color-shadow:#00000060;--color-secondary-bg:#393230;--color-text-focus:#fff;--color-expand-button:#413937;--color-placeholder-text:#90817b;--color-editor-line-highlight:#393230;--color-project-board-bg:#312c2b;--color-project-board-light-label:#81d0c9;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#393230;--color-label-active-bg:#4e433f;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#393230;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e4e3e1}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#90817b}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f86882}.chroma + .o,.chroma .ow{color:#f08d71}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#f0c66f}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#9fa0e1}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#81d0c9}.chroma .nf,.chroma .nb,.chroma .na{color:#a6cd77}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#f08d71}.ui.primary.button,.ui.primary.buttons + .button{background:#85dad2;background-color:#85dad2 !important;color:#312c2b}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(133,218,210,0.8);background-color:rgba(133,218,210,0.8) + !important;color:#312c2b}.ui.green.buttons .button,.ui.green.button{background:#adda78;background-color:#adda78;color:#312c2b}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(173,218,120,0.8);background-color:rgba(173,218,120,0.8);color:#312c2b}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#f86882}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#f86882}.repo-title{color:#f86882}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#81d0c9}.ui.basic.labels + .label,.ui.basic.label{color:#81d0c9}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#312c2b}.ui.green.labels .label,.ui.ui.ui.green.label{color:#312c2b}i.grey.icon.icon.icon.icon{color:#90817b}*{scrollbar-color:#f86882 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #f86882 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #f86882 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #f86882 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#fd6883;background-color:#fd6883;color:#312c2b}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(253,104,131,0.8);background-color:rgba(253,104,131,0.8);color:#312c2b}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#312c2b}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#312c2b}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f08d71;background-color:#f08d71;color:#312c2b}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(240,141,113,0.8);background-color:rgba(240,141,113,0.8);color:#312c2b}' + theme-sonokai-maia.css: ':root{--is-dark-theme:true;--color-primary:#78cee9;--color-primary-contrast:#273136;--color-primary-dark-1:#78cee9;--color-primary-dark-2:#8ad1e8;--color-primary-dark-3:#9bd5e7;--color-primary-dark-4:#add8e6;--color-primary-dark-5:#bedbe5;--color-primary-dark-6:#cfdfe4;--color-primary-dark-7:#e1e2e3;--color-primary-light-1:#78cee9;--color-primary-light-2:#6db6ce;--color-primary-light-3:#629eb3;--color-primary-light-4:#578798;--color-primary-light-5:#4b6f7c;--color-primary-light-6:#405761;--color-primary-light-7:#353f46;--color-primary-alpha-10:rgba(120,206,233,0.1);--color-primary-alpha-20:rgba(120,206,233,0.2);--color-primary-alpha-30:rgba(120,206,233,0.3);--color-primary-alpha-40:rgba(120,206,233,0.4);--color-primary-alpha-50:rgba(120,206,233,0.5);--color-primary-alpha-60:rgba(120,206,233,0.6);--color-primary-alpha-70:rgba(120,206,233,0.7);--color-primary-alpha-80:rgba(120,206,233,0.8);--color-primary-alpha-90:rgba(120,206,233,0.9);--color-secondary:#414b53;--color-secondary-dark-1:#414b53;--color-secondary-dark-2:#4e585f;--color-secondary-dark-3:#5c646b;--color-secondary-dark-4:#697177;--color-secondary-dark-5:#767d83;--color-secondary-dark-6:#848a8f;--color-secondary-dark-7:#91979b;--color-secondary-dark-8:#9ea3a7;--color-secondary-dark-9:#acb0b3;--color-secondary-dark-10:#b9bcbf;--color-secondary-dark-11:#c6c9cb;--color-secondary-dark-12:#d4d5d7;--color-secondary-dark-13:#e1e2e3;--color-secondary-light-1:#414b53;--color-secondary-light-2:#3a444b;--color-secondary-light-3:#353f46;--color-secondary-light-4:#313b42;--color-secondary-alpha-10:rgba(65,75,83,0.1);--color-secondary-alpha-20:rgba(65,75,83,0.2);--color-secondary-alpha-30:rgba(65,75,83,0.3);--color-secondary-alpha-40:rgba(65,75,83,0.4);--color-secondary-alpha-50:rgba(65,75,83,0.5);--color-secondary-alpha-60:rgba(65,75,83,0.6);--color-secondary-alpha-70:rgba(65,75,83,0.7);--color-secondary-alpha-80:rgba(65,75,83,0.8);--color-secondary-alpha-90:rgba(65,75,83,0.9);--color-red:#f76c7c;--color-orange:#f3a96a;--color-yellow:#e3d367;--color-olive:#9cd57b;--color-green:#9cd57b;--color-teal:#78cee9;--color-blue:#78cee9;--color-violet:#baa0f8;--color-purple:#baa0f8;--color-pink:#baa0f8;--color-brown:#f3a96a;--color-grey:#82878b;--color-red-light:#f76c7c;--color-orange-light:#f3a96a;--color-yellow-light:#e3d367;--color-olive-light:#9cd57b;--color-green-light:#9cd57b;--color-teal-light:#78cee9;--color-blue-light:#78cee9;--color-violet-light:#baa0f8;--color-purple-light:#baa0f8;--color-pink-light:#baa0f8;--color-brown-light:#f3a96a;--color-grey-light:#82878b;--color-black:#353f46;--color-gold:#f3a96a;--color-white:#273136;--color-diff-removed-word-bg:#864850;--color-diff-added-word-bg:#577149;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a6535d;--color-diff-moved-row-border:#5788a0;--color-diff-added-row-border:#6b8e58;--color-diff-inactive:#353f46;--color-error-border:#a6535d;--color-error-bg:#55393d;--color-error-text:#f76c7c;--color-success-border:#6b8e58;--color-success-bg:#394634;--color-success-text:#9cd57b;--color-warning-border:#998b4b;--color-warning-bg:#4e432f;--color-warning-text:#e3d367;--color-info-border:#5788a0;--color-info-bg:#354157;--color-info-text:#78cee9;--color-body:#273136;--color-box-header:#3a444b;--color-box-body:#313b42;--color-box-body-highlight:#353f46;--color-text-dark:#f76c7c;--color-text:#e1e2e3;--color-text-hover:rgba(225,226,227,0.8);--color-text-light:#9cd57b;--color-text-light-1:#82878b;--color-text-light-2:#e1e2e3;--color-text-light-3:#82878b;--color-footer:#313b42;--color-timeline:#414b53;--color-input-text:#e1e2e3;--color-input-background:#273136;--color-input-toggle-background:#313b42;--color-input-border:#414b53;--color-input-border-hover:#62696f;--color-navbar:#313b42;--color-navbar-transparent:#313b42;--color-light:#313b42;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#414b53;--color-hover:rgba(65,75,83,0.5);--color-active:rgba(65,75,83,0.5);--color-menu:#313b42;--color-card:#313b42;--color-markup-table-row:#3a444b;--color-markup-code-block:#273136;--color-button:#3a444b;--color-code-bg:#273136;--color-code-sidebar-bg:#313b42;--color-shadow:#00000060;--color-secondary-bg:#313b42;--color-text-focus:#fff;--color-expand-button:#353f46;--color-placeholder-text:#82878b;--color-editor-line-highlight:#313b42;--color-project-board-bg:#273136;--color-project-board-light-label:#78cee9;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#313b42;--color-label-active-bg:#414b53;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#313b42;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e1e2e3}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#82878b}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f76c7c}.chroma + .o,.chroma .ow{color:#f3a96a}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#e3d367}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#baa0f8}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#78cee9}.chroma .nf,.chroma .nb,.chroma .na{color:#9cd57b}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#f3a96a}.ui.primary.button,.ui.primary.buttons + .button{background:#7cd5f1;background-color:#7cd5f1 !important;color:#273136}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(124,213,241,0.8);background-color:rgba(124,213,241,0.8) + !important;color:#273136}.ui.green.buttons .button,.ui.green.button{background:#a2e57b;background-color:#a2e57b;color:#273136}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(162,229,123,0.8);background-color:rgba(162,229,123,0.8);color:#273136}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#f76c7c}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#f76c7c}.repo-title{color:#f76c7c}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#78cee9}.ui.basic.labels + .label,.ui.basic.label{color:#78cee9}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#273136}.ui.green.labels .label,.ui.ui.ui.green.label{color:#273136}i.grey.icon.icon.icon.icon{color:#82878b}*{scrollbar-color:#f76c7c + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #f76c7c !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #f76c7c !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #f76c7c !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ff6d7e;background-color:#ff6d7e;color:#273136}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,109,126,0.8);background-color:rgba(255,109,126,0.8);color:#273136}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#273136}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#273136}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f3a96a;background-color:#f3a96a;color:#273136}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(243,169,106,0.8);background-color:rgba(243,169,106,0.8);color:#273136}' + theme-sonokai-shusia.css: ':root{--is-dark-theme:true;--color-primary:#7accd7;--color-primary-contrast:#2d2a2e;--color-primary-dark-1:#7accd7;--color-primary-dark-2:#8cd0d9;--color-primary-dark-3:#9dd3db;--color-primary-dark-4:#afd7de;--color-primary-dark-5:#c0dae0;--color-primary-dark-6:#d1dde2;--color-primary-dark-7:#e3e1e4;--color-primary-light-1:#7accd7;--color-primary-light-2:#6fb3bd;--color-primary-light-3:#659ba4;--color-primary-light-4:#5b828b;--color-primary-light-5:#506971;--color-primary-light-6:#465158;--color-primary-light-7:#3b383e;--color-primary-alpha-10:rgba(122,204,215,0.1);--color-primary-alpha-20:rgba(122,204,215,0.2);--color-primary-alpha-30:rgba(122,204,215,0.3);--color-primary-alpha-40:rgba(122,204,215,0.4);--color-primary-alpha-50:rgba(122,204,215,0.5);--color-primary-alpha-60:rgba(122,204,215,0.6);--color-primary-alpha-70:rgba(122,204,215,0.7);--color-primary-alpha-80:rgba(122,204,215,0.8);--color-primary-alpha-90:rgba(122,204,215,0.9);--color-secondary:#49464e;--color-secondary-dark-1:#49464e;--color-secondary-dark-2:#56535a;--color-secondary-dark-3:#636067;--color-secondary-dark-4:#706d74;--color-secondary-dark-5:#7c7a80;--color-secondary-dark-6:#89878d;--color-secondary-dark-7:#969499;--color-secondary-dark-8:#a3a0a5;--color-secondary-dark-9:#b0adb2;--color-secondary-dark-10:#bdbabf;--color-secondary-dark-11:#c9c7cb;--color-secondary-dark-12:#d6d4d8;--color-secondary-dark-13:#e3e1e4;--color-secondary-light-1:#49464e;--color-secondary-light-2:#423f46;--color-secondary-light-3:#3b383e;--color-secondary-light-4:#37343a;--color-secondary-alpha-10:rgba(73,70,78,0.1);--color-secondary-alpha-20:rgba(73,70,78,0.2);--color-secondary-alpha-30:rgba(73,70,78,0.3);--color-secondary-alpha-40:rgba(73,70,78,0.4);--color-secondary-alpha-50:rgba(73,70,78,0.5);--color-secondary-alpha-60:rgba(73,70,78,0.6);--color-secondary-alpha-70:rgba(73,70,78,0.7);--color-secondary-alpha-80:rgba(73,70,78,0.8);--color-secondary-alpha-90:rgba(73,70,78,0.9);--color-red:#f85e84;--color-orange:#ef9062;--color-yellow:#e5c463;--color-olive:#9ecd6f;--color-green:#9ecd6f;--color-teal:#7accd7;--color-blue:#7accd7;--color-violet:#ab9df2;--color-purple:#ab9df2;--color-pink:#ab9df2;--color-brown:#ef9062;--color-grey:#848089;--color-red-light:#f85e84;--color-orange-light:#ef9062;--color-yellow-light:#e5c463;--color-olive-light:#9ecd6f;--color-green-light:#9ecd6f;--color-teal-light:#7accd7;--color-blue-light:#7accd7;--color-violet-light:#ab9df2;--color-purple-light:#ab9df2;--color-pink-light:#ab9df2;--color-brown-light:#ef9062;--color-grey-light:#848089;--color-black:#3b383e;--color-gold:#ef9062;--color-white:#2d2a2e;--color-diff-removed-word-bg:#864452;--color-diff-added-word-bg:#576f46;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a74c61;--color-diff-moved-row-border:#588797;--color-diff-added-row-border:#6c8a52;--color-diff-inactive:#3b383e;--color-error-border:#a74c61;--color-error-bg:#55393d;--color-error-text:#f85e84;--color-success-border:#6c8a52;--color-success-bg:#394634;--color-success-text:#9ecd6f;--color-warning-border:#9a8449;--color-warning-bg:#4e432f;--color-warning-text:#e5c463;--color-info-border:#588797;--color-info-bg:#354157;--color-info-text:#7accd7;--color-body:#2d2a2e;--color-box-header:#423f46;--color-box-body:#37343a;--color-box-body-highlight:#3b383e;--color-text-dark:#f85e84;--color-text:#e3e1e4;--color-text-hover:rgba(227,225,228,0.8);--color-text-light:#9ecd6f;--color-text-light-1:#848089;--color-text-light-2:#e3e1e4;--color-text-light-3:#848089;--color-footer:#37343a;--color-timeline:#49464e;--color-input-text:#e3e1e4;--color-input-background:#2d2a2e;--color-input-toggle-background:#37343a;--color-input-border:#49464e;--color-input-border-hover:#67636c;--color-navbar:#37343a;--color-navbar-transparent:#37343a;--color-light:#37343a;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#49464e;--color-hover:rgba(73,70,78,0.5);--color-active:rgba(73,70,78,0.5);--color-menu:#37343a;--color-card:#37343a;--color-markup-table-row:#423f46;--color-markup-code-block:#2d2a2e;--color-button:#423f46;--color-code-bg:#2d2a2e;--color-code-sidebar-bg:#37343a;--color-shadow:#00000060;--color-secondary-bg:#37343a;--color-text-focus:#fff;--color-expand-button:#3b383e;--color-placeholder-text:#848089;--color-editor-line-highlight:#37343a;--color-project-board-bg:#2d2a2e;--color-project-board-light-label:#7accd7;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#37343a;--color-label-active-bg:#49464e;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#37343a;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e3e1e4}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#848089}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#f85e84}.chroma + .o,.chroma .ow{color:#ef9062}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#e5c463}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#ab9df2}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#7accd7}.chroma .nf,.chroma .nb,.chroma .na{color:#9ecd6f}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#ef9062}.ui.primary.button,.ui.primary.buttons + .button{background:#78dce8;background-color:#78dce8 !important;color:#2d2a2e}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(120,220,232,0.8);background-color:rgba(120,220,232,0.8) + !important;color:#2d2a2e}.ui.green.buttons .button,.ui.green.button{background:#a9dc76;background-color:#a9dc76;color:#2d2a2e}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(169,220,118,0.8);background-color:rgba(169,220,118,0.8);color:#2d2a2e}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#f85e84}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#f85e84}.repo-title{color:#f85e84}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#7accd7}.ui.basic.labels + .label,.ui.basic.label{color:#7accd7}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2d2a2e}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2d2a2e}i.grey.icon.icon.icon.icon{color:#848089}*{scrollbar-color:#f85e84 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #f85e84 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #f85e84 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #f85e84 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ff6188;background-color:#ff6188;color:#2d2a2e}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,97,136,0.8);background-color:rgba(255,97,136,0.8);color:#2d2a2e}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2d2a2e}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2d2a2e}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#ef9062;background-color:#ef9062;color:#2d2a2e}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(239,144,98,0.8);background-color:rgba(239,144,98,0.8);color:#2d2a2e}' + theme-sonokai.css: ':root{--is-dark-theme:true;--color-primary:#76cce0;--color-primary-contrast:#2c2e34;--color-primary-dark-1:#76cce0;--color-primary-dark-2:#88d0e1;--color-primary-dark-3:#9ad3e1;--color-primary-dark-4:#acd7e2;--color-primary-dark-5:#bedbe2;--color-primary-dark-6:#d0dee2;--color-primary-dark-7:#e2e2e3;--color-primary-light-1:#76cce0;--color-primary-light-2:#6bb3c6;--color-primary-light-3:#619bac;--color-primary-light-4:#568392;--color-primary-light-5:#4b6a78;--color-primary-light-6:#41525e;--color-primary-light-7:#363944;--color-primary-alpha-10:rgba(118,204,224,0.1);--color-primary-alpha-20:rgba(118,204,224,0.2);--color-primary-alpha-30:rgba(118,204,224,0.3);--color-primary-alpha-40:rgba(118,204,224,0.4);--color-primary-alpha-50:rgba(118,204,224,0.5);--color-primary-alpha-60:rgba(118,204,224,0.6);--color-primary-alpha-70:rgba(118,204,224,0.7);--color-primary-alpha-80:rgba(118,204,224,0.8);--color-primary-alpha-90:rgba(118,204,224,0.9);--color-secondary:#414550;--color-secondary-dark-1:#414550;--color-secondary-dark-2:#4e525c;--color-secondary-dark-3:#5c5f69;--color-secondary-dark-4:#696c75;--color-secondary-dark-5:#777981;--color-secondary-dark-6:#84868d;--color-secondary-dark-7:#92949a;--color-secondary-dark-8:#9fa1a6;--color-secondary-dark-9:#acaeb2;--color-secondary-dark-10:#babbbe;--color-secondary-dark-11:#c7c8ca;--color-secondary-dark-12:#d5d5d7;--color-secondary-dark-13:#e2e2e3;--color-secondary-light-1:#414550;--color-secondary-light-2:#3b3e48;--color-secondary-light-3:#363944;--color-secondary-light-4:#33353f;--color-secondary-alpha-10:rgba(65,69,80,0.1);--color-secondary-alpha-20:rgba(65,69,80,0.2);--color-secondary-alpha-30:rgba(65,69,80,0.3);--color-secondary-alpha-40:rgba(65,69,80,0.4);--color-secondary-alpha-50:rgba(65,69,80,0.5);--color-secondary-alpha-60:rgba(65,69,80,0.6);--color-secondary-alpha-70:rgba(65,69,80,0.7);--color-secondary-alpha-80:rgba(65,69,80,0.8);--color-secondary-alpha-90:rgba(65,69,80,0.9);--color-red:#fc5d7c;--color-orange:#f39660;--color-yellow:#e7c664;--color-olive:#9ed072;--color-green:#9ed072;--color-teal:#76cce0;--color-blue:#76cce0;--color-violet:#b39df3;--color-purple:#b39df3;--color-pink:#b39df3;--color-brown:#f39660;--color-grey:#7f8490;--color-red-light:#fc5d7c;--color-orange-light:#f39660;--color-yellow-light:#e7c664;--color-olive-light:#9ed072;--color-green-light:#9ed072;--color-teal-light:#76cce0;--color-blue-light:#76cce0;--color-violet-light:#b39df3;--color-purple-light:#b39df3;--color-pink-light:#b39df3;--color-brown-light:#f39660;--color-grey-light:#7f8490;--color-black:#363944;--color-gold:#f39660;--color-white:#2c2e34;--color-diff-removed-word-bg:#874450;--color-diff-added-word-bg:#576f47;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#a94b5d;--color-diff-moved-row-border:#56879c;--color-diff-added-row-border:#6c8b53;--color-diff-inactive:#363944;--color-error-border:#a94b5d;--color-error-bg:#55393d;--color-error-text:#fc5d7c;--color-success-border:#6c8b53;--color-success-bg:#394634;--color-success-text:#9ed072;--color-warning-border:#9b854a;--color-warning-bg:#4e432f;--color-warning-text:#e7c664;--color-info-border:#56879c;--color-info-bg:#354157;--color-info-text:#76cce0;--color-body:#2c2e34;--color-box-header:#3b3e48;--color-box-body:#33353f;--color-box-body-highlight:#363944;--color-text-dark:#fc5d7c;--color-text:#e2e2e3;--color-text-hover:rgba(226,226,227,0.8);--color-text-light:#9ed072;--color-text-light-1:#7f8490;--color-text-light-2:#e2e2e3;--color-text-light-3:#7f8490;--color-footer:#33353f;--color-timeline:#414550;--color-input-text:#e2e2e3;--color-input-background:#2c2e34;--color-input-toggle-background:#33353f;--color-input-border:#414550;--color-input-border-hover:#606570;--color-navbar:#33353f;--color-navbar-transparent:#33353f;--color-light:#33353f;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#414550;--color-hover:rgba(65,69,80,0.5);--color-active:rgba(65,69,80,0.5);--color-menu:#33353f;--color-card:#33353f;--color-markup-table-row:#3b3e48;--color-markup-code-block:#2c2e34;--color-button:#3b3e48;--color-code-bg:#2c2e34;--color-code-sidebar-bg:#33353f;--color-shadow:#00000060;--color-secondary-bg:#33353f;--color-text-focus:#fff;--color-expand-button:#363944;--color-placeholder-text:#7f8490;--color-editor-line-highlight:#33353f;--color-project-board-bg:#2c2e34;--color-project-board-light-label:#76cce0;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#33353f;--color-label-active-bg:#414550;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#33353f;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#e2e2e3}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#7f8490}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#fc5d7c}.chroma + .o,.chroma .ow{color:#f39660}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#e7c664}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#b39df3}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#76cce0}.chroma .nf,.chroma .nb,.chroma .na{color:#9ed072}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#f39660}.ui.primary.button,.ui.primary.buttons + .button{background:#85d3f2;background-color:#85d3f2 !important;color:#2c2e34}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(133,211,242,0.8);background-color:rgba(133,211,242,0.8) + !important;color:#2c2e34}.ui.green.buttons .button,.ui.green.button{background:#a7df78;background-color:#a7df78;color:#2c2e34}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(167,223,120,0.8);background-color:rgba(167,223,120,0.8);color:#2c2e34}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#fc5d7c}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#fc5d7c}.repo-title{color:#fc5d7c}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#76cce0}.ui.basic.labels + .label,.ui.basic.label{color:#76cce0}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2c2e34}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2c2e34}i.grey.icon.icon.icon.icon{color:#7f8490}*{scrollbar-color:#fc5d7c + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #fc5d7c !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #fc5d7c !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #fc5d7c !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#ff6077;background-color:#ff6077;color:#2c2e34}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(255,96,119,0.8);background-color:rgba(255,96,119,0.8);color:#2c2e34}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2c2e34}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2c2e34}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#f39660;background-color:#f39660;color:#2c2e34}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(243,150,96,0.8);background-color:rgba(243,150,96,0.8);color:#2c2e34}' + theme-theme-nord.css: ':root{--is-dark-theme:true;--color-primary:#81A1C1;--color-primary-contrast:#2E3440;--color-primary-dark-1:#81A1C1;--color-primary-dark-2:#90abc8;--color-primary-dark-3:#9eb5ce;--color-primary-dark-4:#adc0d5;--color-primary-dark-5:#bbcadc;--color-primary-dark-6:#c9d4e2;--color-primary-dark-7:#D8DEE9;--color-primary-light-1:#81A1C1;--color-primary-light-2:#7591ae;--color-primary-light-3:#6a829c;--color-primary-light-4:#5e728a;--color-primary-light-5:#526277;--color-primary-light-6:#465265;--color-primary-light-7:#3b4352;--color-primary-alpha-10:rgba(129,161,193,0.1);--color-primary-alpha-20:rgba(129,161,193,0.2);--color-primary-alpha-30:rgba(129,161,193,0.3);--color-primary-alpha-40:rgba(129,161,193,0.4);--color-primary-alpha-50:rgba(129,161,193,0.5);--color-primary-alpha-60:rgba(129,161,193,0.6);--color-primary-alpha-70:rgba(129,161,193,0.7);--color-primary-alpha-80:rgba(129,161,193,0.8);--color-primary-alpha-90:rgba(129,161,193,0.9);--color-secondary:#485164;--color-secondary-dark-1:#485164;--color-secondary-dark-2:#535d6f;--color-secondary-dark-3:#60697a;--color-secondary-dark-4:#6c7485;--color-secondary-dark-5:#788090;--color-secondary-dark-6:#848c9b;--color-secondary-dark-7:#9098a7;--color-secondary-dark-8:#9ca3b2;--color-secondary-dark-9:#a8afbd;--color-secondary-dark-10:#b4bbc8;--color-secondary-dark-11:#c0c6d3;--color-secondary-dark-12:#ccd2de;--color-secondary-dark-13:#D8DEE9;--color-secondary-light-1:#485164;--color-secondary-light-2:#414a5b;--color-secondary-light-3:#3b4352;--color-secondary-light-4:#343b49;--color-secondary-alpha-10:rgba(72,81,100,0.1);--color-secondary-alpha-20:rgba(72,81,100,0.2);--color-secondary-alpha-30:rgba(72,81,100,0.3);--color-secondary-alpha-40:rgba(72,81,100,0.4);--color-secondary-alpha-50:rgba(72,81,100,0.5);--color-secondary-alpha-60:rgba(72,81,100,0.6);--color-secondary-alpha-70:rgba(72,81,100,0.7);--color-secondary-alpha-80:rgba(72,81,100,0.8);--color-secondary-alpha-90:rgba(72,81,100,0.9);--color-red:#BF616A;--color-orange:#D08770;--color-yellow:#EBCB8B;--color-olive:#A3BE8C;--color-green:#A3BE8C;--color-teal:#88C0D0;--color-blue:#81A1C1;--color-violet:#B48EAD;--color-purple:#B48EAD;--color-pink:#B48EAD;--color-brown:#D08770;--color-grey:#7f8aa0;--color-red-light:#BF616A;--color-orange-light:#D08770;--color-yellow-light:#EBCB8B;--color-olive-light:#A3BE8C;--color-green-light:#A3BE8C;--color-teal-light:#88C0D0;--color-blue-light:#81A1C1;--color-violet-light:#B48EAD;--color-purple-light:#B48EAD;--color-pink-light:#B48EAD;--color-brown-light:#D08770;--color-grey-light:#7f8aa0;--color-black:#3b4352;--color-gold:#D08770;--color-white:#2E3440;--color-diff-removed-word-bg:#75454b;--color-diff-added-word-bg:#596a4e;--color-diff-removed-row-bg:#55393d;--color-diff-moved-row-bg:#354157;--color-diff-added-row-bg:#394634;--color-diff-removed-row-border:#8a4d54;--color-diff-moved-row-border:#5b718c;--color-diff-added-row-border:#6e8260;--color-diff-inactive:#3b4352;--color-error-border:#8a4d54;--color-error-bg:#55393d;--color-error-text:#BF616A;--color-success-border:#6e8260;--color-success-bg:#394634;--color-success-text:#A3BE8C;--color-warning-border:#9d875d;--color-warning-bg:#4e432f;--color-warning-text:#EBCB8B;--color-info-border:#5b718c;--color-info-bg:#354157;--color-info-text:#81A1C1;--color-body:#2E3440;--color-box-header:#414a5b;--color-box-body:#343b49;--color-box-body-highlight:#3b4352;--color-text-dark:#88C0D0;--color-text:#D8DEE9;--color-text-hover:rgba(216,222,233,0.8);--color-text-light:#8FBCBB;--color-text-light-1:#7f8aa0;--color-text-light-2:#D8DEE9;--color-text-light-3:#7f8aa0;--color-footer:#343b49;--color-timeline:#485164;--color-input-text:#D8DEE9;--color-input-background:#2E3440;--color-input-toggle-background:#343b49;--color-input-border:#485164;--color-input-border-hover:#636e82;--color-navbar:#343b49;--color-navbar-transparent:#343b49;--color-light:#343b49;--color-light-mimic-enabled:rgba(0, + 0, 0, calc(40 / 255 * 222 / 255 / var(--opacity-disabled)));--color-light-border:#485164;--color-hover:rgba(72,81,100,0.5);--color-active:rgba(72,81,100,0.5);--color-menu:#343b49;--color-card:#343b49;--color-markup-table-row:#414a5b;--color-markup-code-block:#2E3440;--color-button:#414a5b;--color-code-bg:#2E3440;--color-code-sidebar-bg:#343b49;--color-shadow:#00000060;--color-secondary-bg:#343b49;--color-text-focus:#fff;--color-expand-button:#3b4352;--color-placeholder-text:#7f8aa0;--color-editor-line-highlight:#343b49;--color-project-board-bg:#2E3440;--color-project-board-light-label:#81A1C1;--color-caret:var(--color-text);--color-reaction-bg:#ffffff12;--color-reaction-active-bg:var(--color-primary-alpha-40);--color-header-bar:#343b49;--color-label-active-bg:#485164;--color-accent:var(--color-primary-light-1);--color-small-accent:var(--color-primary-light-5);--color-active-line:#343b49;accent-color:var(--color-accent);color-scheme:dark}.chroma + .nx{color:#D8DEE9}.chroma .c,.chroma .c1,.chroma .ch,.chroma .cm,.chroma .sd{color:#7f8aa0}.chroma + .k,.chroma .kc,.chroma .kd,.chroma .kn,.chroma .kp,.chroma .kr{color:#81A1C1}.chroma + .o,.chroma .ow{color:#88C0D0}.chroma .s,.chroma .s1,.chroma .s2,.chroma .sa,.chroma + .sb,.chroma .sc,.chroma .sh,.chroma .si,.chroma .sr,.chroma .ss,.chroma .sx,.chroma + .nt,.chroma .cpf{color:#A3BE8C}.chroma .m,.chroma .mb,.chroma .mf,.chroma .mh,.chroma + .mi,.chroma .mo,.chroma .nl{color:#A3BE8C}.chroma .kt,.chroma .nc,.chroma .nn,.chroma + .nv{color:#8FBCBB}.chroma .nf,.chroma .nb,.chroma .na{color:#81A1C1}.chroma .bp,.chroma + .cp,.chroma .ne,.chroma .nd,.chroma .se{color:#EBCB8B}.ui.primary.button,.ui.primary.buttons + .button{background:#81A1C1;background-color:#81A1C1 !important;color:#2E3440}.ui.primary.button:hover,.ui.primary.buttons + .button:hover{background:rgba(129,161,193,0.8);background-color:rgba(129,161,193,0.8) + !important;color:#2E3440}.ui.green.buttons .button,.ui.green.button{background:#88C0D0;background-color:#88C0D0;color:#2E3440}.ui.green.buttons + .button:hover,.ui.green.button:hover{background:rgba(136,192,208,0.8);background-color:rgba(136,192,208,0.8);color:#2E3440}.ui.labeled.button.disabled>.button,.ui.basic.buttons + .button,.ui.basic.button{color:#88C0D0}.ui.labeled.button.disabled>.button:hover,.ui.basic.buttons + .button:hover,.ui.basic.button:hover{color:#88C0D0}.repo-title{color:#88C0D0}.repo-buttons + button[disabled]~.label,.repo-buttons .ui.labeled.button.disabled>.label{color:#81A1C1}.ui.basic.labels + .label,.ui.basic.label{color:#81A1C1}.repository .ui.segment.sub-menu .list .item + a:hover,.ui.tabular.menu .item:hover{color:var(--color-text-hover)}.ui.primary.labels + .label,.ui.ui.ui.primary.label{color:#2E3440}.ui.green.labels .label,.ui.ui.ui.green.label{color:#2E3440}i.grey.icon.icon.icon.icon{color:#7f8aa0}*{scrollbar-color:#88C0D0 + transparent !important}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{box-shadow:inset + 0 0 0 6px #88C0D0 !important;border:2px solid transparent;border-radius:5px !important}::-webkit-scrollbar-thumb:window-inactive{box-shadow:inset + 0 0 0 6px #88C0D0 !important}::-webkit-scrollbar-thumb:hover{box-shadow:inset + 0 0 0 6px #88C0D0 !important}::-webkit-scrollbar-corner{background:transparent}.ui.red.labels + .label,.ui.ui.ui.red.label,.ui.red.button,.ui.red.buttons .button{background:#BF616A;background-color:#BF616A;color:#2E3440}.ui.red.labels + .label:hover,.ui.ui.ui.red.label:hover,.ui.red.button:hover,.ui.red.buttons .button:hover{background:rgba(191,97,106,0.8);background-color:rgba(191,97,106,0.8);color:#2E3440}.ui.grey.labels + .label,.ui.ui.ui.grey.label,.ui.grey.button,.ui.grey.buttons .button{color:#2E3440}.ui.grey.labels + .label:hover,.ui.ui.ui.grey.label:hover,.ui.grey.button:hover,.ui.grey.buttons + .button:hover{color:#2E3440}.ui.orange.labels .label,.ui.ui.ui.orange.label,.ui.orange.button,.ui.orange.buttons + .button{background:#D08770;background-color:#D08770;color:#2E3440}.ui.orange.labels + .label:hover,.ui.ui.ui.orange.label:hover,.ui.orange.button:hover,.ui.orange.buttons + .button:hover{background:rgba(208,135,112,0.8);background-color:rgba(208,135,112,0.8);color:#2E3440}' \ No newline at end of file diff --git a/apps/gitea/v1_Secret_gitea-init.yaml b/apps/gitea/v1_Secret_gitea-init.yaml new file mode 100644 index 0000000..dc3c0db --- /dev/null +++ b/apps/gitea/v1_Secret_gitea-init.yaml @@ -0,0 +1,90 @@ +# Source: gitea/templates/gitea/init.yaml +apiVersion: v1 +kind: Secret +metadata: + name: gitea-init + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm +type: Opaque +stringData: + configure_gpg_environment.sh: |- + #!/usr/bin/env bash + set -eu + + gpg --batch --import /raw/private.asc + init_directory_structure.sh: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + chown 1000:1000 /data + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chown 1000:1000 "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" + + + + configure_gitea.sh: |- + #!/usr/bin/env bash + + set -euo pipefail + + echo '==== BEGIN GITEA CONFIGURATION ====' + + { # try + gitea migrate + } || { # catch + echo "Gitea migrate might fail due to database connection...This init-container will try again in a few seconds" + exit 1 + } + function configure_admin_user() { + local ACCOUNT_ID=$(gitea admin user list --admin | grep -e "\s\+${GITEA_ADMIN_USERNAME}\s\+" | awk -F " " "{printf \$1}") + if [[ -z "${ACCOUNT_ID}" ]]; then + echo "No admin user '${GITEA_ADMIN_USERNAME}' found. Creating now..." + gitea admin user create --admin --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" --email "git-admin@local.com" --must-change-password=false + echo '...created.' + else + echo "Admin account '${GITEA_ADMIN_USERNAME}' already exist. Running update to sync password..." + gitea admin user change-password --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" + echo '...password sync done.' + fi + } + + configure_admin_user + + function configure_ldap() { + local LDAP_NAME='Authentik' + local GITEA_AUTH_ID=$(gitea admin auth list --vertical-bars | grep -E "\|${LDAP_NAME}\s+\|" | grep -iE '\|LDAP \(via BindDN\)\s+\|' | awk -F " " "{print \$1}") + + if [[ -z "${GITEA_AUTH_ID}" ]]; then + echo "No ldap configuration found with name "${LDAP_NAME}". Installing it now..." + gitea admin auth add-ldap --admin-filter "${LDAP_ADMIN_FILTER}" --avatar-attribute 'jpegPhoto' --bind-dn "${GITEA_LDAP_BIND_DN_0}" --bind-password "${GITEA_LDAP_PASSWORD_0}" --email-attribute 'mail' --firstname-attribute 'givenname' --host "${LDAP_HOST}" --name 'Authentik' --port 389 --security-protocol 'unencrypted' --surname-attribute 'name' --user-filter "${LDAP_USER_FILTER}" --user-search-base "${LDAP_USER_SEARCH_BASE}" --username-attribute 'cn' + echo '...installed.' + else + echo "Existing ldap configuration with name "${LDAP_NAME}": '${GITEA_AUTH_ID}'. Running update to sync settings..." + gitea admin auth update-ldap --id "${GITEA_AUTH_ID}" --admin-filter "${LDAP_ADMIN_FILTER}" --avatar-attribute 'jpegPhoto' --bind-dn "${GITEA_LDAP_BIND_DN_0}" --bind-password "${GITEA_LDAP_PASSWORD_0}" --email-attribute 'mail' --firstname-attribute 'givenname' --host "${LDAP_HOST}" --name 'Authentik' --port 389 --security-protocol 'unencrypted' --surname-attribute 'name' --user-filter "${LDAP_USER_FILTER}" --user-search-base "${LDAP_USER_SEARCH_BASE}" --username-attribute 'cn' + echo '...sync settings done.' + fi + } + + configure_ldap + + function configure_oauth() { + echo 'no oauth configuration... skipping.' + } + + configure_oauth + + echo '==== END GITEA CONFIGURATION ====' \ No newline at end of file diff --git a/apps/gitea/v1_Secret_gitea.yaml b/apps/gitea/v1_Secret_gitea.yaml new file mode 100644 index 0000000..c280f38 --- /dev/null +++ b/apps/gitea/v1_Secret_gitea.yaml @@ -0,0 +1,169 @@ +# Source: gitea/templates/gitea/config.yaml +apiVersion: v1 +kind: Secret +metadata: + name: gitea + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm +type: Opaque +stringData: + config_environment.sh: |- + #!/usr/bin/env bash + set -euo pipefail + + function env2ini::log() { + printf "${1}\n" + } + + function env2ini::read_config_to_env() { + local section="${1}" + local line="${2}" + + if [[ -z "${line}" ]]; then + # skip empty line + return + fi + + # 'xargs echo -n' trims all leading/trailing whitespaces and a trailing new line + local setting="$(awk -F '=' '{print $1}' <<< "${line}" | xargs echo -n)" + + if [[ -z "${setting}" ]]; then + env2ini::log ' ! invalid setting' + exit 1 + fi + + local value='' + local regex="^${setting}(\s*)=(\s*)(.*)" + if [[ $line =~ $regex ]]; then + value="${BASH_REMATCH[3]}" + else + env2ini::log ' ! invalid setting' + exit 1 + fi + + env2ini::log " + '${setting}'" + + if [[ -z "${section}" ]]; then + export "ENV_TO_INI____${setting^^}=${value}" # '^^' makes the variable content uppercase + return + fi + + local masked_section="${section//./_0X2E_}" # '//' instructs to replace all matches + masked_section="${masked_section//-/_0X2D_}" + + export "ENV_TO_INI__${masked_section^^}__${setting^^}=${value}" # '^^' makes the variable content uppercase + } + + function env2ini::reload_preset_envs() { + env2ini::log "Reloading preset envs..." + + while read -r line; do + if [[ -z "${line}" ]]; then + # skip empty line + return + fi + + # 'xargs echo -n' trims all leading/trailing whitespaces and a trailing new line + local setting="$(awk -F '=' '{print $1}' <<< "${line}" | xargs echo -n)" + + if [[ -z "${setting}" ]]; then + env2ini::log ' ! invalid setting' + exit 1 + fi + + local value='' + local regex="^${setting}(\s*)=(\s*)(.*)" + if [[ $line =~ $regex ]]; then + value="${BASH_REMATCH[3]}" + else + env2ini::log ' ! invalid setting' + exit 1 + fi + + env2ini::log " + '${setting}'" + + export "${setting^^}=${value}" # '^^' makes the variable content uppercase + done < "/tmp/existing-envs" + + rm /tmp/existing-envs + } + + + function env2ini::process_config_file() { + local config_file="${1}" + local section="$(basename "${config_file}")" + + if [[ $section == '_generals_' ]]; then + env2ini::log " [ini root]" + section='' + else + env2ini::log " ${section}" + fi + + while read -r line; do + env2ini::read_config_to_env "${section}" "${line}" + done < <(awk 1 "${config_file}") # Helm .toYaml trims the trailing new line which breaks line processing; awk 1 ... adds it back while reading + } + + function env2ini::load_config_sources() { + local path="${1}" + + if [[ -d "${path}" ]]; then + env2ini::log "Processing $(basename "${path}")..." + + while read -d '' configFile; do + env2ini::process_config_file "${configFile}" + done < <(find "${path}" -type l -not -name '..data' -print0) + + env2ini::log "\n" + fi + } + + function env2ini::generate_initial_secrets() { + # These environment variables will either be + # - overwritten with user defined values, + # - initially used to set up Gitea + # Anyway, they won't harm existing app.ini files + + export ENV_TO_INI__SECURITY__INTERNAL_TOKEN=$(gitea generate secret INTERNAL_TOKEN) + export ENV_TO_INI__SECURITY__SECRET_KEY=$(gitea generate secret SECRET_KEY) + export ENV_TO_INI__OAUTH2__JWT_SECRET=$(gitea generate secret JWT_SECRET) + export ENV_TO_INI__SERVER__LFS_JWT_SECRET=$(gitea generate secret LFS_JWT_SECRET) + + env2ini::log "...Initial secrets generated\n" + } + + env | (grep ENV_TO_INI || [[ $? == 1 ]]) > /tmp/existing-envs + + # MUST BE CALLED BEFORE OTHER CONFIGURATION + env2ini::generate_initial_secrets + + env2ini::load_config_sources '/env-to-ini-mounts/inlines/' + env2ini::load_config_sources '/env-to-ini-mounts/additionals/' + + # load existing envs to override auto generated envs + env2ini::reload_preset_envs + + env2ini::log "=== All configuration sources loaded ===\n" + + # safety to prevent rewrite of secret keys if an app.ini already exists + if [ -f ${GITEA_APP_INI} ]; then + env2ini::log 'An app.ini file already exists. To prevent overwriting secret keys, these settings are dropped and remain unchanged:' + env2ini::log ' - security.INTERNAL_TOKEN' + env2ini::log ' - security.SECRET_KEY' + env2ini::log ' - oauth2.JWT_SECRET' + env2ini::log ' - server.LFS_JWT_SECRET' + + unset ENV_TO_INI__SECURITY__INTERNAL_TOKEN + unset ENV_TO_INI__SECURITY__SECRET_KEY + unset ENV_TO_INI__OAUTH2__JWT_SECRET + unset ENV_TO_INI__SERVER__LFS_JWT_SECRET + fi + + environment-to-ini -o $GITEA_APP_INI -p ENV_TO_INI \ No newline at end of file diff --git a/apps/gitea/v1_Service_gitea-http.yaml b/apps/gitea/v1_Service_gitea-http.yaml new file mode 100644 index 0000000..7992d59 --- /dev/null +++ b/apps/gitea/v1_Service_gitea-http.yaml @@ -0,0 +1,25 @@ +# Source: gitea/templates/gitea/http-svc.yaml +apiVersion: v1 +kind: Service +metadata: + name: gitea-http + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm + annotations: + {} +spec: + type: ClusterIP + clusterIP: None + ports: + - name: http + port: 3000 + targetPort: 3000 + selector: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea \ No newline at end of file diff --git a/apps/gitea/v1_Service_gitea-memcached.yaml b/apps/gitea/v1_Service_gitea-memcached.yaml new file mode 100644 index 0000000..8b7bcd2 --- /dev/null +++ b/apps/gitea/v1_Service_gitea-memcached.yaml @@ -0,0 +1,23 @@ +# Source: gitea/charts/memcached/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: gitea-memcached + namespace: vynil-ci + labels: + app.kubernetes.io/name: memcached + helm.sh/chart: memcached-6.3.14 + app.kubernetes.io/instance: gitea + app.kubernetes.io/managed-by: Helm + annotations: +spec: + type: ClusterIP + sessionAffinity: None + ports: + - name: memcache + port: 11211 + targetPort: memcache + nodePort: null + selector: + app.kubernetes.io/name: memcached + app.kubernetes.io/instance: gitea \ No newline at end of file diff --git a/apps/gitea/v1_Service_gitea-ssh.yaml b/apps/gitea/v1_Service_gitea-ssh.yaml new file mode 100644 index 0000000..30b5f5d --- /dev/null +++ b/apps/gitea/v1_Service_gitea-ssh.yaml @@ -0,0 +1,28 @@ +# Source: gitea/templates/gitea/ssh-svc.yaml +apiVersion: v1 +kind: Service +metadata: + name: gitea-ssh + labels: + helm.sh/chart: gitea-8.3.0 + app: gitea + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + app.kubernetes.io/version: "1.19.3" + version: "1.19.3" + app.kubernetes.io/managed-by: Helm + annotations: + metallb.universe.tf/address-pool: mlb-pool-public + metallb.universe.tf/allow-shared-ip: traefik-public-ip +spec: + type: LoadBalancer + loadBalancerIP: 1.2.3.4 + ipFamilyPolicy: PreferDualStack + ports: + - name: ssh + port: 2222 + targetPort: 2222 + protocol: TCP + selector: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea \ No newline at end of file diff --git a/apps/k8s-ui/datas.tf b/apps/k8s-ui/datas.tf new file mode 100644 index 0000000..ac5f6fe --- /dev/null +++ b/apps/k8s-ui/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/apps/k8s-ui/index.yaml b/apps/k8s-ui/index.yaml new file mode 100644 index 0000000..43d2fab --- /dev/null +++ b/apps/k8s-ui/index.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: k8s-ui + description: Access to the kubernetes api +options: + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + sub-domain: + default: api + examples: + - api + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + domain: + default: your-company + examples: + - your-company + type: string +dependencies: +- dist: null + category: share + component: authentik-forward +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true diff --git a/apps/k8s-ui/ingress.tf b/apps/k8s-ui/ingress.tf new file mode 100644 index 0000000..87bde10 --- /dev/null +++ b/apps/k8s-ui/ingress.tf @@ -0,0 +1,51 @@ +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = [] + services = [{ + "kind" = "Service" + "name" = "kubernetes" + "namespace" = "default" + "port" = 443 + }] + routes = [ for v in local.dns-names : { + "kind" = "Rule" + "match" = "Host(`${v}`)" + "middlewares" = local.middlewares + "services" = local.services + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "IngressRoute" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + entryPoints: ["websecure"] + routes: ${jsonencode(local.routes)} + tls: + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/nextcloud/apps_v1_Deployment_nextcloud-metrics.yaml b/apps/nextcloud/apps_v1_Deployment_nextcloud-metrics.yaml new file mode 100644 index 0000000..976edf4 --- /dev/null +++ b/apps/nextcloud/apps_v1_Deployment_nextcloud-metrics.yaml @@ -0,0 +1,54 @@ +# Source: nextcloud/templates/metrics-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nextcloud-metrics + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: metrics +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: metrics + template: + metadata: + annotations: + null + labels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: metrics + spec: + containers: + - name: metrics-exporter + image: "xperimental/nextcloud-exporter:0.6.1" + imagePullPolicy: IfNotPresent + env: + - name: NEXTCLOUD_USERNAME + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-username + - name: NEXTCLOUD_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-password + - name: NEXTCLOUD_SERVER + value: http://nextcloud.kube.home + - name: NEXTCLOUD_TIMEOUT + value: 5s + - name: NEXTCLOUD_TLS_SKIP_VERIFY + value: "false" + ports: + - name: metrics + containerPort: 9205 + securityContext: + runAsUser: 1000 + runAsNonRoot: true \ No newline at end of file diff --git a/apps/nextcloud/apps_v1_Deployment_nextcloud.yaml b/apps/nextcloud/apps_v1_Deployment_nextcloud.yaml new file mode 100644 index 0000000..10168ff --- /dev/null +++ b/apps/nextcloud/apps_v1_Deployment_nextcloud.yaml @@ -0,0 +1,229 @@ +# Source: nextcloud/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nextcloud + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: app +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: app + template: + metadata: + labels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: app + nextcloud-redis-client: "true" + annotations: + nextcloud-config-hash: a5aae02b1b8278a9c8a2dc143e82d3737fc295f62c34afd617207f37d1b2b438 + php-config-hash: 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a + nginx-config-hash: 18dd8f905a93ed27f032e9ae68084222ed7e5926f7144cda17b979780f4da54b + spec: + containers: + - name: nextcloud + image: nextcloud:27.0.0-apache + imagePullPolicy: IfNotPresent + env: + - name: POSTGRES_HOST + value: + - name: POSTGRES_DB + value: "nextcloud" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: nextcloud-admin + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud-admin + key: password + - name: NEXTCLOUD_ADMIN_USER + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-username + - name: NEXTCLOUD_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-password + - name: NEXTCLOUD_TRUSTED_DOMAINS + value: nextcloud.kube.home + - name: NEXTCLOUD_DATA_DIR + value: "/var/www/html/data" + - name: REDIS_HOST + value: nextcloud-redis-master + - name: REDIS_HOST_PORT + value: "6379" + - name: REDIS_HOST_PASSWORD + value: changeme + resources: + {} + volumeMounts: + - name: nextcloud-main + mountPath: /var/www/ + subPath: root + - name: nextcloud-main + mountPath: /var/www/html + subPath: html + - name: nextcloud-main + mountPath: /var/www/html/data + subPath: data + - name: nextcloud-main + mountPath: /var/www/html/config + subPath: config + - name: nextcloud-main + mountPath: /var/www/html/custom_apps + subPath: custom_apps + - name: nextcloud-main + mountPath: /var/www/tmp + subPath: tmp + - name: nextcloud-main + mountPath: /var/www/html/themes + subPath: themes + - name: nextcloud-cron + image: nextcloud:27.0.0-apache + imagePullPolicy: IfNotPresent + command: + - /cron.sh + env: + - name: POSTGRES_HOST + value: + - name: POSTGRES_DB + value: "nextcloud" + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: nextcloud-admin + key: username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud-admin + key: password + - name: NEXTCLOUD_ADMIN_USER + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-username + - name: NEXTCLOUD_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud + key: nextcloud-password + - name: NEXTCLOUD_TRUSTED_DOMAINS + value: nextcloud.kube.home + - name: NEXTCLOUD_DATA_DIR + value: "/var/www/html/data" + - name: REDIS_HOST + value: nextcloud-redis-master + - name: REDIS_HOST_PORT + value: "6379" + - name: REDIS_HOST_PASSWORD + value: changeme + resources: + {} + volumeMounts: + - name: nextcloud-main + mountPath: /var/www/ + subPath: root + - name: nextcloud-main + mountPath: /var/www/html + subPath: html + - name: nextcloud-main + mountPath: /var/www/html/data + subPath: data + - name: nextcloud-main + mountPath: /var/www/html/config + subPath: config + - name: nextcloud-main + mountPath: /var/www/html/custom_apps + subPath: custom_apps + - name: nextcloud-main + mountPath: /var/www/tmp + subPath: tmp + - name: nextcloud-main + mountPath: /var/www/html/themes + subPath: themes + - name: nextcloud-nginx + image: "nginx:alpine" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: /status.php + port: http + httpHeaders: + - name: Host + value: "nextcloud.kube.home" + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /status.php + port: 80 + httpHeaders: + - name: Host + value: "nextcloud.kube.home" + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + + resources: + {} + volumeMounts: + - name: nextcloud-main + mountPath: /var/www/ + subPath: root + - name: nextcloud-main + mountPath: /var/www/html + subPath: html + - name: nextcloud-main + mountPath: /var/www/html/data + subPath: data + - name: nextcloud-main + mountPath: /var/www/html/config + subPath: config + - name: nextcloud-main + mountPath: /var/www/html/custom_apps + subPath: custom_apps + - name: nextcloud-main + mountPath: /var/www/tmp + subPath: tmp + - name: nextcloud-main + mountPath: /var/www/html/themes + subPath: themes + - name: nextcloud-nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + volumes: + - name: nextcloud-main + persistentVolumeClaim: + claimName: nextcloud-nextcloud + - name: nextcloud-nginx-config + configMap: + name: nextcloud-nginxconfig + securityContext: + # Will mount configuration files as www-data (id: 82) for nextcloud + fsGroup: 82 + serviceAccountName: nextcloud-serviceaccount \ No newline at end of file diff --git a/apps/nextcloud/autoscaling_v1_HorizontalPodAutoscaler_nextcloud.yaml b/apps/nextcloud/autoscaling_v1_HorizontalPodAutoscaler_nextcloud.yaml new file mode 100644 index 0000000..f0a258e --- /dev/null +++ b/apps/nextcloud/autoscaling_v1_HorizontalPodAutoscaler_nextcloud.yaml @@ -0,0 +1,19 @@ +# Source: nextcloud/templates/hpa.yaml +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: nextcloud + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: app +spec: + scaleTargetRef: + kind: Deployment + apiVersion: apps/v1 + name: nextcloud + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 60 \ No newline at end of file diff --git a/apps/nextcloud/index.yaml b/apps/nextcloud/index.yaml new file mode 100644 index 0000000..a0ded02 --- /dev/null +++ b/apps/nextcloud/index.yaml @@ -0,0 +1,80 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: nextcloud + description: null +options: + ingress-class: + default: traefik + examples: + - traefik + type: string + images: + default: + nextcloud: + pullPolicy: IfNotPresent + registry: docker.io + repository: nextcloud + tag: 27.0.0-fpm + examples: + - nextcloud: + pullPolicy: IfNotPresent + registry: docker.io + repository: nextcloud + tag: 27.0.0-fpm + properties: + nextcloud: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: nextcloud + tag: 27.0.0-fpm + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: nextcloud + type: string + tag: + default: 27.0.0-fpm + type: string + type: object + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + domain: + default: your-company + examples: + - your-company + type: string + sub-domain: + default: cloud + examples: + - cloud + type: string +dependencies: [] +providers: + kubernetes: true + authentik: true + kubectl: null + postgresql: null + restapi: null + http: null diff --git a/apps/nextcloud/monitoring.coreos.com_v1_ServiceMonitor_nextcloud.yaml b/apps/nextcloud/monitoring.coreos.com_v1_ServiceMonitor_nextcloud.yaml new file mode 100644 index 0000000..4565c7e --- /dev/null +++ b/apps/nextcloud/monitoring.coreos.com_v1_ServiceMonitor_nextcloud.yaml @@ -0,0 +1,26 @@ +# Source: nextcloud/templates/metrics-servicemonitor.yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: nextcloud + namespace: "vynil-cloud" + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: metrics +spec: + jobLabel: "" + selector: + matchLabels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: metrics + namespaceSelector: + matchNames: + - "vynil-cloud" + endpoints: + - port: metrics + path: "/" + interval: 30s \ No newline at end of file diff --git a/apps/nextcloud/rbac.authorization.k8s.io_v1_RoleBinding_nextcloud-privileged.yaml b/apps/nextcloud/rbac.authorization.k8s.io_v1_RoleBinding_nextcloud-privileged.yaml new file mode 100644 index 0000000..9f8caf7 --- /dev/null +++ b/apps/nextcloud/rbac.authorization.k8s.io_v1_RoleBinding_nextcloud-privileged.yaml @@ -0,0 +1,14 @@ +# Source: nextcloud/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: nextcloud-privileged + namespace: vynil-cloud +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: nextcloud-privileged +subjects: +- kind: ServiceAccount + name: nextcloud-serviceaccount + namespace: vynil-cloud \ No newline at end of file diff --git a/apps/nextcloud/rbac.authorization.k8s.io_v1_Role_nextcloud-privileged.yaml b/apps/nextcloud/rbac.authorization.k8s.io_v1_Role_nextcloud-privileged.yaml new file mode 100644 index 0000000..8f21b70 --- /dev/null +++ b/apps/nextcloud/rbac.authorization.k8s.io_v1_Role_nextcloud-privileged.yaml @@ -0,0 +1,15 @@ +# Source: nextcloud/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: nextcloud-privileged + namespace: vynil-cloud +rules: +- apiGroups: + - extensions + resourceNames: + - privileged + resources: + - podsecuritypolicies + verbs: + - use \ No newline at end of file diff --git a/apps/nextcloud/v1_ConfigMap_nextcloud-nginxconfig.yaml b/apps/nextcloud/v1_ConfigMap_nextcloud-nginxconfig.yaml new file mode 100644 index 0000000..fd41a4f --- /dev/null +++ b/apps/nextcloud/v1_ConfigMap_nextcloud-nginxconfig.yaml @@ -0,0 +1,173 @@ +# Source: nextcloud/templates/nginx-config.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: nextcloud-nginxconfig + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm +data: + nginx.conf: |- + worker_processes auto; + + error_log /var/log/nginx/error.log warn; + pid /var/run/nginx.pid; + + + events { + worker_connections 1024; + } + + + http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + upstream php-handler { + server 127.0.0.1:9000; + } + + server { + listen 80; + + # HSTS settings + # WARNING: Only add the preload option once you read about + # the consequences in https://hstspreload.org/. This option + # will add the domain to a hardcoded list that is shipped + # in all major browsers and getting removed from this list + # could take several months. + #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always; + + # set max upload size + client_max_body_size 10G; + fastcgi_buffers 64 4K; + + # Enable gzip but do not remove ETag headers + gzip on; + gzip_vary on; + gzip_comp_level 4; + gzip_min_length 256; + gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; + gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; + + # Pagespeed is not supported by Nextcloud, so if your server is built + # with the `ngx_pagespeed` module, uncomment this line to disable it. + #pagespeed off; + + # HTTP response headers borrowed from Nextcloud `.htaccess` + add_header Referrer-Policy "no-referrer" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Download-Options "noopen" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Remove X-Powered-By, which is an information leak + fastcgi_hide_header X-Powered-By; + + # Path to the root of your installation + root /var/www/html; + + # Specify how to handle directories -- specifying `/index.php$request_uri` + # here as the fallback means that Nginx always exhibits the desired behaviour + # when a client requests a path that corresponds to a directory that exists + # on the server. In particular, if that directory contains an index.php file, + # that file is correctly served; if it doesn't, then the request is passed to + # the front-end controller. This consistent behaviour means that we don't need + # to specify custom rules for certain paths (e.g. images and other assets, + # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus + # `try_files $uri $uri/ /index.php$request_uri` + # always provides the desired behaviour. + index index.php index.html /index.php$request_uri; + + # Rule borrowed from `.htaccess` to handle Microsoft DAV clients + location = / { + if ( $http_user_agent ~ ^DavClnt ) { + return 302 /remote.php/webdav/$is_args$args; + } + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Make a regex exception for `/.well-known` so that clients can still + # access it despite the existence of the regex rule + # `location ~ /(\.|autotest|...)` which would otherwise handle requests + # for `/.well-known`. + location ^~ /.well-known { + # The following 6 rules are borrowed from `.htaccess` + + location = /.well-known/carddav { return 301 /remote.php/dav/; } + location = /.well-known/caldav { return 301 /remote.php/dav/; } + # Anything else is dynamically handled by Nextcloud + location ^~ /.well-known { return 301 /index.php$uri; } + + try_files $uri $uri/ =404; + } + + # Rules borrowed from `.htaccess` to hide certain paths from clients + location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; } + location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; } + + # Ensure this block, which passes PHP files to the PHP process, is above the blocks + # which handle static assets (as seen below). If this block is not declared first, + # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php` + # to the URI, resulting in a HTTP 500 error response. + location ~ \.php(?:$|/) { + # Required for legacy support + rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri; + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + set $path_info $fastcgi_path_info; + + try_files $fastcgi_script_name =404; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $path_info; + #fastcgi_param HTTPS on; + + fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice + fastcgi_param front_controller_active true; # Enable pretty urls + fastcgi_pass php-handler; + + fastcgi_intercept_errors on; + fastcgi_request_buffering off; + } + + location ~ \.(?:css|js|svg|gif)$ { + try_files $uri /index.php$request_uri; + expires 6M; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + } + + location ~ \.woff2?$ { + try_files $uri /index.php$request_uri; + expires 7d; # Cache-Control policy borrowed from `.htaccess` + access_log off; # Optional: Don't log access to assets + } + + location / { + try_files $uri $uri/ /index.php$request_uri; + } + } + } \ No newline at end of file diff --git a/apps/nextcloud/v1_PersistentVolumeClaim_nextcloud-nextcloud.yaml b/apps/nextcloud/v1_PersistentVolumeClaim_nextcloud-nextcloud.yaml new file mode 100644 index 0000000..6fb5419 --- /dev/null +++ b/apps/nextcloud/v1_PersistentVolumeClaim_nextcloud-nextcloud.yaml @@ -0,0 +1,17 @@ +# Source: nextcloud/templates/nextcloud-pvc.yaml +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: nextcloud-nextcloud + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: app +spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: "8Gi" \ No newline at end of file diff --git a/apps/nextcloud/v1_Secret_nextcloud.yaml b/apps/nextcloud/v1_Secret_nextcloud.yaml new file mode 100644 index 0000000..ef59242 --- /dev/null +++ b/apps/nextcloud/v1_Secret_nextcloud.yaml @@ -0,0 +1,15 @@ +# Source: nextcloud/templates/secrets.yaml +apiVersion: v1 +kind: Secret +metadata: + name: nextcloud + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm +type: Opaque +data: + nextcloud-username: "YWRtaW4=" + nextcloud-password: "Y2hhbmdlbWU=" + nextcloud-token: "WXVzZmluOWo4Ug==" \ No newline at end of file diff --git a/apps/nextcloud/v1_ServiceAccount_nextcloud-serviceaccount.yaml b/apps/nextcloud/v1_ServiceAccount_nextcloud-serviceaccount.yaml new file mode 100644 index 0000000..c1bbce5 --- /dev/null +++ b/apps/nextcloud/v1_ServiceAccount_nextcloud-serviceaccount.yaml @@ -0,0 +1,5 @@ +# Source: nextcloud/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: nextcloud-serviceaccount \ No newline at end of file diff --git a/apps/nextcloud/v1_Service_nextcloud-metrics.yaml b/apps/nextcloud/v1_Service_nextcloud-metrics.yaml new file mode 100644 index 0000000..3ecaa7d --- /dev/null +++ b/apps/nextcloud/v1_Service_nextcloud-metrics.yaml @@ -0,0 +1,24 @@ +# Source: nextcloud/templates/metrics-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: nextcloud-metrics + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: metrics + annotations: + prometheus.io/port: "9205" + prometheus.io/scrape: "true" +spec: + type: ClusterIP + ports: + - name: metrics + port: 9205 + targetPort: metrics + selector: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: metrics \ No newline at end of file diff --git a/apps/nextcloud/v1_Service_nextcloud.yaml b/apps/nextcloud/v1_Service_nextcloud.yaml new file mode 100644 index 0000000..460871c --- /dev/null +++ b/apps/nextcloud/v1_Service_nextcloud.yaml @@ -0,0 +1,22 @@ +# Source: nextcloud/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: nextcloud + labels: + app.kubernetes.io/name: nextcloud + helm.sh/chart: nextcloud-3.5.19 + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: app +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: app \ No newline at end of file diff --git a/apps/traefik-ui/datas.tf b/apps/traefik-ui/datas.tf new file mode 100644 index 0000000..ac5f6fe --- /dev/null +++ b/apps/traefik-ui/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = "${var.domain}-auth" + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/apps/traefik-ui/forward.tf b/apps/traefik-ui/forward.tf new file mode 100644 index 0000000..7f7fa22 --- /dev/null +++ b/apps/traefik-ui/forward.tf @@ -0,0 +1,121 @@ +locals { + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + forward-outpost-providers = jsondecode(data.http.get_forward_outpost.response_body).results[0].providers + forward-outpost-pk = jsondecode(data.http.get_forward_outpost.response_body).results[0].pk + app-name = var.component == var.instance ? var.instance : format("%s-%s", var.component, var.instance) + app-icon = "dashboard/statics/icons/favicon-96x96.png" + main-group = format("%s-users", local.app-name) + sub-groups = [] + access-token-validity = "minutes=10" +} + + +data "authentik_flow" "default-authorization-flow" { + depends_on = [authentik_group.prj_users] + slug = "default-provider-authorization-implicit-consent" +} + +resource "authentik_provider_proxy" "prj_forward" { + name = local.app-name + external_host = format("https://%s.%s", var.sub-domain, var.domain-name) + authorization_flow = data.authentik_flow.default-authorization-flow.id + mode = "forward_single" + access_token_validity = local.access-token-validity +} + + +resource "authentik_application" "prj_application" { + name = local.app-name + slug = local.app-name + protocol_provider = authentik_provider_proxy.prj_forward.id + meta_launch_url = format("https://%s.%s", var.sub-domain, var.domain-name) + meta_icon = format("https://%s.%s/%s", var.sub-domain, var.domain-name, local.app-icon) +} + +resource "authentik_group" "prj_users" { + name = local.main-group +} + +resource "authentik_group" "subgroup" { + count = length(local.sub-groups) + name = format("%s-%s", local.app-name, local.sub-groups[count.index]) + parent = authentik_group.prj_users.id +} + +data "authentik_group" "vynil-admin" { + depends_on = [authentik_group.prj_users] # fake dependency so it is not evaluated at plan stage + name = "vynil-forward-admins" +} + +resource "authentik_policy_binding" "prj_access_users" { + target = authentik_application.prj_application.uuid + group = authentik_group.prj_users.id + order = 0 +} +resource "authentik_policy_binding" "prj_access_vynil" { + target = authentik_application.prj_application.uuid + group = data.authentik_group.vynil-admin.id + order = 1 +} + +data "http" "get_forward_outpost" { + depends_on = [authentik_provider_proxy.prj_forward] + url = "http://authentik.${var.domain}-auth.svc/api/v3/outposts/instances/?name__iexact=forward" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +provider "restapi" { + uri = "http://authentik.${var.domain}-auth.svc/api/v3/" + headers = local.request_headers + create_method = "PATCH" + update_method = "PATCH" + destroy_method = "PATCH" + write_returns_object = true + id_attribute = "name" +} + +resource "restapi_object" "forward_outpost_binding" { + path = "/outposts/instances/${local.forward-outpost-pk}/" + data = jsonencode({ + name = "forward" + providers = contains(local.forward-outpost-providers, authentik_provider_proxy.prj_forward.id) ? local.forward-outpost-providers : concat(local.forward-outpost-providers, [authentik_provider_proxy.prj_forward.id]) + }) +} + +resource "kubectl_manifest" "prj_middleware" { + yaml_body = <<-EOF + apiVersion: traefik.containo.us/v1alpha1 + kind: Middleware + metadata: + name: "forward-${local.app-name}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forwardAuth: + address: http://ak-outpost-forward.${var.domain}-auth.svc:9000/outpost.goauthentik.io/auth/traefik + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + # - X-authentik-groups + # - X-authentik-email + # - X-authentik-name + # - X-authentik-uid + # - X-authentik-jwt + # - X-authentik-meta-jwks + # - X-authentik-meta-outpost + # - X-authentik-meta-provider + # - X-authentik-meta-app + # - X-authentik-meta-version + EOF +} diff --git a/apps/traefik-ui/index.yaml b/apps/traefik-ui/index.yaml new file mode 100644 index 0000000..48577f8 --- /dev/null +++ b/apps/traefik-ui/index.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: apps +metadata: + name: traefik-ui + description: Access to the Traefik UI +options: + sub-domain: + default: traefik + examples: + - traefik + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + domain: + default: your-company + examples: + - your-company + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string +dependencies: +- dist: null + category: share + component: authentik-forward +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: true + http: true diff --git a/apps/traefik-ui/ingress.tf b/apps/traefik-ui/ingress.tf new file mode 100644 index 0000000..383aa97 --- /dev/null +++ b/apps/traefik-ui/ingress.tf @@ -0,0 +1,75 @@ +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = ["${var.instance}-https", "forward-${local.app-name}"] + service = { + "name" = "${var.component}-${var.instance}" + "port" = { + "number" = 80 + } + } + rules = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + annotations: + "traefik.ingress.kubernetes.io/router.middlewares": "${join(",", [for m in local.middlewares : format("%s-%s@kubernetescrd", var.namespace, m)])}" + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} diff --git a/apps/traefik-ui/svc.tf b/apps/traefik-ui/svc.tf new file mode 100644 index 0000000..ee76f69 --- /dev/null +++ b/apps/traefik-ui/svc.tf @@ -0,0 +1,21 @@ +resource "kubectl_manifest" "service" { + yaml_body = <<-EOF + apiVersion: v1 + kind: Service + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 9000 + selector: + vynil.solidite.fr/owner-namespace: ${var.namespace} + vynil.solidite.fr/owner-component: traefik + vynil.solidite.fr/owner-category: share + EOF +} diff --git a/meta/domain-auth/apps.tf b/meta/domain-auth/apps.tf new file mode 100644 index 0000000..328915d --- /dev/null +++ b/meta/domain-auth/apps.tf @@ -0,0 +1,81 @@ +locals { + annotations = { + "vynil.solidite.fr/meta" = "domain-auth" + "vynil.solidite.fr/name" = "${var.namespace}-auth" + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/ingress" = var.ingress-class + } + global = { + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer + "ingress-class" = var.ingress-class + } + authentik = { for k, v in var.authentik : k => v if k!="enable" } + authentik-ldap = { for k, v in var.authentik-ldap : k => v if k!="enable" } + authentik-forward = { for k, v in var.authentik-forward : k => v if k!="enable" } +} + +resource "kubernetes_namespace_v1" "auth-ns" { + count = var.authentik.enable || var.authentik-ldap.enable || var.authentik-forward.enable ? 1 : 0 + metadata { + annotations = local.annotations + labels = merge(local.common-labels, local.annotations) + name = "${var.namespace}-auth" + } +} + +resource "kubectl_manifest" "authentik" { + count = var.authentik.enable || var.authentik-ldap.enable || var.authentik-forward.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.auth-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "authentik" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "share" + component: "authentik" + options: ${jsonencode(merge(local.global, local.authentik))} + EOF +} + +resource "kubectl_manifest" "authentik-ldap" { + count = var.authentik-ldap.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.auth-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "authentik-ldap" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "share" + component: "authentik-ldap" + options: ${jsonencode(merge(local.global, local.authentik-ldap))} + EOF +} + +resource "kubectl_manifest" "authentik-forward" { + count = var.authentik-forward.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.auth-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "authentik-forward" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "share" + component: "authentik-forward" + options: ${jsonencode(merge(local.global, local.authentik-forward))} + EOF +} diff --git a/meta/domain-auth/index.yaml b/meta/domain-auth/index.yaml new file mode 100644 index 0000000..03640d8 --- /dev/null +++ b/meta/domain-auth/index.yaml @@ -0,0 +1,66 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: meta +metadata: + name: domain-auth + description: null +options: + authentik-ldap: + default: + enable: false + examples: + - enable: false + properties: + enable: + default: false + type: boolean + type: object + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + authentik: + default: + enable: true + examples: + - enable: true + properties: + enable: + default: true + type: boolean + type: object + authentik-forward: + default: + enable: false + examples: + - enable: false + properties: + enable: + default: false + type: boolean + type: object + domain: + default: your-company + examples: + - your-company + type: string +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/meta/domain-ci/apps.tf b/meta/domain-ci/apps.tf new file mode 100644 index 0000000..ff500a5 --- /dev/null +++ b/meta/domain-ci/apps.tf @@ -0,0 +1,43 @@ +locals { + annotations = { + "vynil.solidite.fr/meta" = "domain-ci" + "vynil.solidite.fr/name" = var.namespace + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/ingress" = var.ingress-class + } + global = { + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer + "ingress-class" = var.ingress-class + } + gitea = { for k, v in var.gitea : k => v if k!="enable" } +} + +resource "kubernetes_namespace_v1" "ci-ns" { + count = ( var.gitea.enable )? 1 : 0 + metadata { + annotations = local.annotations + labels = merge(local.common-labels, local.annotations) + name = "${var.namespace}-ci" + } +} + +resource "kubectl_manifest" "gitea" { + count = var.gitea.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.ci-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "gitea" + namespace: "${var.namespace}-ci" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "apps" + component: "gitea" + options: ${jsonencode(merge(local.global, local.gitea))} + EOF +} diff --git a/meta/domain-ci/index.yaml b/meta/domain-ci/index.yaml new file mode 100644 index 0000000..4042c81 --- /dev/null +++ b/meta/domain-ci/index.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: meta +metadata: + name: domain-ci + description: null +options: + gitea: + default: + enable: true + examples: + - enable: true + properties: + enable: + default: true + type: boolean + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + domain: + default: your-company + examples: + - your-company + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/meta/domain-erp/apps.tf b/meta/domain-erp/apps.tf new file mode 100644 index 0000000..e8f2e4e --- /dev/null +++ b/meta/domain-erp/apps.tf @@ -0,0 +1,43 @@ +locals { + annotations = { + "vynil.solidite.fr/meta" = "domain-ci" + "vynil.solidite.fr/name" = var.namespace + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/ingress" = var.ingress-class + } + global = { + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer + "ingress-class" = var.ingress-class + } + dolibarr = { for k, v in var.dolibarr : k => v if k!="enable" } +} + +resource "kubernetes_namespace_v1" "erp-ns" { + count = ( var.dolibarr.enable )? 1 : 0 + metadata { + annotations = local.annotations + labels = merge(local.common-labels, local.annotations) + name = "${var.namespace}-erp" + } +} + +resource "kubectl_manifest" "dolibarr" { + count = var.dolibarr.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.erp-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "dolibarr" + namespace: "${kubernetes_namespace_v1.erp-ns[0].metadata[0].name}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "apps" + component: "dolibarr" + options: ${jsonencode(merge(local.global, local.dolibarr))} + EOF +} diff --git a/meta/domain-erp/index.yaml b/meta/domain-erp/index.yaml new file mode 100644 index 0000000..dc0d882 --- /dev/null +++ b/meta/domain-erp/index.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: meta +metadata: + name: domain-erp + description: null +options: + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + dolibarr: + default: + enable: true + examples: + - enable: true + properties: + enable: + default: true + type: boolean + type: object + ingress-class: + default: traefik + examples: + - traefik + type: string + domain: + default: your-company + examples: + - your-company + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/meta/domain-infra/apps.tf b/meta/domain-infra/apps.tf new file mode 100644 index 0000000..9471a4e --- /dev/null +++ b/meta/domain-infra/apps.tf @@ -0,0 +1,80 @@ +locals { + annotations = { + "vynil.solidite.fr/meta" = "domain-ci" + "vynil.solidite.fr/name" = var.namespace + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/ingress" = var.ingress-class + } + global = { + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer + "ingress-class" = var.ingress-class + } + traefik = { for k, v in var.traefik : k => v if k!="enable" } + dns = { for k, v in var.dns : k => v if k!="enable" } + api = { for k, v in var.api : k => v if k!="enable" } +} + +resource "kubernetes_namespace_v1" "infra-ns" { + count = ( var.dns.enable )? 1 : 0 + metadata { + annotations = local.annotations + labels = merge(local.common-labels, local.annotations) + name = "${var.namespace}-infra" + } +} + +resource "kubectl_manifest" "dns" { + count = var.dns.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.infra-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "dns" + namespace: "${kubernetes_namespace_v1.infra-ns.name}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "share" + component: "dns" + options: ${jsonencode(merge(local.global, local.dns))} + EOF +} + +resource "kubectl_manifest" "traefik" { + count = var.traefik.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.infra-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "traefik-ui-${var.namespace}" + namespace: "${var.traefik.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "apps" + component: "traefik-ui" + options: ${jsonencode(merge(local.global, local.traefik))} + EOF +} +resource "kubectl_manifest" "traefik" { + count = var.traefik.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.infra-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "k8s-api-${var.namespace}" + namespace: "default" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "apps" + component: "k8s-ui" + options: ${jsonencode(merge(local.global, local.api))} + EOF +} diff --git a/meta/domain-infra/index.yaml b/meta/domain-infra/index.yaml new file mode 100644 index 0000000..0822f83 --- /dev/null +++ b/meta/domain-infra/index.yaml @@ -0,0 +1,71 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: meta +metadata: + name: domain-infra + description: null +options: + ingress-class: + default: traefik + examples: + - traefik + type: string + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + traefik: + default: + enable: false + namespace: traefik + examples: + - enable: false + namespace: traefik + properties: + enable: + default: false + type: boolean + namespace: + default: traefik + type: string + type: object + domain: + default: your-company + examples: + - your-company + type: string + api: + default: + enable: false + examples: + - enable: false + properties: + enable: + default: false + type: boolean + type: object + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + dns: + default: + enable: false + examples: + - enable: false + properties: + enable: + default: false + type: boolean + type: object +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/meta/domain/index.yaml b/meta/domain/index.yaml new file mode 100644 index 0000000..1a2b2c9 --- /dev/null +++ b/meta/domain/index.yaml @@ -0,0 +1,107 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: meta +metadata: + name: domain + description: null +options: + auth: + default: + enable: true + examples: + - enable: true + properties: + enable: + default: true + type: boolean + type: object + ci: + default: + enable: false + gitea: + enable: true + examples: + - enable: false + gitea: + enable: true + properties: + enable: + default: false + type: boolean + gitea: + default: + enable: true + properties: + enable: + default: true + type: boolean + type: object + type: object + erp: + default: + dolibarr: + enable: true + enable: false + examples: + - dolibarr: + enable: true + enable: false + properties: + dolibarr: + default: + enable: true + properties: + enable: + default: true + type: boolean + type: object + enable: + default: false + type: boolean + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + infra: + default: + enable: false + traefik: + enable: false + examples: + - enable: false + traefik: + enable: false + properties: + enable: + default: false + type: boolean + traefik: + default: + enable: false + properties: + enable: + default: false + type: boolean + type: object + type: object + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string +dependencies: [] +providers: + kubernetes: null + authentik: null + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/meta/domain/installs.tf b/meta/domain/installs.tf new file mode 100644 index 0000000..4113eba --- /dev/null +++ b/meta/domain/installs.tf @@ -0,0 +1,98 @@ +locals { + global = { + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer + "ingress-class" = var.ingress-class + } + annotations = { + "vynil.solidite.fr/meta" = "domain" + "vynil.solidite.fr/name" = var.namespace + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/ingress" = var.ingress-class + } + auth = { for k, v in var.auth : k => v if k!="enable" } + infra = { for k, v in var.infra : k => v if k!="enable" } + ci = { for k, v in var.ci : k => v if k!="enable" } + erp = { for k, v in var.erp : k => v if k!="enable" } + + # Force install authentik and it's modules when any are needed + use-ldap = (var.ci.enable && var.ci.gitea.enable) || (var.erp.enable && var.erp.dolibarr.enable) + use-forward = var.infra.enable && var.infra.traefik.enable + use-other-auth = false + added-auth-ldap = local.use-ldap?{ + "authentik-ldap" = {"enable"= true} + }:{} + added-auth-forward = local.use-forward?{ + "authentik-forward" = {"enable"= true} + }:{} + added-auth = local.use-ldap||local.use-forward||local.use-other-auth?merge({ + "authentik" = {"enable" = true} + },local.added-auth-ldap,local.added-auth-forward):{} +} + +resource "kubectl_manifest" "auth" { + count = var.auth.enable ? 1 : 0 + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "auth" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "meta" + component: "domain-auth" + options: ${jsonencode(merge(merge(local.global, local.auth), local.added-auth))} + EOF +} +resource "kubectl_manifest" "infra" { + count = var.infra.enable ? 1 : 0 + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "infra" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "meta" + component: "domain-infra" + options: ${jsonencode(merge(local.global, local.infra))} + EOF +} +resource "kubectl_manifest" "ci" { + count = var.ci.enable ? 1 : 0 + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "ci" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "meta" + component: "domain-ci" + options: ${jsonencode(merge(local.global, local.ci))} + EOF +} +resource "kubectl_manifest" "erp" { + count = var.erp.enable ? 1 : 0 + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "erp" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "core" + category: "meta" + component: "domain-erp" + options: ${jsonencode(merge(local.global, local.erp))} + EOF +} diff --git a/share/authentik-forward/datas.tf b/share/authentik-forward/datas.tf new file mode 100644 index 0000000..821c848 --- /dev/null +++ b/share/authentik-forward/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/share/authentik-forward/index.yaml b/share/authentik-forward/index.yaml new file mode 100644 index 0000000..8dcc8bc --- /dev/null +++ b/share/authentik-forward/index.yaml @@ -0,0 +1,41 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: authentik-forward + description: null +options: + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + sub-domain: + default: null + domain: + default: your-company + examples: + - your-company + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string +dependencies: +- dist: null + category: share + component: authentik +providers: + kubernetes: true + authentik: true + kubectl: null + postgresql: null + restapi: null + http: true diff --git a/share/authentik-forward/outpost-forward.tf b/share/authentik-forward/outpost-forward.tf new file mode 100644 index 0000000..f771af4 --- /dev/null +++ b/share/authentik-forward/outpost-forward.tf @@ -0,0 +1,80 @@ +locals { + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + forward-outpost-json = jsondecode(data.http.get_forward_outpost.response_body).results + forward-outpost-providers = length(local.forward-outpost-json)>0?(contains(local.forward-outpost-json[0].providers, authentik_provider_proxy.provider_forward.id)?local.forward-outpost-json[0].providers:concat(local.forward-outpost-json[0].providers, [authentik_provider_proxy.provider_forward.id])):[authentik_provider_proxy.provider_forward.id] +} + +data "http" "get_forward_outpost" { + depends_on = [authentik_provider_proxy.provider_forward] + url = "http://authentik.${var.namespace}.svc/api/v3/outposts/instances/?name__iexact=forward" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +resource "authentik_service_connection_kubernetes" "local" { + depends_on = [data.kubernetes_secret_v1.authentik] + name = "local-forward" + local = true +} + +data "authentik_flow" "default-authorization-flow" { + depends_on = [authentik_service_connection_kubernetes.local] + slug = "default-provider-authorization-implicit-consent" +} + +resource "authentik_provider_proxy" "provider_forward" { + name = "authentik-forward-provider" + internal_host = "http://authentik" + external_host = "http://authentik" + authorization_flow = data.authentik_flow.default-authorization-flow.id +} + +data "kubernetes_ingress_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +resource "authentik_outpost" "outpost-forward" { + name = "forward" + type = "proxy" + service_connection = authentik_service_connection_kubernetes.local.id + config = jsonencode({ + "log_level": "info", + "authentik_host": "http://authentik", + "docker_map_ports": true, + "kubernetes_replicas": 1, + "kubernetes_namespace": var.namespace, + "authentik_host_browser": "https://${data.kubernetes_ingress_v1.authentik.spec[0].rule[0].host}", + "object_naming_template": "ak-outpost-%(name)s", + "authentik_host_insecure": false, + "kubernetes_service_type": "ClusterIP", + "kubernetes_image_pull_secrets": [], + "kubernetes_disabled_components": [], + "kubernetes_ingress_annotations": {}, + "kubernetes_ingress_secret_name": "authentik-outpost-tls" + }) + protocol_providers = local.forward-outpost-providers +} + +data "authentik_user" "akadmin" { + depends_on = [authentik_outpost.outpost-forward] + username = "akadmin" +} + +resource "authentik_group" "group" { + name = "vynil-forward-admins" + users = [data.authentik_user.akadmin.id] + is_superuser = true +} diff --git a/share/authentik-ldap/datas.tf b/share/authentik-ldap/datas.tf new file mode 100644 index 0000000..821c848 --- /dev/null +++ b/share/authentik-ldap/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/share/authentik-ldap/index.yaml b/share/authentik-ldap/index.yaml new file mode 100644 index 0000000..a9f0bdb --- /dev/null +++ b/share/authentik-ldap/index.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: authentik-ldap + description: null +options: + domain: + default: your-company + examples: + - your-company + type: string +dependencies: +- dist: null + category: share + component: authentik +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: null + http: true diff --git a/share/authentik-ldap/outpost-ldap.tf b/share/authentik-ldap/outpost-ldap.tf new file mode 100644 index 0000000..da52503 --- /dev/null +++ b/share/authentik-ldap/outpost-ldap.tf @@ -0,0 +1,108 @@ +locals { + request_headers = { + "Content-Type" = "application/json" + Authorization = "Bearer ${local.authentik-token}" + } + authentik-token = data.kubernetes_secret_v1.authentik.data["AUTHENTIK_BOOTSTRAP_TOKEN"] + ldap-outpost-json = jsondecode(data.http.get_ldap_outpost.response_body).results + ldap-outpost-prividers = length(local.ldap-outpost-json)>0?(contains(local.ldap-outpost-json[0].providers, authentik_provider_ldap.provider_ldap.id)?local.ldap-outpost-json[0].providers:concat(local.ldap-outpost-json[0].providers, [authentik_provider_ldap.provider_ldap.id])):[authentik_provider_ldap.provider_ldap.id] +} +//TODO: trouver un moyen d'attendre que le service soit ready +data "http" "get_ldap_outpost" { + depends_on = [authentik_provider_ldap.provider_ldap] + url = "http://authentik.${var.namespace}.svc/api/v3/outposts/instances/?name__iexact=ldap" + method = "GET" + request_headers = local.request_headers + lifecycle { + postcondition { + condition = contains([200], self.status_code) + error_message = "Status code invalid" + } + } +} + +resource "authentik_stage_password" "ldap-password-stage" { + depends_on = [data.kubernetes_secret_v1.authentik] + name = "ldap-authentication-password" + backends = [ + "authentik.core.auth.InbuiltBackend", + "authentik.core.auth.TokenBackend", + "authentik.sources.ldap.auth.LDAPBackend" + ] +} + +resource "authentik_stage_identification" "ldap-identification-stage" { + name = "ldap-identification-stage" + user_fields = ["username","email"] + password_stage = authentik_stage_password.ldap-password-stage.id +} + +resource "authentik_stage_user_login" "ldap-authentication-login" { + depends_on = [data.kubernetes_secret_v1.authentik] + name = "ldap-authentication-login" +} + +resource "authentik_flow" "ldap-authentication-flow" { + depends_on = [data.kubernetes_secret_v1.authentik] + name = "ldap-authentication-flow" + title = "ldap authentication flow" + slug = "ldap-authentication-flow" + designation = "authentication" +} + +resource "authentik_flow_stage_binding" "ldap-authentication-flow-10" { + target = authentik_flow.ldap-authentication-flow.uuid + stage = authentik_stage_identification.ldap-identification-stage.id + order = 10 +} + +resource "authentik_flow_stage_binding" "ldap-authentication-flow-30" { + target = authentik_flow.ldap-authentication-flow.uuid + stage = authentik_stage_user_login.ldap-authentication-login.id + order = 30 +} + +data "authentik_user" "akadmin" { + depends_on = [kustomization_resource.post,authentik_flow_stage_binding.ldap-authentication-flow-30] + username = "akadmin" +} + +resource "authentik_group" "group" { + name = "vynil-ldap-admins" + users = [data.authentik_user.akadmin.id] + is_superuser = true +} + +resource "authentik_service_connection_kubernetes" "local" { + depends_on = [data.kubernetes_secret_v1.authentik] + name = "local-ldap" + local = true +} + +resource "authentik_provider_ldap" "provider_ldap" { + name = "authentik-ldap-provider" + base_dn = "dc=${var.namespace},dc=namespace" + bind_flow = authentik_flow.ldap-authentication-flow.uuid +} + +resource "authentik_outpost" "outpost-ldap" { + name = "ldap" + type = "ldap" + service_connection = authentik_service_connection_kubernetes.local.id + config = jsonencode({ + "log_level": "info", + "authentik_host": "http://authentik", + "docker_map_ports": true, + "kubernetes_replicas": 1, + "kubernetes_namespace": var.namespace, + "authentik_host_browser": "", + "object_naming_template": "ak-outpost-%(name)s", + "authentik_host_insecure": false, + "kubernetes_service_type": "ClusterIP", + "kubernetes_image_pull_secrets": [], + "kubernetes_disabled_components": [], + "kubernetes_ingress_annotations": {}, + "kubernetes_ingress_secret_name": "authentik-outpost-tls" + }) + protocol_providers = local.ldap-outpost-prividers +} diff --git a/share/authentik/apps_v1_Deployment_authentik-server.yaml b/share/authentik/apps_v1_Deployment_authentik-server.yaml new file mode 100644 index 0000000..11072c0 --- /dev/null +++ b/share/authentik/apps_v1_Deployment_authentik-server.yaml @@ -0,0 +1,71 @@ +# Source: authentik/templates/server-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: authentik-server + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "server" +spec: + selector: + matchLabels: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/component: "server" + template: + metadata: + labels: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/component: "server" + app.kubernetes.io/version: "2023.6.1" + annotations: + goauthentik.io/config-checksum: 39339b4fd4c8511ca989fe40932e07b38befc9e3642eab092900cdde5cdf8f37 + spec: + enableServiceLinks: true + securityContext: + {} + containers: + - name: authentik + image: "ghcr.io/goauthentik/server:2023.6.1" + imagePullPolicy: "IfNotPresent" + args: ["server"] + env: + envFrom: + - secretRef: + name: authentik + volumeMounts: + ports: + - name: http + containerPort: 9000 + protocol: TCP + - name: http-metrics + containerPort: 9300 + protocol: TCP + - name: https + containerPort: 9443 + protocol: TCP + livenessProbe: + httpGet: + path: /-/health/live/ + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + startupProbe: + failureThreshold: 60 + httpGet: + path: /-/health/live/ + port: http + periodSeconds: 5 + readinessProbe: + httpGet: + path: /-/health/ready/ + port: http + periodSeconds: 10 + securityContext: + {} + volumes: \ No newline at end of file diff --git a/share/authentik/apps_v1_Deployment_authentik-worker.yaml b/share/authentik/apps_v1_Deployment_authentik-worker.yaml new file mode 100644 index 0000000..b475cea --- /dev/null +++ b/share/authentik/apps_v1_Deployment_authentik-worker.yaml @@ -0,0 +1,45 @@ +# Source: authentik/templates/worker-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: authentik-worker + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "worker" +spec: + selector: + matchLabels: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/component: "worker" + template: + metadata: + labels: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/component: "worker" + app.kubernetes.io/version: "2023.6.1" + annotations: + goauthentik.io/config-checksum: 39339b4fd4c8511ca989fe40932e07b38befc9e3642eab092900cdde5cdf8f37 + spec: + serviceAccountName: authentik + enableServiceLinks: true + securityContext: + {} + containers: + - name: authentik + image: "ghcr.io/goauthentik/server:2023.6.1" + imagePullPolicy: "IfNotPresent" + args: ["worker"] + env: + envFrom: + - secretRef: + name: authentik + volumeMounts: + securityContext: + {} + volumes: \ No newline at end of file diff --git a/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-server.yaml b/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-server.yaml new file mode 100644 index 0000000..aacf39a --- /dev/null +++ b/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-server.yaml @@ -0,0 +1,26 @@ +# Source: authentik/templates/server-hpa.yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: authentik-server + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "server" +spec: + minReplicas: 1 + maxReplicas: 5 + metrics: + - resource: + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: authentik-server \ No newline at end of file diff --git a/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-worker.yaml b/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-worker.yaml new file mode 100644 index 0000000..47aedd5 --- /dev/null +++ b/share/authentik/autoscaling_v2_HorizontalPodAutoscaler_authentik-worker.yaml @@ -0,0 +1,26 @@ +# Source: authentik/templates/worker-hpa.yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: authentik-worker + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/component: "worker" +spec: + minReplicas: 1 + maxReplicas: 5 + metrics: + - resource: + name: cpu + target: + averageUtilization: 80 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: authentik-worker \ No newline at end of file diff --git a/share/authentik/datas.tf b/share/authentik/datas.tf new file mode 100644 index 0000000..d2e2df1 --- /dev/null +++ b/share/authentik/datas.tf @@ -0,0 +1,153 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +data "kustomization_overlay" "data" { + namespace = var.namespace + common_labels = local.common-labels + resources = [for file in fileset(path.module, "*.yaml"): file if file != "index.yaml"] + images { + name = "ghcr.io/goauthentik/server" + new_name = "${var.image.registry}/${var.image.repository}" + new_tag = "${var.image.tag}" + } + config_map_generator { + name = var.component + behavior = "create" + literals = [ + "AUTHENTIK_EMAIL__PORT=${var.email.port}", + "AUTHENTIK_EMAIL__TIMEOUT=${var.email.timeout}", + "AUTHENTIK_EMAIL__USE_TLS=${var.email.use_tls}", + "AUTHENTIK_EMAIL__USE_SSL=${var.email.use_ssl}", + "AUTHENTIK_ERROR_REPORTING__ENABLED=${var.error_reporting.enabled}", + "AUTHENTIK_ERROR_REPORTING__ENVIRONMENT=${var.error_reporting.environment}", + "AUTHENTIK_ERROR_REPORTING__SEND_PII=${var.error_reporting.send_pii}", + "AUTHENTIK_GEOIP=${var.geoip}", + "AUTHENTIK_LOG_LEVEL=${var.loglevel}", + "AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=${var.image.registry}/${var.image.project}/%(type)s:%(version)s", + "AUTHENTIK_POSTGRESQL__HOST=${var.instance}-${var.component}.${var.namespace}.svc", + "AUTHENTIK_POSTGRESQL__NAME=${var.component}", + "AUTHENTIK_POSTGRESQL__PORT=5432", + "AUTHENTIK_POSTGRESQL__USER=${var.component}", + "AUTHENTIK_REDIS__HOST=${var.name}-${var.component}-redis", + "AUTHENTIK_BOOTSTRAP_EMAIL=${var.admin.email}@${var.domain-name}", + ] + } + patches { + target { + kind = "Deployment" + name = "authentik-server" + } + patch = <<-EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: authentik-server + spec: + template: + spec: + containers: + - name: authentik + image: "${var.image.registry}/${var.image.repository}:${var.image.tag}" + imagePullPolicy: "${var.image.pullPolicy}" + env: + - name: AUTHENTIK_POSTGRESQL__PASSWORD + valueFrom: + secretKeyRef: + name: ${var.component}.${var.instance}-${var.component}.credentials.postgresql.acid.zalan.do + key: password + envFrom: + - secretRef: + name: ${var.component} + - configMapRef: + name: ${var.component} + EOF + } + patches { + target { + kind = "Deployment" + name = "authentik-worker" + } + patch = <<-EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: authentik-worker + spec: + template: + spec: + containers: + - name: authentik + image: "${var.image.registry}/${var.image.repository}:${var.image.tag}" + imagePullPolicy: "${var.image.pullPolicy}" + env: + - name: AUTHENTIK_POSTGRESQL__PASSWORD + valueFrom: + secretKeyRef: + name: ${var.component}.${var.name}-${var.component}.credentials.postgresql.acid.zalan.do + key: password + envFrom: + - secretRef: + name: ${var.component} + - configMapRef: + name: ${var.component} + EOF + } + patches { + target { + kind = "ClusterRole" + name = "authentik-vynil-auth" + } + patch = <<-EOF + - op: replace + path: /metadata/name + value: authentik-${var.namespace} + EOF + } + patches { + target { + kind = "ClusterRoleBinding" + name = "authentik-vynil-auth" + } + patch = <<-EOF + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + name: authentik-vynil-auth + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: authentik-${var.namespace} + subjects: + - kind: ServiceAccount + name: authentik + namespace: ${var.namespace} + EOF + } + patches { + target { + kind = "ClusterRoleBinding" + name = "authentik-vynil-auth" + } + patch = <<-EOF + - op: replace + path: /metadata/name + value: authentik-${var.namespace} + EOF + } +} diff --git a/share/authentik/index.yaml b/share/authentik/index.yaml new file mode 100644 index 0000000..17a2df9 --- /dev/null +++ b/share/authentik/index.yaml @@ -0,0 +1,207 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: authentik + description: authentik is an open-source Identity Provider focused on flexibility and versatility +options: + email: + default: + port: 587 + timeout: 30 + use_ssl: false + use_tls: false + examples: + - port: 587 + timeout: 30 + use_ssl: false + use_tls: false + properties: + port: + default: 587 + type: integer + timeout: + default: 30 + type: integer + use_ssl: + default: false + type: boolean + use_tls: + default: false + type: boolean + type: object + geoip: + default: /geoip/GeoLite2-City.mmdb + examples: + - /geoip/GeoLite2-City.mmdb + type: string + admin: + default: + email: auth-admin + examples: + - email: auth-admin + properties: + email: + default: auth-admin + type: string + type: object + image: + default: + project: goauthentik + pullPolicy: IfNotPresent + registry: ghcr.io + repository: goauthentik/server + tag: 2023.5.4 + examples: + - project: goauthentik + pullPolicy: IfNotPresent + registry: ghcr.io + repository: goauthentik/server + tag: 2023.5.4 + properties: + project: + default: goauthentik + type: string + pullPolicy: + default: IfNotPresent + type: string + registry: + default: ghcr.io + type: string + repository: + default: goauthentik/server + type: string + tag: + default: 2023.5.4 + type: string + type: object + error_reporting: + default: + enabled: false + environment: k8s + send_pii: false + examples: + - enabled: false + environment: k8s + send_pii: false + properties: + enabled: + default: false + type: boolean + environment: + default: k8s + type: string + send_pii: + default: false + type: boolean + type: object + sub-domain: + default: auth + examples: + - auth + type: string + redis: + default: + exporter: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + image: quay.io/opstree/redis:v7.0.5 + storage: 8Gi + examples: + - exporter: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + image: quay.io/opstree/redis:v7.0.5 + storage: 8Gi + properties: + exporter: + default: + enabled: true + image: quay.io/opstree/redis-exporter:v1.44.0 + properties: + enabled: + default: true + type: boolean + image: + default: quay.io/opstree/redis-exporter:v1.44.0 + type: string + type: object + image: + default: quay.io/opstree/redis:v7.0.5 + type: string + storage: + default: 8Gi + type: string + type: object + postgres: + default: + replicas: 1 + storage: 8Gi + version: '14' + examples: + - replicas: 1 + storage: 8Gi + version: '14' + properties: + replicas: + default: 1 + type: integer + storage: + default: 8Gi + type: string + version: + default: '14' + type: string + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + domain: + default: your-company + examples: + - your-company + type: string + loglevel: + default: info + examples: + - info + type: string +dependencies: +- dist: null + category: core + component: cert-manager +- dist: null + category: core + component: secret-generator +- dist: null + category: crd + component: prometheus +- dist: null + category: crd + component: traefik +- dist: null + category: dbo + component: postgresql +- dist: null + category: dbo + component: redis +providers: + kubernetes: null + authentik: true + kubectl: true + postgresql: null + restapi: null + http: null diff --git a/share/authentik/ingress.tf b/share/authentik/ingress.tf new file mode 100644 index 0000000..cc84f10 --- /dev/null +++ b/share/authentik/ingress.tf @@ -0,0 +1,75 @@ +locals { + dns-names = ["${var.sub-domain}.${var.domain-name}"] + middlewares = ["${var.instance}-https"] + service = { + "name" = "${var.instance}" + "port" = { + "number" = 80 + } + } + rules = [ for v in local.dns-names : { + "host" = "${v}" + "http" = { + "paths" = [{ + "backend" = { + "service" = local.service + } + "path" = "/" + "pathType" = "Prefix" + }] + } + }] +} + +resource "kubectl_manifest" "prj_certificate" { + yaml_body = <<-EOF + apiVersion: "cert-manager.io/v1" + kind: "Certificate" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + secretName: "${var.instance}-cert" + dnsNames: ${jsonencode(local.dns-names)} + issuerRef: + name: "${var.issuer}" + kind: "ClusterIssuer" + group: "cert-manager.io" + EOF +} + +resource "kubectl_manifest" "prj_https_redirect" { + yaml_body = <<-EOF + apiVersion: "traefik.containo.us/v1alpha1" + kind: "Middleware" + metadata: + name: "${var.instance}-https" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + redirectScheme: + scheme: "https" + permanent: true + EOF +} + +resource "kubectl_manifest" "prj_ingress" { + force_conflicts = true + yaml_body = <<-EOF + apiVersion: "networking.k8s.io/v1" + kind: "Ingress" + metadata: + name: "${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + annotations: + "traefik.ingress.kubernetes.io/router.middlewares": "${join(",", [for m in local.middlewares : format("%s-%s@kubernetescrd", var.namespace, m)])}" + spec: + ingressClassName: "${var.ingress-class}" + rules: ${jsonencode(local.rules)} + tls: + - hosts: ${jsonencode(local.dns-names)} + secretName: "${var.instance}-cert" + EOF +} diff --git a/share/authentik/monitoring.coreos.com_v1_PrometheusRule_authentik.yaml b/share/authentik/monitoring.coreos.com_v1_PrometheusRule_authentik.yaml new file mode 100644 index 0000000..6bf250e --- /dev/null +++ b/share/authentik/monitoring.coreos.com_v1_PrometheusRule_authentik.yaml @@ -0,0 +1,162 @@ +# Source: authentik/templates/prom-rules.yaml +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: authentik + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm +spec: + groups: + - name: authentik Aggregate request counters + rules: + - record: job:django_http_requests_before_middlewares_total:sum_rate30s + expr: sum(rate(django_http_requests_before_middlewares_total[30s])) by (job) + - record: job:django_http_requests_unknown_latency_total:sum_rate30s + expr: sum(rate(django_http_requests_unknown_latency_total[30s])) by (job) + - record: job:django_http_ajax_requests_total:sum_rate30s + expr: sum(rate(django_http_ajax_requests_total[30s])) by (job) + - record: job:django_http_responses_before_middlewares_total:sum_rate30s + expr: sum(rate(django_http_responses_before_middlewares_total[30s])) by (job) + - record: job:django_http_requests_unknown_latency_including_middlewares_total:sum_rate30s + expr: sum(rate(django_http_requests_unknown_latency_including_middlewares_total[30s])) by (job) + - record: job:django_http_requests_body_total_bytes:sum_rate30s + expr: sum(rate(django_http_requests_body_total_bytes[30s])) by (job) + - record: job:django_http_responses_streaming_total:sum_rate30s + expr: sum(rate(django_http_responses_streaming_total[30s])) by (job) + - record: job:django_http_responses_body_total_bytes:sum_rate30s + expr: sum(rate(django_http_responses_body_total_bytes[30s])) by (job) + - record: job:django_http_requests_total:sum_rate30s + expr: sum(rate(django_http_requests_total_by_method[30s])) by (job) + - record: job:django_http_requests_total_by_method:sum_rate30s + expr: sum(rate(django_http_requests_total_by_method[30s])) by (job,method) + - record: job:django_http_requests_total_by_transport:sum_rate30s + expr: sum(rate(django_http_requests_total_by_transport[30s])) by (job,transport) + - record: job:django_http_requests_total_by_view:sum_rate30s + expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view) + - record: job:django_http_requests_total_by_view_transport_method:sum_rate30s + expr: sum(rate(django_http_requests_total_by_view_transport_method[30s])) by (job,view,transport,method) + - record: job:django_http_responses_total_by_templatename:sum_rate30s + expr: sum(rate(django_http_responses_total_by_templatename[30s])) by (job,templatename) + - record: job:django_http_responses_total_by_status:sum_rate30s + expr: sum(rate(django_http_responses_total_by_status[30s])) by (job,status) + - record: job:django_http_responses_total_by_status_name_method:sum_rate30s + expr: sum(rate(django_http_responses_total_by_status_name_method[30s])) by (job,status,name,method) + - record: job:django_http_responses_total_by_charset:sum_rate30s + expr: sum(rate(django_http_responses_total_by_charset[30s])) by (job,charset) + - record: job:django_http_exceptions_total_by_type:sum_rate30s + expr: sum(rate(django_http_exceptions_total_by_type[30s])) by (job,type) + - record: job:django_http_exceptions_total_by_view:sum_rate30s + expr: sum(rate(django_http_exceptions_total_by_view[30s])) by (job,view) + + - name: authentik Aggregate latency histograms + rules: + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "50" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "95" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "99" + - record: job:django_http_requests_latency_including_middlewares_seconds:quantile_rate30s + expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_including_middlewares_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "99.9" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.50, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "50" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.95, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "95" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.99, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "99" + - record: job:django_http_requests_latency_seconds:quantile_rate30s + expr: histogram_quantile(0.999, sum(rate(django_http_requests_latency_seconds_bucket[30s])) by (job, le)) + labels: + quantile: "99.9" + + - name: authentik Aggregate model operations + rules: + - record: job:django_model_inserts_total:sum_rate1m + expr: sum(rate(django_model_inserts_total[1m])) by (job, model) + - record: job:django_model_updates_total:sum_rate1m + expr: sum(rate(django_model_updates_total[1m])) by (job, model) + - record: job:django_model_deletes_total:sum_rate1m + expr: sum(rate(django_model_deletes_total[1m])) by (job, model) + - name: authentik Aggregate database operations + rules: + - record: job:django_db_new_connections_total:sum_rate30s + expr: sum(rate(django_db_new_connections_total[30s])) by (alias, vendor) + - record: job:django_db_new_connection_errors_total:sum_rate30s + expr: sum(rate(django_db_new_connection_errors_total[30s])) by (alias, vendor) + - record: job:django_db_execute_total:sum_rate30s + expr: sum(rate(django_db_execute_total[30s])) by (alias, vendor) + - record: job:django_db_execute_many_total:sum_rate30s + expr: sum(rate(django_db_execute_many_total[30s])) by (alias, vendor) + - record: job:django_db_errors_total:sum_rate30s + expr: sum(rate(django_db_errors_total[30s])) by (alias, vendor, type) + + - name: authentik Aggregate migrations + rules: + - record: job:django_migrations_applied_total:max + expr: max(django_migrations_applied_total) by (job, connection) + - record: job:django_migrations_unapplied_total:max + expr: max(django_migrations_unapplied_total) by (job, connection) + + - name: authentik Alerts + rules: + - alert: NoWorkersConnected + labels: + severity: critical + expr: max without (pid) (authentik_admin_workers) < 1 + for: 10m + annotations: + + summary: No workers connected + message: authentik instance {{ $labels.instance }}'s worker are either not running or not connected. + + + + - alert: PendingMigrations + labels: + severity: critical + expr: max without (pid) (django_migrations_unapplied_total) > 0 + for: 10m + annotations: + + summary: Pending database migrations + message: authentik instance {{ $labels.instance }} has pending database migrations + + + - alert: FailedSystemTasks + labels: + severity: critical + expr: sum(increase(authentik_system_tasks{status="error"}[2h])) > 0 + for: 2h + annotations: + + summary: Failed system tasks + message: System task {{ $labels.task_name }} has failed + + + - alert: DisconnectedOutposts + labels: + severity: critical + expr: sum by (outpost) (max without (pid) (authentik_outposts_connected{uid!~"specific.*"})) < 1 + for: 30m + annotations: + + summary: Disconnected outpost + message: Outpost {{ $labels.outpost }} has at least 1 disconnected instance \ No newline at end of file diff --git a/share/authentik/monitoring.coreos.com_v1_ServiceMonitor_authentik.yaml b/share/authentik/monitoring.coreos.com_v1_ServiceMonitor_authentik.yaml new file mode 100644 index 0000000..e350744 --- /dev/null +++ b/share/authentik/monitoring.coreos.com_v1_ServiceMonitor_authentik.yaml @@ -0,0 +1,20 @@ +# Source: authentik/templates/prom-service-monitor.yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: authentik + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm +spec: + endpoints: + - port: http-metrics + scrapeTimeout: 3s + interval: 30s + selector: + matchLabels: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik \ No newline at end of file diff --git a/share/authentik/postgresql.tf b/share/authentik/postgresql.tf new file mode 100644 index 0000000..6b8c703 --- /dev/null +++ b/share/authentik/postgresql.tf @@ -0,0 +1,26 @@ +resource "kubectl_manifest" "authentik_postgresql" { + yaml_body = <<-EOF + apiVersion: "acid.zalan.do/v1" + kind: "postgresql" + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + databases: + ${var.component}: "${var.component}" + numberOfInstances: ${var.postgres.replicas} + podAnnotations: + "k8up.io/backupcommand": "pg_dump -U postgres -d ${var.component} --clean" + "k8up.io/file-extension": ".sql" + postgresql: + version: "${var.postgres.version}" + teamId: "${var.instance}" + users: + ${var.component}: + - "superuser" + - "createdb" + volume: + size: "${var.postgres.storage}" + EOF +} diff --git a/share/authentik/rbac.authorization.k8s.io_v1_ClusterRoleBinding_authentik-vynil-auth.yaml b/share/authentik/rbac.authorization.k8s.io_v1_ClusterRoleBinding_authentik-vynil-auth.yaml new file mode 100644 index 0000000..3159645 --- /dev/null +++ b/share/authentik/rbac.authorization.k8s.io_v1_ClusterRoleBinding_authentik-vynil-auth.yaml @@ -0,0 +1,19 @@ +# Source: authentik/charts/serviceAccount/templates/cluster-role-binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: authentik-vynil-auth + labels: + helm.sh/chart: serviceAccount-1.2.2 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.0" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: authentik-vynil-auth +subjects: + - kind: ServiceAccount + name: authentik + namespace: vynil-auth \ No newline at end of file diff --git a/share/authentik/rbac.authorization.k8s.io_v1_ClusterRole_authentik-vynil-auth.yaml b/share/authentik/rbac.authorization.k8s.io_v1_ClusterRole_authentik-vynil-auth.yaml new file mode 100644 index 0000000..00bf5f7 --- /dev/null +++ b/share/authentik/rbac.authorization.k8s.io_v1_ClusterRole_authentik-vynil-auth.yaml @@ -0,0 +1,18 @@ +# Source: authentik/charts/serviceAccount/templates/cluster-role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: authentik-vynil-auth + labels: + helm.sh/chart: serviceAccount-1.2.2 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.0" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - list \ No newline at end of file diff --git a/share/authentik/rbac.authorization.k8s.io_v1_RoleBinding_authentik.yaml b/share/authentik/rbac.authorization.k8s.io_v1_RoleBinding_authentik.yaml new file mode 100644 index 0000000..07445e0 --- /dev/null +++ b/share/authentik/rbac.authorization.k8s.io_v1_RoleBinding_authentik.yaml @@ -0,0 +1,20 @@ +# Source: authentik/charts/serviceAccount/templates/role-binding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: authentik + namespace: vynil-auth + labels: + helm.sh/chart: serviceAccount-1.2.2 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.0" + app.kubernetes.io/managed-by: Helm +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: authentik +subjects: + - kind: ServiceAccount + name: authentik + namespace: vynil-auth \ No newline at end of file diff --git a/share/authentik/rbac.authorization.k8s.io_v1_Role_authentik.yaml b/share/authentik/rbac.authorization.k8s.io_v1_Role_authentik.yaml new file mode 100644 index 0000000..d239846 --- /dev/null +++ b/share/authentik/rbac.authorization.k8s.io_v1_Role_authentik.yaml @@ -0,0 +1,74 @@ +# Source: authentik/charts/serviceAccount/templates/role.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: authentik + namespace: vynil-auth + labels: + helm.sh/chart: serviceAccount-1.2.2 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.0" + app.kubernetes.io/managed-by: Helm +rules: + - apiGroups: + - "" + resources: + - secrets + - services + - configmaps + verbs: + - get + - create + - delete + - list + - patch + - apiGroups: + - extensions + - apps + resources: + - deployments + verbs: + - get + - create + - delete + - list + - patch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - create + - delete + - list + - patch + - apiGroups: + - traefik.containo.us + - traefik.io + resources: + - middlewares + verbs: + - get + - create + - delete + - list + - patch + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - get + - create + - delete + - list + - patch + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - list \ No newline at end of file diff --git a/share/authentik/redis.tf b/share/authentik/redis.tf new file mode 100644 index 0000000..dba040b --- /dev/null +++ b/share/authentik/redis.tf @@ -0,0 +1,30 @@ +resource "kubectl_manifest" "authentik_redis" { + yaml_body = <<-EOF + apiVersion: "redis.redis.opstreelabs.in/v1beta1" + kind: "Redis" + metadata: + name: "${var.name}-${var.component}-redis" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + kubernetesConfig: + image: "${var.redis.image}" + imagePullPolicy: "IfNotPresent" + redisSecret: + name: "${var.component}" + key: "AUTHENTIK_REDIS__PASSWORD" + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: "${var.redis.storage}" + redisExporter: + enabled: ${var.redis.exporter.enabled} + image: "${var.redis.exporter.image}" + securityContext: + runAsUser: 1000 + fsGroup: 1000 + EOF +} diff --git a/share/authentik/secret.tf b/share/authentik/secret.tf new file mode 100644 index 0000000..c030fbd --- /dev/null +++ b/share/authentik/secret.tf @@ -0,0 +1,23 @@ + +resource "kubectl_manifest" "authentik_secret" { + ignore_fields = ["metadata.annotations"] + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + spec: + forceRegenerate: false + fields: + - fieldName: "AUTHENTIK_SECRET_KEY" + length: "128" + - fieldName: "AUTHENTIK_BOOTSTRAP_PASSWORD" + length: "32" + - fieldName: "AUTHENTIK_BOOTSTRAP_TOKEN" + length: "64" + - fieldName: "AUTHENTIK_REDIS__PASSWORD" + length: "32" + EOF +} diff --git a/share/authentik/v1_ServiceAccount_authentik.yaml b/share/authentik/v1_ServiceAccount_authentik.yaml new file mode 100644 index 0000000..941cac7 --- /dev/null +++ b/share/authentik/v1_ServiceAccount_authentik.yaml @@ -0,0 +1,12 @@ +# Source: authentik/charts/serviceAccount/templates/service-account.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: authentik + namespace: vynil-auth + labels: + helm.sh/chart: serviceAccount-1.2.2 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.0" + app.kubernetes.io/managed-by: Helm \ No newline at end of file diff --git a/share/authentik/v1_Service_authentik.yaml b/share/authentik/v1_Service_authentik.yaml new file mode 100644 index 0000000..79e9ede --- /dev/null +++ b/share/authentik/v1_Service_authentik.yaml @@ -0,0 +1,26 @@ +# Source: authentik/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: authentik + labels: + helm.sh/chart: authentik-2023.6.3 + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/version: "2023.6.1" + app.kubernetes.io/managed-by: Helm +spec: + type: ClusterIP + ports: + - port: 9300 + name: http-metrics + protocol: TCP + targetPort: http-metrics + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: authentik + app.kubernetes.io/instance: authentik + app.kubernetes.io/component: "server" \ No newline at end of file diff --git a/share/dns/apps_v1_Deployment_coredns-coredns.yaml b/share/dns/apps_v1_Deployment_coredns-coredns.yaml new file mode 100644 index 0000000..5abf09d --- /dev/null +++ b/share/dns/apps_v1_Deployment_coredns-coredns.yaml @@ -0,0 +1,89 @@ +# Source: coredns/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coredns-coredns + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns + app.kubernetes.io/version: "1.10.1" +spec: + replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 25% + selector: + matchLabels: + app.kubernetes.io/instance: "coredns" + k8s-app: coredns + app.kubernetes.io/name: coredns + template: + metadata: + labels: + k8s-app: coredns + app.kubernetes.io/name: coredns + app.kubernetes.io/instance: "coredns" + annotations: + checksum/config: 2c80ea26dcf7cd4d57c4ccbe0561210d06f8e048704a7edb5c495e4e2d60999d + scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]' + spec: + terminationGracePeriodSeconds: 30 + serviceAccountName: coredns-coredns + dnsPolicy: Default + containers: + - name: "coredns" + image: "coredns/coredns:1.10.1" + imagePullPolicy: IfNotPresent + args: [ "-conf", "/etc/coredns/Corefile" ] + volumeMounts: + - name: config-volume + mountPath: /etc/coredns + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + ports: + - {"containerPort":53,"name":"udp-53","protocol":"UDP"} + - {"containerPort":53,"name":"tcp-53","protocol":"TCP"} + + livenessProbe: + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + httpGet: + path: /ready + port: 8181 + scheme: HTTP + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + securityContext: + capabilities: + add: + - NET_BIND_SERVICE + volumes: + - name: config-volume + configMap: + name: coredns-coredns + items: + - key: Corefile + path: Corefile \ No newline at end of file diff --git a/share/dns/config.tf b/share/dns/config.tf new file mode 100644 index 0000000..cbb56b3 --- /dev/null +++ b/share/dns/config.tf @@ -0,0 +1,52 @@ +locals { + begin-core = <<-EOF + .:53 { + errors { + consolidate 5m ".* i/o timeout$" warning + consolidate 30s "^Failed to .+" + } + health { + lameduck 5s + } + ready + EOF + end-core = <<-EOF + } + EOF + soa-ns = <<-EOF + @ IN SOA ${var.sub-domain}.${var.domain-name}. ${var.domain-name}. ( + ${formatdate("YYYYMMDDhh",timestamp())} ; Serial + 4H ; Refresh + 1H ; Retry + 7D ; Expire + 4H ) ; Negative Cache TTL + @ IN NS ${var.sub-domain}.${var.domain-name}. + EOF + files = merge({ + "Corefile" = join("", concat([local.begin-core],[for z in var.zones: format("file /etc/coredns/%s.db %s", z.name,z.name)],[local.end-core])) + },[for z in var.zones: { + "${z.name}" = join("\n", concat([ + "$TTL 60", + "$ORIGIN ${z.name}.", + local.soa-ns + ], + [for k,v in z.hosts: format("%s IN A %s", k, v)], + [for k,v in z.hosts6: format("%s IN AAAA %s", k, v)], + [for k,v in z.alias: format("%s IN CNAME %s", k, v)], + z.wildcard!=""?[format("*.%s. IN A %s", z.name, z.wildcard)]:[], + z.wildcard6!=""?[format("*.%s. IN AAAA %s", z.namz, z.wildcard6)]:[], + )) + }]...) +} + +resource "kubectl_manifest" "coredns-config" { + yaml_body = <<-EOF + apiVersion: v1 + kind: ConfigMap + metadata: + name: "${var.component}-${var.instance}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.common-labels)} + data: ${jsonencode(local.files)} + EOF +} diff --git a/share/dns/datas.tf b/share/dns/datas.tf new file mode 100644 index 0000000..3e70482 --- /dev/null +++ b/share/dns/datas.tf @@ -0,0 +1,53 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } + items = concat([{ + "key" = "Corefile" + "path" = "Corefile" + }],[for z in var.zones: { + "key" = z.name + "path" = z.name + }]) +} + +data "kustomization_overlay" "data" { + namespace = var.namespace + common_labels = local.common-labels + resources = [for file in fileset(path.module, "*.yaml"): file if file != "index.yaml"] + images { + name = "coredns/coredns" + new_name = "${var.image.registry}/${var.image.repository}" + new_tag = "${var.image.tag}" + } + patches { + target { + kind = "Deployment" + name = "coredns-coredns" + } + patch = <<-EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: coredns-coredns + spec: + template: + spec: + containers: + - name: coredns + image: "${var.image.registry}/${var.image.repository}:${var.image.tag}" + imagePullPolicy: "${var.image.pullPolicy}" + volumes: + - name: config-volume + configMap: + name: "${var.component}-${var.instance}" + items: ${jsonencode(local.items)} + EOF + } +} diff --git a/share/dns/index.yaml b/share/dns/index.yaml new file mode 100644 index 0000000..4b0053a --- /dev/null +++ b/share/dns/index.yaml @@ -0,0 +1,84 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: dns + description: null +options: + domain: + default: your-company + examples: + - your-company + type: string + sub-domain: + default: dns + examples: + - dns + type: string + zones: + default: [] + items: + properties: + alias: + default: {} + type: object + hosts: + default: {} + type: object + hosts6: + default: {} + type: object + name: + default: local.domain + type: string + wildcard: + default: '' + type: string + wildcard6: + default: '' + type: string + type: object + type: array + image: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: coredns/coredns + tag: 1.10.1 + examples: + - pullPolicy: IfNotPresent + registry: docker.io + repository: coredns/coredns + tag: 1.10.1 + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: coredns/coredns + type: string + tag: + default: 1.10.1 + type: string + type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: null + postgresql: null + restapi: null + http: null diff --git a/share/dns/rbac.authorization.k8s.io_v1_ClusterRoleBinding_coredns-coredns.yaml b/share/dns/rbac.authorization.k8s.io_v1_ClusterRoleBinding_coredns-coredns.yaml new file mode 100644 index 0000000..ebfd79b --- /dev/null +++ b/share/dns/rbac.authorization.k8s.io_v1_ClusterRoleBinding_coredns-coredns.yaml @@ -0,0 +1,21 @@ +# Source: coredns/templates/clusterrolebinding.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: coredns-coredns + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: coredns-coredns +subjects: +- kind: ServiceAccount + name: coredns-coredns + namespace: vynil-infra \ No newline at end of file diff --git a/share/dns/rbac.authorization.k8s.io_v1_ClusterRole_coredns-coredns.yaml b/share/dns/rbac.authorization.k8s.io_v1_ClusterRole_coredns-coredns.yaml new file mode 100644 index 0000000..3f69800 --- /dev/null +++ b/share/dns/rbac.authorization.k8s.io_v1_ClusterRole_coredns-coredns.yaml @@ -0,0 +1,31 @@ +# Source: coredns/templates/clusterrole.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: coredns-coredns + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns +rules: +- apiGroups: + - "" + resources: + - endpoints + - services + - pods + - namespaces + verbs: + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch \ No newline at end of file diff --git a/share/dns/v1_ServiceAccount_coredns-coredns.yaml b/share/dns/v1_ServiceAccount_coredns-coredns.yaml new file mode 100644 index 0000000..af6a494 --- /dev/null +++ b/share/dns/v1_ServiceAccount_coredns-coredns.yaml @@ -0,0 +1,13 @@ +# Source: coredns/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: coredns-coredns + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns \ No newline at end of file diff --git a/share/dns/v1_Service_coredns-coredns-metrics.yaml b/share/dns/v1_Service_coredns-coredns-metrics.yaml new file mode 100644 index 0000000..cf82ffd --- /dev/null +++ b/share/dns/v1_Service_coredns-coredns-metrics.yaml @@ -0,0 +1,26 @@ +# Source: coredns/templates/service-metrics.yaml +apiVersion: v1 +kind: Service +metadata: + name: coredns-coredns-metrics + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns + app.kubernetes.io/component: metrics + annotations: + prometheus.io/port: "9153" + prometheus.io/scrape: "true" +spec: + selector: + app.kubernetes.io/instance: "coredns" + k8s-app: coredns + app.kubernetes.io/name: coredns + ports: + - name: metrics + port: 9153 + targetPort: 9153 \ No newline at end of file diff --git a/share/dns/v1_Service_coredns-coredns.yaml b/share/dns/v1_Service_coredns-coredns.yaml new file mode 100644 index 0000000..cefc843 --- /dev/null +++ b/share/dns/v1_Service_coredns-coredns.yaml @@ -0,0 +1,22 @@ +# Source: coredns/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: coredns-coredns + labels: + app.kubernetes.io/managed-by: "Helm" + app.kubernetes.io/instance: "coredns" + helm.sh/chart: "coredns-1.24.1" + k8s-app: coredns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + app.kubernetes.io/name: coredns +spec: + selector: + app.kubernetes.io/instance: "coredns" + k8s-app: coredns + app.kubernetes.io/name: coredns + ports: + - {"name":"udp-53","port":53,"protocol":"UDP"} + - {"name":"tcp-53","port":53,"protocol":"TCP"} + type: LoadBalancer \ No newline at end of file