mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
Extract release source archive packaging
This commit is contained in:
parent
885ba9f6f9
commit
a51d0fb605
10 changed files with 243 additions and 34 deletions
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
|
|
@ -109,16 +109,8 @@ jobs:
|
|||
|
||||
- name: Build source archive
|
||||
run: |
|
||||
set -eu
|
||||
version=$(sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' include/common.h)
|
||||
commit=$(git rev-list -n 1 "${GITHUB_REF_NAME}")
|
||||
mkdir -p dist
|
||||
git archive --format=tar.gz --prefix="TNT-${version}/" \
|
||||
-o "dist/tnt-chat-v${version}-source.tar.gz" "$commit"
|
||||
tar -tzf "dist/tnt-chat-v${version}-source.tar.gz" >/dev/null
|
||||
tar -tzf "dist/tnt-chat-v${version}-source.tar.gz" | grep -q "^TNT-${version}/LICENSE$"
|
||||
tar -tzf "dist/tnt-chat-v${version}-source.tar.gz" | grep -q "^TNT-${version}/packaging/README.md$"
|
||||
sha256sum "dist/tnt-chat-v${version}-source.tar.gz"
|
||||
archive=$(scripts/package_source_archive.sh "${GITHUB_REF_NAME}" dist)
|
||||
sha256sum "$archive"
|
||||
|
||||
- name: Upload source archive
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -134,6 +134,7 @@ script-test: all
|
|||
@cd tests && ./test_docs_help_surface.sh
|
||||
@cd tests && ./test_logrotate.sh
|
||||
@cd tests && ./test_message_log_tool.sh
|
||||
@cd tests && ./test_source_archive.sh
|
||||
@cd tests && ./test_release_artifact_gate.sh
|
||||
|
||||
integration-test: all
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
- Added CI governance layers for fast PR checks, release-branch validation,
|
||||
extended runtime validation, container portability builds, and package recipe
|
||||
validation.
|
||||
- Added `scripts/package_source_archive.sh` so the explicit release source
|
||||
archive can be built and tested locally instead of living only inside the
|
||||
GitHub release workflow.
|
||||
- Added a `config_defaults` module and unit coverage for runtime default
|
||||
values, env keys, and accepted numeric ranges.
|
||||
- Added a dedicated `tntctl_text` module with unit coverage for local
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ Runs only for SemVer tags matching `vMAJOR.MINOR.PATCH`:
|
|||
- Builds Linux glibc AMD64 and ARM64 binaries.
|
||||
- Builds macOS Intel and Apple Silicon binaries.
|
||||
- Verifies binary architecture labels.
|
||||
- Builds an explicit source archive: `tnt-chat-vX.Y.Z-source.tar.gz`.
|
||||
- Builds an explicit source archive with `scripts/package_source_archive.sh`:
|
||||
`tnt-chat-vX.Y.Z-source.tar.gz`.
|
||||
- Runs `scripts/package_release_assets.sh` to collect release assets, verify
|
||||
expected asset names, verify binary architecture labels again after artifact
|
||||
download, verify source archive contents, generate `checksums.txt`, and verify
|
||||
|
|
@ -217,6 +218,7 @@ Stage 1, implemented now:
|
|||
- Draft release, manual publish.
|
||||
- Binary architecture validation.
|
||||
- Source archive validation.
|
||||
- Local source-archive dry-run coverage.
|
||||
- SHA-256 checksums for every release asset.
|
||||
- Package recipe checksum preflight.
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@ sha256_of() {
|
|||
fi
|
||||
}
|
||||
|
||||
require_archive_entry() {
|
||||
entry=$1
|
||||
label=$2
|
||||
|
||||
printf '%s\n' "$source_listing" |
|
||||
awk -v target="$entry" '$0 == target { found = 1 } END { exit found ? 0 : 1 }' ||
|
||||
fail "SOURCE_TARBALL is missing $label"
|
||||
}
|
||||
|
||||
version=$(sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' include/common.h)
|
||||
[ -n "$version" ] || fail "could not read TNT_VERSION from include/common.h"
|
||||
release_source="tnt-chat-v${version}-source.tar.gz"
|
||||
|
|
@ -30,14 +39,13 @@ source_tarball=${SOURCE_TARBALL:-${RELEASE_SOURCE_TARBALL:-}}
|
|||
fail "set SOURCE_TARBALL to the explicit release source archive"
|
||||
[ -f "$source_tarball" ] ||
|
||||
fail "SOURCE_TARBALL does not exist: $source_tarball"
|
||||
tar -tzf "$source_tarball" >/dev/null ||
|
||||
source_listing=$(tar -tzf "$source_tarball") ||
|
||||
fail "SOURCE_TARBALL is not a readable tar.gz archive"
|
||||
tar -tzf "$source_tarball" | grep -q "^TNT-$version/LICENSE$" ||
|
||||
fail "SOURCE_TARBALL is missing LICENSE"
|
||||
tar -tzf "$source_tarball" | grep -q "^TNT-$version/packaging/README.md$" ||
|
||||
fail "SOURCE_TARBALL is missing packaging/README.md"
|
||||
tar -tzf "$source_tarball" | grep -q "^TNT-$version/src/tntctl.c$" ||
|
||||
fail "SOURCE_TARBALL is missing src/tntctl.c"
|
||||
require_archive_entry "TNT-$version/LICENSE" "LICENSE"
|
||||
require_archive_entry "TNT-$version/packaging/README.md" "packaging/README.md"
|
||||
require_archive_entry "TNT-$version/src/tntctl.c" "src/tntctl.c"
|
||||
require_archive_entry "TNT-$version/tnt.1" "tnt.1"
|
||||
require_archive_entry "TNT-$version/tntctl.1" "tntctl.1"
|
||||
|
||||
! grep -R "REPLACE_WITH_EMAIL" packaging/arch packaging/debian >/dev/null ||
|
||||
fail "replace maintainer email placeholders before package publishing"
|
||||
|
|
|
|||
|
|
@ -49,6 +49,21 @@ require_file_label() {
|
|||
fail "unexpected file type for $(basename "$path"): $label"
|
||||
}
|
||||
|
||||
archive_has_entry() {
|
||||
entry=$1
|
||||
|
||||
printf '%s\n' "$archive_listing" |
|
||||
awk -v target="$entry" '$0 == target { found = 1 } END { exit found ? 0 : 1 }'
|
||||
}
|
||||
|
||||
require_archive_entry() {
|
||||
entry=$1
|
||||
label=$2
|
||||
|
||||
archive_has_entry "$entry" ||
|
||||
fail "source archive is missing $label"
|
||||
}
|
||||
|
||||
verify_asset() {
|
||||
name=$1
|
||||
path=$2
|
||||
|
|
@ -69,13 +84,13 @@ verify_asset() {
|
|||
require_file_label "$path" 'Mach-O 64-bit.*arm64'
|
||||
;;
|
||||
tnt-chat-v*-source.tar.gz)
|
||||
tar -tzf "$path" >/dev/null
|
||||
tar -tzf "$path" | grep -q "^TNT-$VERSION/LICENSE$" ||
|
||||
fail "source archive is missing LICENSE"
|
||||
tar -tzf "$path" | grep -q "^TNT-$VERSION/src/tntctl.c$" ||
|
||||
fail "source archive is missing src/tntctl.c"
|
||||
tar -tzf "$path" | grep -q "^TNT-$VERSION/packaging/README.md$" ||
|
||||
fail "source archive is missing packaging/README.md"
|
||||
archive_listing=$(tar -tzf "$path") ||
|
||||
fail "source archive is not a readable tar.gz: $name"
|
||||
require_archive_entry "TNT-$VERSION/LICENSE" "LICENSE"
|
||||
require_archive_entry "TNT-$VERSION/src/tntctl.c" "src/tntctl.c"
|
||||
require_archive_entry "TNT-$VERSION/packaging/README.md" "packaging/README.md"
|
||||
require_archive_entry "TNT-$VERSION/tnt.1" "tnt.1"
|
||||
require_archive_entry "TNT-$VERSION/tntctl.1" "tntctl.1"
|
||||
;;
|
||||
*)
|
||||
fail "unexpected release artifact: $name"
|
||||
|
|
|
|||
89
scripts/package_source_archive.sh
Executable file
89
scripts/package_source_archive.sh
Executable file
|
|
@ -0,0 +1,89 @@
|
|||
#!/bin/sh
|
||||
# Build and validate the explicit release source archive.
|
||||
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/package_source_archive.sh REF [OUT_DIR]
|
||||
|
||||
REF is a git ref, commit, or release tag to archive. OUT_DIR defaults to dist.
|
||||
The archive name is tnt-chat-v$TNT_VERSION-source.tar.gz, and its top-level
|
||||
directory is TNT-$TNT_VERSION/.
|
||||
|
||||
When REF is a SemVer tag such as v1.2.3 or refs/tags/v1.2.3, the tag must match
|
||||
TNT_VERSION from that ref. This script only builds and validates the archive; it
|
||||
does not tag, publish, upload, or deploy.
|
||||
USAGE
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "source-archive: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_archive_entry() {
|
||||
entry=$1
|
||||
label=$2
|
||||
|
||||
printf '%s\n' "$archive_listing" |
|
||||
awk -v target="$entry" '$0 == target { found = 1 } END { exit found ? 0 : 1 }' ||
|
||||
fail "source archive is missing $label"
|
||||
}
|
||||
|
||||
[ "${1:-}" != "-h" ] && [ "${1:-}" != "--help" ] || {
|
||||
usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
REF=${1:-}
|
||||
OUT_DIR=${2:-dist}
|
||||
|
||||
[ -n "$REF" ] || {
|
||||
usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
cd "$ROOT"
|
||||
|
||||
commit=$(git rev-parse --verify "$REF^{commit}") ||
|
||||
fail "could not resolve git ref: $REF"
|
||||
|
||||
case "$OUT_DIR" in
|
||||
/*) ;;
|
||||
*) OUT_DIR="$ROOT/$OUT_DIR" ;;
|
||||
esac
|
||||
|
||||
version=$(git show "$commit:include/common.h" |
|
||||
sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p')
|
||||
[ -n "$version" ] || fail "could not read TNT_VERSION from $REF"
|
||||
|
||||
case "$REF" in
|
||||
refs/tags/v[0-9]*.[0-9]*.[0-9]*)
|
||||
tag=${REF#refs/tags/}
|
||||
[ "$tag" = "v$version" ] ||
|
||||
fail "release tag $tag does not match TNT_VERSION $version"
|
||||
;;
|
||||
v[0-9]*.[0-9]*.[0-9]*)
|
||||
[ "$REF" = "v$version" ] ||
|
||||
fail "release tag $REF does not match TNT_VERSION $version"
|
||||
;;
|
||||
esac
|
||||
|
||||
archive="$OUT_DIR/tnt-chat-v$version-source.tar.gz"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
[ ! -e "$archive" ] || fail "output already exists: $archive"
|
||||
|
||||
git archive --format=tar.gz --prefix="TNT-$version/" -o "$archive" "$commit"
|
||||
|
||||
archive_listing=$(tar -tzf "$archive") ||
|
||||
fail "archive is not a readable tar.gz: $archive"
|
||||
require_archive_entry "TNT-$version/LICENSE" "LICENSE"
|
||||
require_archive_entry "TNT-$version/src/tntctl.c" "src/tntctl.c"
|
||||
require_archive_entry "TNT-$version/packaging/README.md" "packaging/README.md"
|
||||
require_archive_entry "TNT-$version/tnt.1" "tnt.1"
|
||||
require_archive_entry "TNT-$version/tntctl.1" "tntctl.1"
|
||||
|
||||
echo "$archive"
|
||||
|
|
@ -198,6 +198,7 @@ sh -n install.sh
|
|||
sh -n scripts/check_release_ref.sh
|
||||
sh -n scripts/package_publish_check.sh
|
||||
sh -n scripts/package_release_assets.sh
|
||||
sh -n scripts/package_source_archive.sh
|
||||
scripts/check_release_ref.sh "v$version"
|
||||
bad_ref=v0.0.0
|
||||
[ "$version" != "0.0.0" ] || bad_ref=v9.9.9
|
||||
|
|
@ -266,13 +267,12 @@ if [ "$STRICT" -eq 1 ]; then
|
|||
fail "replace maintainer email placeholders before strict release"
|
||||
|
||||
step "checking tagged source archive"
|
||||
archive="$tmpdir/tnt-$version-source.tar.gz"
|
||||
archive="$tmpdir/tnt-chat-v$version-source.tar.gz"
|
||||
archive_extract="$tmpdir/source"
|
||||
archive_install="$tmpdir/source-install"
|
||||
archive_root="$archive_extract/TNT-$version"
|
||||
|
||||
git archive --format=tar.gz --prefix="TNT-$version/" \
|
||||
-o "$archive" "refs/tags/v$version"
|
||||
scripts/package_source_archive.sh "refs/tags/v$version" "$tmpdir" >/dev/null
|
||||
mkdir -p "$archive_extract"
|
||||
tar -xzf "$archive" -C "$archive_extract"
|
||||
|
||||
|
|
|
|||
|
|
@ -67,12 +67,8 @@ build_artifact_tree() {
|
|||
write_macho_arm64 "$artifact_root/darwin-arm64/tntctl-darwin-arm64"
|
||||
|
||||
if [ "$include_source" = "yes" ]; then
|
||||
source_root="$STATE_DIR/source-$ver/TNT-$ver"
|
||||
mkdir -p "$source_root/src" "$source_root/packaging" "$artifact_root/source"
|
||||
printf 'MIT\n' > "$source_root/LICENSE"
|
||||
printf 'int main(void) { return 0; }\n' > "$source_root/src/tntctl.c"
|
||||
printf '# Packaging\n' > "$source_root/packaging/README.md"
|
||||
(cd "$STATE_DIR/source-$ver" && tar -czf "$artifact_root/source/tnt-chat-v$ver-source.tar.gz" "TNT-$ver")
|
||||
mkdir -p "$artifact_root/source"
|
||||
../scripts/package_source_archive.sh HEAD "$artifact_root/source" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
|||
103
tests/test_source_archive.sh
Executable file
103
tests/test_source_archive.sh
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
#!/bin/sh
|
||||
# Release source-archive regression tests.
|
||||
|
||||
set -u
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
SCRIPT="../scripts/package_source_archive.sh"
|
||||
STATE_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tnt-source-archive-test.XXXXXX")
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$STATE_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
pass() {
|
||||
echo "✓ $1"
|
||||
PASS=$((PASS + 1))
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "✗ $1"
|
||||
if [ "${2:-}" ]; then
|
||||
printf '%s\n' "$2"
|
||||
fi
|
||||
FAIL=$((FAIL + 1))
|
||||
}
|
||||
|
||||
version() {
|
||||
sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' ../include/common.h
|
||||
}
|
||||
|
||||
listing_has_entry() {
|
||||
entry=$1
|
||||
|
||||
printf '%s\n' "$ARCHIVE_LISTING" |
|
||||
awk -v target="$entry" '$0 == target { found = 1 } END { exit found ? 0 : 1 }'
|
||||
}
|
||||
|
||||
echo "=== TNT Source Archive Tests ==="
|
||||
|
||||
if [ ! -x "$SCRIPT" ]; then
|
||||
echo "Error: script $SCRIPT not found or not executable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VER=$(version)
|
||||
OUT_DIR="$STATE_DIR/out"
|
||||
OUTPUT=$("$SCRIPT" HEAD "$OUT_DIR" 2>&1)
|
||||
STATUS=$?
|
||||
ARCHIVE="$OUT_DIR/tnt-chat-v$VER-source.tar.gz"
|
||||
|
||||
if [ "$STATUS" -eq 0 ] &&
|
||||
[ "$OUTPUT" = "$ARCHIVE" ] &&
|
||||
[ -s "$ARCHIVE" ]; then
|
||||
pass "HEAD source archive is built"
|
||||
else
|
||||
fail "HEAD source archive build" "$OUTPUT"
|
||||
fi
|
||||
|
||||
ARCHIVE_LISTING=$(tar -tzf "$ARCHIVE" 2>&1)
|
||||
if listing_has_entry "TNT-$VER/LICENSE" &&
|
||||
listing_has_entry "TNT-$VER/src/tntctl.c" &&
|
||||
listing_has_entry "TNT-$VER/packaging/README.md" &&
|
||||
listing_has_entry "TNT-$VER/tnt.1" &&
|
||||
listing_has_entry "TNT-$VER/tntctl.1"; then
|
||||
pass "source archive contains required release files"
|
||||
else
|
||||
fail "source archive required files" "$(printf '%s\n' "$ARCHIVE_LISTING" | sed -n '1,40p')"
|
||||
fi
|
||||
|
||||
DUP_OUTPUT=$("$SCRIPT" HEAD "$OUT_DIR" 2>&1)
|
||||
DUP_STATUS=$?
|
||||
if [ "$DUP_STATUS" -ne 0 ] &&
|
||||
printf '%s\n' "$DUP_OUTPUT" | grep -q 'output already exists'; then
|
||||
pass "existing archive is not overwritten"
|
||||
else
|
||||
fail "existing archive handling" "$DUP_OUTPUT"
|
||||
fi
|
||||
|
||||
BAD_OUTPUT=$("$SCRIPT" refs/heads/does-not-exist "$STATE_DIR/bad" 2>&1)
|
||||
BAD_STATUS=$?
|
||||
if [ "$BAD_STATUS" -ne 0 ] &&
|
||||
printf '%s\n' "$BAD_OUTPUT" | grep -q 'could not resolve git ref'; then
|
||||
pass "missing git ref is rejected"
|
||||
else
|
||||
fail "missing git ref handling" "$BAD_OUTPUT"
|
||||
fi
|
||||
|
||||
HELP_OUTPUT=$("$SCRIPT" --help 2>&1)
|
||||
HELP_STATUS=$?
|
||||
if [ "$HELP_STATUS" -eq 0 ] &&
|
||||
printf '%s\n' "$HELP_OUTPUT" | grep -q 'Usage: scripts/package_source_archive.sh REF'; then
|
||||
pass "help output is available"
|
||||
else
|
||||
fail "help output handling" "$HELP_OUTPUT"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "PASSED: $PASS"
|
||||
echo "FAILED: $FAIL"
|
||||
[ "$FAIL" -eq 0 ] && echo "All tests passed" || echo "Some tests failed"
|
||||
exit "$FAIL"
|
||||
Loading…
Reference in a new issue