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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Get commit metadata
id: commit
run: |
echo "hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "time=$(git log -1 --format=%ct)" >> $GITHUB_OUTPUT

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

Expand Down Expand Up @@ -126,6 +132,9 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GIT_COMMIT_HASH=${{ steps.commit.outputs.hash }}
GIT_COMMIT_TIME=${{ steps.commit.outputs.time }}
cache-from: type=gha
cache-to: type=gha,mode=max

Expand Down
22 changes: 17 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,49 @@ RUN npm ci --prefix frontend
# build.rs will run `npm run build` (node_modules already present)
# and embed the resulting dist/ into the binary.
COPY . .
RUN cargo build --release

# Accept git metadata as build args so that build.rs can embed the correct
# version string even though .git is excluded from the Docker build context.
ARG GIT_COMMIT_HASH=unknown
ARG GIT_COMMIT_TIME=
RUN GIT_COMMIT_HASH=${GIT_COMMIT_HASH} GIT_COMMIT_TIME=${GIT_COMMIT_TIME} cargo build --release

# ---- Stage 2: Runtime ----
FROM debian:bookworm-slim AS runtime

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
ca-certificates gosu \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy only the compiled binary
# Copy only the compiled binary and the entrypoint helper
COPY --from=builder /app/target/release/motdtracker .
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

# Create the default data directory for the SQLite database
RUN mkdir -p data

# Run as a non-root user for better security
# Create the unprivileged runtime user.
# chown the data dir so anonymous Docker volumes are initialised with the
# correct ownership; the entrypoint.sh re-applies chown for bind mounts.
RUN useradd -r -u 10001 -s /bin/false motdtracker \
&& chown motdtracker:motdtracker /app/data
USER motdtracker

# Persist the SQLite database across container restarts
VOLUME ["/app/data"]

# Default listen port (matches the default in AppConfig)
EXPOSE 5011

# entrypoint.sh runs as root, fixes /app/data ownership, then drops to the
# motdtracker user via gosu before executing the application.
#
# Mount your config.toml at /app/config.toml before starting.
# Example:
# docker run -v ./config.toml:/app/config.toml \
# -v motdtracker_data:/app/data \
# -p 5011:5011 ghcr.io/poicraft/motdtracker
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["./motdtracker"]
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

Rust 高性能后端 + React 前端 · 单文件部署 · 前端内嵌

