blob: 53115c0e7c0a0aa01aa8965a57f8fb2ad7c5fb34 [file] [log] [blame]
garciadeblas70461c52024-07-03 09:17:56 +02001#!/bin/bash
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
garciadeblas8a28f6d2025-06-11 11:11:56 +020016
garciadeblas70461c52024-07-03 09:17:56 +020017# Convert input string to a safe name for K8s resources
18function safe_name() {
19 local INPUT="$1"
20
21 echo "${INPUT,,}" | \
22 sed '/\.\// s|./||' | \
23 sed 's|\.|-|g' | \
24 sed 's|/|-|g' | \
25 sed 's|_|-|g' | \
26 sed 's| |-|g'
27}
28
29
30# Helper function to create a new age key pair
31function create_age_keypair() {
32 local AGE_KEY_NAME="$1"
33 local CREDENTIALS_DIR="${2:-"${CREDENTIALS_DIR}"}"
34
35 # Delete the keys in case they existed already
36 rm -f "${CREDENTIALS_DIR}/${AGE_KEY_NAME}.key" "${CREDENTIALS_DIR}/${AGE_KEY_NAME}.pub"
37
38 # Private key
39 age-keygen -o "${CREDENTIALS_DIR}/${AGE_KEY_NAME}.key"
40
41 # Public key (extracted from comment at private key)
42 age-keygen -y "${CREDENTIALS_DIR}/${AGE_KEY_NAME}.key" > "${CREDENTIALS_DIR}/${AGE_KEY_NAME}.pub"
43}
44
45
46# Helper function to in-place encrypt secrets in manifest
47function encrypt_secret_inplace() {
48 local FILE="$1"
49 local PUBLIC_KEY="$2"
50
51 sops \
52 --age=${PUBLIC_KEY} \
53 --encrypt \
54 --encrypted-regex '^(data|stringData)$' \
55 --in-place "${FILE}"
56}
57
58
59# Helper function to encrypt secrets from stdin
60function encrypt_secret_from_stdin() {
61 local PUBLIC_KEY="$1"
62
63 # Save secret manifest to temporary file
64 local TMPFILE=$(mktemp /tmp/secret.XXXXXXXXXX) || exit 1
65 cat > "${TMPFILE}"
66 # NOTE: Required workaround for busybox's version of `mktemp`, which is quite limited and does not support temporary files with extensions.
67 # `.yaml` is required for proper `sops` behaviour.
68 mv "${TMPFILE}" "${TMPFILE}.yaml"
69
70 # Encrypt
71 sops \
72 --age=${PUBLIC_KEY} \
73 --encrypt \
74 --encrypted-regex '^(data|stringData)$' \
75 --in-place "${TMPFILE}.yaml"
76
77 # Outputs the result and removes the temporary file
78 cat "${TMPFILE}.yaml" && rm -f "${TMPFILE}.yaml"
79}
80
81
82# Helper function to create secret manifest and encrypt with public key
83function kubectl_encrypt() {
84 local PUBLIC_KEY="$1"
85
86 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
87 local ALL_PARAMS=( "${@}" )
88 local PARAMS=( "${ALL_PARAMS[@]:1}" )
89
90 kubectl \
91 "${PARAMS[@]}" | \
92 encrypt_secret_from_stdin \
93 "${PUBLIC_KEY}"
94}
95
96
97# Generator function to convert source folder to `ResourceList`
98function folder2list_generator() {
99 local FOLDER="${1:-}"
100 local SUBSTENV="${2:-"false"}"
101 local FILTER="${3:-""}"
102
103 if [[ "${SUBSTENV,,}" == "true" ]];
104 then
105 # Mix input with new generated manifests and replace environment variables
106 join_lists \
107 <(cat) \
108 <(
109 kpt fn source "${FOLDER}" | \
110 replace_env_vars "${FILTER}"
111 )
112 else
113 # Mix input with new generated manifests
114 join_lists \
115 <(cat) \
116 <(
117 kpt fn source "${FOLDER}"
118 )
119 fi
120
121}
122
123
124# Function to convert source folder to `ResourceList` (no generator)
125function folder2list() {
126 local FOLDER="${1:-}"
127
128 kpt fn source "${FOLDER}"
129}
130
131
132# Helper function to convert manifest to `ResourceList`
133function manifest2list() {
134 kustomize cfg cat --wrap-kind ResourceList
135}
136
137
138# Helper function to convert `ResourceList` to manifests in folder structure.
139# - New folder must be created to render the manifests.
140function list2folder() {
141 local FOLDER="${1:-}"
142 local DRY_RUN="${2:-${DRY_RUN:-false}}"
143
144 if [[ "${DRY_RUN,,}" == "true" ]];
145 then
146 cat
147 else
148 kpt fn sink "${FOLDER}"
149 fi
150}
151
152
153# Helper function to convert `ResourceList` to manifests in folder structure.
154# - It copies (cp) the generated files/subfolders over the target folder.
155# - Pre-existing files and subfolder structure in target folder is preserved.
156function list2folder_cp_over() {
157 local FOLDER="${1:-}"
158 local DRY_RUN="${2:-${DRY_RUN:-false}}"
159
160 if [[ "${DRY_RUN,,}" == "true" ]];
161 then
162 cat
163 else
164 local TMPFOLDER=$(mktemp -d) || exit 1
165 kpt fn sink "${TMPFOLDER}/manifests"
166
167 # Copy the generated files over the target folder
168 mkdir -p "${FOLDER}/"
169 cp -r "${TMPFOLDER}/manifests/"* "${FOLDER}/"
170
171 # Delete temporary folder
172 rm -rf "${TMPFOLDER}"
173 fi
174}
175
176
177# Helper function to convert `ResourceList` to manifests in folder structure.
178# - It syncs the generated files/subfolders over the target folder.
179# - Pre-existing files and subfolder structure in target folder is deleted if not present in `ResourceList`.
180function list2folder_sync_replace() {
181 local FOLDER="${1:-}"
182 local DRY_RUN="${2:-${DRY_RUN:-false}}"
183
184 if [[ "${DRY_RUN,,}" == "true" ]];
185 then
186 cat
187 else
188 local TMPFOLDER=$(mktemp -d) || exit 1
189 kpt fn sink "${TMPFOLDER}/manifests"
190
191 # Copy the generated files over the target folder
192 mkdir -p "${FOLDER}/"
193 rsync -arh --exclude ".git" --exclude ".*" --delete \
194 "${TMPFOLDER}/manifests/" "${FOLDER}/"
195
196 # Delete temporary folder
197 rm -rf "${TMPFOLDER}"
198 fi
199}
200
201
202# Helper function to render **SAFELY** a single manifest coming from stdin into a profile, with a proper KSU subfolder
203function render_manifest_over_ksu() {
204 local KSU_NAME="$1"
205 local TARGET_PROFILE_FOLDER="$2"
206 local MANIFEST_FILENAME="$3"
207
208 manifest2list | \
209 set_filename_to_items \
210 "${MANIFEST_FILENAME}" | \
211 prepend_folder_path \
212 "${KSU_NAME}/" | \
213 list2folder_cp_over \
214 "${TARGET_PROFILE_FOLDER}"
215}
216
217
218# Set filename to `ResourceList` item
219function set_filename_to_items() {
220 local FILENAME="$1"
221
222 yq "(.items[]).metadata.annotations.\"config.kubernetes.io/path\" |= \"${FILENAME}\"" | \
223 yq "(.items[]).metadata.annotations.\"internal.config.kubernetes.io/path\" |= \"${FILENAME}\""
224}
225
226
227# Prepend folder path to `ResourceList`
228function prepend_folder_path() {
229 local PREFIX="$1"
230
231 if [[ (-z "${PREFIX}") || ("${PREFIX}" == ".") ]];
232 then
233 cat
234 else
235 yq "(.items[]).metadata.annotations.\"config.kubernetes.io/path\" |= \"${PREFIX}\" + ." | \
236 yq "(.items[]).metadata.annotations.\"internal.config.kubernetes.io/path\" |= \"${PREFIX}\" + ."
237 fi
238}
239
240
241# Rename file in `ResourceList`
242function rename_file_in_items() {
243 local SOURCE_NAME="$1"
244 local DEST_NAME="$2"
245
246 yq "(.items[].metadata.annotations | select (.\"config.kubernetes.io/path\" == \"${SOURCE_NAME}\")).\"config.kubernetes.io/path\" = \"${DEST_NAME}\"" | \
247 yq "(.items[].metadata.annotations | select (.\"internal.config.kubernetes.io/path\" == \"${SOURCE_NAME}\")).\"internal.config.kubernetes.io/path\" = \"${DEST_NAME}\""
248}
249
250
251# Get value from key in object in `ResourceList`
252function get_value_from_resourcelist() {
253 local KEY_PATH="$1"
254 local TARGET_FILTERS="${2:-}"
255 # Example: To get a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
256 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
257
258 yq "(.items[]${TARGET_FILTERS})${KEY_PATH}"
259}
260
261
262# Patch "replace" to item in `ResourceList`
263function patch_replace() {
264 local KEY_PATH="$1"
265 local VALUE="$2"
266 local TARGET_FILTERS="${3:-}"
267 # Example: To only patch a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
268 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
269
270 yq "(.items[]${TARGET_FILTERS})${KEY_PATH} = \"${VALUE}\""
271}
272
273
274# Add label to item in `ResourceList`
275function set_label() {
276 local KEY="$1"
277 local VALUE="$2"
278 local TARGET_FILTERS="${3:-}"
279 # Example: To only patch a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
280 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
281
282 yq "(.items[]${TARGET_FILTERS}).metadata.labels.${KEY} = \"${VALUE}\""
283}
284
285
286# Patch which "appends" to list existing in item in `ResourceList`
287function patch_add_to_list() {
288 local KEY_PATH="$1"
289 local VALUE="$2"
290 local TARGET_FILTERS="${3:-}"
291 # Example: To only patch a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
292 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
293
294 local VALUE_AS_JSON="$(echo "${VALUE}" | yq -o json -I0)"
295
296 yq "(.items[]${TARGET_FILTERS})${KEY_PATH} += ${VALUE_AS_JSON}"
297}
298
299
300# Patch which removes from list, existing in item in `ResourceList`
301function patch_delete_from_list() {
302 local KEY_PATH="$1"
303 local TARGET_FILTERS="${2:-}"
304
305 # local VALUE_AS_JSON="$(echo "${VALUE}" | yq -o json -I0)"
306
307 yq "del((.items[]${TARGET_FILTERS})${KEY_PATH})"
308}
309
310
311# Check if an element/value is in a given list, existing in item in `ResourceList`
312function is_element_on_list() {
313 local KEY_PATH="$1"
314 local VALUE="$2"
315 local TARGET_FILTERS="${3:-}"
316 # Example: To only patch a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
317 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
318
319 TEST_RESULT=$(
320 cat | \
321 yq "(.items[]${TARGET_FILTERS})${KEY_PATH} == \"${VALUE}\"" | grep "true"
322 )
323
324 if [[ "${TEST_RESULT}" != "true" ]]
325 then
326 echo "false"
327 else
328 echo "true"
329 fi
330}
331
332
333# Patch "replace" to item in `ResourceList` using a JSON as value
334function patch_replace_inline_json() {
335 local KEY_PATH="$1"
336 local VALUE="$2"
337 local TARGET_FILTERS="${3:-}"
338 # Example: To only patch a specific kind ("ProviderConfig") with a specific name ("default"). (TIP: Note the escaped double quotes).
339 # TARGET_FILTERS="| select(.kind == \"ProviderConfig\") | select(.metadata.name == \"default\")"
340
341 VALUE_AS_JSON="$(echo "${VALUE}" | yq -o=json)" yq "(.items[]${TARGET_FILTERS})${KEY_PATH} = strenv(VALUE_AS_JSON)"
342}
343
344
345# Delete full object from `ResourceList`
346function delete_object() {
347 local OBJECT_NAME="$1"
348 local KIND_NAME="$2"
349 local API_VERSION="${3:-""}"
350
351 # Calculated inputs
352 if [[ -z "${API_VERSION}" ]]
353 then
354 # If `apiVersion` is not specified
355 local TARGET_FILTER="| select(.kind == \"${KIND_NAME}\") | select(.metadata.name == \"${OBJECT_NAME}\")"
356 else
357 # Otherwise, it is taken into account
358 local TARGET_FILTER="| select(.kind == \"${KIND_NAME}\") | select(.apiVersion == \"${API_VERSION}\") | select(.metadata.name == \"${OBJECT_NAME}\")"
359 fi
360
361 # Delete object
362 yq "del((.items[]${TARGET_FILTER}))"
363}
364
365
366# Empty transformer function
367function noop_transformer() {
368 cat
369}
370
371
372# Add patch to `Kustomization` item in `ResourceList`
373function add_patch_to_kustomization() {
374 local KUSTOMIZATION_NAME="$1"
375 local FULL_PATCH_CONTENT="$2"
376
377 patch_add_to_list \
378 ".spec.patches" \
379 "${FULL_PATCH_CONTENT}" \
380 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
381}
382
rshri4240a7d2025-06-13 11:30:35 +0000383function patch_add_value_as_list() {
384 local KEY_PATH="$1"
385 local VALUE="$2"
386 local TARGET_FILTERS="${3:-}"
387
388 yq "(.items[]${TARGET_FILTERS})${KEY_PATH} += [${VALUE}]"
389}
390
391function add_patch_to_kustomization_as_list() {
392 local KUSTOMIZATION_NAME="$1"
393 local PATCH_VALUE="$2"
394
395 local VALUE_AS_JSON=$(echo "$PATCH_VALUE" | yq -o json -I0)
396
397 patch_add_value_as_list \
398 ".spec.patches" \
399 "${VALUE_AS_JSON}" \
400 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
401}
402
403function add_component_to_kustomization_as_list() {
404 local KUSTOMIZATION_NAME="$1"
405 shift
406 local COMPONENT=("$@")
407
408 local COMPONENT_JSON=$(printf '"%s",' "${COMPONENT[@]}" | sed 's/,$//')
409
410 patch_add_value_as_list \
411 ".spec.components" \
412 "${COMPONENT_JSON}" \
413 "| select(.kind == \"Kustomization\") | select(.metadata.name == \"${KUSTOMIZATION_NAME}\")"
414}
415
416function add_config_to_kustomization() {
417 local KUSTOMIZATION_NAME="$1"
418
419 yq '
420 (.items[] | select(.kind == "Kustomization") | select(.metadata.name == "'"${KUSTOMIZATION_NAME}"'"))
421 .spec.postBuild.substituteFrom = [{"kind": "ConfigMap", "name": "'"${KUSTOMIZATION_NAME}"'-parameters"}]
422 '
423}
garciadeblas70461c52024-07-03 09:17:56 +0200424
425# Helper function to produce a JSON Patch as specified in RFC 6902
426function as_json_patch() {
427 local OPERATION="$1"
428 local PATCH_PATH="$2"
429 local VALUES="$3"
430
431 # Convert to JSON dictionary to insert as map instead of string
432 local VALUES_AS_DICT=$(echo "${VALUES}" | yq -o=json)
433
434 # Generate a patch list
435 cat <<EOF | yq ".[0].value = ${VALUES_AS_DICT}"
436- op: ${OPERATION}
437 path: ${PATCH_PATH}
438EOF
439}
440
441
442# Helper function to produce a full patch, with target object + JSON Patch RFC 6902
443function full_json_patch() {
444 local TARGET_KIND="$1"
445 local TARGET_NAME="$2"
446 local OPERATION="$3"
447 local PATCH_PATH="$4"
garciadeblasc4afd542025-06-18 17:37:59 +0200448 # Gathers all optional parameters for transformer function (if any) and puts them into an array for further use
garciadeblas70461c52024-07-03 09:17:56 +0200449 local ALL_PARAMS=( "${@}" )
450 local VALUES=( "${ALL_PARAMS[@]:4}" )
451
452 # Accumulates value items into the patch
453 local PATCH_CONTENT=""
454 for VAL in "${VALUES[@]}"
455 do
456 local VAL_AS_DICT=$(echo "${VAL}" | yq -o=json)
457
458 ITEM=$(
459 yq --null-input ".op = \"${OPERATION}\", .path = \"${PATCH_PATH}\"" | \
460 yq ".value = ${VAL_AS_DICT}" | \
461 yq "[ . ]"
462 )
463
464 PATCH_CONTENT="$(echo -e "${PATCH_CONTENT}\n${ITEM}")"
465 done
466
467 # Wrap a full patch around, adding target specification
468 local PATCH_FULL=$(
469 yq --null-input ".target.kind = \"${TARGET_KIND}\", .target.name = \"${TARGET_NAME}\"" | \
garciadeblas62453242025-07-18 18:12:40 +0200470 yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' - \
471 <(printf "patch: |-\n%s\n" "$(echo "${PATCH_CONTENT}" | sed 's/^/ /')" ) | \
472 yq "[.]"
garciadeblas70461c52024-07-03 09:17:56 +0200473 )
474
475 echo "${PATCH_FULL}"
476}
477
478
479# Add values to `HelmRelease` by patch into `Kustomization` item in `ResourceList`
480function add_values_to_helmrelease_via_ks() {
481 local KUSTOMIZATION_NAME="$1"
482 local HELMRELEASE_NAME="$2"
483 local VALUES="$3"
484
485 # Embed into patch list
486 local FULL_PATCH_CONTENT="$(
487 full_json_patch \
488 "HelmRelease" \
489 "${HELMRELEASE_NAME}" \
490 "add" \
491 "/spec/values" \
492 "${VALUES}"
493 )"
494
495 # Path via intermediate Kustomization object
496 add_patch_to_kustomization \
497 "${KUSTOMIZATION_NAME}" \
498 "${FULL_PATCH_CONTENT}"
499}
500
501
502# Add values from Secret/ConfigMap to `HelmRelease` by patch into `Kustomization` item in `ResourceList`
503function add_referenced_values_to_helmrelease_via_ks() {
504 local KUSTOMIZATION_NAME="$1"
505 local HELMRELEASE_NAME="$2"
506 local VALUES_FROM="$3"
507
508 # Embed into patch list
509 local FULL_PATCH_CONTENT="$(
510 full_json_patch \
511 "HelmRelease" \
512 "${HELMRELEASE_NAME}" \
513 "add" \
514 "/spec/valuesFrom" \
515 "${VALUES_FROM}"
516 )"
517
518 # Path via intermediate Kustomization object
519 add_patch_to_kustomization \
520 "${KUSTOMIZATION_NAME}" \
521 "${FULL_PATCH_CONTENT}"
522}
523
524
525# High level function to add values from Secret, ConfigMap or both to `HelmRelease` by patch into `Kustomization` item in `ResourceList`
526function add_ref_values_to_hr_via_ks() {
527 local KUSTOMIZATION_NAME="$1"
528 local HELMRELEASE_NAME="$2"
529 local VALUES_SECRET_NAME="${3:-""}"
530 local VALUES_CM_NAME="${4:-""}"
531
532 local YAML_VALUES_FROM_BOTH=$(cat <<EOF
533- kind: Secret
534 name: "${VALUES_SECRET_NAME}"
535- kind: ConfigMap
536 name: "${VALUES_CM_NAME}"
537EOF
538 )
539 local YAML_VALUES_FROM_SECRET=$(cat <<EOF
540- kind: Secret
541 name: "${VALUES_SECRET_NAME}"
542EOF
543 )
544 local YAML_VALUES_FROM_CM=$(cat <<EOF
545- kind: ConfigMap
546 name: "${VALUES_CM_NAME}"
547EOF
548 )
549
550 # Chooses the appropriate YAML
551 VALUES_FROM=""
552 if [[ ( -n "${VALUES_SECRET_NAME}" ) && ( -n "${VALUES_CM_NAME}" ) ]];
553 then
554 VALUES_FROM="${YAML_VALUES_FROM_BOTH}"
555 elif [[ -n "${VALUES_SECRET_NAME}" ]];
556 then
557 VALUES_FROM="${YAML_VALUES_FROM_SECRET}"
558 elif [[ -n "${VALUES_CM_NAME}" ]];
559 then
560 VALUES_FROM="${YAML_VALUES_FROM_CM}"
561 else
562 # If none is set, it must be an error
563 return 1
564 fi
565
566 # Calls the low-level function
567 add_referenced_values_to_helmrelease_via_ks \
568 "${KUSTOMIZATION_NAME}" \
569 "${HELMRELEASE_NAME}" \
570 "${VALUES_FROM}"
571}
572
573# Substitute environment variables from stdin
574function replace_env_vars() {
575 # Optional parameter to filter environment variables that can be replaced
576 local FILTER=${1:-}
577
578 if [[ -n "${FILTER}" ]];
579 then
580 envsubst "${FILTER}"
581 else
582 envsubst
583 fi
584}
585
586
587# Join two `ResourceList` **files**
588#
589# Examples of use:
590# $ join_lists list_file1.yaml list_file2.yaml
591# $ join_lists <(manifest2list < manifest_file1.yaml) <(manifest2list < manifest_file2.yaml)
592# $ cat prueba1.yaml | manifest2list | join_lists - <(manifest2list < prueba2.yaml)
593#
594# NOTE: Duplicated keys and arrays may be overwritten by the latest file.
595# See: https://stackoverflow.com/questions/66694238/merging-two-yaml-documents-while-concatenating-arrays
596function join_lists() {
597 local FILE1="$1"
598 local FILE2="$2"
599
600 yq eval-all '. as $item ireduce ({}; . *+ $item)' \
601 "${FILE1}" \
602 "${FILE2}"
603}
604
605
606# Helper function to create a generator from a function that creates manifests
607function make_generator() {
608 local MANIFEST_FILENAME="$1"
609 local SOURCER_FUNCTION="$2"
610 # Gathers all optional parameters for the funcion (if any) and puts them into an array for further use
611 local ALL_PARAMS=( "${@}" )
612 local PARAMS=( "${ALL_PARAMS[@]:2}" )
613
614 # Mix input with new generated manifests
615 join_lists \
616 <(cat) \
617 <(
618 "${SOURCER_FUNCTION}" \
619 "${PARAMS[@]}" | \
620 manifest2list | \
621 set_filename_to_items "${MANIFEST_FILENAME}"
622 )
623}
624
625
626function transform_if() {
627 local TEST_RESULT=$1
628
629 # Gathers all optional parameters for transformer funcion (if any) and puts them into an array for further use
630 local ALL_PARAMS=( "${@}" )
631 local PARAMS=( "${ALL_PARAMS[@]:1}" )
632
633 # If test result is true (==0), then runs the transformation normally
634 if [[ "${TEST_RESULT}" == "0" ]];
635 then
636 "${PARAMS[@]}"
637 # Otherwise, just pass through
638 else
639 cat
640 fi
641}
642
643
644# Helper function to convert multiline input from stdin to comma-separed output
645function multiline2commalist() {
646 mapfile -t TMP_ARRAY < <(cat)
647 printf -v TMP_LIST '%s,' "${TMP_ARRAY[@]}"
648 echo "${TMP_LIST}" | sed 's/,$//g'
649}
650
651
652# Helper function to check pending changes in workdir to `fleet` repo
653function check_fleet_workdir_status() {
654 local FLEET_REPO_DIR="${1:-${FLEET_REPO_DIR}}"
655
656 pushd "${FLEET_REPO_DIR}"
657 git status
658 popd
659}
660
661
662# Helper function to commit changes in workdir to `fleet` repo
663function commit_and_push_to_fleet() {
664 local DEFAULT_COMMIT_MESSAGE="Committing latest changes to fleet repo at $(date +'%Y-%m-%d %H:%M:%S')"
665 local COMMIT_MESSAGE="${1:-${DEFAULT_COMMIT_MESSAGE}}"
666 local FLEET_REPO_DIR="${2:-${FLEET_REPO_DIR}}"
667
668 pushd "${FLEET_REPO_DIR}"
669 git status
670 git add -A
671 git commit -m "${COMMIT_MESSAGE}"
672 echo "${COMMIT_MESSAGE}"
673 git push
674 popd
675}