Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ The project publishes 0.x prerelease versions; a stable release line is not yet

### Fixed

- Release validation requires comparison links to start at the preceding
CHANGELOG release. Checksum generation handles each asset path separately on
GNU and BSD tools, including directories with spaces, and rejects empty sets.
- The npm installer no longer aborts a concurrent first run on Windows. The
per-asset cache lock previously treated only `EEXIST` as contention, but a
contended `mkdir` on Windows may raise `EPERM` or `EACCES`, so a process
Expand Down
4 changes: 2 additions & 2 deletions scripts/generate_release_checksums.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ actual_assets=()
while IFS= read -r actual_asset; do
actual_assets[${#actual_assets[@]}]="${actual_asset}"
done < <(
find "${asset_dir}" -mindepth 1 -maxdepth 1 -type f -printf '%f\n' | LC_ALL=C sort
find "${asset_dir}" -mindepth 1 -maxdepth 1 -type f -exec basename {} \; | LC_ALL=C sort
)
if [[ "${actual_assets[*]}" != "${assets[*]}" ]]; then
if [[ "${#actual_assets[@]}" -eq 0 ]] || [[ "${actual_assets[*]}" != "${assets[*]}" ]]; then
printf 'ERROR: release assets differ from the exact expected set\n' >&2
printf 'expected: %s\n' "${assets[*]}" >&2
printf 'actual: %s\n' "${actual_assets[*]:-<none>}" >&2
Expand Down
54 changes: 53 additions & 1 deletion scripts/test_release_guards.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,47 @@ fi
expect_failure "version mismatch" \
"${repo_root}/scripts/validate_release_version.sh" 999.999.999

# Keep CHANGELOG mutations in a fixture tree. All other version surfaces remain
# the real checkout, so failures below must reach the comparison-link guard.
version_fixture="${tmp_dir}/version fixture"
mkdir -p -- "${version_fixture}/scripts"
cp -- "${repo_root}/scripts/validate_release_version.sh" "${version_fixture}/scripts/"
for surface in npm server worker web deploy docs; do
ln -s -- "${repo_root}/${surface}" "${version_fixture}/${surface}"
done
fixture_validator="${version_fixture}/scripts/validate_release_version.sh"
previous_version=0.0.1

write_changelog_fixture() {
local link="$1"
local include_previous="${2:-yes}"
{
printf '## [Unreleased]\n\n## [%s] - 2026-01-01\n\n' "${current_version}"
if [[ "${include_previous}" == yes ]]; then
printf '## [%s] - 2025-01-01\n\n' "${previous_version}"
fi
printf '[Unreleased]: https://github.com/bytefolk/mem/compare/v%s...HEAD\n' "${current_version}"
printf '[%s]: https://github.com/bytefolk/mem/%s\n' "${current_version}" "${link}"
} > "${version_fixture}/CHANGELOG.md"
}

correct_compare="compare/v${previous_version}...${current_tag}"
write_changelog_fixture "${correct_compare}"
"${fixture_validator}" "${current_version}" >/dev/null
for wrong_base in v0.0.0 "${current_tag}" arbitrary; do
write_changelog_fixture "compare/${wrong_base}...${current_tag}"
expect_failure "wrong compare base ${wrong_base}" "${fixture_validator}" "${current_version}"
done
write_changelog_fixture "compare/v${previous_version}...v999.999.999"
expect_failure "wrong compare endpoint" "${fixture_validator}" "${current_version}"
write_changelog_fixture "${correct_compare}/extra"
expect_failure "compare link suffix" "${fixture_validator}" "${current_version}"
write_changelog_fixture "${correct_compare}" no
expect_failure "missing compare predecessor" "${fixture_validator}" "${current_version}"
write_changelog_fixture "releases/tag/${current_tag}" no
"${fixture_validator}" "${current_version}" >/dev/null
printf 'PASS: compare links require the exact predecessor and endpoint; tag links remain valid\n'

notes_file="${tmp_dir}/release-notes.md"
"${repo_root}/scripts/render_release_notes.sh" "${current_tag}" > "${notes_file}"
[[ -s "${notes_file}" ]] || die "release notes are empty"
Expand Down Expand Up @@ -190,8 +231,17 @@ expect_failure "annotated tag version mismatch" env \
FAKE_HEAD_COMMIT="${same_commit}" \
"${repo_root}/scripts/validate_release_source.sh" v999.999.999

asset_dir="${tmp_dir}/assets"
asset_dir="${tmp_dir}/assets with spaces"
mkdir -p -- "${asset_dir}"
# An empty set must fail with the intended diagnostic, including on Bash 3.2.
if "${repo_root}/scripts/generate_release_checksums.sh" \
"${current_tag}" "${same_commit}" "${asset_dir}" > "${tmp_dir}/empty-assets.log" 2>&1; then
die "empty asset directory: command unexpectedly succeeded"
fi
grep -Fq -- 'actual: <none>' "${tmp_dir}/empty-assets.log" ||
die "empty asset directory must report the missing set"
[[ ! -e "${asset_dir}/mem-mcp-checksums.txt" ]] ||
die "empty asset directory must not produce a manifest"
assets=(
mem-mcp-darwin-amd64
mem-mcp-darwin-arm64
Expand All @@ -203,6 +253,8 @@ assets=(
for asset in "${assets[@]}"; do
printf 'test payload for %s\n' "${asset}" > "${asset_dir}/${asset}"
done
# Use the real find, basename and sha256sum here. Unlike the Bash-only compat
# suite, this must also catch GNU basename rejecting batched find -exec paths.
"${repo_root}/scripts/generate_release_checksums.sh" \
"${current_tag}" "${same_commit}" "${asset_dir}" >/dev/null

Expand Down
28 changes: 23 additions & 5 deletions scripts/validate_release_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,29 @@ while IFS= read -r version_link; do
done < <(grep -F -- "[${version}]: " "${changelog}" || true)
[[ "${#version_links[@]}" == 1 ]] ||
die "CHANGELOG.md: expected exactly one [${version}] comparison link"
if [[ "${version_links[0]}" != \
"[${version}]: https://github.com/bytefolk/mem/releases/tag/v${version}" &&
"${version_links[0]}" != \
"[${version}]: https://github.com/bytefolk/mem/compare/"*"...v${version}" ]]; then
die "CHANGELOG.md: [${version}] link must terminate at v${version}"
compare_base="$(awk '
/^## \[/ && $0 != "## [Unreleased]" {
if (seen++) {
gsub(/^## \[/, "", $0)
gsub(/\].*/, "", $0)
print $0
exit
}
}
' "${changelog}")"

expected_compare_link=
if [[ -n "${compare_base}" ]]; then
expected_compare_link="[${version}]: https://github.com/bytefolk/mem/compare/v${compare_base}...v${version}"
fi
expected_release_link="[${version}]: https://github.com/bytefolk/mem/releases/tag/v${version}"

if [[ "${version_links[0]}" != "${expected_release_link}" ]] &&
[[ -z "${expected_compare_link}" || "${version_links[0]}" != "${expected_compare_link}" ]]; then
if [[ -z "${compare_base}" ]]; then
die "CHANGELOG.md: cannot derive compare base (need a second versioned heading below [${version}])"
fi
die "CHANGELOG.md: [${version}] link must be exactly:"$'\n'" ${expected_release_link}"$'\n'" or:"$'\n'" ${expected_compare_link}"
fi

printf 'PASS: all release version surfaces match %s\n' "${version}"
100 changes: 50 additions & 50 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading