@@ -22,7 +22,7 @@ inputs:
2222 required : false
2323 default : ' .'
2424 args :
25- description : ' Additional arguments to pass to specsync check'
25+ description : ' Additional whitespace-separated arguments to pass to specsync check (shell quoting is not supported) '
2626 required : false
2727 default : ' '
2828 lifecycle-enforce :
@@ -87,34 +87,30 @@ runs:
8787 ARCHIVE="specsync-${OS}-${ARCH}.exe.zip"
8888 curl -fsSL "${BASE_URL}/${ARCHIVE}" -o "${INSTALL_DIR}/specsync.zip"
8989
90- # Verify checksum if available
91- if curl -fsSL "${BASE_URL}/${ARCHIVE}.sha256" -o "${INSTALL_DIR}/specsync.sha256" 2>/dev/null; then
92- echo "Verifying checksum..."
93- cd "$INSTALL_DIR"
94- EXPECTED=$(awk '{print $1}' specsync.sha256)
95- ACTUAL=$(shasum -a 256 specsync.zip | awk '{print $1}')
96- if [ "$EXPECTED" != "$ACTUAL" ]; then
97- echo "::error::Checksum verification failed! Expected: $EXPECTED, Got: $ACTUAL"
98- exit 1
99- fi
100- echo "::notice::Checksum verified"
101- cd -
90+ curl -fsSL "${BASE_URL}/${ARCHIVE}.sha256" -o "${INSTALL_DIR}/specsync.sha256"
91+ echo "Verifying checksum..."
92+ cd "$INSTALL_DIR"
93+ EXPECTED=$(awk '{print $1}' specsync.sha256)
94+ ACTUAL=$(shasum -a 256 specsync.zip | awk '{print $1}')
95+ if [ "$EXPECTED" != "$ACTUAL" ]; then
96+ echo "::error::Checksum verification failed! Expected: $EXPECTED, Got: $ACTUAL"
97+ exit 1
10298 fi
99+ echo "::notice::Checksum verified"
100+ cd -
103101
104102 unzip -o "${INSTALL_DIR}/specsync.zip" -d "$INSTALL_DIR"
105103 mv "${INSTALL_DIR}/specsync-${OS}-${ARCH}.exe" "${INSTALL_DIR}/specsync.exe"
106104 else
107105 ARCHIVE="specsync-${OS}-${ARCH}.tar.gz"
108106 curl -fsSL "${BASE_URL}/${ARCHIVE}" -o "${INSTALL_DIR}/${ARCHIVE}"
109107
110- # Verify checksum if available
111- if curl -fsSL "${BASE_URL}/${ARCHIVE}.sha256" -o "${INSTALL_DIR}/${ARCHIVE}.sha256" 2>/dev/null; then
112- echo "Verifying checksum..."
113- cd "$INSTALL_DIR"
114- shasum -a 256 -c "${ARCHIVE}.sha256"
115- echo "::notice::Checksum verified"
116- cd -
117- fi
108+ curl -fsSL "${BASE_URL}/${ARCHIVE}.sha256" -o "${INSTALL_DIR}/${ARCHIVE}.sha256"
109+ echo "Verifying checksum..."
110+ cd "$INSTALL_DIR"
111+ shasum -a 256 -c "${ARCHIVE}.sha256"
112+ echo "::notice::Checksum verified"
113+ cd -
118114
119115 tar xz -C "$INSTALL_DIR" -f "${INSTALL_DIR}/${ARCHIVE}"
120116 mv "${INSTALL_DIR}/specsync-${OS}-${ARCH}" "${INSTALL_DIR}/specsync"
@@ -138,26 +134,36 @@ runs:
138134 run : |
139135 set -euo pipefail
140136
137+ if ! [[ "$INPUT_REQUIRE_COVERAGE" =~ ^[0-9]+$ ]] || [ "$INPUT_REQUIRE_COVERAGE" -gt 100 ]; then
138+ echo "::error::require-coverage must be an integer from 0 to 100"
139+ exit 1
140+ fi
141+
141142 # Always use --force in CI — hash cache is not committed, so
142143 # every CI run validates all specs from scratch.
143- CMD=" specsync check --force"
144+ CMD=( specsync check --force)
144145
145146 if [ "$INPUT_STRICT" = "true" ]; then
146- CMD="$CMD --strict"
147+ CMD+=( --strict)
147148 fi
148149
149150 if [ "$INPUT_REQUIRE_COVERAGE" != "0" ]; then
150- CMD="$CMD --require-coverage $INPUT_REQUIRE_COVERAGE"
151+ CMD+=( --require-coverage " $INPUT_REQUIRE_COVERAGE")
151152 fi
152153
153154 if [ -n "$INPUT_ARGS" ]; then
154- CMD="$CMD $INPUT_ARGS"
155+ # INPUT_ARGS is intentionally split on whitespace only. Shell quoting,
156+ # substitutions, pipes, and other shell syntax are not evaluated.
157+ read -r -a EXTRA_ARGS <<< "$INPUT_ARGS"
158+ CMD+=("${EXTRA_ARGS[@]}")
155159 fi
156160
157161 echo "::group::SpecSync Check"
158- echo "Running: $CMD"
162+ printf 'Running:'
163+ printf ' %q' "${CMD[@]}"
164+ printf '\n'
159165 EXIT_CODE=0
160- eval "$ CMD" || EXIT_CODE=$?
166+ "${ CMD[@]} " || EXIT_CODE=$?
161167 echo "::endgroup::"
162168
163169 # If lifecycle enforcement is enabled, run it (may override exit code)
@@ -171,14 +177,14 @@ runs:
171177 # Uses the same `specsync comment` pipeline as our own CI workflow
172178 # for identical output between the marketplace action and direct usage.
173179 if [ "$INPUT_COMMENT" = "true" ]; then
174- COMMENT_CMD=" specsync comment"
180+ COMMENT_CMD=( specsync comment)
175181 if [ "$INPUT_STRICT" = "true" ]; then
176- COMMENT_CMD="$COMMENT_CMD --strict"
182+ COMMENT_CMD+=( --strict)
177183 fi
178184 if [ "$INPUT_REQUIRE_COVERAGE" != "0" ]; then
179- COMMENT_CMD="$COMMENT_CMD --require-coverage $INPUT_REQUIRE_COVERAGE"
185+ COMMENT_CMD+=( --require-coverage " $INPUT_REQUIRE_COVERAGE")
180186 fi
181- COMMENT_OUTPUT=$(eval "$ COMMENT_CMD" 2>/dev/null) || true
187+ COMMENT_OUTPUT=$("${ COMMENT_CMD[@]} " 2>/dev/null) || true
182188 {
183189 echo "SPECSYNC_MARKDOWN<<SPECSYNC_EOF"
184190 echo "$COMMENT_OUTPUT"
0 commit comments