-
Notifications
You must be signed in to change notification settings - Fork 2
186 lines (146 loc) · 6.62 KB
/
verify-zip.yml
File metadata and controls
186 lines (146 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
name: Verify ZIPs - Checksum + CAP Structure
on:
pull_request:
types: [opened, reopened, synchronize, edited, ready_for_review]
jobs:
verify-zips:
runs-on: ubuntu-latest
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
steps:
- name: Checkout PR HEAD
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Step 1 - Verify sha256 in manifest.json for changed ZIPs
shell: bash
run: |
set -euo pipefail
# Collect changed ZIPs into a file that the next step can reuse
: > changed_zips.txt
while IFS= read -r -d '' f; do
[[ "$f" == *.zip ]] && printf '%s\n' "$f" >> changed_zips.txt
done < <(git diff --name-only -z "$BASE_SHA" "$HEAD_SHA")
if [[ ! -s changed_zips.txt ]]; then
echo "No .zip files changed in this PR. Nothing to verify."
exit 0
fi
echo "Changed ZIP files:"
sed 's/^/ - /' changed_zips.txt
while IFS= read -r zip_path; do
# If deleted in PR head, skip (change if you want deletions to fail)
if [[ ! -f "$zip_path" ]]; then
echo "Skipping (not present in PR head): $zip_path"
continue
fi
dir="$(dirname "$zip_path")"
manifest_path="$dir/manifest.json"
if [[ ! -f "$manifest_path" ]]; then
echo "::error file=$manifest_path::manifest.json not found next to ZIP ($zip_path)"
exit 1
fi
# Compute checksum of the ZIP
computed="$(sha256sum "$zip_path" | awk '{print $1}' | tr '[:upper:]' '[:lower:]')"
# Read sha256 from manifest.json
manifest_sha="$(jq -r '.sha256 // empty' "$manifest_path" | tr '[:upper:]' '[:lower:]')"
if [[ -z "$manifest_sha" || "$manifest_sha" == "null" ]]; then
echo "::error file=$manifest_path::Missing or empty \"sha256\" field in manifest.json"
exit 1
fi
echo "ZIP: $zip_path"
echo "Computed: $computed"
echo "Manifest: $manifest_sha"
if [[ "$computed" != "$manifest_sha" ]]; then
echo "::error file=$manifest_path::sha256 mismatch for $zip_path (computed=$computed, manifest=$manifest_sha)"
exit 1
fi
echo "SUCCESS - sha256 matches for $zip_path"
done < changed_zips.txt
- name: Step 2 - Unzip and verify root children are allowed
shell: bash
run: |
set -euo pipefail
allowed=("impex" "app-configuration" "storefront-next" "cartridges")
[[ -s changed_zips.txt ]] || exit 0
while IFS= read -r zip_path; do
[[ -f "$zip_path" ]] || continue
tmpdir="$(mktemp -d)"
unzip -q "$zip_path" -d "$tmpdir"
# Root should be exactly one directory (the wrapper folder)
mapfile -t roots < <(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | grep -v '^__MACOSX$' | sort -u)
if [[ ${#roots[@]} -ne 1 ]]; then
echo "::error file=$zip_path::Expected exactly 1 root directory after unzip, found ${#roots[@]}: ${roots[*]}"
rm -rf "$tmpdir"
exit 1
fi
root="$tmpdir/${roots[0]}"
# Check immediate child directories of root are allowed
mapfile -t children < <(find "$root" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | grep -v '^__MACOSX$' | sort -u)
for c in "${children[@]}"; do
ok=false
for a in "${allowed[@]}"; do
[[ "$c" == "$a" ]] && ok=true && break
done
if [[ "$ok" == "false" ]]; then
echo "::error file=$zip_path::Disallowed directory under root: \"$c\". Allowed: ${allowed[*]}"
rm -rf "$tmpdir"
exit 1
fi
done
rm -rf "$tmpdir"
done < changed_zips.txt
- name: Step 3 - Verify tax hooks exist and scripts resolve
shell: bash
run: |
set -euo pipefail
required_hooks=(
"dw.apps.checkout.tax.calculate"
"dw.apps.checkout.tax.commit"
"dw.apps.checkout.tax.cancel"
)
[[ -s changed_zips.txt ]] || exit 0
while IFS= read -r zip_path; do
[[ -f "$zip_path" ]] || continue
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' RETURN
unzip -q "$zip_path" -d "$tmpdir"
# Root should be exactly one directory (wrapper)
mapfile -t roots < <(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | grep -v '^__MACOSX$' | sort -u)
if [[ ${#roots[@]} -ne 1 ]]; then
echo "::error file=$zip_path::Expected exactly 1 root directory after unzip, found ${#roots[@]}: ${roots[*]}"
exit 1
fi
root="$tmpdir/${roots[0]}"
base="$root/cartridges/site_cartridges"
if [[ ! -d "$base" ]]; then
echo "::error file=$zip_path::Missing directory cartridges/site_cartridges in ZIP"
exit 1
fi
hooks_file="$(find "$base" -type f -name hooks.json -print -quit)"
if [[ -z "$hooks_file" ]]; then
echo "::error file=$zip_path::hooks.json not found under cartridges/site_cartridges"
exit 1
fi
hooks_dir="$(dirname "$hooks_file")"
echo "ZIP: $zip_path"
echo "hooks.json: $hooks_file"
# Validate required hooks are present, and each has a script that exists
for hook in "${required_hooks[@]}"; do
script="$(jq -r --arg name "$hook" '.hooks[]? | select(.name == $name) | .script // empty' "$hooks_file" | head -n 1)"
if [[ -z "$script" || "$script" == "null" ]]; then
echo "::error file=$hooks_file::Missing hook or script for \"$hook\""
exit 1
fi
# script is relative to hooks.json location (strip leading ./)
rel="${script#./}"
target="$hooks_dir/$rel"
if [[ ! -f "$target" ]]; then
echo "::error file=$hooks_file::Script for \"$hook\" points to missing file: $script (resolved: $target)"
exit 1
fi
done
echo "SUCCESS - required tax hooks and scripts verified for $zip_path"
rm -rf "$tmpdir"
trap - RETURN
done < changed_zips.txt