[![CI](https://github.com/PoiCraft/motdtracker-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/PoiCraft/motdtracker-rs/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/PoiCraft/motdtracker-rs?label=Latest)](https://github.com/PoiCraft/motdtracker-rs/releases/latest)
[![CI](https://github.com/PoiCraft/MotdTracker/actions/workflows/ci.yml/badge.svg)](https://github.com/PoiCraft/MotdTracker/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/PoiCraft/MotdTracker?label=Latest)](https://github.com/PoiCraft/MotdTracker/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Rust](https://img.shields.io/badge/Rust-1.75+-orange.svg)](https://www.rust-lang.org/)

Expand Down Expand Up @@ -36,7 +36,7 @@ MotdTracker 是一个专为 Minecraft 服务器设计的多入口点实时监控

## 下载预编译版本

前往 [GitHub Releases](https://github.com/PoiCraft/motdtracker-rs/releases/latest) 下载对应平台的预编译二进制:
前往 [GitHub Releases](https://github.com/PoiCraft/MotdTracker/releases/latest) 下载对应平台的预编译二进制:

| 平台 | 文件 |
|------|------|
Expand All @@ -45,7 +45,7 @@ MotdTracker 是一个专为 Minecraft 服务器设计的多入口点实时监控
| macOS x86_64 | `motdtracker-x86_64-apple-darwin.tar.gz` |
| macOS ARM64 | `motdtracker-aarch64-apple-darwin.tar.gz` |

> 每次 push 到 main 分支会自动构建,可在 [Actions](https://github.com/PoiCraft/motdtracker-rs/actions/workflows/ci.yml) 页面下载最新开发版 artifact。
> 每次 push 到 main 分支会自动构建,可在 [Actions](https://github.com/PoiCraft/MotdTracker/actions/workflows/ci.yml) 页面下载最新开发版 artifact。

下载解压后直接运行:

Expand Down Expand Up @@ -307,7 +307,7 @@ git config core.hooksPath .githooks

## CI / CD

- **push / PR** → 自动 check + test + 多平台构建,产物上传到 [Actions](https://github.com/PoiCraft/motdtracker-rs/actions)
- **push / PR** → 自动 check + test + 多平台构建,产物上传到 [Actions](https://github.com/PoiCraft/MotdTracker/actions)
- **打 tag(`v*`)** → 自动构建 + 生成 GitHub Release + 上传预编译二进制 + SHA256 校验和

```bash
Expand All @@ -326,15 +326,15 @@ git push origin v0.1.0
### 直接运行(Docker)

```bash
# 拉取镜像(替换为实际 owner/repo)
docker pull ghcr.io/poicraft/motdtracker-rs:latest
# 拉取镜像
docker pull ghcr.io/poicraft/motdtracker:latest

# 以后台模式运行,映射端口并挂载数据目录
docker run -d --name motdtracker \
-p 5011:5011 \
-v $(pwd)/data:/app/data \
-v $(pwd)/config.toml:/app/config.toml \
ghcr.io/poicraft/motdtracker-rs:latest
-p 5011:5011 \
-v $(pwd)/data:/app/data \
-v $(pwd)/config.toml:/app/config.toml \
ghcr.io/poicraft/motdtracker:latest
Comment thread
gggxbbb marked this conversation as resolved.

# 查看日志
docker logs -f motdtracker
Expand All @@ -347,17 +347,17 @@ docker logs -f motdtracker
```yaml
version: "3.8"
services:
motdtracker:
image: ghcr.io/poicraft/motdtracker-rs:latest
container_name: motdtracker
restart: unless-stopped
ports:
- "5011:5011"
volumes:
- ./data:/app/data
- ./config.toml:/app/config.toml
environment:
- TZ=Asia/Shanghai
motdtracker:
image: ghcr.io/poicraft/motdtracker:latest
container_name: motdtracker
restart: unless-stopped
ports:
- "5011:5011"
volumes:
- ./data:/app/data
- ./config.toml:/app/config.toml
environment:
- TZ=Asia/Shanghai
```

启动服务:
Expand Down
37 changes: 33 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,51 @@ fn main() {
println!("cargo:rerun-if-changed=.git/{}", ref_path);
}
}
println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH");
println!("cargo:rerun-if-env-changed=GIT_COMMIT_TIME");

// Generate pseudo version: vA.B.C-yyyyMMddhhmmss-{git-hash-short}
// Falls back to GIT_COMMIT_HASH / GIT_COMMIT_TIME env vars when the .git
// directory is absent (e.g. Docker builds where .git is in .dockerignore).
let pkg_version = std::env::var("CARGO_PKG_VERSION").unwrap();

let git_hash = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.or_else(|| std::env::var("GIT_COMMIT_HASH").ok())
.unwrap_or_else(|| "unknown".to_string());
Comment on lines 20 to 32

// Use the commit's own timestamp, not the build time.
let commit_secs = Command::new("git")
.args(["log", "-1", "--format=%ct"])
.output()
.ok()
.filter(|o| o.status.success())
.and_then(|o| {
String::from_utf8_lossy(&o.stdout)
.trim()
.parse::<u64>()
.ok()
})
.or_else(|| {
std::env::var("GIT_COMMIT_TIME")
.ok()
.and_then(|s| s.parse::<u64>().ok())
.filter(|&t| t > 0)
})
.unwrap_or_else(|| {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
});
Comment on lines +46 to +58

let now = {
use std::time::{SystemTime, UNIX_EPOCH};
let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let secs = duration.as_secs();
let tm = time_from_epoch(secs);
let tm = time_from_epoch(commit_secs);
format!(
"{:04}{:02}{:02}{:02}{:02}{:02}",
tm.0, tm.1, tm.2, tm.3, tm.4, tm.5
Expand Down
13 changes: 13 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh
set -e

# When /app/data is bind-mounted from the host Docker may create the directory
# as root, preventing the unprivileged motdtracker user from writing the SQLite
# database. Fix ownership only when necessary (avoids startup latency on large
# bind mounts).
if [ "$(stat -c '%u:%g' /app/data 2>/dev/null)" != "10001:10001" ]; then
chown motdtracker:motdtracker /app/data || \
echo "Warning: could not chown /app/data – database writes may fail if the directory is root-owned"
fi

exec gosu motdtracker "$@"