Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,82 @@
|
||||
#!/bin/sh
|
||||
# 干净容器验收;先上传归档、checksum、项目夹具和 test/dever-tests/fixtures/tls。
|
||||
set -eu
|
||||
cd /work
|
||||
for tool in rustc cargo rustup gcc cc ld; do
|
||||
if command -v "$tool"; then
|
||||
echo "unexpected preinstalled compiler: $tool" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
test ! -f /usr/include/stdio.h
|
||||
cat /etc/os-release
|
||||
if [ "${1:-}" = --installed ]; then
|
||||
mv "$2" /work/dever
|
||||
else
|
||||
archive=${1:-dever-0.0.1-linux-x86_64-development.tar.xz}
|
||||
sha256sum -c "$archive.sha256"
|
||||
python3 -m tarfile -e "$archive" --filter=data
|
||||
fi
|
||||
validation_path=$PATH
|
||||
export PATH="/work/dever/bin:$PATH"
|
||||
dever new hello
|
||||
dever check hello
|
||||
dever fmt hello --check
|
||||
dever test hello
|
||||
dever run hello -- hello.greeting.greet '{"name":"Dever"}' > hello.json
|
||||
grep -F '"data":"Hello, Dever"' hello.json
|
||||
dever build hello --output /work/hello-app
|
||||
mv dever 'dever moved'
|
||||
export PATH="/work/dever moved/bin:$validation_path"
|
||||
dever build hello --output /work/hello-moved
|
||||
./hello-moved hello.greeting.greet '{"name":"moved"}' > moved.json
|
||||
grep -F '"data":"Hello, moved"' moved.json
|
||||
mkdir http database
|
||||
dever build fixtures/http-application --output /work/http/app
|
||||
dever build fixtures/database --output /work/database/app
|
||||
mkdir database/config
|
||||
cat > database/config/setting.json <<'JSON'
|
||||
{"database":{"default":{"type":"sqlite","path":"data/account.db","max_connections":1,"max_page_size":20}}}
|
||||
JSON
|
||||
./database/app demo.account.create '{"name":"offline"}' > created.json
|
||||
grep -F '"name":"offline"' created.json
|
||||
./database/app demo.account.get '{"id":1}' > fetched.json
|
||||
cmp created.json fetched.json
|
||||
if ./database/app demo.account.explode '{"name":"rollback","value":9223372036854775807}' > rollback.json 2> rollback.log; then
|
||||
echo 'transaction fault unexpectedly succeeded' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -F 'integer arithmetic fault' rollback.log
|
||||
./database/app demo.account.count '{}' > count.json
|
||||
grep -F '"data":1' count.json
|
||||
# 全部运行库特性须在裁剪后的 vendor 中仍能离线解析和编译,包括 PostgreSQL、Job、Worker。
|
||||
toolchain_root=$(dirname "$(dever skill path)")
|
||||
python3 - "$toolchain_root" <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tomllib
|
||||
|
||||
runtime = Path(sys.argv[1]) / 'rust/src/runtime'
|
||||
features = sorted(tomllib.loads((runtime / 'Cargo.toml').read_text())['features'])
|
||||
project = Path('/work/runtime-all')
|
||||
(project / 'src').mkdir(parents=True)
|
||||
(project / 'src/main.rs').write_text('fn main() {}\n')
|
||||
(project / 'Cargo.toml').write_text(
|
||||
'[package]\nname = "all-runtime-features"\nversion = "0.0.0"\nedition = "2024"\n'
|
||||
'[workspace]\n[dependencies]\n'
|
||||
f'dever-runtime = {{ path = {json.dumps(str(runtime))}, features = {json.dumps(features)} }}\n'
|
||||
)
|
||||
PY
|
||||
"$toolchain_root/toolchain/bin/cargo" build --offline --release \
|
||||
--manifest-path /work/runtime-all/Cargo.toml --target-dir "$toolchain_root/rust/target"
|
||||
"$toolchain_root/rust/target/release/all-runtime-features"
|
||||
echo 'ALL_RUNTIME_FEATURES_PASS'
|
||||
# 隐藏整个开发包后,正式二进制依然必须正常执行。
|
||||
mv 'dever moved' hidden-toolchain
|
||||
./hello-app hello.greeting.greet '{"name":"standalone"}' > standalone.json
|
||||
grep -F '"data":"Hello, standalone"' standalone.json
|
||||
echo 'BUILD_AND_DATABASE_PASS'
|
||||
python3 /work/transport.py
|
||||
wc -c /work/hello-app /work/http/app /work/database/app
|
||||
echo 'CLEAN_DEVELOPMENT_PASS'
|
||||
@@ -0,0 +1,60 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: dever-development-check
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 86400
|
||||
activeDeadlineSeconds: 7200
|
||||
backoffLimit: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dever-development-check
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
restartPolicy: Never
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: check
|
||||
image: docker.io/library/python@sha256:aaf08c5490d52cd92bc27142a5215b1e47355a5eaf9adfad2a0fbc022d0b5490
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- 'while [ ! -f /work/start ]; do sleep 1; done; exec /bin/sh /work/check.sh'
|
||||
env:
|
||||
- name: CARGO_BUILD_JOBS
|
||||
value: "4"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
ephemeral-storage: 4Gi
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: 8Gi
|
||||
ephemeral-storage: 12Gi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
volumeMounts:
|
||||
- name: work
|
||||
mountPath: /work
|
||||
volumes:
|
||||
- name: work
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: dever-development-check
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: dever-development-check
|
||||
policyTypes: [Egress]
|
||||
egress: []
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
# 真实下载与共享缓存验收;准备完成后由调用方关闭本 Job 的外网再跑通用开发验收。
|
||||
set -euo pipefail
|
||||
cd /work
|
||||
export SHELL=/bin/bash
|
||||
for certificate in root.pem server.pem server-key.pem; do
|
||||
test -f "/work/tls/$certificate" || { echo "Missing TLS fixture: $certificate" >&2; exit 1; }
|
||||
done
|
||||
for tool in rustc cargo rustup gcc cc ld; do
|
||||
if command -v "$tool"; then
|
||||
echo "unexpected preinstalled compiler: $tool" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
python3 /work/server.py > server.log 2>&1 &
|
||||
server=$!
|
||||
finish() {
|
||||
local code=$?
|
||||
if (( code )); then
|
||||
echo "ONLINE_VALIDATION_FAILED: $code" >&2
|
||||
for log in retry.log first-run.err first-test.err wait.log http/server-False.log http/server-True.log server.log; do
|
||||
if [[ -f $log ]]; then printf '\n%s\n' "$log"; tail -n 20 "$log"; fi
|
||||
done
|
||||
fi
|
||||
kill "$server"
|
||||
wait "$server" || true
|
||||
}
|
||||
trap finish EXIT
|
||||
python3 - <<'PY'
|
||||
import socket
|
||||
import time
|
||||
for attempt in range(100):
|
||||
try:
|
||||
with socket.create_connection(('127.0.0.1', 18080), timeout=1):
|
||||
break
|
||||
except ConnectionRefusedError:
|
||||
time.sleep(0.1)
|
||||
else:
|
||||
raise RuntimeError('local release server did not start')
|
||||
PY
|
||||
url=http://127.0.0.1:18080
|
||||
prefix='/work/install/dever tools'
|
||||
export RUSTUP_DIST_SERVER=$url
|
||||
touch /work/reject-environment
|
||||
curl -fsSL "$url/install" | timeout 30 bash -s -- --prefix "$prefix" --release-url "$url" > install.log 2>&1
|
||||
cat install.log
|
||||
if grep -Ei 'rustc|cargo|private Rust' install.log; then exit 1; fi
|
||||
test "$("$prefix/bin/dever" --version)" = 'dever 0.0.1'
|
||||
"$prefix/bin/dever" new editing
|
||||
"$prefix/bin/dever" check editing
|
||||
"$prefix/bin/dever" fmt editing --check
|
||||
test ! -e "$prefix/versions/0.0.1/toolchain/ready"
|
||||
# 安装已返回且编辑命令可用;首次编译须报告真实下载失败,不能假成功。
|
||||
if "$prefix/bin/dever" build editing --output /work/rejected-app > failed-build.log 2>&1; then
|
||||
echo 'build unexpectedly succeeded without an environment' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -F 'Dever environment preparation failed' failed-build.log
|
||||
grep -F '404' "$prefix/versions/0.0.1/toolchain/prepare.log"
|
||||
test ! -e /work/rejected-app
|
||||
rm /work/reject-environment
|
||||
touch /work/hold-environment
|
||||
# 用真实归档前缀模拟上次断线已下载的内容,服务端记录并验证 Range 恢复。
|
||||
native=$(basename /work/assets/dever-native-*.tar.xz)
|
||||
head -c 65536 "/work/assets/$native" > "$prefix/cache/downloads/$native.part"
|
||||
curl -fsSL "$url/install" | timeout 30 bash -s -- --prefix "$prefix" --release-url "$url" > retry.log 2>&1
|
||||
"$prefix/bin/dever" run editing -- hello.greeting.greet '{"name":"background"}' > first-run.log 2> first-run.err &
|
||||
running=$!
|
||||
"$prefix/bin/dever" test editing > first-test.log 2> first-test.err &
|
||||
testing=$!
|
||||
bash assets/install --prefix "$prefix" --release-url "$url" --wait > wait.log 2>&1 &
|
||||
waiting=$!
|
||||
for attempt in {1..100}; do
|
||||
if [[ -f /work/parallel-downloads ]]; then break; fi
|
||||
sleep 0.1
|
||||
done
|
||||
test -f /work/parallel-downloads
|
||||
kill -0 "$running" "$testing" "$waiting"
|
||||
test ! -e "$prefix/versions/0.0.1/toolchain/ready"
|
||||
if flock -n "$prefix/.prepare.lock" true; then
|
||||
echo 'preparation lock was not held' >&2
|
||||
exit 1
|
||||
fi
|
||||
echo BACKGROUND_EDITING_AND_PARALLEL_DOWNLOAD_PASS
|
||||
rm /work/hold-environment
|
||||
wait "$running"
|
||||
wait "$testing"
|
||||
wait "$waiting"
|
||||
cat first-run.log first-run.err first-test.log
|
||||
grep -F '"data":"Hello, background"' first-run.log
|
||||
test "$(cat /work/resumed-offset)" = 65536
|
||||
test -f "$prefix/versions/0.0.1/toolchain/ready"
|
||||
if grep -Ei 'rustc|cargo|rust-std' first-run.err first-test.err wait.log; then exit 1; fi
|
||||
echo RESUME_AND_CONCURRENT_BUILD_PASS
|
||||
test "$(grep -c 'dever.*env' "$HOME/.bashrc")" -eq 1
|
||||
first=$(find "$prefix/toolchains" -type f -printf '%P %s %T@\n' | sort | sha256sum)
|
||||
curl -fsSL "$url/install" | bash -s -- --prefix "$prefix" --release-url "$url" --wait
|
||||
second=$(find "$prefix/toolchains" -type f -printf '%P %s %T@\n' | sort | sha256sum)
|
||||
test "$first" = "$second"
|
||||
test "$(grep -c 'dever.*env' "$HOME/.bashrc")" -eq 1
|
||||
# 校验失败不能切换现有命令,下载错误不得伪报成功。
|
||||
active=$(readlink "$prefix/bin/dever")
|
||||
cp assets/dever-0.0.1-linux-x86_64.tar.xz assets/dever-0.0.2-linux-x86_64.tar.xz
|
||||
printf '%064d dever-0.0.2-linux-x86_64.tar.xz\n' 0 > assets/dever-0.0.2-linux-x86_64.tar.xz.sha256
|
||||
if bash assets/install --prefix "$prefix" --release-url "$url" --version 0.0.2 > rejected.log 2>&1; then
|
||||
echo 'corrupt release unexpectedly installed' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -F FAILED rejected.log
|
||||
test "$(readlink "$prefix/bin/dever")" = "$active"
|
||||
test ! -e "$prefix/versions/0.0.2"
|
||||
test -z "$(find "$prefix" -maxdepth 1 -name '.install.*' -type d -print)"
|
||||
du -sk "$prefix" "$prefix/cache/cargo" "$prefix/toolchains"
|
||||
echo ONLINE_INSTALL_PASS
|
||||
touch /work/online-ready
|
||||
while [ ! -f /work/offline-start ]; do sleep 1; done
|
||||
bash /work/check.sh --installed "$prefix"
|
||||
@@ -0,0 +1,44 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: dever-online-check
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 86400
|
||||
activeDeadlineSeconds: 7200
|
||||
backoffLimit: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dever-online-check
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: check
|
||||
image: git.dever.cc/platform/workspace@sha256:795ba3591832e16622bc949adae46fb907eacb54499ba3f80dae708361bc2752
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- |
|
||||
while [ ! -f /work/start ]; do sleep 1; done
|
||||
exec bash /work/online.sh
|
||||
env:
|
||||
- name: CARGO_BUILD_JOBS
|
||||
value: "4"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
ephemeral-storage: 4Gi
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: 8Gi
|
||||
ephemeral-storage: 12Gi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
volumeMounts:
|
||||
- name: work
|
||||
mountPath: /work
|
||||
volumes:
|
||||
- name: work
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,61 @@
|
||||
"""仅为安装验收控制下载边界;工具链内容仍来自官方源。"""
|
||||
|
||||
from functools import partial
|
||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
WORK = Path('/work')
|
||||
lock = threading.Lock()
|
||||
active = 0
|
||||
|
||||
|
||||
class Handler(SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
global active
|
||||
environment = self.path.startswith(('/dist/', '/dever-native-'))
|
||||
if environment and (WORK / 'reject-environment').exists():
|
||||
self.send_error(404, 'intentional preparation failure')
|
||||
return
|
||||
archive = environment and self.path.endswith('.tar.xz')
|
||||
if archive:
|
||||
with lock:
|
||||
active += 1
|
||||
if active >= 2:
|
||||
(WORK / 'parallel-downloads').touch()
|
||||
try:
|
||||
while (WORK / 'hold-environment').exists():
|
||||
time.sleep(0.1)
|
||||
self.download()
|
||||
finally:
|
||||
with lock:
|
||||
active -= 1
|
||||
else:
|
||||
self.download()
|
||||
|
||||
def download(self):
|
||||
if self.path.startswith('/dist/'):
|
||||
self.send_response(302)
|
||||
self.send_header('Location', 'https://static.rust-lang.org' + self.path)
|
||||
self.end_headers()
|
||||
return
|
||||
resume = self.headers.get('Range')
|
||||
if resume and self.path.startswith('/dever-native-'):
|
||||
offset = int(resume.removeprefix('bytes=').removesuffix('-'))
|
||||
path = Path(self.translate_path(self.path))
|
||||
size = path.stat().st_size
|
||||
(WORK / 'resumed-offset').write_text(str(offset))
|
||||
self.send_response(206)
|
||||
self.send_header('Content-Range', f'bytes {offset}-{size - 1}/{size}')
|
||||
self.send_header('Content-Length', str(size - offset))
|
||||
self.end_headers()
|
||||
with path.open('rb') as source:
|
||||
source.seek(offset)
|
||||
self.copyfile(source, self.wfile)
|
||||
return
|
||||
super().do_GET()
|
||||
|
||||
|
||||
ThreadingHTTPServer(('127.0.0.1', 18080), partial(Handler, directory='/work/assets')).serve_forever()
|
||||
@@ -0,0 +1,52 @@
|
||||
"""在验收容器的回环接口验证已脱离开发包的 HTTP/TLS 产物。"""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import ssl
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
root = Path("/work/http")
|
||||
(root / "config").mkdir(exist_ok=True)
|
||||
certificates = Path("/work/tls")
|
||||
for encrypted in (False, True):
|
||||
with socket.socket() as listener:
|
||||
listener.bind(("127.0.0.1", 0))
|
||||
port = listener.getsockname()[1]
|
||||
settings = {"http": {"host": "127.0.0.1", "port": port},
|
||||
"sites": {"public": {"path": "", "hosts": ["localhost"]}}}
|
||||
if encrypted:
|
||||
settings["http"]["tls"] = {"certificate": str(certificates / "server.pem"),
|
||||
"key": str(certificates / "server-key.pem")}
|
||||
(root / "config/setting.json").write_text(json.dumps(settings))
|
||||
with (root / f"server-{encrypted}.log").open("w") as log:
|
||||
server = subprocess.Popen([str(root / "app")], stdout=log, stderr=log)
|
||||
try:
|
||||
deadline = time.monotonic() + 10
|
||||
while True:
|
||||
assert server.poll() is None, (root / f"server-{encrypted}.log").read_text()
|
||||
try:
|
||||
with socket.create_connection(("127.0.0.1", port), timeout=1):
|
||||
break
|
||||
except ConnectionRefusedError:
|
||||
assert time.monotonic() < deadline, "HTTP server startup timed out"
|
||||
time.sleep(0.05)
|
||||
if encrypted:
|
||||
context = ssl.create_default_context(cafile=str(certificates / "root.pem"))
|
||||
client = http.client.HTTPSConnection("localhost", port, context=context, timeout=5)
|
||||
else:
|
||||
client = http.client.HTTPConnection("localhost", port, timeout=5)
|
||||
try:
|
||||
client.request("GET", "/demo/greeting/greet?name=Dever")
|
||||
response = client.getresponse()
|
||||
assert response.status == 200, response.status
|
||||
assert json.load(response)["data"] == "Hello, Dever"
|
||||
finally:
|
||||
client.close()
|
||||
finally:
|
||||
server.terminate()
|
||||
server.wait(timeout=10)
|
||||
print("HTTP_TLS_PASS")
|
||||
Reference in new issue
Block a user