You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
sensitive diff scan: no real token, internal IP/URL, or local user path
Safety
This PR does not change the CLI version, create or publish a tag/Release, publish npm, change Doc Agent mapping, or change changelog generation. It only hardens the real-run GitHub Tag/Draft Release boundary.
When releaseCreateStatus: 1 and releaseCreateMaterializes: true, the mock writes the state file and resets MOCK_RELEASE_LOOKUP_COUNT to 0 before returning the non-zero exit code. If the production script were to call gh release create a second time (the exact idempotency failure this test is guarding against), the counter would be reset again, restarting the visibility clock. While the current production code is correct, this mock behaviour makes a second accidental create call harder to detect: the second call would appear to succeed and the lookup would start fresh. Adding a guard such as if [[ ! -s "${MOCK_RELEASE_STATE_FILE}" ]] around the state-file write would cause a second create call to produce a detectable no-op, making the existing assertion assert.equal(...gh release create..., 1) the only defence needed.
if [[ "${MOCK_RELEASE_CREATE_MATERIALIZES}" == "true" ]]; then
" # Only materialize on the first create call; a second call should be a detectable no-op."
" if [[ ! -s \"${MOCK_RELEASE_STATE_FILE}\" ]]; then"
" created_state='draft'"
" if [[ \" $* \" == *' --prerelease '* ]]; then created_state='prerelease-draft'; fi"
" printf '%s\\n' \"${created_state}\" > \"${MOCK_RELEASE_STATE_FILE}\""
" printf '0\\n' > \"${MOCK_RELEASE_LOOKUP_COUNT}\""
" fi"
" fi"
" exit \"${MOCK_RELEASE_CREATE_STATUS}\""
The "reconciles an ambiguous create response" test does not assert that no second gh release create call was issued in the reconciled case — only that create appears exactly once in total. Because the production code is correct, this currently passes, but if a regression caused the code to retry the create (resetting the mock counter), the test would still see exactly one create in the log if the mock never incremented it. Adding assert.doesNotMatch(reconciled.callLog, /gh release create.*\ngh release create/) or checking total count before and after the ambiguous path would make the guard explicit.
assetVisibilityDelay: 100 is used here to ensure no assets are shown during reconciliation before the upload runs. However, as described in the mock logic, this delay is overridden the moment MOCK_ASSETS_VISIBLE_FILE is created by the upload handler — so any value ≥ 1 would have the same effect given RELEASE_RETRY_ATTEMPTS: "4". The magic number 100 strongly implies a time delay and misleads readers into thinking it controls post-upload asset polling. A comment explaining the intent (or a lower value like 5 matching the retry budget) would make the fixture self-documenting.
💡 Suggested Change
Before:
test("publish state machine resumes an incomplete Draft instead of creating a second Release", () => {
const result = runPublishFixture({
releaseCreateStatus: 1,
releaseCreateMaterializes: true,
assetVisibilityDelay: 100,
});
After:
const result = runPublishFixture({
releaseCreateStatus: 1,
releaseCreateMaterializes: true,
// Use a large delay to guarantee assets appear 'absent' before upload runs;
// the upload mock itself marks assets visible, overriding this counter.
assetVisibilityDelay: 100,
});
The run_retryable_command function only captures stderr (2>"${retry_log}"), so any stdout output from the wrapped command (e.g., gh release upload progress lines) is passed straight to the caller's stdout. This is intentional for normal output, but if a command writes its actionable error message to stdout rather than stderr, the error will not appear in the log and will be silently lost on retry (only the last attempt's stderr is printed). gh CLI typically uses stderr for errors, so this is low-risk today, but it is worth documenting the assumption or capturing both streams.
Additionally, the same fixed log path is shared across every invocation of run_retryable_command within a single process ($$ is the PID, not per-call). Inside upload_and_update_draft, the upload's log is silently overwritten by the edit invocation. If the upload succeeds but the edit fails on its last attempt, the upload's earlier stderr is gone — only the edit's last-attempt stderr is printed. This is harmless today because the upload would have already exited on failure, but the shared log makes the retry logging fragile.
💡 Suggested Change
Before:
local retry_log="${RUNNER_TEMP}/memos-cli-release-command-$$.log"
for ((attempt = 1; attempt <= release_retry_attempts; attempt += 1)); do
set +e
"$@" 2>"${retry_log}"
After:
# Use a per-call unique log path to avoid overwriting logs between consecutive
# run_retryable_command invocations within the same process.
local retry_log="${RUNNER_TEMP}/memos-cli-release-command-$$-${RANDOM}.log"
for ((attempt = 1; attempt <= release_retry_attempts; attempt += 1)); do
set +e
"$@" 2>"${retry_log}"
When wait_for_visibility=true and ls-remote succeeds but returns an empty SHA on every attempt (i.e., the tag never becomes visible within release_retry_attempts polls), the for loop exhausts all retries and falls through to return 0 at line 119 — returning success with empty output. The caller at line 307 treats empty output as "tag not found" and emits a hard error, so the end-to-end behaviour is correct. However, the function itself returns 0 in a situation that is not a success (visibility timeout), making it harder to distinguish a clean "tag absent" result from a polling timeout. Consider returning a distinct non-zero status (or at minimum a comment) so the semantics are explicit.
💡 Suggested Change
Before:
if [[ "${attempt}" != "${release_retry_attempts}" ]]; then
retry_sleep "${attempt}"
fi
done
return 0
}
After:
if [[ "${attempt}" != "${release_retry_attempts}" ]]; then
retry_sleep "${attempt}"
fi
done
# Visibility timeout: tag was not observed within the retry budget.
# Return empty output with success so callers check -z on the output;
# a dedicated exit code could make the timeout case explicit if needed.
return 0
}
6. .github/scripts/publish-cli-release.sh (L353)
validate_draft_release (and the inline check at line 353) relies on set -e to propagate its non-zero exit rather than an explicit || exit 1. Inside a complex if/else chain that has already seen several set +e/set -e toggles, relying on implicit set -e propagation is brittle: any future wrapping of this call in an if, &&, ||, or ! expression would silently suppress the exit. An explicit guard makes intent clear.
💡 Suggested Change
Before:
validate_draft_release
After:
if ! validate_draft_release; then
echo "::error::Draft Release ${CURRENT_TAG} could not be verified after asset resume."
exit 1
fi
🧹 Filtered 1 low-confidence OCR finding(s) before posting/fix-loop (existing_code_mismatch: 1).
Generated by cloud-assistant via Open Code Review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
area:coreMOS 编排层 / 框架底座 / 跨模块问题status:readyReady for implementation; waiting for assignee or AI dispatch | 可进入实现,等待认领或派发
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
gh release create --verify-tagValidation
node --test .github/scripts/prepare-cli-release.test.mjs— 45 passedbash -n .github/scripts/publish-cli-release.shnode --check .github/scripts/prepare-cli-release.mjsbash -n scripts/build-binary.shgit diff --checkSafety
This PR does not change the CLI version, create or publish a tag/Release, publish npm, change Doc Agent mapping, or change changelog generation. It only hardens the real-run GitHub Tag/Draft Release boundary.