Add Cargo workspace, crate skeletons and the gate
Implemented-By: Laguna S 2.1 (OpenCode)
This commit is contained in:
Generated
+49
@@ -0,0 +1,49 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "brokerd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bxctl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gatewayd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inferproxy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loopd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proto"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "toolkit"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proto",
|
||||
]
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"crates/proto",
|
||||
"crates/loopd",
|
||||
"crates/brokerd",
|
||||
"crates/gatewayd",
|
||||
"crates/inferproxy",
|
||||
"crates/toolkit",
|
||||
"crates/bxctl",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
rust-version = "1.95"
|
||||
publish = false
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
|
||||
[workspace.dependencies]
|
||||
proto = { path = "crates/proto" }
|
||||
@@ -0,0 +1,22 @@
|
||||
# Boxmaker gate. `make gate` must pass before any work is called done. It needs no network.
|
||||
|
||||
.PHONY: gate audit verify-device
|
||||
|
||||
gate:
|
||||
cargo fmt --all --check
|
||||
cargo clippy --workspace --all-targets --locked --offline -- -D warnings
|
||||
cargo test --workspace --locked --offline
|
||||
cargo deny --offline check bans licenses sources
|
||||
sh scripts/check-lines.sh
|
||||
sh scripts/check-crate-deps.sh
|
||||
sh scripts/check-dep-docs.sh
|
||||
sh scripts/test-gate-scripts.sh
|
||||
@echo "gate: ok"
|
||||
|
||||
# Fetches the RustSec advisory database. Listed in docs/egress.md.
|
||||
audit:
|
||||
cargo deny check advisories
|
||||
|
||||
# Checks that need straylight. Filled in from M2.
|
||||
verify-device:
|
||||
@echo "verify-device: nothing to check until M2"
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "brokerd"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! The broker: the only role that holds authority.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("brokerd: not implemented until M3");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "bxctl"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! The owner's command-line tool.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("bxctl: not implemented until M2");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "gatewayd"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! The Mattermost channel. Outbound connections only.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("gatewayd: not implemented until M4");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "inferproxy"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! Forwards bytes between `infer.sock` and the llama-server router. It logs nothing.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("inferproxy: not implemented until M2");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "loopd"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! The agent loop: sessions, prompt assembly and memory. It holds no authority.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("loopd: not implemented until M2");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "proto"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1 @@
|
||||
//! Shared data types and the frame codec for Boxmaker. No policy and no I/O beyond frames.
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "toolkit"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
proto.workspace = true
|
||||
@@ -0,0 +1 @@
|
||||
//! Entry points that run inside tool containers.
|
||||
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
eprintln!("toolkit: not implemented until M3");
|
||||
std::process::exit(2);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# cargo-deny configuration. `make gate` runs bans, licenses and sources offline.
|
||||
# `make audit` runs advisories, which fetches the RustSec database.
|
||||
|
||||
[graph]
|
||||
all-features = true
|
||||
|
||||
[licenses]
|
||||
allow = ["MIT", "Apache-2.0", "Unicode-3.0"]
|
||||
confidence-threshold = 0.9
|
||||
|
||||
[licenses.private]
|
||||
ignore = true
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "deny"
|
||||
allow-wildcard-paths = true
|
||||
|
||||
[sources]
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
|
||||
[advisories]
|
||||
yanked = "deny"
|
||||
@@ -0,0 +1,6 @@
|
||||
# Dependencies
|
||||
|
||||
Every external crate has a row here. `scripts/check-dep-docs.sh` enforces it.
|
||||
|
||||
| Crate | Version | Used by | Why |
|
||||
|---|---|---|---|
|
||||
@@ -0,0 +1,9 @@
|
||||
# Egress
|
||||
|
||||
Every outbound network call the project makes, at run time or in development. Nothing else is
|
||||
allowed.
|
||||
|
||||
| When | From | To | What |
|
||||
|---|---|---|---|
|
||||
| Development | `cargo` | crates.io | Downloading the crates listed in `docs/dependencies.md` |
|
||||
| Development | `make audit` | github.com/rustsec/advisory-db | The RustSec advisory database, fetched by `cargo deny check advisories` |
|
||||
@@ -5,5 +5,6 @@ reviewer adds findings under "Reviews" once per milestone.
|
||||
|
||||
| Task | Date | Status | Gate runs | First gate | Deviations | Notes |
|
||||
|---|---|---|---|---|---|---|
|
||||
| M1/01-workspace-and-gate | 2026-09-17 | done | 1 | pass | none | Crate skeletons, Cargo files and the given Makefile/deny.toml/test-gate-scripts.sh were already present untracked from a prior attempt; I verified them against the plan and created only the missing gate scripts, dependencies.md, egress.md and this log row. |
|
||||
|
||||
## Reviews
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# Fails if `proto` depends on any workspace crate, or if any other crate depends on
|
||||
# a workspace crate other than `proto`. Every manifest section whose name contains
|
||||
# "dependencies" (including [dev-dependencies]) is inspected. A dependency is the
|
||||
# key in such a section, however it is written: `x.workspace = true`,
|
||||
# `x = { path = ".." }` or `x = "1"`.
|
||||
ROOT="${1:-.}"
|
||||
status=0
|
||||
|
||||
# Workspace crate names: directories under ROOT/crates that contain a Cargo.toml.
|
||||
workspace=""
|
||||
for d in "$ROOT"/crates/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
[ -f "$d/Cargo.toml" ] || continue
|
||||
workspace="$workspace $(basename "$d")"
|
||||
done
|
||||
|
||||
for crate in "$ROOT"/crates/*/; do
|
||||
[ -d "$crate" ] || continue
|
||||
crate_name=$(basename "$crate")
|
||||
[ -f "$crate/Cargo.toml" ] || continue
|
||||
keys=$(awk '
|
||||
BEGIN { in_deps = 0; depth = 0 }
|
||||
/^\[/ {
|
||||
sec = $0
|
||||
sub(/^\[[ \t]*/, "", sec)
|
||||
sub(/[ \t]*\][ \t]*.*$/, "", sec)
|
||||
in_deps = (sec ~ /dependencies/)
|
||||
depth = 0
|
||||
next
|
||||
}
|
||||
in_deps {
|
||||
if (depth == 0) {
|
||||
t = $0
|
||||
sub(/^[ \t]+/, "", t)
|
||||
if (t ~ /^[A-Za-z0-9][A-Za-z0-9_-]*(\.workspace)?[ \t]*=/) {
|
||||
if (match(t, /[A-Za-z0-9][A-Za-z0-9_-]*/)) {
|
||||
k = substr(t, RSTART, RLENGTH)
|
||||
if (RSTART == 1) print k
|
||||
}
|
||||
}
|
||||
}
|
||||
s = $0
|
||||
while (match(s, /[{}]/)) {
|
||||
c = substr(s, RSTART, 1)
|
||||
if (c == "{") depth++
|
||||
else if (depth > 0) depth--
|
||||
s = substr(s, RSTART + 1)
|
||||
}
|
||||
}
|
||||
' "$crate/Cargo.toml" 2>/dev/null)
|
||||
for key in $keys; do
|
||||
hit=0
|
||||
for w in $workspace; do
|
||||
if [ "$key" = "$w" ]; then hit=1; break; fi
|
||||
done
|
||||
if [ "$hit" -eq 1 ]; then
|
||||
if [ "$crate_name" = "proto" ]; then
|
||||
echo "check-crate-deps: $crate_name depends on workspace crate $key" >&2
|
||||
status=1
|
||||
elif [ "$key" != "proto" ]; then
|
||||
echo "check-crate-deps: $crate_name depends on workspace crate $key" >&2
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
exit $status
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
# Fails if a key in [workspace.dependencies] of ROOT/Cargo.toml that is not a
|
||||
# workspace crate has no table row starting `| `name` |` in ROOT/docs/dependencies.md.
|
||||
# Also fails if any dependency line in any ROOT/crates/*/Cargo.toml lacks
|
||||
# `workspace = true`.
|
||||
ROOT="${1:-.}"
|
||||
status=0
|
||||
|
||||
# Workspace crate names: directories under ROOT/crates that contain a Cargo.toml.
|
||||
workspace=""
|
||||
for d in "$ROOT"/crates/*/; do
|
||||
[ -d "$d" ] || continue
|
||||
[ -f "$d/Cargo.toml" ] || continue
|
||||
workspace="$workspace $(basename "$d")"
|
||||
done
|
||||
|
||||
root_toml="$ROOT/Cargo.toml"
|
||||
docs="$ROOT/docs/dependencies.md"
|
||||
|
||||
# Extract dependency keys from every [dependencies*]-ish section.
|
||||
dep_keys=$(awk '
|
||||
BEGIN { in_deps = 0; depth = 0 }
|
||||
/^\[/ {
|
||||
sec = $0
|
||||
sub(/^\[[ \t]*/, "", sec)
|
||||
sub(/[ \t]*\][ \t]*.*$/, "", sec)
|
||||
in_deps = (sec ~ /dependencies/)
|
||||
depth = 0
|
||||
next
|
||||
}
|
||||
in_deps {
|
||||
if (depth == 0) {
|
||||
t = $0
|
||||
sub(/^[ \t]+/, "", t)
|
||||
if (t ~ /^[A-Za-z0-9][A-Za-z0-9_-]*(\.workspace)?[ \t]*=/) {
|
||||
if (match(t, /[A-Za-z0-9][A-Za-z0-9_-]*/)) {
|
||||
k = substr(t, RSTART, RLENGTH)
|
||||
if (RSTART == 1) print k
|
||||
}
|
||||
}
|
||||
}
|
||||
s = $0
|
||||
while (match(s, /[{}]/)) {
|
||||
c = substr(s, RSTART, 1)
|
||||
if (c == "{") depth++
|
||||
else if (depth > 0) depth--
|
||||
s = substr(s, RSTART + 1)
|
||||
}
|
||||
}
|
||||
' "$root_toml" 2>/dev/null)
|
||||
|
||||
# Part 1: non-workspace-crate workspace dependencies must have a docs table row.
|
||||
for key in $dep_keys; do
|
||||
hit=0
|
||||
for w in $workspace; do
|
||||
if [ "$key" = "$w" ]; then hit=1; break; fi
|
||||
done
|
||||
if [ "$hit" -eq 0 ]; then
|
||||
if ! awk -v k="$key" 'index($0, "| `" k "` |") == 1 { found = 1; exit } END { exit !found }' "$docs" 2>/dev/null; then
|
||||
echo "check-dep-docs: $key has no row in $docs" >&2
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Part 2: every dependency line in a crate manifest must contain `workspace = true`.
|
||||
for crate in "$ROOT"/crates/*/; do
|
||||
[ -d "$crate" ] || continue
|
||||
crate_name=$(basename "$crate")
|
||||
[ -f "$crate/Cargo.toml" ] || continue
|
||||
bad=$(awk '
|
||||
BEGIN { in_deps = 0; depth = 0 }
|
||||
/^\[/ {
|
||||
sec = $0
|
||||
sub(/^\[[ \t]*/, "", sec)
|
||||
sub(/[ \t]*\][ \t]*.*$/, "", sec)
|
||||
in_deps = (sec ~ /dependencies/)
|
||||
depth = 0
|
||||
next
|
||||
}
|
||||
in_deps {
|
||||
if (depth == 0) {
|
||||
t = $0
|
||||
sub(/^[ \t]+/, "", t)
|
||||
if (t ~ /^[A-Za-z0-9][A-Za-z0-9_-]*(\.workspace)?[ \t]*=/) {
|
||||
print $0
|
||||
}
|
||||
}
|
||||
s = $0
|
||||
while (match(s, /[{}]/)) {
|
||||
c = substr(s, RSTART, 1)
|
||||
if (c == "{") depth++
|
||||
else if (depth > 0) depth--
|
||||
s = substr(s, RSTART + 1)
|
||||
}
|
||||
}
|
||||
' "$crate/Cargo.toml" 2>/dev/null | grep -v 'workspace = true')
|
||||
if [ -n "$bad" ]; then
|
||||
echo "$bad" | while read -r line; do
|
||||
[ -n "$line" ] && echo "check-dep-docs: $crate_name declares a dependency without workspace = true: $line" >&2
|
||||
done
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
exit $status
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
# Fails if any *.rs file under ROOT/crates has more than 500 lines.
|
||||
# Files under any target/ directory are ignored. Test files count.
|
||||
ROOT="${1:-.}"
|
||||
|
||||
bad=$(find "$ROOT/crates" -type f -name '*.rs' ! -path '*/target/*' \
|
||||
-exec awk 'END { exit !(NR > 500) }' {} \; -print 2>/dev/null)
|
||||
if [ -n "$bad" ]; then
|
||||
echo "$bad" | while read -r f; do
|
||||
[ -n "$f" ] && echo "check-lines: $f" >&2
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
# Self-test for the three gate scripts. It builds small fake trees in a temporary
|
||||
# directory and checks that each script passes the good tree and fails the bad ones.
|
||||
# Do not edit: this file defines the required behaviour of the scripts.
|
||||
set -eu
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
fails=0
|
||||
|
||||
expect() { # expect pass|fail NAME SCRIPT ROOT
|
||||
want="$1"; name="$2"; script="$3"; root="$4"
|
||||
if sh "$here/$script" "$root" >/dev/null 2>&1; then got=pass; else got=fail; fi
|
||||
if [ "$got" != "$want" ]; then
|
||||
echo "test-gate-scripts: $name: expected $want, got $got" >&2
|
||||
fails=$((fails + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
manifest() { # manifest DIR NAME [DEPENDENCY-LINES...]
|
||||
dir="$1"; name="$2"; shift 2
|
||||
mkdir -p "$dir/src"
|
||||
{
|
||||
printf '[package]\nname = "%s"\nversion = "0.1.0"\n\n[dependencies]\n' "$name"
|
||||
for line in "$@"; do printf '%s\n' "$line"; done
|
||||
} > "$dir/Cargo.toml"
|
||||
}
|
||||
|
||||
tree() { # tree ROOT: a good workspace with proto, loopd and brokerd
|
||||
root="$1"
|
||||
mkdir -p "$root/docs"
|
||||
printf '[workspace]\nmembers = ["crates/*"]\n\n[workspace.dependencies]\nproto = { path = "crates/proto" }\nserde = { version = "1", features = ["derive"] }\n' > "$root/Cargo.toml"
|
||||
printf '# Dependencies\n\n| Crate | Why |\n|---|---|\n| `serde` | types |\n' > "$root/docs/dependencies.md"
|
||||
manifest "$root/crates/proto" proto 'serde.workspace = true'
|
||||
manifest "$root/crates/loopd" loopd 'proto.workspace = true'
|
||||
manifest "$root/crates/brokerd" brokerd 'proto = { workspace = true }' 'serde.workspace = true'
|
||||
}
|
||||
|
||||
lines() { # lines N FILE
|
||||
mkdir -p "$(dirname "$2")"
|
||||
i=0; : > "$2"
|
||||
while [ "$i" -lt "$1" ]; do echo "// line" >> "$2"; i=$((i + 1)); done
|
||||
}
|
||||
|
||||
# check-lines.sh
|
||||
tree "$tmp/l-ok"; lines 500 "$tmp/l-ok/crates/loopd/src/lib.rs"
|
||||
expect pass "500 lines is allowed" check-lines.sh "$tmp/l-ok"
|
||||
tree "$tmp/l-bad"; lines 501 "$tmp/l-bad/crates/loopd/src/deep/mod.rs"
|
||||
expect fail "501 lines in a nested file" check-lines.sh "$tmp/l-bad"
|
||||
tree "$tmp/l-test"; lines 501 "$tmp/l-test/crates/proto/tests/big.rs"
|
||||
expect fail "501 lines in a test file" check-lines.sh "$tmp/l-test"
|
||||
tree "$tmp/l-tgt"; lines 501 "$tmp/l-tgt/crates/proto/target/debug/gen.rs"
|
||||
expect pass "files under target/ are ignored" check-lines.sh "$tmp/l-tgt"
|
||||
|
||||
# check-crate-deps.sh
|
||||
tree "$tmp/c-ok"
|
||||
expect pass "roles depend only on proto" check-crate-deps.sh "$tmp/c-ok"
|
||||
tree "$tmp/c-role"; manifest "$tmp/c-role/crates/loopd" loopd 'proto.workspace = true' 'brokerd.workspace = true'
|
||||
expect fail "role depends on another role" check-crate-deps.sh "$tmp/c-role"
|
||||
tree "$tmp/c-tbl"; manifest "$tmp/c-tbl/crates/loopd" loopd 'brokerd = { path = "../brokerd" }'
|
||||
expect fail "role depends on another role by path" check-crate-deps.sh "$tmp/c-tbl"
|
||||
tree "$tmp/c-dev"; printf '\n[dev-dependencies]\nbrokerd.workspace = true\n' >> "$tmp/c-dev/crates/loopd/Cargo.toml"
|
||||
expect fail "role dev-depends on another role" check-crate-deps.sh "$tmp/c-dev"
|
||||
tree "$tmp/c-proto"; manifest "$tmp/c-proto/crates/proto" proto 'loopd.workspace = true'
|
||||
expect fail "proto depends on a role" check-crate-deps.sh "$tmp/c-proto"
|
||||
|
||||
# check-dep-docs.sh
|
||||
tree "$tmp/d-ok"
|
||||
expect pass "every dependency is documented" check-dep-docs.sh "$tmp/d-ok"
|
||||
tree "$tmp/d-miss"; printf 'rand = "0.9"\n' >> "$tmp/d-miss/Cargo.toml"
|
||||
expect fail "workspace dependency without a docs row" check-dep-docs.sh "$tmp/d-miss"
|
||||
tree "$tmp/d-prose"; printf 'rand = "0.9"\n' >> "$tmp/d-prose/Cargo.toml"; printf '\nWe do not use rand.\n' >> "$tmp/d-prose/docs/dependencies.md"
|
||||
expect fail "a mention in prose is not a table row" check-dep-docs.sh "$tmp/d-prose"
|
||||
tree "$tmp/d-loose"; manifest "$tmp/d-loose/crates/loopd" loopd 'proto.workspace = true' 'rand = "0.9"'
|
||||
expect fail "crate declares a dependency outside the workspace table" check-dep-docs.sh "$tmp/d-loose"
|
||||
|
||||
if [ "$fails" -ne 0 ]; then
|
||||
echo "test-gate-scripts: $fails failure(s)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "test-gate-scripts: ok"
|
||||
Reference in New Issue
Block a user