mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
Add multiplatform CI governance gates
This commit is contained in:
parent
d4b260c160
commit
b6f92968d0
18 changed files with 775 additions and 184 deletions
98
.github/workflows/ci.yml
vendored
98
.github/workflows/ci.yml
vendored
|
|
@ -2,22 +2,34 @@ name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main, 'release/**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main, 'release/**' ]
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '17 3 * * *'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
pr-gate:
|
||||||
|
name: PR gate (${{ matrix.os }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-24.04, macos-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install dependencies (Ubuntu)
|
- name: Install dependencies (Ubuntu)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
@ -42,11 +54,26 @@ jobs:
|
||||||
- name: Run release preflight
|
- name: Run release preflight
|
||||||
run: make release-check
|
run: make release-check
|
||||||
|
|
||||||
|
extended-linux-runtime:
|
||||||
|
name: Extended Linux runtime
|
||||||
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y expect libssh-dev valgrind
|
||||||
|
|
||||||
|
- name: Run extended release preflight
|
||||||
|
run: |
|
||||||
|
RUN_INTEGRATION=1 RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check
|
||||||
|
|
||||||
- name: Check for memory leaks
|
- name: Check for memory leaks
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
sudo apt-get install -y valgrind
|
|
||||||
STATE_DIR=$(mktemp -d)
|
STATE_DIR=$(mktemp -d)
|
||||||
SERVER_LOG="$STATE_DIR/server.log"
|
SERVER_LOG="$STATE_DIR/server.log"
|
||||||
VALGRIND_LOG="$STATE_DIR/valgrind.log"
|
VALGRIND_LOG="$STATE_DIR/valgrind.log"
|
||||||
|
|
@ -101,3 +128,60 @@ jobs:
|
||||||
cat "$VALGRIND_LOG"
|
cat "$VALGRIND_LOG"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
portable-container-builds:
|
||||||
|
name: Portable build (${{ matrix.name }})
|
||||||
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: debian-stable-glibc
|
||||||
|
image: debian:stable-slim
|
||||||
|
setup: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential libssh-dev ca-certificates
|
||||||
|
- name: ubuntu-24.04-glibc
|
||||||
|
image: ubuntu:24.04
|
||||||
|
setup: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential libssh-dev ca-certificates
|
||||||
|
- name: alpine-musl
|
||||||
|
image: alpine:3.20
|
||||||
|
setup: apk add --no-cache build-base libssh-dev ca-certificates
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Build in container
|
||||||
|
run: |
|
||||||
|
docker run --rm -v "$PWD:/src:ro" "${{ matrix.image }}" sh -c '
|
||||||
|
set -eu
|
||||||
|
${{ matrix.setup }}
|
||||||
|
mkdir /work
|
||||||
|
cp -R /src/. /work/
|
||||||
|
cd /work
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
./tnt --version
|
||||||
|
./tntctl --version
|
||||||
|
'
|
||||||
|
|
||||||
|
package-recipe-gate:
|
||||||
|
name: Package recipe gate
|
||||||
|
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install packaging tools
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ruby cpio
|
||||||
|
|
||||||
|
- name: Validate packaging metadata
|
||||||
|
run: |
|
||||||
|
for script in scripts/*.sh; do
|
||||||
|
sh -n "$script"
|
||||||
|
done
|
||||||
|
bash -n packaging/arch/PKGBUILD
|
||||||
|
ruby -c packaging/homebrew/tnt-chat.rb
|
||||||
|
scripts/package_debian_source.sh "$RUNNER_TEMP/debian-source"
|
||||||
|
|
|
||||||
92
.github/workflows/release.yml
vendored
92
.github/workflows/release.yml
vendored
|
|
@ -8,11 +8,15 @@ on:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build ${{ matrix.target }}
|
name: Build ${{ matrix.target }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
|
|
@ -33,7 +37,7 @@ jobs:
|
||||||
ctl_artifact: tntctl-darwin-arm64
|
ctl_artifact: tntctl-darwin-arm64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Verify release tag matches source version
|
- name: Verify release tag matches source version
|
||||||
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
||||||
|
|
@ -91,40 +95,91 @@ jobs:
|
||||||
${{ matrix.artifact }}
|
${{ matrix.artifact }}
|
||||||
${{ matrix.ctl_artifact }}
|
${{ matrix.ctl_artifact }}
|
||||||
|
|
||||||
|
source-archive:
|
||||||
|
name: Source archive
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Verify release tag matches source version
|
||||||
|
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
|
- 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"
|
||||||
|
|
||||||
|
- name: Upload source archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: tnt-chat-source
|
||||||
|
path: dist/tnt-chat-v*-source.tar.gz
|
||||||
|
|
||||||
|
artifact-gate:
|
||||||
|
name: Release artifact gate
|
||||||
|
needs: [build, source-archive]
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Verify release tag matches source version
|
||||||
|
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: ./artifacts
|
||||||
|
|
||||||
|
- name: Verify and package release assets
|
||||||
|
run: scripts/package_release_assets.sh ./artifacts ./dist/release-assets
|
||||||
|
|
||||||
|
- name: Upload release asset bundle
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release-assets
|
||||||
|
path: dist/release-assets/*
|
||||||
|
|
||||||
release:
|
release:
|
||||||
needs: build
|
needs: [artifact-gate, source-archive]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Verify release tag matches source version
|
- name: Verify release tag matches source version
|
||||||
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
run: scripts/check_release_ref.sh "${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download release asset bundle
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: ./artifacts
|
name: release-assets
|
||||||
|
path: ./release-assets
|
||||||
|
|
||||||
- name: Create checksums
|
- name: Verify release checksums
|
||||||
run: |
|
run: |
|
||||||
cd artifacts
|
cd release-assets
|
||||||
: > checksums.txt
|
sha256sum -c checksums.txt
|
||||||
for artifact in */tnt-* */tntctl-*; do
|
|
||||||
[ -f "$artifact" ] || continue
|
|
||||||
sha256sum "$artifact" | sed "s# $artifact# $(basename "$artifact")#" >> checksums.txt
|
|
||||||
done
|
|
||||||
cat checksums.txt
|
cat checksums.txt
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
artifacts/*/tnt-*
|
release-assets/*
|
||||||
artifacts/*/tntctl-*
|
|
||||||
artifacts/checksums.txt
|
|
||||||
body: |
|
body: |
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -190,11 +245,16 @@ jobs:
|
||||||
sha256sum -c checksums.txt --ignore-missing
|
sha256sum -c checksums.txt --ignore-missing
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
for f in tnt-* tntctl-*; do
|
for f in tnt-* tntctl-* tnt-chat-*-source.tar.gz; do
|
||||||
grep " $f$" checksums.txt | shasum -a 256 -c -
|
grep " $f$" checksums.txt | shasum -a 256 -c -
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The release also includes `tnt-chat-${{ github.ref_name }}-source.tar.gz`
|
||||||
|
for package-manager recipes. Verify it with the same `checksums.txt`
|
||||||
|
before updating Arch, Homebrew, Debian, Ubuntu, or container package
|
||||||
|
metadata.
|
||||||
|
|
||||||
## What's Changed
|
## What's Changed
|
||||||
See [docs/CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/docs/CHANGELOG.md)
|
See [docs/CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ github.ref_name }}/docs/CHANGELOG.md)
|
||||||
draft: true
|
draft: true
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -134,6 +134,7 @@ script-test: all
|
||||||
@cd tests && ./test_docs_help_surface.sh
|
@cd tests && ./test_docs_help_surface.sh
|
||||||
@cd tests && ./test_logrotate.sh
|
@cd tests && ./test_logrotate.sh
|
||||||
@cd tests && ./test_message_log_tool.sh
|
@cd tests && ./test_message_log_tool.sh
|
||||||
|
@cd tests && ./test_release_artifact_gate.sh
|
||||||
|
|
||||||
integration-test: all
|
integration-test: all
|
||||||
@echo "Running integration tests..."
|
@echo "Running integration tests..."
|
||||||
|
|
|
||||||
|
|
@ -398,14 +398,14 @@ make release-check
|
||||||
Longer local preflight can opt into runtime soak and slow-client coverage:
|
Longer local preflight can opt into runtime soak and slow-client coverage:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check
|
RUN_INTEGRATION=1 RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check
|
||||||
```
|
```
|
||||||
|
|
||||||
Before publishing package recipes, download the final GitHub source archive,
|
Before publishing package recipes, download the explicit release source archive,
|
||||||
replace placeholder checksums, and run:
|
replace placeholder checksums, and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z.tar.gz make package-publish-check
|
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z-source.tar.gz make package-publish-check
|
||||||
```
|
```
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@
|
||||||
- Added a release tag/version guard used by the GitHub release workflow, so a
|
- Added a release tag/version guard used by the GitHub release workflow, so a
|
||||||
`vX.Y.Z` tag must match `TNT_VERSION` before release assets are built.
|
`vX.Y.Z` tag must match `TNT_VERSION` before release assets are built.
|
||||||
- Added `make package-publish-check` for verifying Arch/Homebrew source
|
- Added `make package-publish-check` for verifying Arch/Homebrew source
|
||||||
checksums against the final GitHub source archive after a tag exists.
|
checksums against the explicit release source archive after a tag exists.
|
||||||
|
- Added a release artifact gate that bundles Linux/macOS binaries, the explicit
|
||||||
|
release source archive, and `checksums.txt` before opening the draft release.
|
||||||
|
- Added CI governance layers for fast PR checks, release-branch validation,
|
||||||
|
extended runtime validation, container portability builds, and package recipe
|
||||||
|
validation.
|
||||||
- Added a `config_defaults` module and unit coverage for runtime default
|
- Added a `config_defaults` module and unit coverage for runtime default
|
||||||
values, env keys, and accepted numeric ranges.
|
values, env keys, and accepted numeric ranges.
|
||||||
- Added a dedicated `tntctl_text` module with unit coverage for local
|
- Added a dedicated `tntctl_text` module with unit coverage for local
|
||||||
|
|
@ -53,7 +58,7 @@
|
||||||
matches, avoiding the impression that the pager is a complete result set.
|
matches, avoiding the impression that the pager is a complete result set.
|
||||||
- Release checks now separate tag/source-archive readiness from package-manager
|
- Release checks now separate tag/source-archive readiness from package-manager
|
||||||
checksum publishing, avoiding self-referential checksum requirements before
|
checksum publishing, avoiding self-referential checksum requirements before
|
||||||
the final GitHub source archive exists.
|
the explicit release source archive exists.
|
||||||
- `tntctl --help` now gets its exec command list from `exec_catalog`, reducing
|
- `tntctl --help` now gets its exec command list from `exec_catalog`, reducing
|
||||||
duplicate command metadata between the local wrapper and SSH exec mode.
|
duplicate command metadata between the local wrapper and SSH exec mode.
|
||||||
- Updated `tnt(1)` to document the current TUI search and pager keys, and
|
- Updated `tnt(1)` to document the current TUI search and pager keys, and
|
||||||
|
|
|
||||||
367
docs/CICD.md
367
docs/CICD.md
|
|
@ -1,171 +1,268 @@
|
||||||
CI / RELEASE GUIDE
|
# CI/CD and Release Governance
|
||||||
==================
|
|
||||||
|
|
||||||
AUTOMATIC TESTING
|
TNT is a C SSH terminal chat server. The CI/CD system is designed for a public
|
||||||
-----------------
|
open-source project: fast feedback on pull requests, broader scheduled
|
||||||
Every push or PR automatically runs:
|
validation across target environments, reproducible release artifacts, and a
|
||||||
- Build on Ubuntu
|
manual production deployment boundary.
|
||||||
- AddressSanitizer build
|
|
||||||
- `make ci-test` (strict integration, anonymous access, connection limits,
|
|
||||||
and security feature checks)
|
|
||||||
- Release/package preflight (`make release-check`)
|
|
||||||
|
|
||||||
Check status:
|
Production deployment is intentionally manual. Workflows must not SSH into
|
||||||
https://github.com/m1ngsama/TNT/actions
|
production, restart services, upload to OSS buckets, publish package-manager
|
||||||
|
recipes, or mutate running servers.
|
||||||
|
|
||||||
Production deployment is intentionally manual. The CI workflow must not SSH
|
## Pipeline Layers
|
||||||
into production or restart services on push.
|
|
||||||
|
|
||||||
|
### PR Fast Gate
|
||||||
|
|
||||||
CREATING RELEASES
|
Workflow: `.github/workflows/ci.yml`
|
||||||
-----------------
|
|
||||||
Release policy:
|
|
||||||
- Use SemVer-style tags: vMAJOR.MINOR.PATCH.
|
|
||||||
- Bump PATCH for compatible bug fixes and release hardening.
|
|
||||||
- Bump MINOR for new commands, new documented flags, JSON field additions,
|
|
||||||
or visible user-interface behavior changes.
|
|
||||||
- Bump MAJOR for incompatible command, config, storage, or package behavior.
|
|
||||||
- Keep GitHub draft release review manual. Do not auto-publish releases.
|
|
||||||
- Keep production deployment manual. Do not SSH into production from CI.
|
|
||||||
|
|
||||||
1. Update version metadata:
|
Runs on pull requests targeting `main` or `release/**`, and pushes to `main`
|
||||||
- include/common.h
|
or `release/**`:
|
||||||
- tnt.1
|
|
||||||
- docs/CHANGELOG.md
|
|
||||||
- packaging/arch/PKGBUILD
|
|
||||||
- packaging/homebrew/tnt-chat.rb
|
|
||||||
- packaging/debian/debian/changelog
|
|
||||||
- maintainer metadata, when preparing public package recipes
|
|
||||||
|
|
||||||
2. Run the local preflight:
|
- Ubuntu 24.04 and macOS latest builds.
|
||||||
make release-check
|
- Normal build with `make`.
|
||||||
|
- AddressSanitizer build with `make asan`.
|
||||||
|
- Integration/security gate with `make ci-test`.
|
||||||
|
- Local release/package preflight with `make release-check`.
|
||||||
|
|
||||||
For a longer local runtime gate before publishing or production rollout:
|
Purpose:
|
||||||
RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check
|
|
||||||
|
|
||||||
3. Commit the release changes and create a local tag. Do not push the tag
|
- Keep contributor feedback fast enough for normal review.
|
||||||
until strict checks pass:
|
- Catch build, integration, packaging metadata, and release-preflight regressions
|
||||||
git tag vX.Y.Z
|
before merge.
|
||||||
|
- Avoid slow soak, valgrind, and container matrix jobs on every PR.
|
||||||
|
|
||||||
4. Run strict release checks:
|
### Extended and Nightly Validation
|
||||||
make release-check-strict
|
|
||||||
|
|
||||||
Strict mode requires the local `vX.Y.Z` tag to point at HEAD. It also
|
Workflow: `.github/workflows/ci.yml`
|
||||||
builds from the tagged source archive, so it catches files that were left
|
|
||||||
untracked and would be missing from GitHub's source archive.
|
|
||||||
|
|
||||||
5. Push the tag:
|
Runs on `main` or `release/**` pushes, manual dispatch, and the nightly
|
||||||
git push origin vX.Y.Z
|
schedule:
|
||||||
|
|
||||||
6. GitHub Actions automatically:
|
- `extended-linux-runtime`
|
||||||
- Builds `tnt` and `tntctl` binaries (Linux/macOS, AMD64/ARM64)
|
- Runs `RUN_INTEGRATION=1 RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check`.
|
||||||
- Creates a draft release
|
- Runs a valgrind smoke test against a temporary server.
|
||||||
- Uploads binaries
|
- `portable-container-builds`
|
||||||
- Generates one `checksums.txt` file
|
- Builds in Debian stable glibc.
|
||||||
- Verifies that artifact architecture matches the asset name
|
- Builds in Ubuntu 24.04 glibc.
|
||||||
|
- Builds in Alpine musl.
|
||||||
|
- `package-recipe-gate`
|
||||||
|
- Syntax-checks shell scripts.
|
||||||
|
- Syntax-checks the Arch `PKGBUILD`.
|
||||||
|
- Syntax-checks the Homebrew formula.
|
||||||
|
- Assembles the Debian source tree.
|
||||||
|
|
||||||
7. Review the draft release, smoke-test downloaded assets, then publish it
|
Purpose:
|
||||||
manually from GitHub.
|
|
||||||
|
|
||||||
8. Release appears at:
|
- Broaden platform confidence without making every PR wait for the full matrix.
|
||||||
https://github.com/m1ngsama/TNT/releases
|
- Detect musl/glibc portability issues early.
|
||||||
|
- Keep package metadata reviewable before public registry submission.
|
||||||
|
|
||||||
|
### Release Artifact Gates
|
||||||
|
|
||||||
RELEASE REVIEW CHECKLIST
|
Workflow: `.github/workflows/release.yml`
|
||||||
------------------------
|
|
||||||
Before publishing a draft release:
|
|
||||||
- Confirm `git tag` points at the intended commit.
|
|
||||||
- Download every release asset from GitHub, not from the local workspace.
|
|
||||||
- Verify downloaded assets against `checksums.txt` (`sha256sum -c
|
|
||||||
checksums.txt --ignore-missing` on Linux, or `shasum -a 256 -c` for each
|
|
||||||
downloaded asset on macOS).
|
|
||||||
- Run downloaded `tnt --version` and `tntctl --version`.
|
|
||||||
- Start a temporary server and check:
|
|
||||||
ssh -p 2222 server health
|
|
||||||
ssh -p 2222 server stats --json
|
|
||||||
ssh -p 2222 server users --json
|
|
||||||
ssh -p 2222 operator@server post "release smoke"
|
|
||||||
ssh -p 2222 server "tail -n 1"
|
|
||||||
- Check runtime dynamic links (`ldd` on Linux, `otool -L` on macOS) and make
|
|
||||||
sure `libssh` is documented for the target install path.
|
|
||||||
- Confirm `make release-check-strict` passed before pushing the tag.
|
|
||||||
- For package-manager recipes, download the final GitHub source archive,
|
|
||||||
replace Arch/Homebrew source checksums, then run:
|
|
||||||
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z.tar.gz make package-publish-check
|
|
||||||
|
|
||||||
|
Runs only for SemVer tags matching `vMAJOR.MINOR.PATCH`:
|
||||||
|
|
||||||
ROLLBACK
|
- Verifies the tag matches `TNT_VERSION` through `scripts/check_release_ref.sh`.
|
||||||
--------
|
- Builds Linux glibc AMD64 and ARM64 binaries.
|
||||||
Production rollback stays manual:
|
- Builds macOS Intel and Apple Silicon binaries.
|
||||||
1. Keep the previous binary before replacing it.
|
- Verifies binary architecture labels.
|
||||||
2. Stop or restart only the intended `tnt` service.
|
- Builds an explicit source archive: `tnt-chat-vX.Y.Z-source.tar.gz`.
|
||||||
3. Restore the previous binary if smoke checks fail.
|
- Runs `scripts/package_release_assets.sh` to collect release assets, verify
|
||||||
4. Re-run `health`, `stats --json`, and one post/tail smoke test.
|
expected asset names, verify binary architecture labels again after artifact
|
||||||
|
download, verify source archive contents, generate `checksums.txt`, and verify
|
||||||
|
the checksum file.
|
||||||
|
- Creates a GitHub draft release only. Publishing stays manual.
|
||||||
|
|
||||||
Do not overwrite `TNT_STATE_DIR` during rollback. If a future release changes
|
The release workflow does not publish package-manager recipes or deploy
|
||||||
the message log format, its release notes must include the downgrade behavior.
|
production servers.
|
||||||
|
|
||||||
|
## Platform Policy
|
||||||
|
|
||||||
DEPLOYING TO SERVERS
|
Current release assets:
|
||||||
--------------------
|
|
||||||
Deployments are operator-driven:
|
|
||||||
1. Build and test locally or in a temporary server directory.
|
|
||||||
2. Back up the installed binary.
|
|
||||||
3. Install the new binary.
|
|
||||||
4. Restart the service.
|
|
||||||
5. Run black-box checks (`health`, `stats --json`, `users --json`,
|
|
||||||
and a post/tail smoke test).
|
|
||||||
|
|
||||||
The installer can still be used manually on a server:
|
- Linux glibc AMD64: `tnt-linux-amd64`, `tntctl-linux-amd64`
|
||||||
curl -sSL https://raw.githubusercontent.com/m1ngsama/TNT/main/install.sh | sh
|
- Linux glibc ARM64: `tnt-linux-arm64`, `tntctl-linux-arm64`
|
||||||
|
- macOS Intel: `tnt-darwin-amd64`, `tntctl-darwin-amd64`
|
||||||
|
- macOS Apple Silicon: `tnt-darwin-arm64`, `tntctl-darwin-arm64`
|
||||||
|
- Source archive: `tnt-chat-vX.Y.Z-source.tar.gz`
|
||||||
|
|
||||||
|
Current CI validation:
|
||||||
|
|
||||||
PRODUCTION SETUP (systemd)
|
- Ubuntu 24.04
|
||||||
---------------------------
|
- macOS latest
|
||||||
1. Install binary (see above)
|
- Debian stable glibc container build
|
||||||
|
- Ubuntu 24.04 glibc container build
|
||||||
|
- Alpine musl container build
|
||||||
|
|
||||||
2. Setup service:
|
Package-manager routes:
|
||||||
sudo useradd -r -s /bin/false tnt
|
|
||||||
sudo mkdir -p /var/lib/tnt
|
|
||||||
sudo chown tnt:tnt /var/lib/tnt
|
|
||||||
sudo cp tnt.service /etc/systemd/system/
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable --now tnt
|
|
||||||
|
|
||||||
3. Check status:
|
- Debian/Ubuntu: maintain draft Debian metadata and start with a Launchpad PPA.
|
||||||
sudo systemctl status tnt
|
- Arch/AUR: maintain `packaging/arch/PKGBUILD` and `.SRCINFO`; submit manually.
|
||||||
sudo journalctl -u tnt -f
|
- Homebrew/macOS: maintain a tap formula first; Homebrew core can wait for a
|
||||||
|
stable release cadence and broader adoption.
|
||||||
|
- Source archive: every public package recipe must pin the final GitHub release
|
||||||
|
source archive checksum.
|
||||||
|
- Containers: first stage is Docker-based build validation in CI. Publishing
|
||||||
|
images should wait until image labels, SBOM, provenance, CVE scanning, and
|
||||||
|
registry ownership are defined.
|
||||||
|
|
||||||
|
## Release Policy
|
||||||
|
|
||||||
UPDATING SERVERS
|
- Use SemVer-style tags: `vMAJOR.MINOR.PATCH`.
|
||||||
----------------
|
- Bump PATCH for compatible bug fixes and release hardening.
|
||||||
Manual binary replacement pattern:
|
- Bump MINOR for new commands, new documented flags, JSON field additions, or
|
||||||
backup=/usr/local/bin/tnt.bak-$(date +%Y%m%d%H%M%S)
|
visible user-interface behavior changes.
|
||||||
sudo cp -a /usr/local/bin/tnt "$backup"
|
- Bump MAJOR for incompatible command, config, storage, or package behavior.
|
||||||
sudo install -m 755 ./tnt /usr/local/bin/tnt
|
- Keep GitHub release publishing manual by using draft releases.
|
||||||
sudo systemctl restart tnt
|
- Keep production deployment manual.
|
||||||
|
|
||||||
|
Update version metadata before tagging:
|
||||||
|
|
||||||
PLATFORMS SUPPORTED
|
- `include/common.h`
|
||||||
-------------------
|
- `tnt.1`
|
||||||
✓ Linux AMD64 (x86_64)
|
- `tntctl.1`
|
||||||
✓ Linux ARM64 (aarch64)
|
- `docs/CHANGELOG.md`
|
||||||
✓ macOS Intel (x86_64)
|
- `packaging/arch/PKGBUILD`
|
||||||
✓ macOS Apple Silicon (arm64)
|
- `packaging/arch/.SRCINFO`
|
||||||
|
- `packaging/homebrew/tnt-chat.rb`
|
||||||
|
- `packaging/debian/debian/changelog`
|
||||||
|
|
||||||
|
Local preflight:
|
||||||
|
|
||||||
EXAMPLE WORKFLOW
|
```sh
|
||||||
----------------
|
make release-check
|
||||||
# Local development
|
```
|
||||||
make && make asan && make release-check
|
|
||||||
./tnt
|
|
||||||
|
|
||||||
# Create release
|
Longer local runtime gate:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
RUN_INTEGRATION=1 RUN_SOAK=1 RUN_SLOW_CLIENT=1 make release-check
|
||||||
|
```
|
||||||
|
|
||||||
|
Strict local release gate before pushing a tag:
|
||||||
|
|
||||||
|
```sh
|
||||||
git tag vX.Y.Z
|
git tag vX.Y.Z
|
||||||
git push origin vX.Y.Z
|
make release-check-strict
|
||||||
# Wait 5 minutes for builds
|
```
|
||||||
|
|
||||||
# Deploy to production manually after validation
|
Strict mode requires the local `vX.Y.Z` tag to point at `HEAD` and builds from
|
||||||
ssh server "sudo install -m 755 /tmp/tnt-build/tnt /usr/local/bin/tnt"
|
the tagged source archive, so it catches files that were left untracked and
|
||||||
ssh server "sudo systemctl restart tnt"
|
would be missing from the release source archive.
|
||||||
ssh -p 2222 server health
|
|
||||||
|
After strict checks pass:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git push origin vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
GitHub Actions then builds artifacts and opens a draft release. Review and
|
||||||
|
publish that draft manually.
|
||||||
|
|
||||||
|
## Release Review Checklist
|
||||||
|
|
||||||
|
Before publishing a draft release:
|
||||||
|
|
||||||
|
- Confirm the Git tag points at the intended commit.
|
||||||
|
- Confirm the release workflow passed.
|
||||||
|
- Download every release asset from GitHub, not from the local workspace.
|
||||||
|
- Verify downloaded assets against `checksums.txt`.
|
||||||
|
- Run downloaded `tnt --version` and `tntctl --version`.
|
||||||
|
- Start a temporary server and check:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh -p 2222 server health
|
||||||
|
ssh -p 2222 server stats --json
|
||||||
|
ssh -p 2222 server users --json
|
||||||
|
ssh -p 2222 operator@server post "release smoke"
|
||||||
|
ssh -p 2222 server "tail -n 1"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Check runtime dynamic links with `ldd` on Linux or `otool -L` on macOS.
|
||||||
|
- Confirm `libssh` runtime installation is documented for the target install
|
||||||
|
path.
|
||||||
|
- Verify the explicit source archive checksum before updating Arch, Homebrew,
|
||||||
|
Debian, Ubuntu, or container package metadata.
|
||||||
|
- Run package publication preflight after package recipes pin final source
|
||||||
|
checksums:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z-source.tar.gz make package-publish-check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checksums
|
||||||
|
|
||||||
|
Release assets include `checksums.txt`.
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sha256sum -c checksums.txt --ignore-missing
|
||||||
|
```
|
||||||
|
|
||||||
|
macOS:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in tnt-* tntctl-* tnt-chat-*-source.tar.gz; do
|
||||||
|
grep " $f$" checksums.txt | shasum -a 256 -c -
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supply Chain Roadmap
|
||||||
|
|
||||||
|
Stage 1, implemented now:
|
||||||
|
|
||||||
|
- Tag/version gate.
|
||||||
|
- Draft release, manual publish.
|
||||||
|
- Binary architecture validation.
|
||||||
|
- Source archive validation.
|
||||||
|
- SHA-256 checksums for every release asset.
|
||||||
|
- Package recipe checksum preflight.
|
||||||
|
|
||||||
|
Stage 2, next:
|
||||||
|
|
||||||
|
- Generate an SBOM for release artifacts, preferably CycloneDX or SPDX.
|
||||||
|
- Attach SBOM files to draft releases.
|
||||||
|
- Add package lint jobs for Debian source packages, Arch packages, Homebrew
|
||||||
|
audit, and container image metadata.
|
||||||
|
|
||||||
|
Stage 3, later:
|
||||||
|
|
||||||
|
- Sign release checksums and/or artifacts with a documented maintainer key or
|
||||||
|
Sigstore flow.
|
||||||
|
- Add SLSA provenance for GitHub Actions builds.
|
||||||
|
- Define container image ownership, tag policy, vulnerability scan policy, and
|
||||||
|
rollback behavior before publishing images.
|
||||||
|
|
||||||
|
## Manual Production Deployment
|
||||||
|
|
||||||
|
Deployment remains operator-driven:
|
||||||
|
|
||||||
|
1. Build and test locally or in a temporary server directory.
|
||||||
|
2. Back up the installed binary.
|
||||||
|
3. Install the new binary.
|
||||||
|
4. Restart only the intended `tnt` service.
|
||||||
|
5. Run black-box checks: `health`, `stats --json`, `users --json`, and one
|
||||||
|
post/tail smoke test.
|
||||||
|
|
||||||
|
Manual binary replacement pattern:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
backup=/usr/local/bin/tnt.bak-$(date +%Y%m%d%H%M%S)
|
||||||
|
sudo cp -a /usr/local/bin/tnt "$backup"
|
||||||
|
sudo install -m 755 ./tnt /usr/local/bin/tnt
|
||||||
|
sudo systemctl restart tnt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
Production rollback stays manual:
|
||||||
|
|
||||||
|
1. Keep the previous binary before replacing it.
|
||||||
|
2. Stop or restart only the intended `tnt` service.
|
||||||
|
3. Restore the previous binary if smoke checks fail.
|
||||||
|
4. Re-run `health`, `stats --json`, and one post/tail smoke test.
|
||||||
|
|
||||||
|
Do not overwrite `TNT_STATE_DIR` during rollback. If a future release changes
|
||||||
|
the message log format, its release notes must include downgrade behavior.
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@ Goal: make regressions harder to introduce.
|
||||||
|
|
||||||
These are the next changes that should happen before new feature work expands the surface area.
|
These are the next changes that should happen before new feature work expands the surface area.
|
||||||
|
|
||||||
1. Replace remaining source-archive checksum placeholders only after the final
|
1. Replace remaining source-archive checksum placeholders only after the
|
||||||
GitHub source archive exists, then run `make package-publish-check`.
|
explicit release source archive exists, then run `make package-publish-check`.
|
||||||
2. Create or move the `vX.Y.Z` tag only when the release commit is final, then
|
2. Create or move the `vX.Y.Z` tag only when the release commit is final, then
|
||||||
run `make release-check-strict` before pushing it.
|
run `make release-check-strict` before pushing it.
|
||||||
3. Decide whether admin-only moderation controls belong in 1.0.x or should
|
3. Decide whether admin-only moderation controls belong in 1.0.x or should
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,30 @@ any public registry.
|
||||||
Package installs include both `tnt` and `tntctl`. `tnt` is the server process;
|
Package installs include both `tnt` and `tntctl`. `tnt` is the server process;
|
||||||
`tntctl` is a thin wrapper around the documented SSH exec interface.
|
`tntctl` is a thin wrapper around the documented SSH exec interface.
|
||||||
|
|
||||||
|
## CI governance
|
||||||
|
|
||||||
|
Package recipes are validated in stages:
|
||||||
|
|
||||||
|
- PR fast gate: `make release-check` verifies package metadata stays aligned
|
||||||
|
with `TNT_VERSION`.
|
||||||
|
- Extended CI: package syntax and Debian source-tree assembly run on `main` and
|
||||||
|
`release/**` pushes, nightly, and manual workflow dispatch.
|
||||||
|
- Release gate: the workflow builds an explicit release source archive, verifies
|
||||||
|
it, and includes it in `checksums.txt`.
|
||||||
|
- Publishing gate: after final source checksums are pinned, run
|
||||||
|
`SOURCE_TARBALL=... make package-publish-check`.
|
||||||
|
|
||||||
|
All package-manager submissions remain manual. CI must not push to AUR, open or
|
||||||
|
merge Homebrew tap updates, upload Debian/PPA packages, publish container
|
||||||
|
images, or deploy production servers.
|
||||||
|
|
||||||
## Release checklist
|
## Release checklist
|
||||||
|
|
||||||
1. Confirm `TNT_VERSION` in `include/common.h` and the manpage version match.
|
1. Confirm `TNT_VERSION` in `include/common.h` and the manpage version match.
|
||||||
Also update package versions in Arch, Homebrew, and Debian drafts.
|
Also update package versions in Arch, Homebrew, and Debian drafts.
|
||||||
2. Create a GitHub release tag such as `vX.Y.Z`.
|
2. Create a GitHub release tag such as `vX.Y.Z`.
|
||||||
3. Build and upload release tarballs or rely on GitHub source archives.
|
3. Let the release workflow build the explicit release source archive and draft
|
||||||
|
release assets.
|
||||||
4. Replace placeholder checksums in package drafts.
|
4. Replace placeholder checksums in package drafts.
|
||||||
5. Verify package contents in an isolated directory:
|
5. Verify package contents in an isolated directory:
|
||||||
|
|
||||||
|
|
@ -35,11 +53,11 @@ Package installs include both `tnt` and `tntctl`. `tnt` is the server process;
|
||||||
Use `scripts/package_debian_source.sh --build` on a Debian/Ubuntu system
|
Use `scripts/package_debian_source.sh --build` on a Debian/Ubuntu system
|
||||||
with `dpkg-buildpackage` installed to build the unsigned source package.
|
with `dpkg-buildpackage` installed to build the unsigned source package.
|
||||||
|
|
||||||
7. Before submitting package recipes, download the final GitHub source archive,
|
7. Before submitting package recipes, download the explicit release source archive,
|
||||||
replace checksum placeholders, and run:
|
replace checksum placeholders, and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z.tar.gz make package-publish-check
|
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z-source.tar.gz make package-publish-check
|
||||||
```
|
```
|
||||||
|
|
||||||
8. Submit packages manually:
|
8. Submit packages manually:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pkgbase = tnt-chat
|
||||||
makedepends = gcc
|
makedepends = gcc
|
||||||
makedepends = make
|
makedepends = make
|
||||||
depends = libssh
|
depends = libssh
|
||||||
source = tnt-chat-1.0.1.tar.gz::https://github.com/m1ngsama/TNT/archive/refs/tags/v1.0.1.tar.gz
|
source = tnt-chat-v1.0.1-source.tar.gz::https://github.com/m1ngsama/TNT/releases/download/v1.0.1/tnt-chat-v1.0.1-source.tar.gz
|
||||||
source = tnt-chat.sysusers
|
source = tnt-chat.sysusers
|
||||||
sha256sums = SKIP
|
sha256sums = SKIP
|
||||||
sha256sums = 8a1f7dfbdc9f1305c4ed50d80e89f91333ffdf937890c497f93e41abaf76e3ed
|
sha256sums = 8a1f7dfbdc9f1305c4ed50d80e89f91333ffdf937890c497f93e41abaf76e3ed
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ url='https://github.com/m1ngsama/TNT'
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=('libssh')
|
depends=('libssh')
|
||||||
makedepends=('gcc' 'make')
|
makedepends=('gcc' 'make')
|
||||||
source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/v${pkgver}.tar.gz"
|
source=("${pkgname}-v${pkgver}-source.tar.gz::${url}/releases/download/v${pkgver}/${pkgname}-v${pkgver}-source.tar.gz"
|
||||||
"${pkgname}.sysusers")
|
"${pkgname}.sysusers")
|
||||||
sha256sums=('SKIP'
|
sha256sums=('SKIP'
|
||||||
'8a1f7dfbdc9f1305c4ed50d80e89f91333ffdf937890c497f93e41abaf76e3ed')
|
'8a1f7dfbdc9f1305c4ed50d80e89f91333ffdf937890c497f93e41abaf76e3ed')
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ After editing `PKGBUILD`, regenerate `.SRCINFO`:
|
||||||
makepkg --printsrcinfo > .SRCINFO
|
makepkg --printsrcinfo > .SRCINFO
|
||||||
```
|
```
|
||||||
|
|
||||||
Before AUR submission, replace `sha256sums=('SKIP')` with the real GitHub
|
Before AUR submission, replace `sha256sums=('SKIP')` with the real release
|
||||||
source archive checksum, regenerate `.SRCINFO`, then run the package publish
|
source archive checksum, regenerate `.SRCINFO`, then run the package publish
|
||||||
check:
|
check:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z.tar.gz make package-publish-check
|
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z-source.tar.gz make package-publish-check
|
||||||
```
|
```
|
||||||
|
|
||||||
## Manual AUR submission
|
## Manual AUR submission
|
||||||
|
|
|
||||||
|
|
@ -34,16 +34,16 @@ ruby -c packaging/homebrew/tnt-chat.rb
|
||||||
2. Download or hash the release source archive:
|
2. Download or hash the release source archive:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -L -o dist/tnt-chat-vX.Y.Z.tar.gz \
|
curl -L -o dist/tnt-chat-vX.Y.Z-source.tar.gz \
|
||||||
https://github.com/m1ngsama/TNT/archive/refs/tags/vX.Y.Z.tar.gz
|
https://github.com/m1ngsama/TNT/releases/download/vX.Y.Z/tnt-chat-vX.Y.Z-source.tar.gz
|
||||||
shasum -a 256 dist/tnt-chat-vX.Y.Z.tar.gz
|
shasum -a 256 dist/tnt-chat-vX.Y.Z-source.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Replace `REPLACE_WITH_RELEASE_TARBALL_SHA256` in `tnt-chat.rb`.
|
3. Replace `REPLACE_WITH_RELEASE_TARBALL_SHA256` in `tnt-chat.rb`.
|
||||||
4. Run:
|
4. Run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z.tar.gz make package-publish-check
|
SOURCE_TARBALL=dist/tnt-chat-vX.Y.Z-source.tar.gz make package-publish-check
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Copy the formula into the tap repository and open a normal review PR.
|
5. Copy the formula into the tap repository and open a normal review PR.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
class TntChat < Formula
|
class TntChat < Formula
|
||||||
desc "SSH-native terminal chat server with a Vim-style interface"
|
desc "SSH-native terminal chat server with a Vim-style interface"
|
||||||
homepage "https://github.com/m1ngsama/TNT"
|
homepage "https://github.com/m1ngsama/TNT"
|
||||||
url "https://github.com/m1ngsama/TNT/archive/refs/tags/v1.0.1.tar.gz"
|
url "https://github.com/m1ngsama/TNT/releases/download/v1.0.1/tnt-chat-v1.0.1-source.tar.gz"
|
||||||
sha256 "REPLACE_WITH_RELEASE_TARBALL_SHA256"
|
sha256 "REPLACE_WITH_RELEASE_TARBALL_SHA256"
|
||||||
license "MIT"
|
license "MIT"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,21 @@ sha256_of() {
|
||||||
|
|
||||||
version=$(sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' include/common.h)
|
version=$(sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' include/common.h)
|
||||||
[ -n "$version" ] || fail "could not read TNT_VERSION from include/common.h"
|
[ -n "$version" ] || fail "could not read TNT_VERSION from include/common.h"
|
||||||
|
release_source="tnt-chat-v${version}-source.tar.gz"
|
||||||
|
|
||||||
source_tarball=${SOURCE_TARBALL:-${RELEASE_SOURCE_TARBALL:-}}
|
source_tarball=${SOURCE_TARBALL:-${RELEASE_SOURCE_TARBALL:-}}
|
||||||
[ -n "$source_tarball" ] ||
|
[ -n "$source_tarball" ] ||
|
||||||
fail "set SOURCE_TARBALL to the final GitHub source archive"
|
fail "set SOURCE_TARBALL to the explicit release source archive"
|
||||||
[ -f "$source_tarball" ] ||
|
[ -f "$source_tarball" ] ||
|
||||||
fail "SOURCE_TARBALL does not exist: $source_tarball"
|
fail "SOURCE_TARBALL does not exist: $source_tarball"
|
||||||
|
tar -tzf "$source_tarball" >/dev/null ||
|
||||||
|
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"
|
||||||
|
|
||||||
! grep -R "REPLACE_WITH_EMAIL" packaging/arch packaging/debian >/dev/null ||
|
! grep -R "REPLACE_WITH_EMAIL" packaging/arch packaging/debian >/dev/null ||
|
||||||
fail "replace maintainer email placeholders before package publishing"
|
fail "replace maintainer email placeholders before package publishing"
|
||||||
|
|
@ -60,8 +69,12 @@ grep -q "^pkgver=$version$" packaging/arch/PKGBUILD ||
|
||||||
fail "PKGBUILD pkgver does not match $version"
|
fail "PKGBUILD pkgver does not match $version"
|
||||||
grep -q "pkgver = $version" packaging/arch/.SRCINFO ||
|
grep -q "pkgver = $version" packaging/arch/.SRCINFO ||
|
||||||
fail ".SRCINFO pkgver does not match $version"
|
fail ".SRCINFO pkgver does not match $version"
|
||||||
grep -q "v${version}.tar.gz" packaging/homebrew/tnt-chat.rb ||
|
grep -q '${pkgname}-v${pkgver}-source.tar.gz' packaging/arch/PKGBUILD ||
|
||||||
fail "Homebrew URL does not match v$version"
|
fail "PKGBUILD source must use the release source archive"
|
||||||
|
grep -q "$release_source" packaging/arch/.SRCINFO ||
|
||||||
|
fail ".SRCINFO source does not match $release_source"
|
||||||
|
grep -q "$release_source" packaging/homebrew/tnt-chat.rb ||
|
||||||
|
fail "Homebrew URL does not match $release_source"
|
||||||
grep -q "^tnt-chat (${version}-1)" packaging/debian/debian/changelog ||
|
grep -q "^tnt-chat (${version}-1)" packaging/debian/debian/changelog ||
|
||||||
fail "Debian changelog version does not match $version"
|
fail "Debian changelog version does not match $version"
|
||||||
|
|
||||||
|
|
|
||||||
150
scripts/package_release_assets.sh
Executable file
150
scripts/package_release_assets.sh
Executable file
|
|
@ -0,0 +1,150 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Collect release workflow artifacts into one flat, checksum-verified bundle.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage: scripts/package_release_assets.sh ARTIFACT_ROOT [OUT_DIR]
|
||||||
|
|
||||||
|
ARTIFACT_ROOT is the directory produced by actions/download-artifact.
|
||||||
|
OUT_DIR defaults to dist/release-assets.
|
||||||
|
|
||||||
|
The script validates the expected release asset names, verifies binary
|
||||||
|
architecture labels, verifies the source archive shape, writes checksums.txt,
|
||||||
|
and verifies that checksums.txt matches the assembled bundle. It never
|
||||||
|
publishes a release.
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "release-artifact-gate: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256_of() {
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
|
sha256sum "$1" | awk '{print $1}'
|
||||||
|
elif command -v shasum >/dev/null 2>&1; then
|
||||||
|
shasum -a 256 "$1" | awk '{print $1}'
|
||||||
|
else
|
||||||
|
fail "sha256sum or shasum is required"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
find_unique() {
|
||||||
|
name=$1
|
||||||
|
matches=$(find "$ARTIFACT_ROOT" -type f -name "$name" -print)
|
||||||
|
count=$(printf '%s\n' "$matches" | sed '/^$/d' | wc -l | tr -d ' ')
|
||||||
|
[ "$count" = "1" ] ||
|
||||||
|
fail "expected exactly one artifact named $name, found $count"
|
||||||
|
printf '%s\n' "$matches"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_file_label() {
|
||||||
|
path=$1
|
||||||
|
pattern=$2
|
||||||
|
label=$(file "$path")
|
||||||
|
printf '%s\n' "$label" | grep -E "$pattern" >/dev/null ||
|
||||||
|
fail "unexpected file type for $(basename "$path"): $label"
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_asset() {
|
||||||
|
name=$1
|
||||||
|
path=$2
|
||||||
|
|
||||||
|
[ -s "$path" ] || fail "empty artifact: $name"
|
||||||
|
|
||||||
|
case "$name" in
|
||||||
|
tnt-linux-amd64|tntctl-linux-amd64)
|
||||||
|
require_file_label "$path" 'ELF 64-bit.*x86-64'
|
||||||
|
;;
|
||||||
|
tnt-linux-arm64|tntctl-linux-arm64)
|
||||||
|
require_file_label "$path" 'ELF 64-bit.*(aarch64|ARM aarch64)'
|
||||||
|
;;
|
||||||
|
tnt-darwin-amd64|tntctl-darwin-amd64)
|
||||||
|
require_file_label "$path" 'Mach-O 64-bit.*x86_64'
|
||||||
|
;;
|
||||||
|
tnt-darwin-arm64|tntctl-darwin-arm64)
|
||||||
|
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"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
fail "unexpected release artifact: $name"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "${1:-}" != "-h" ] && [ "${1:-}" != "--help" ] || {
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ARTIFACT_ROOT=${1:-}
|
||||||
|
OUT_DIR=${2:-dist/release-assets}
|
||||||
|
|
||||||
|
[ -n "$ARTIFACT_ROOT" ] || {
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
[ -d "$ARTIFACT_ROOT" ] || fail "ARTIFACT_ROOT does not exist: $ARTIFACT_ROOT"
|
||||||
|
ARTIFACT_ROOT=$(CDPATH= cd -- "$ARTIFACT_ROOT" && pwd)
|
||||||
|
|
||||||
|
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
case "$OUT_DIR" in
|
||||||
|
/*) ;;
|
||||||
|
*) OUT_DIR="$ROOT/$OUT_DIR" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
VERSION=$(sed -n 's/^#define TNT_VERSION "\([^"]*\)".*/\1/p' include/common.h)
|
||||||
|
[ -n "$VERSION" ] || fail "could not read TNT_VERSION"
|
||||||
|
|
||||||
|
SOURCE_ASSET="tnt-chat-v$VERSION-source.tar.gz"
|
||||||
|
EXPECTED_ASSETS="
|
||||||
|
tnt-linux-amd64
|
||||||
|
tntctl-linux-amd64
|
||||||
|
tnt-linux-arm64
|
||||||
|
tntctl-linux-arm64
|
||||||
|
tnt-darwin-amd64
|
||||||
|
tntctl-darwin-amd64
|
||||||
|
tnt-darwin-arm64
|
||||||
|
tntctl-darwin-arm64
|
||||||
|
$SOURCE_ASSET
|
||||||
|
"
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
for name in $EXPECTED_ASSETS; do
|
||||||
|
dst="$OUT_DIR/$name"
|
||||||
|
[ ! -e "$dst" ] || fail "output already exists: $dst"
|
||||||
|
src=$(find_unique "$name")
|
||||||
|
verify_asset "$name" "$src"
|
||||||
|
cp "$src" "$dst"
|
||||||
|
done
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$OUT_DIR"
|
||||||
|
: > checksums.txt
|
||||||
|
for name in $EXPECTED_ASSETS; do
|
||||||
|
printf '%s %s\n' "$(sha256_of "$name")" "$name" >> checksums.txt
|
||||||
|
done
|
||||||
|
|
||||||
|
while read -r expected name; do
|
||||||
|
[ -n "$expected" ] || continue
|
||||||
|
actual=$(sha256_of "$name")
|
||||||
|
[ "$actual" = "$expected" ] ||
|
||||||
|
fail "checksum mismatch for $name"
|
||||||
|
done < checksums.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "release artifact bundle ready: $OUT_DIR"
|
||||||
|
|
@ -28,7 +28,7 @@ Environment:
|
||||||
Strict checks additionally require a clean tree, a vX.Y.Z tag at HEAD, a
|
Strict checks additionally require a clean tree, a vX.Y.Z tag at HEAD, a
|
||||||
matching changelog release section, non-placeholder maintainer metadata, and a
|
matching changelog release section, non-placeholder maintainer metadata, and a
|
||||||
build from the tagged source archive. Run `make package-publish-check` after
|
build from the tagged source archive. Run `make package-publish-check` after
|
||||||
the final GitHub source archive exists to verify package checksums.
|
the explicit release source archive exists to verify package checksums.
|
||||||
USAGE
|
USAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,8 +78,12 @@ grep -q "^pkgname=tnt-chat$" packaging/arch/PKGBUILD ||
|
||||||
fail "packaging/arch/PKGBUILD pkgname is not tnt-chat"
|
fail "packaging/arch/PKGBUILD pkgname is not tnt-chat"
|
||||||
grep -q "^pkgname = tnt-chat$" packaging/arch/.SRCINFO ||
|
grep -q "^pkgname = tnt-chat$" packaging/arch/.SRCINFO ||
|
||||||
fail "packaging/arch/.SRCINFO pkgname is not tnt-chat"
|
fail "packaging/arch/.SRCINFO pkgname is not tnt-chat"
|
||||||
grep -q "v${version}.tar.gz" packaging/homebrew/tnt-chat.rb ||
|
grep -q '${pkgname}-v${pkgver}-source.tar.gz' packaging/arch/PKGBUILD ||
|
||||||
fail "packaging/homebrew/tnt-chat.rb URL does not match v$version"
|
fail "packaging/arch/PKGBUILD source must use the release source archive"
|
||||||
|
grep -q "tnt-chat-v${version}-source.tar.gz" packaging/arch/.SRCINFO ||
|
||||||
|
fail "packaging/arch/.SRCINFO source does not match v$version release archive"
|
||||||
|
grep -q "tnt-chat-v${version}-source.tar.gz" packaging/homebrew/tnt-chat.rb ||
|
||||||
|
fail "packaging/homebrew/tnt-chat.rb URL does not match v$version release archive"
|
||||||
grep -q "^class TntChat < Formula$" packaging/homebrew/tnt-chat.rb ||
|
grep -q "^class TntChat < Formula$" packaging/homebrew/tnt-chat.rb ||
|
||||||
fail "packaging/homebrew/tnt-chat.rb formula class is not TntChat"
|
fail "packaging/homebrew/tnt-chat.rb formula class is not TntChat"
|
||||||
grep -q 'depends_on "libssh"' packaging/homebrew/tnt-chat.rb ||
|
grep -q 'depends_on "libssh"' packaging/homebrew/tnt-chat.rb ||
|
||||||
|
|
@ -193,6 +197,7 @@ step "checking installer syntax"
|
||||||
sh -n install.sh
|
sh -n install.sh
|
||||||
sh -n scripts/check_release_ref.sh
|
sh -n scripts/check_release_ref.sh
|
||||||
sh -n scripts/package_publish_check.sh
|
sh -n scripts/package_publish_check.sh
|
||||||
|
sh -n scripts/package_release_assets.sh
|
||||||
scripts/check_release_ref.sh "v$version"
|
scripts/check_release_ref.sh "v$version"
|
||||||
bad_ref=v0.0.0
|
bad_ref=v0.0.0
|
||||||
[ "$version" != "0.0.0" ] || bad_ref=v9.9.9
|
[ "$version" != "0.0.0" ] || bad_ref=v9.9.9
|
||||||
|
|
|
||||||
158
tests/test_release_artifact_gate.sh
Executable file
158
tests/test_release_artifact_gate.sh
Executable file
|
|
@ -0,0 +1,158 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Release-artifact gate regression tests.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
SCRIPT="../scripts/package_release_assets.sh"
|
||||||
|
STATE_DIR=$(mktemp -d "${TMPDIR:-/tmp}/tnt-release-artifact-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
|
||||||
|
}
|
||||||
|
|
||||||
|
write_elf_x86_64() {
|
||||||
|
printf '\177ELF\002\001\001\000\000\000\000\000\000\000\000\000\002\000\076\000\001\000\000\000' > "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_elf_aarch64() {
|
||||||
|
printf '\177ELF\002\001\001\000\000\000\000\000\000\000\000\000\002\000\267\000\001\000\000\000' > "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_macho_x86_64() {
|
||||||
|
printf '\317\372\355\376\007\000\000\001\003\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000' > "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_macho_arm64() {
|
||||||
|
printf '\317\372\355\376\014\000\000\001\000\000\000\000\002\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000' > "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_artifact_tree() {
|
||||||
|
artifact_root=$1
|
||||||
|
include_source=$2
|
||||||
|
ver=$(version)
|
||||||
|
|
||||||
|
mkdir -p \
|
||||||
|
"$artifact_root/linux-amd64" \
|
||||||
|
"$artifact_root/linux-arm64" \
|
||||||
|
"$artifact_root/darwin-amd64" \
|
||||||
|
"$artifact_root/darwin-arm64"
|
||||||
|
|
||||||
|
write_elf_x86_64 "$artifact_root/linux-amd64/tnt-linux-amd64"
|
||||||
|
write_elf_x86_64 "$artifact_root/linux-amd64/tntctl-linux-amd64"
|
||||||
|
write_elf_aarch64 "$artifact_root/linux-arm64/tnt-linux-arm64"
|
||||||
|
write_elf_aarch64 "$artifact_root/linux-arm64/tntctl-linux-arm64"
|
||||||
|
write_macho_x86_64 "$artifact_root/darwin-amd64/tnt-darwin-amd64"
|
||||||
|
write_macho_x86_64 "$artifact_root/darwin-amd64/tntctl-darwin-amd64"
|
||||||
|
write_macho_arm64 "$artifact_root/darwin-arm64/tnt-darwin-arm64"
|
||||||
|
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")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_file() {
|
||||||
|
path=$1
|
||||||
|
name=$2
|
||||||
|
[ -f "$path" ] && pass "$name" || fail "$name missing"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "=== TNT Release Artifact Gate Tests ==="
|
||||||
|
|
||||||
|
if [ ! -x "$SCRIPT" ]; then
|
||||||
|
echo "Error: script $SCRIPT not found or not executable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VER=$(version)
|
||||||
|
ARTIFACT_ROOT="$STATE_DIR/artifacts"
|
||||||
|
OUT_DIR="$STATE_DIR/out"
|
||||||
|
build_artifact_tree "$ARTIFACT_ROOT" yes
|
||||||
|
|
||||||
|
OUTPUT=$("$SCRIPT" "$ARTIFACT_ROOT" "$OUT_DIR" 2>&1)
|
||||||
|
STATUS=$?
|
||||||
|
if [ "$STATUS" -eq 0 ] &&
|
||||||
|
printf '%s\n' "$OUTPUT" | grep -q 'release artifact bundle ready'; then
|
||||||
|
pass "complete artifact set is accepted"
|
||||||
|
else
|
||||||
|
fail "complete artifact set failed" "$OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for asset in \
|
||||||
|
tnt-linux-amd64 \
|
||||||
|
tntctl-linux-amd64 \
|
||||||
|
tnt-linux-arm64 \
|
||||||
|
tntctl-linux-arm64 \
|
||||||
|
tnt-darwin-amd64 \
|
||||||
|
tntctl-darwin-amd64 \
|
||||||
|
tnt-darwin-arm64 \
|
||||||
|
tntctl-darwin-arm64 \
|
||||||
|
"tnt-chat-v$VER-source.tar.gz" \
|
||||||
|
checksums.txt
|
||||||
|
do
|
||||||
|
expect_file "$OUT_DIR/$asset" "bundles $asset"
|
||||||
|
done
|
||||||
|
|
||||||
|
if grep -q " tnt-linux-amd64$" "$OUT_DIR/checksums.txt" &&
|
||||||
|
grep -q " tnt-chat-v$VER-source.tar.gz$" "$OUT_DIR/checksums.txt"; then
|
||||||
|
pass "checksums include binaries and source archive"
|
||||||
|
else
|
||||||
|
fail "checksums are incomplete" "$(cat "$OUT_DIR/checksums.txt" 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DUP_ROOT="$STATE_DIR/duplicate"
|
||||||
|
DUP_OUT="$STATE_DIR/duplicate-out"
|
||||||
|
build_artifact_tree "$DUP_ROOT" yes
|
||||||
|
mkdir -p "$DUP_ROOT/duplicate"
|
||||||
|
cp "$DUP_ROOT/linux-amd64/tnt-linux-amd64" "$DUP_ROOT/duplicate/tnt-linux-amd64"
|
||||||
|
DUP_OUTPUT=$("$SCRIPT" "$DUP_ROOT" "$DUP_OUT" 2>&1)
|
||||||
|
DUP_STATUS=$?
|
||||||
|
if [ "$DUP_STATUS" -ne 0 ] &&
|
||||||
|
printf '%s\n' "$DUP_OUTPUT" | grep -q 'expected exactly one artifact named tnt-linux-amd64'; then
|
||||||
|
pass "duplicate artifact is rejected"
|
||||||
|
else
|
||||||
|
fail "duplicate artifact handling" "$DUP_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
MISSING_ROOT="$STATE_DIR/missing"
|
||||||
|
MISSING_OUT="$STATE_DIR/missing-out"
|
||||||
|
build_artifact_tree "$MISSING_ROOT" no
|
||||||
|
MISSING_OUTPUT=$("$SCRIPT" "$MISSING_ROOT" "$MISSING_OUT" 2>&1)
|
||||||
|
MISSING_STATUS=$?
|
||||||
|
if [ "$MISSING_STATUS" -ne 0 ] &&
|
||||||
|
printf '%s\n' "$MISSING_OUTPUT" | grep -q "expected exactly one artifact named tnt-chat-v$VER-source.tar.gz"; then
|
||||||
|
pass "missing source archive is rejected"
|
||||||
|
else
|
||||||
|
fail "missing source archive handling" "$MISSING_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "PASSED: $PASS"
|
||||||
|
echo "FAILED: $FAIL"
|
||||||
|
[ "$FAIL" -eq 0 ] && echo "All tests passed" || echo "Some tests failed"
|
||||||
|
exit "$FAIL"
|
||||||
|
|
@ -106,7 +106,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IDLE_READY="$STATE_DIR/idle.ready"
|
IDLE_READY="$STATE_DIR/idle.ready"
|
||||||
IDLE_HOLD=$((DURATION + 2))
|
IDLE_HOLD=$((DURATION + 20))
|
||||||
cat >"$STATE_DIR/idle.expect" <<EOF
|
cat >"$STATE_DIR/idle.expect" <<EOF
|
||||||
set timeout [expr {$IDLE_HOLD + 20}]
|
set timeout [expr {$IDLE_HOLD + 20}]
|
||||||
spawn ssh $SSH_TTY_OPTS idle@127.0.0.1
|
spawn ssh $SSH_TTY_OPTS idle@127.0.0.1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue