diff --git a/.gitea/workflows/validate.yaml b/.gitea/workflows/validate.yaml index b099317..bf1f821 100644 --- a/.gitea/workflows/validate.yaml +++ b/.gitea/workflows/validate.yaml @@ -15,9 +15,6 @@ jobs: curl -sL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz \ | tar xz -C /usr/local/bin curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - curl -fsSL https://github.com/getsops/sops/releases/latest/download/sops-v3.x.linux.amd64 \ - -o /usr/local/bin/sops - chmod +x /usr/local/bin/sops curl -fsSL https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 \ -o /usr/local/bin/yq chmod +x /usr/local/bin/yq @@ -68,11 +65,9 @@ jobs: mapfile -t manifests < <( find . -type f -name '*.yaml' \ ! -path './.gitea/*' \ - ! -name '.sops.yaml' \ ! -name '*.enc.yaml' \ ! -name '*.secret.yaml' \ ! -name '*kustomization.yaml' \ - ! -name 'secret-generator.yaml' \ ! -path './bootstrap/config.yaml' \ | sort ) @@ -95,31 +90,12 @@ jobs: | xargs kubeconform \ -strict \ -kubernetes-version 1.35.0 \ + -ignore-missing-schemas \ -schema-location default \ -schema-location '.ci-schemas/{{.Group}}/{{.ResourceKind}}{{.KindSuffix}}.json' \ -schema-location '.ci-schemas/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \ -summary - - name: SOPS - check no secret files committed unencrypted - run: | - fail=0 - - while IFS= read -r file; do - if ! grep -q 'sops:' "$file"; then - echo "ERROR: $file appears to be unencrypted" - fail=1 - fi - done < <( - find . -type f \( -name '*.secret.yaml' -o -name '*.enc.yaml' \) | sort - ) - - exit "$fail" - - - name: Check SOPS sync - env: - SOPS_SYNC_HMAC_KEY: ${{ secrets.SOPS_SYNC_HMAC_KEY }} - run: scripts/ci/check-sops-sync - - name: Apply bootstrap ApplicationSet env: KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }} diff --git a/.sops.yaml b/.sops.yaml deleted file mode 100644 index 76d53dd..0000000 --- a/.sops.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Replace the age recipient below with the public key used by ArgoCD / ksops -# in the target environment before storing real secrets in this repository. -creation_rules: - - path_regex: manifest/(overlays|components)/.*/.*\.enc\.yaml$ - age: age1s0gzgh8c00tgnkgxrqyz2nu0k56xvc9ev3jdenkmu90egux9xfmsxvvj43 - - path_regex: manifest/(overlays|components)/.*/secret\.secret\.yaml$ - age: age1s0gzgh8c00tgnkgxrqyz2nu0k56xvc9ev3jdenkmu90egux9xfmsxvvj43 diff --git a/bootstrap/applicationset.yaml b/bootstrap/applicationset.yaml index 2c07d4e..705f0ba 100644 --- a/bootstrap/applicationset.yaml +++ b/bootstrap/applicationset.yaml @@ -20,13 +20,11 @@ spec: app.kubernetes.io/managed-by: argocd app.kubernetes.io/name: authentik spec: - project: default + project: dormant source: repoURL: http://gitea-ha-http.apps:3000/olb42/authentik.git targetRevision: main path: '{{ .path }}' - plugin: - name: ksops destination: server: https://kubernetes.default.svc namespace: '{{ .namespace }}' diff --git a/manifest/components/example-component/example-kustomization.yaml b/manifest/components/example-component/example-kustomization.yaml deleted file mode 100644 index bf3dc02..0000000 --- a/manifest/components/example-component/example-kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# +argocd:skip-file-rendering -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -configMapGenerator: -- files: - - example-test.env diff --git a/manifest/components/example-component/example-test.env b/manifest/components/example-component/example-test.env deleted file mode 100644 index 0567ba3..0000000 --- a/manifest/components/example-component/example-test.env +++ /dev/null @@ -1,3 +0,0 @@ - # +argocd:skip-file-rendering - - test=works \ No newline at end of file diff --git a/manifest/overlays/production/kustomization.yaml b/manifest/overlays/production/kustomization.yaml index f211345..fdaf81e 100644 --- a/manifest/overlays/production/kustomization.yaml +++ b/manifest/overlays/production/kustomization.yaml @@ -7,6 +7,4 @@ resources: # - ../../base # - ingressroute.yaml - storage/persistentvolumeclaim.yaml - -# generators: - # - secret-generator.yaml + - secret.sealed.secret.yaml diff --git a/manifest/overlays/production/secret-generator.yaml b/manifest/overlays/production/secret-generator.yaml deleted file mode 100644 index f6809df..0000000 --- a/manifest/overlays/production/secret-generator.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: viaduct.ai/v1 -kind: ksops -metadata: - name: authentik-secret-generator - annotations: - config.kubernetes.io/function: | - exec: - path: ksops -files: - - ./secret.secret.yaml diff --git a/manifest/overlays/production/secret.sealed-secret.yaml b/manifest/overlays/production/secret.sealed-secret.yaml new file mode 100644 index 0000000..abac68a --- /dev/null +++ b/manifest/overlays/production/secret.sealed-secret.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: authentik-secrets + namespace: security +spec: + encryptedData: + db_password: AgAaBgA3OsRTJxySD4aEjWnSJAyxS0MT5mIGxg9hscwIaDnFd/PwTRGWP/dqpGpFbbrfQrDv/v4XoiKIPpqaOVNlT8k50NZifmPXx3+g3Z9Vh2hAIBoYNBqr3Cppo29yxQUnH0RccLWSI7wK3jp95LBIdrTPRnCSL4E6ma8gkRRiA0ZZBcf6wk2dBzSoV3NBzx5UYvLX0R16JbA+5U9FndeMw8dyzmqSxCLJoh9oTATodaQ0+vMY/WYGl4SdeC33AicLSLp9sx3A8ypJOVjtz1wMhIEK0y6BZ7YDU5/Wj5IMhAbjKpIIn4tUez2DN+JojsAGvLdje9ccZaWWB8aimGoRK9OAu2SjlmB+NXIlJ8Qn6UUCkF+BdT+AXNOnQKHvbpE1DxUWX6zTsb2f2rWoCJPb+E4iLBu4f4lLJot3CtWbX0G/OeFxdnm0eF5rT4YtskeG987rskkvMzIllQWjB4tCrGD1Zt5i/V4YqWx2hpmDrF5PUhWAlW3a5SsNidQPEawLp9ua6/ZaWzF+hQDjNNOXDEQij+9QF40ckB8xe5XB6xRVzBP7Lz/NKCITRPphEPjRdbqCil2fJ/mywN9BDRvWsltSAO3yGS0+s5n14DB/ehTqQDviIA87hjv0G4rP2Rl8QMSa2VtwKp5k7B0pQa8KxCiHXG7XyUQ2GKIr1VPssCR4KPPash72IyggATcWNV3WcR9LzGdNlUQLSi45TnNEEah4akWJ94fM3f0fQJSqzilVE022168bDpOpjcEacVY= + secret_key: AgAfJ3iwLbOfrslj9BRrOxq2bLIR12rwhNJn110TOCIwlb7IMnV2cFC9seS1yxt87HHYsQ62MiLioMwaW9Ih9+KIqFeMQDn/FRQEC7ZHMZRL2EVeNtTgWcx/SozzMfX7Azf3hbwvq7uVvijzeNGTriVseC6BxHiDyL6QWCb3LhfuFU5pNg9kPV8+nhc2y2zkBpXga2xvmkD+Wz4s+SVNcLiauYdwT0IiAcDS/yvN4ztw7+1NFBPp1yeVTK4MAHR2Znnfryq95QuT6bOWy5eHtJ4yQuvadRouWvunLGTJld6meVWFxNAjZudIvcDbnN5iuuZK+ILIiQX+IOsfOy/2ecHM0E3nuh6vnuO4bcRA73IkthFmTuG80El8U8LyYFa8cvAfxS9EUUtAyhOuW9q7K0VTgB2LYgUGeIhONWt6xSkPuW/S4WSRWrvKLX29dJz/r5a5s72uE2xM4OxVlHZt0+LE4XvLgCxAa9DTk8YjVLv2Y1lvuJoAQM83IeGMnXjA1FoOQOdj0pnmkgSRfuI+ElELkiU6wCvbYslLatSsG9EhSO2/QR041S09WiPEf9Io0aIboS3dWDP4X0HAkrKiDRp3rtHlXYPmXMjWLkc6MbtlVcD14c2oJoWy+twjXqJfHWpVpWXS+MQ2JSyZcOQNFuHERUiJ93RQIrl71wAnQEL8f/defpMyYkOI3vwU7r8AiFDsJRRIRXdA+EtG4E5k92o/VQxwEP0phsSUEPUtAJRM/K4rKycl+JlwbxK+iuyV4eQmH21afSS4/XXFFsCLqyA6Bk4Z5mHHBC68sSK5D1f+Og== + template: + metadata: + name: authentik-secrets + namespace: security + type: Opaque diff --git a/manifest/overlays/production/secret.secret.yaml b/manifest/overlays/production/secret.secret.yaml deleted file mode 100644 index 0470750..0000000 --- a/manifest/overlays/production/secret.secret.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: ENC[AES256_GCM,data:nhU=,iv:+igd9RTOMy7mwVDjx05WQIdytoCrWLjcnP2Wq8xBS1s=,tag:61hSvgd/g88pjdhPW6OQsg==,type:str] -kind: ENC[AES256_GCM,data:T9Wrg72s,iv:1+cSHl0Dwwc1ddnw4dQI26r8/aEfMAPpsmz6BxxetrQ=,tag:y8HSeCa9gl7i+rW1Qpzc0Q==,type:str] -metadata: - name: ENC[AES256_GCM,data:VNJIfO/EtW0Pd3xVKG2R1VM=,iv:53UVCYVV07Qdlbq7j9qghT/DHqv+98muUFpapoD7VBs=,tag:hKBPwm7dudPUlRCgp4D9/g==,type:str] - namespace: ENC[AES256_GCM,data:/STezL4E9q4=,iv:mImCvy0lheb1px4+VEm7Z2c6qgQFerRnciPUy+arG2o=,tag:ZJxPwZ7pO6j+8xgHSDK/Og==,type:str] -type: ENC[AES256_GCM,data:ypwwk80m,iv:gbfQojFcmmGCtALVkCcICc4Z4dDXQK8clTGIMkOMOkY=,tag:Y0AskWriaazAlDkNN6Ufuw==,type:str] -stringData: - db_password: ENC[AES256_GCM,data:vx+4cvOPHycDmgp3fhTqsRC6ZjdviJy3svt5Kuhtzg48l8DNlxQpTvLgtdGYZmGS,iv:k84Nwxnuyc3D9ceXuMPhSTwzRrCucyUpJ/b7lrUTBlI=,tag:JOMAcJN9frgVkpeV8pm39A==,type:str] - secret_key: ENC[AES256_GCM,data:ZhH2xkHf/wLXiooCBP+9ZctsGSOMW1de4xz8IGPS0z1GSTReM9h7sSHhw1vJl7iHsr8ne4ehXQYvLd/QcvLVShS2WVDSt1wXcKejnHgRTBk=,iv:xFSp0TRzBl0c4hx6UPv2CdFx9fMQ/oAes8JpWf1Zj7U=,tag:j8jrfilCe2gfyQr1kls4WQ==,type:str] -sops: - age: - - recipient: age1s0gzgh8c00tgnkgxrqyz2nu0k56xvc9ev3jdenkmu90egux9xfmsxvvj43 - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSdWlWdTRWOWhTNnFocWlI - QTlLNmJtUkZLSUNOdi9yQmNVQkVkQi94WFc4Cno2NjJCeURlc3FZd0hTK0IzUXJq - STZtU0pBZ25SaFJqMFZNaE41a1p0SkEKLS0tIFg2VlphajFlSjdtSFM1NTVZdjZm - SmVrN2pFQ0MzVS9aUUtrVnFUaUd6TzAKksOI8fvGkE8/Qmk8yT7jvvakFWSLDYKa - 7/pcTR0msB9P8zowsfe4pL+BJIObctXpyTRPCyyJg7/vOHqhuUv9Wg== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-19T23:31:55Z" - mac: ENC[AES256_GCM,data:sRk5KdZXohSwRWs6jP/jUA1OMBgnxKQu82Wp2w/4q1K0XXe1KRSDyiIIklgdK6JAW5U0291EzKLg8KgTks3YsibteA4L7eFbwFDTXOn7OJwjhKlzTZXqxEjmGHmsKLa7v8Y8nKuJqn2CUrfUuLzyvUaQN2tXfigq/DPC0HyROQs=,iv:zzWBrUcOumbaDArX9xRPzz3J98fA9wAgC1cKafejWT8=,tag:DL9o2cA5Uslq6WWGzlt8QA==,type:str] - unencrypted_suffix: _unencrypted - version: 3.12.2 diff --git a/manifest/overlays/production/storage/persistentvolume-nfs.yaml b/manifest/overlays/production/storage/persistentvolume-nfs.yaml deleted file mode 100644 index ce88611..0000000 --- a/manifest/overlays/production/storage/persistentvolume-nfs.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Optional static PV example for NFS-backed storage. -# This file is not referenced from kustomization.yaml by default because the -# default storage flow uses dynamic provisioning via the Longhorn PVC. -# -# To use this file: -# 1. Add storage/persistentvolume-nfs.yaml to resources in this overlay. -# 2. Update persistentvolumeclaim.yaml to set: -# storageClassName: nfs-shared -# volumeName: app-data -# accessModes: [ReadWriteMany] -# 3. Replace the placeholder NFS server and export path below. -apiVersion: v1 -kind: PersistentVolume -metadata: - name: app-data -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteMany - persistentVolumeReclaimPolicy: Retain - claimRef: - apiVersion: v1 - kind: PersistentVolumeClaim - name: app-data - namespace: security - storageClassName: nfs-shared - mountOptions: - - nfsvers=4.1 - nfs: - server: nfs.example.internal - path: /exports/app-data diff --git a/scripts/ci/check-sops-sync b/scripts/ci/check-sops-sync deleted file mode 100755 index 96f4d62..0000000 --- a/scripts/ci/check-sops-sync +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env zsh - -set -eu - -for cmd in git sops openssl; do - if ! command -v "${cmd}" >/dev/null 2>&1; then - echo "check-sops-sync: required command missing: ${cmd}" >&2 - exit 1 - fi -done - -if [ -z "${SOPS_SYNC_HMAC_KEY:-}" ]; then - echo "check-sops-sync: SOPS_SYNC_HMAC_KEY is required" >&2 - exit 1 -fi - -repo_root=$(git rev-parse --show-toplevel) -regex_script="${repo_root}/scripts/lib/sops-path-regexes" -lib_script="${repo_root}/scripts/lib/sops-sync-lib" - -if [ ! -x "${regex_script}" ]; then - echo "check-sops-sync: missing helper ${regex_script}" >&2 - exit 1 -fi - -if [ ! -f "${lib_script}" ]; then - echo "check-sops-sync: missing helper ${lib_script}" >&2 - exit 1 -fi - -. "${lib_script}" - -regexes=("${(@f)$("${regex_script}")}") -fail=0 - -while IFS= read -r tracked_path; do - [ -n "${tracked_path}" ] || continue - [[ "${tracked_path}" == *.enc.* ]] || continue - - if ! matches_any_rule "${tracked_path}" "${regexes[@]}"; then - continue - fi - - sidecar_path=$(hmac_sidecar_for_enc "${tracked_path}") - - if ! git ls-files --error-unmatch -- "${sidecar_path}" >/dev/null 2>&1; then - echo "check-sops-sync: missing sync sidecar for ${tracked_path}" >&2 - fail=1 - continue - fi - - if ! is_sops_encrypted_file "${tracked_path}"; then - echo "check-sops-sync: file is not valid SOPS-encrypted content: ${tracked_path}" >&2 - fail=1 - continue - fi - - sidecar_value=$(read_sidecar "${sidecar_path}" || true) - if [ -z "${sidecar_value}" ]; then - echo "check-sops-sync: sidecar is empty: ${sidecar_path}" >&2 - fail=1 - continue - fi - - if ! computed_hmac=$(decrypt_enc_to_plain "${tracked_path}" | compute_hmac_for_stdin); then - echo "check-sops-sync: failed to decrypt ${tracked_path}" >&2 - fail=1 - continue - fi - - if [ "${computed_hmac}" != "${sidecar_value}" ]; then - echo "check-sops-sync: decrypted plaintext HMAC does not match sidecar for ${tracked_path}" >&2 - fail=1 - fi -done < <(git ls-files) - -exit "${fail}" diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit deleted file mode 100755 index a3af18e..0000000 --- a/scripts/git-hooks/pre-commit +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env zsh - -set -eu - -for cmd in git sops openssl; do - if ! command -v "${cmd}" >/dev/null 2>&1; then - echo "pre-commit: required command missing: ${cmd}" >&2 - exit 1 - fi -done - -if [ -z "${SOPS_SYNC_HMAC_KEY:-}" ]; then - echo "pre-commit: SOPS_SYNC_HMAC_KEY is required" >&2 - exit 1 -fi - -repo_root=$(git rev-parse --show-toplevel) -regex_script="${repo_root}/scripts/lib/sops-path-regexes" -lib_script="${repo_root}/scripts/lib/sops-sync-lib" - -if [ ! -x "${regex_script}" ]; then - echo "pre-commit: missing helper ${regex_script}" >&2 - exit 1 -fi - -if [ ! -f "${lib_script}" ]; then - echo "pre-commit: missing helper ${lib_script}" >&2 - exit 1 -fi - -. "${lib_script}" - -regexes=("${(@f)$("${regex_script}")}") -typeset -A candidates - -while IFS= read -r staged_path; do - [ -n "${staged_path}" ] || continue - - candidate_enc="" - if [[ "${staged_path}" == *.enc.yaml ]]; then - candidate_enc="${staged_path}" - elif [[ "${staged_path}" == *.yaml ]]; then - if git ls-files --error-unmatch -- "${staged_path}" >/dev/null 2>&1; then - continue - fi - candidate_enc=$(plain_to_enc "${staged_path}") || continue - fi - - [ -n "${candidate_enc}" ] || continue - if matches_any_rule "${candidate_enc}" "${regexes[@]}"; then - candidates["${candidate_enc}"]=1 - fi -done < <(git diff --cached --name-only --diff-filter=ACMR) - -for enc_path in "${(@k)candidates}"; do - plain_path=$(enc_to_plain "${enc_path}") || continue - sidecar_path=$(hmac_sidecar_for_enc "${enc_path}") - - if [ ! -f "${plain_path}" ]; then - continue - fi - - if has_unstaged_changes "${plain_path}"; then - echo "pre-commit: plaintext file has unstaged changes: ${plain_path}" >&2 - echo "pre-commit: stage or revert the plaintext change before committing" >&2 - exit 1 - fi - - current_hmac=$(compute_hmac_for_file "${plain_path}") - tracked_hmac="" - if [ -f "${sidecar_path}" ]; then - tracked_hmac=$(read_sidecar "${sidecar_path}" || true) - fi - - if [ "${current_hmac}" = "${tracked_hmac}" ] && [ -f "${enc_path}" ]; then - continue - fi - - encrypt_plain_to_enc "${plain_path}" "${enc_path}" - write_sidecar "${sidecar_path}" "${current_hmac}" - git add "${enc_path}" "${sidecar_path}" -done - -for enc_path in "${(@k)candidates}"; do - sidecar_path=$(hmac_sidecar_for_enc "${enc_path}") - - if ! git cat-file -e ":${enc_path}" 2>/dev/null; then - echo "pre-commit: staged encrypted file missing: ${enc_path}" >&2 - exit 1 - fi - - if ! git cat-file -e ":${sidecar_path}" 2>/dev/null; then - echo "pre-commit: staged sync sidecar missing: ${sidecar_path}" >&2 - exit 1 - fi - - tmp_enc=$(mktemp) - trap 'rm -f "${tmp_enc}"' EXIT INT TERM - git show ":${enc_path}" > "${tmp_enc}" - - staged_hmac=$(git show ":${sidecar_path}" | tr -d '\r\n') - computed_hmac=$(decrypt_enc_to_plain "${tmp_enc}" | compute_hmac_for_stdin) - - rm -f "${tmp_enc}" - trap - EXIT INT TERM - - if [ "${computed_hmac}" != "${staged_hmac}" ]; then - echo "pre-commit: staged encrypted file and sidecar are out of sync: ${enc_path}" >&2 - exit 1 - fi -done diff --git a/scripts/install-git-hooks b/scripts/install-git-hooks deleted file mode 100755 index 43887bf..0000000 --- a/scripts/install-git-hooks +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env zsh - -set -eu - -repo_root=$(git rev-parse --show-toplevel) -hook_path="${repo_root}/.git/hooks/pre-commit" -target="${repo_root}/scripts/git-hooks/pre-commit" - -mkdir -p "${repo_root}/.git/hooks" -cat > "${hook_path}" </dev/null || pwd) -sops_file="${repo_root}/.sops.yaml" - -if [ ! -f "${sops_file}" ]; then - exit 0 -fi - -if command -v yq >/dev/null 2>&1; then - yq -r '.creation_rules[]?.path_regex | select(. != null)' "${sops_file}" - exit 0 -fi - -if command -v python3 >/dev/null 2>&1; then - python3 - "${sops_file}" <<'PY' -import sys - -try: - import yaml -except Exception as exc: - raise SystemExit(f"python3 fallback requires PyYAML: {exc}") - -with open(sys.argv[1], "r", encoding="utf-8") as handle: - data = yaml.safe_load(handle) or {} - -for rule in data.get("creation_rules") or []: - regex = rule.get("path_regex") - if regex: - print(regex) -PY - exit 0 -fi - -echo "Unable to read .sops.yaml path_regex values: install yq or python3 with PyYAML" >&2 -exit 1 diff --git a/scripts/lib/sops-sync-lib b/scripts/lib/sops-sync-lib deleted file mode 100644 index e0cafcd..0000000 --- a/scripts/lib/sops-sync-lib +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env zsh - -matches_any_rule() { - local path="$1" - shift - local regex - for regex in "$@"; do - [[ -n "${regex}" ]] || continue - if [[ "${path}" =~ ${regex} ]]; then - return 0 - fi - done - return 1 -} - -enc_to_plain() { - local enc="$1" - [[ "${enc}" == *.enc.yaml ]] || return 1 - print -r -- "${enc%.enc.yaml}.yaml" -} - -plain_to_enc() { - local plain="$1" - [[ "${plain}" == *.yaml ]] || return 1 - if [[ "${plain}" == *.enc.yaml ]]; then - print -r -- "${plain}" - else - print -r -- "${plain%.yaml}.enc.yaml" - fi -} - -hmac_sidecar_for_enc() { - local enc="$1" - print -r -- "${enc}.sync-hmac" -} - -encrypt_plain_to_enc() { - local plain="$1" - local enc="$2" - sops --encrypt --input-type yaml --output-type yaml --output "${enc}" "${plain}" -} - -decrypt_enc_to_plain() { - local enc="$1" - sops --decrypt "${enc}" -} - -compute_hmac_for_file() { - local path="$1" - openssl dgst -sha256 -hmac "${SOPS_SYNC_HMAC_KEY}" "${path}" | awk '{print $NF}' -} - -compute_hmac_for_stdin() { - openssl dgst -sha256 -hmac "${SOPS_SYNC_HMAC_KEY}" | awk '{print $NF}' -} - -read_sidecar() { - local sidecar="$1" - [ -f "${sidecar}" ] || return 1 - tr -d '\r\n' < "${sidecar}" -} - -write_sidecar() { - local sidecar="$1" - local value="$2" - print -r -- "${value}" > "${sidecar}" -} - -is_sops_encrypted_file() { - local path="$1" - [ -f "${path}" ] || return 1 - grep -q 'sops:' "${path}" && grep -q 'ENC\[' "${path}" -} - -has_unstaged_changes() { - local path="$1" - if git ls-files --error-unmatch -- "${path}" >/dev/null 2>&1; then - git diff --quiet -- "${path}" >/dev/null 2>&1 - return $? - fi - - if git diff --cached --name-only -- "${path}" | grep -Fxq "${path}"; then - git diff --quiet -- "${path}" >/dev/null 2>&1 - return $? - fi - - return 1 -}