11#! /bin/bash
2+ #
3+ # Note: data returned from "scan.sh" has escape characters, like '\\`, use `\` when putting that data back to DynamoDB (via this script) to avoid double escaping.
4+ #
25# MODE=mode ./utils/skip_cla_entry.sh sun-test-org '*' 'patterns'
3- # put-item Overwrites/adds the entire `skip_cla` entry.
4- # add-key Adds or updates a key/value inside the skip_cla map (preserves other keys)
5- # delete-key Removes a key from the skip_cla map
6- # delete-item Deletes the entire `skip_cla` entry.
6+ # put-item Overwrites/adds the entire `skip_cla` entry.
7+ # add-key Adds or updates a key/value inside the skip_cla map (preserves other keys)
8+ # delete-key Removes a key from the skip_cla map
9+ # delete-item Deletes the entire `skip_cla` entry.
10+ # add-key-item Adds one pattern item into an existing skip_cla key array (idempotent)
11+ # delete-key-item Removes one pattern item from an existing skip_cla key array (idempotent)
712#
813# MODE=add-key ./utils/skip_cla_entry.sh sun-test-org 'repo1' 're:vee?rendra;*;*'
914# MODE=add-key ./utils/skip_cla_entry.sh 'sun-test-org' 'repo1' 'lukaszgryglicki;re:gryglicki'
1621# STAGE=dev MODE=add-key ./utils/skip_cla_entry.sh 'openfga' 'vscode-ext' 'Copilot;re:^\d+\+Copilot@users\.noreply\.github\.com$;copilot-swe-agent[bot]'
1722# STAGE=prod MODE=add-key DEBUG=1 ./utils/skip_cla_entry.sh 'open-telemetry' 'opentelemetry-rust' '*;re:^\d+\+Copilot@users\.noreply\.github\.com$;copilot-swe-agent[bot]'
1823# STAGE=prod MODE=add-key ./utils/skip_cla_entry.sh 'openfga' 'vscode-ext' '[Copilot;re:^\d+\+Copilot@users\.noreply\.github\.com$;copilot-swe-agent[bot]||;re:^\d+\+Copilot@users\.noreply\.github\.com$;copilot-swe-agent[bot]]'
24+ # STAGE=prod MODE=add-key-item ./utils/skip_cla_entry.sh 'openfga' '*' 're:(?i)^copilot$;re:(?i)^\d+\+copilot@users\.noreply\.github\.com$;*'
25+ # STAGE=prod MODE=delete-key-item ./utils/skip_cla_entry.sh 'openfga' '*' 're:(?i)^copilot$;re:(?i)^\d+\+copilot@users\.noreply\.github\.com$;*'
1926
2027if [ -z " $MODE " ]
2128then
22- echo " $0 : MODE must be set, valid values are: put-item, add-key, delete-key, delete-item"
29+ echo " $0 : MODE must be set, valid values are: put-item, add-key, delete-key, delete-item, add-key-item, delete-key-item "
2330 exit 1
2431fi
2532
@@ -30,6 +37,89 @@ if [ -z "$REGION" ]; then
3037 REGION=' us-east-1'
3138fi
3239
40+ aws_key_json () {
41+ jq -cn --arg org " $1 " ' {"organization_name": {"S": $org}}'
42+ }
43+
44+ aws_attr_names_json () {
45+ jq -cn --arg repo " $1 " ' {"#repo": $repo}'
46+ }
47+
48+ aws_attr_value_string_json () {
49+ jq -cn --arg val " $1 " ' {":val": {"S": $val}}'
50+ }
51+
52+ get_skip_cla_key_value () {
53+ local org=" $1 "
54+ local repo=" $2 "
55+ local raw
56+
57+ if ! raw=" $(
58+ aws --profile " lfproduct-${STAGE} " --region " ${REGION} " dynamodb get-item \
59+ --table-name " cla-${STAGE} -github-orgs" \
60+ --key " $( aws_key_json " $org " ) " \
61+ --projection-expression ' skip_cla'
62+ ) " ; then
63+ echo " Error: failed to fetch skip_cla value from DynamoDB for organization='${org} ', repo='${repo} '" >&2
64+ return 1
65+ fi
66+
67+ if ! jq -r --arg repo " $repo " ' .Item.skip_cla.M[$repo].S // empty' <<< " $raw" ; then
68+ echo " Error: failed to parse skip_cla value from DynamoDB for organization='${org} ', repo='${repo} '" >&2
69+ return 1
70+ fi
71+ }
72+
73+ modify_skip_cla_array_value () {
74+ local current=" $1 "
75+ local action=" $2 "
76+ local item=" $3 "
77+ CURRENT=" $current " ACTION=" $action " ITEM=" $item " python3 - << 'PY '
78+ import os
79+
80+ current = os.environ.get("CURRENT", "")
81+ action = os.environ["ACTION"]
82+ item_raw = os.environ.get("ITEM", "")
83+
84+ def parse_flat_value(value: str):
85+ value = (value or "").strip()
86+ if not value:
87+ return []
88+ if value.startswith("["):
89+ if not value.endswith("]"):
90+ raise SystemExit("malformed array value: missing closing ]")
91+ inner = value[1:-1]
92+ parts = inner.split("||") if inner else []
93+ else:
94+ parts = [value]
95+ return [p.strip() for p in parts if p.strip()]
96+
97+ items = parse_flat_value(current)
98+ item_values = parse_flat_value(item_raw)
99+ changed = False
100+
101+ if action == "add":
102+ for item in item_values:
103+ if item not in items:
104+ items.append(item)
105+ changed = True
106+ elif action == "delete":
107+ if item_values:
108+ item_set = set(item_values)
109+ new_items = [p for p in items if p not in item_set]
110+ changed = (new_items != items)
111+ items = new_items
112+ else:
113+ raise SystemExit(f"unsupported action: {action}")
114+
115+ new_value = "" if not items else "[" + "||".join(items) + "]"
116+
117+ print("changed=true" if changed else "changed=false")
118+ print("delete_key=true" if changed and new_value == "" else "delete_key=false")
119+ print(f"new_value={new_value}")
120+ PY
121+ }
122+
33123case " $MODE " in
34124 put-item)
35125 if ( [ -z " ${1} " ] || [ -z " ${2} " ] || [ -z " ${3} " ] ); then
@@ -80,9 +170,76 @@ case "$MODE" in
80170 --key '{\" organization_name\" : {\" S\" : \" ${1} \" }}' \
81171 --update-expression 'REMOVE skip_cla'"
82172 ;;
173+ add-key-item|delete-key-item)
174+ if ( [ -z " ${1} " ] || [ -z " ${2} " ] || [ -z " ${3} " ] ); then
175+ echo " Usage: $0 <organization_name> <repo or re:repo-regexp or *> <pattern-item>"
176+ exit 1
177+ fi
178+
179+ org_name=" ${1} "
180+ repo_key=" ${2} "
181+ item_value=" ${3} "
182+ if ! current_value=" $( get_skip_cla_key_value " $org_name " " $repo_key " ) " ; then
183+ exit 1
184+ fi
185+
186+ if [ " $MODE " = " add-key-item" ]; then
187+ action=" add"
188+ else
189+ action=" delete"
190+ fi
191+
192+ if ! helper_output=" $( modify_skip_cla_array_value " $current_value " " $action " " $item_value " ) " ; then
193+ echo " Error: failed to modify skip_cla value for organization='${org_name} ', repo='${repo_key} ', mode='${MODE} '" >&2
194+ exit 1
195+ fi
196+
197+ mapfile -t result_lines <<< " $helper_output"
198+ if [ " ${# result_lines[@]} " -ne 3 ] || [[ " ${result_lines[0]} " != changed= * ]] || [[ " ${result_lines[1]} " != delete_key= * ]] || [[ " ${result_lines[2]} " != new_value= * ]]; then
199+ echo " Error: unexpected output from modify_skip_cla_array_value" >&2
200+ if [ ! -z " $DEBUG " ]; then
201+ echo " DEBUG: organization='${org_name} ', repo='${repo_key} ', mode='${MODE} ', action='${action} ', item='${item_value} '" >&2
202+ echo " DEBUG: helper_output='${helper_output} '" >&2
203+ fi
204+ exit 1
205+ fi
206+ changed=" ${result_lines[0]# changed=} "
207+ delete_key=" ${result_lines[1]# delete_key=} "
208+ new_value=" ${result_lines[2]# new_value=} "
209+
210+ if [ " $changed " != " true" ]; then
211+ if [ ! -z " $DEBUG " ]; then
212+ echo " No changes needed for organization='${org_name} ', repo='${repo_key} ', mode='${MODE} '"
213+ fi
214+ exit 0
215+ fi
216+
217+ if [ " $delete_key " = " true" ]; then
218+ if [ ! -z " $DEBUG " ]; then
219+ echo " aws --profile \" lfproduct-${STAGE} \" --region \" ${REGION} \" dynamodb update-item --table-name \" cla-${STAGE} -github-orgs\" --key \" $( aws_key_json " $org_name " ) \" --update-expression 'REMOVE skip_cla.#repo' --expression-attribute-names \" $( aws_attr_names_json " $repo_key " ) \" "
220+ fi
221+ aws --profile " lfproduct-${STAGE} " --region " ${REGION} " dynamodb update-item \
222+ --table-name " cla-${STAGE} -github-orgs" \
223+ --key " $( aws_key_json " $org_name " ) " \
224+ --update-expression ' REMOVE skip_cla.#repo' \
225+ --expression-attribute-names " $( aws_attr_names_json " $repo_key " ) "
226+ exit $?
227+ fi
228+
229+ if [ ! -z " $DEBUG " ]; then
230+ echo " aws --profile \" lfproduct-${STAGE} \" --region \" ${REGION} \" dynamodb update-item --table-name \" cla-${STAGE} -github-orgs\" --key \" $( aws_key_json " $org_name " ) \" --update-expression 'SET skip_cla.#repo = :val' --expression-attribute-names \" $( aws_attr_names_json " $repo_key " ) \" --expression-attribute-values \" $( aws_attr_value_string_json " $new_value " ) \" "
231+ fi
232+ aws --profile " lfproduct-${STAGE} " --region " ${REGION} " dynamodb update-item \
233+ --table-name " cla-${STAGE} -github-orgs" \
234+ --key " $( aws_key_json " $org_name " ) " \
235+ --update-expression ' SET skip_cla.#repo = :val' \
236+ --expression-attribute-names " $( aws_attr_names_json " $repo_key " ) " \
237+ --expression-attribute-values " $( aws_attr_value_string_json " $new_value " ) "
238+ exit $?
239+ ;;
83240 * )
84241 echo " $0 : Unknown MODE: $MODE "
85- echo " Valid values are: put-item, add-key, delete-key, delete-item"
242+ echo " Valid values are: put-item, add-key, delete-key, delete-item, add-key-item, delete-key-item "
86243 exit 1
87244 ;;
88245esac
93250fi
94251
95252eval $CMD
96-
0 commit comments