This commit is contained in:
Wang Sen Di
2025-11-23 14:15:20 +08:00
parent dbd83b7705
commit 6ba67a8d85
22 changed files with 519 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
# 本地环境
**/.DS_Store
**/.local/*
**/.venv/*
**/.conda/*
**/.taskfile/.env
# 临时目录
**/tmp/**
**/log/**
**/logs/**
**/temp/**
**/cache/**
# 构建产物
**/dist/**
**/build/**

0
.taskfile/.env.template Normal file
View File

12
.taskfile/cmd.yml Normal file
View File

@@ -0,0 +1,12 @@
# https://taskfile.dev
version: "3"
vars:
GREETING: 用于本项目的常用命令编写
tasks:
default:
desc: 默认任务
cmds:
- echo "${GREETING}"

50
.taskfile/env.yml Normal file
View File

@@ -0,0 +1,50 @@
version: "3"
vars:
NAMESPACE: "wangsendi"
DEVELOPER: "https://yuque.com/wangsendi"
TIME_NOW:
sh: TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M:%S %Z'
PATH_REALPATH:
sh: pwd
PATH_DIRNAME:
sh: dirname "{{.PATH_REALPATH}}"
PATH_BASENAME:
sh: basename "{{.PATH_REALPATH}}"
PATH_PROJECT:
sh: echo "{{.PATH_BASENAME}}" | cut -d'-' -f2-
GIT_TAG_LATEST:
sh: |
_latest_tag=$(git tag --sort=-v:refname | head -n 1 2>/dev/null) || true
if [[ "${_latest_tag}" == "" ]]; then
git tag v0.0.0 -m "init" 2>/dev/null || true
_latest_tag="v0.0.0";
fi
echo ${_latest_tag}
GIT_COMMIT:
sh: git log -n 1 --format=%h 2>/dev/null || echo "0000"
GIT_SOURCE:
sh: grep -A1 '^\[remote' .git/config | grep 'url' | head -n 1 | sed 's|.git$||; s|:|/|; s|git@|https://|' | awk '{print $NF}'
GIT_PROJECT:
sh: echo "{{.GIT_SOURCE}}" | awk -F/ '{print $NF}'
tasks:
env:
desc: "显示环境变量"
silent: true
cmds:
- |
echo "NAMESPACE: {{.NAMESPACE}}"
echo "DEVELOPER: {{.DEVELOPER}}"
echo "TIME_NOW: {{.TIME_NOW}}"
echo
echo "PATH_REALPATH: {{.PATH_REALPATH}}"
echo "PATH_DIRNAME: {{.PATH_DIRNAME}}"
echo "PATH_BASENAME: {{.PATH_BASENAME}}"
echo "PATH_PROJECT: {{.PATH_PROJECT}}"
echo
echo "GIT_TAG_LATEST: {{.GIT_TAG_LATEST}}"
echo "GIT_COMMIT: {{.GIT_COMMIT}}"
echo "GIT_SOURCE: {{.GIT_SOURCE}}"

76
.taskfile/git.yml Normal file
View File

@@ -0,0 +1,76 @@
version: "3"
tasks:
clear:
desc: "清除历史提交记录"
silent: true
cmds:
- |
_branch_name_current=$(git branch --show-current 2>/dev/null || echo "")
_branch_name_backups="local/bak/${_branch_name_current}/$(date +'%Y/%m/%d/%H%M%S')"
if [[ "${_branch_name_current}" == "" ]]; then
echo "当前分支为空,跳过清除 git 缓存"
exit 0
fi
git add -A && git commit -am "clear commit" || true
git rm -r --cached .
git branch -m ${_branch_name_backups}
git checkout --orphan ${_branch_name_current}
git add -A && git commit -am "clear commit" || true
git push -f origin ${_branch_name_current}
reinit:
desc: "重新初始化 git"
cmds:
- |
_remote_url=$(git remote get-url origin | head -n1)
rm -rf .git
git init --initial-branch=main
touch README.md
git add .
git commit -m "first commit"
git tag -a {{.GIT_TAG_LATEST}} -m "init"
git remote add origin ${_remote_url}
git config push.autoSetupRemote true
- task: clear
push:
desc: "推送代码"
cmds:
- |
git add .
git commit -m "- {{.CLI_ARGS}}" || true
git push origin main || true
tag:next:
desc: "创建 tag"
vars:
GIT_TAG_NEXT:
sh: |
echo {{.GIT_TAG_LATEST}} | awk -F. '{print $1"."$2"."$3+1}'
cmds:
- echo '__version__ = "{{.GIT_TAG_NEXT}}"' > $(find . -maxdepth 3 -name version.py) || true
- task: push
- |
git tag -a {{.GIT_TAG_NEXT}} -m "- {{.CLI_ARGS}}"
git push origin {{.GIT_TAG_NEXT}}
keys:gen:
desc: "设置并使用部署秘钥"
cmds:
- |
_path_keys=".taskfile/git/keys"
mkdir -p ${_path_keys}
if [[ ! -f "${_path_keys}/id_ed25519" ]]; then
ssh-keygen -t ed25519 -N '' -f ${_path_keys}/id_ed25519 -C "{{.GIT_PROJECT}}"
fi
chmod 600 ${_path_keys}/id_ed25519
chmod 644 ${_path_keys}/id_ed25519.pub
keys:set:
desc: "设置部署秘钥"
cmds:
- |
_path_keys=".taskfile/git/keys"
git config push.autoSetupRemote true
git config core.sshCommand "ssh -i ${_path_keys}/id_ed25519 -o StrictHostKeyChecking=no -F /dev/null"

18
Taskfile.yml Normal file
View File

@@ -0,0 +1,18 @@
# https://taskfile.dev
version: "3"
dotenv: [".env", ".taskfile/.env", "{{.HOME}}/.env", ".taskfile/.env.template"]
includes:
env:
taskfile: .taskfile/env.yml
optional: true
flatten: true
cmd:
taskfile: .taskfile/cmd.yml
optional: true
git:
taskfile: .taskfile/git.yml
optional: true

15
boot/auto.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
__main() {
:
}
__main
__help() {
cat >/dev/null <<-'AEOF'
AEOF
}

22
boot/conflict.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
# shellcheck disable=SC2046
if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi
__main() {
:
}
# __main
nsenter --mount=/host/proc/1/ns/mnt bash <<<"$(declare -f __main | sed "\$a __main")"
__help() {
cat >/dev/null <<-'EOF'
bash -c 'cd /apps/gitrce/ && git pull -f; bash /apps/gitrce/boot/conflict.sh'
EOF
}

17
boot/cron.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
# shellcheck disable=SC2009
# Admin https://www.yuque.com/wangsendi
__main() {
: # TODO
}
__main
__help() {
cat >/dev/null <<-'AEOF'
AEOF
}

8
boot/env.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034
# 在每一次间隔时间后读取的变量, 作用容易入口文件
# 用于在容器运行的状态下, 远程修改 git 拉取间隔时间
# 注意,最小值不能大于最大值, 否则客户端 Git 死循环
INTERVAL_MIN=200 # 间隔最小值
INTERVAL_MAX=300 # 间隔最大值

84
boot/install.sh Normal file
View File

@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# shellcheck disable=SC2317,SC2086
# Admin https://www.yuque.com/wangsendi
__main() {
{
# 判断是否有 docker 和 docker-compose
if ! command -v docker >/dev/null 2>&1; then
echo "docker not installed"
return 1
fi
if [[ "$(docker compose version 2>/dev/null | grep version -c)" != "1" ]]; then
echo "docker compose not installed"
return 1
fi
}
{
# 镜像准备
_image1="1181.s.kuaicdn.cn:11818/ghcr.io/wangsendi/gitrce:bbiz-t2507281"
_image2="$(echo "$_image1" | awk -F '/' '{print $NF}')"
if [[ "$(docker images "$_image2" | wc -l)" != "2" ]]; then
docker pull $_image1 && docker tag "$_image1" "$_image2"
fi
}
{
_gre_remote_repo="https://codeberg.org/umi-edge/250924-s-speedtest.git"
_container_name="250924-s-speedtest"
_apps_data="/data/umi/gitrce/$_container_name/master"
_compose_file="$_apps_data/boot/docker-compose.yaml"
mkdir -p ${_compose_file%/*}
cat >$_compose_file <<EOF
services:
master:
container_name: $_container_name
image: $_image2
restart: always
network_mode: host
privileged: true
security_opt:
- apparmor:unconfined
environment:
- GIT_REMOTE_REPO=$_gre_remote_repo
- APPS_DATA=$_apps_data
- CONTAINER_NAME=$_container_name
volumes:
- /dev:/host/dev:ro
- /sys:/host/sys:ro
- /proc:/host/proc:ro
- /run:/host/run:rw
- /etc:/host/etc:rw
- /data:/data:rw,rshared
- /disk:/disk:rw,rshared
- "$_apps_data/:/apps/data"
EOF
}
{
# run
docker ps -f name="[0-9a-z]{12}_$_container_name" -aq | xargs -r docker rm -f # 删除变种容器
_cmd="docker compose -p $_container_name -f $_compose_file up -d --remove-orphans"
if ! eval "$_cmd"; then
docker ps -f name=$_container_name -aq | xargs -r -I{} echo 'ps -ef | grep -v $$ | grep {}' | sh | awk '{print $2}' | xargs -r -I{} kill -9 {}
docker rm -f $_container_name
eval "$_cmd"
fi
}
}
__main
__help() {
cat >/dev/null <<-'AEOF'
bash <(curl -fsSL https://codeberg.org/umi-edge/250924-s-speedtest/raw/branch/main/boot/install.sh)
AEOF
}

20
boot/start.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
__main() {
:
ln -sf /host/run/docker.sock /var/run/docker.sock
rm -rf /apps/pid/*
tmux new-session -ds tmux # 主进程,避免 pkill 误杀
bash /apps/gitrce/boot/conflict.sh >/dev/null 2>&1
bash /apps/gitrce/deploy/release/default/start.sh >/dev/null 2>&1
}
__main
_help() {
cat >/dev/null <<EOF
容器启动, 拉取代码成功后执行的路径
EOF
}

20
boot/uninstall.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
if [[ "$CONTAINER_NAME" == "" ]]; then CONTAINER_NAME=$(grep devices </proc/self/cgroup | grep -Po '[0-9a-z]{64}' | cut -c1-12); fi
__main() {
:
env
docker rm -f "$CONTAINER_NAME"
}
__main
__help() {
cat >/dev/null <<-'AEOF'
AEOF
}

12
boot/update.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# 每次 git 更行后的操作
__main() {
# service rsyslog restart
# service cron restart
service cron reload
pkill -f '/default/update.sh sleep'
}
__main

15
boot/upgrade.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
__main() {
:
}
__help() {
cat >/dev/null <<-'AEOF'
AEOF
}
__main

7
cron.d/default Normal file
View File

@@ -0,0 +1,7 @@
# shellcheck disable=SC2148
# shellcheck disable=SC2035
# shellcheck disable=SC2211
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# * * * * * root date

6
cron.d/deploy Normal file
View File

@@ -0,0 +1,6 @@
# shellcheck disable=SC2148
# shellcheck disable=SC2035
# shellcheck disable=SC2211
# shellcheck disable=SC1091
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
# 设置版本启动安装路径并执行, 避免多版本存在时的一些特殊需求
if [[ "$1" == "sleep" ]]; then source /apps/script/hook/sleep.sh "$0" 3 15; fi
__main() {
bash /apps/gitrce/deploy/release/default/init_cache_dir.sh
_version="t20251123"
_workspace="/apps/gitrce/deploy/release/$_version"
_path="/apps/data/deploy/release/default/version.txt"
mkdir -p ${_path%/*}
echo "$_version" >$_path
bash "$_workspace/start.sh"
}
__main
__help() {
cat >/dev/null <<-'EOF'
EOF
}

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# shellcheck source=/dev/null
# shellcheck disable=SC2046
# shellcheck disable=SC2034
# shellcheck disable=SC1091
# shellcheck disable=SC2086
# shellcheck disable=SC2206
# shellcheck disable=SC2116
# shellcheck disable=SC2154
# shellcheck disable=SC2317
if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi
if [[ "$1" == "sleep" ]]; then source /apps/gitrce/hook/sleep.sh "$0" 1 30; fi
__main() {
:
echo 'start.sh' >/apps/data/test.txt
}
__main
__help() {
cat >/dev/null <<-'EOF'
EOF
}

20
hook/nsenter.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# 在要使用宿主机执行命令时引入该脚本
# 示例
# # shellcheck disable=SC1091
# if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/nsenter.sh "$0"; fi
_script_path_this=$(realpath "$(ps -p $$ -o args= 2>/dev/null | awk '{print $2}')")
_latter="${_script_path_this#/apps/data}"
if [[ "$CONTAINER_NAME" == "" ]]; then CONTAINER_NAME=$(grep devices </proc/self/cgroup | grep -Po '[0-9a-z]{64}' | cut -c1-12); fi
if [[ "${APPS_DATA}" == "" ]]; then APPS_DATA=$(docker inspect "$CONTAINER_NAME" -f '{{range .Mounts}}{{if eq .Destination "/apps/data"}}{{.Source}}{{end}}{{end}}'); fi
if [[ "${APPS_DATA}" == "" ]]; then
# echo "nsenter.sh 未挂载 /apps/data 到外部路径"
_data_upper_dir=$(docker inspect "$CONTAINER_NAME" -f '{{.GraphDriver.Data.UpperDir}}')
_absolute_path="${_data_upper_dir}$_script_path_this"
else
_absolute_path="${APPS_DATA}$_latter"
fi
nsenter --mount=/host/proc/1/ns/mnt --net=/host/proc/1/ns/net /usr/bin/bash "$_absolute_path"
exit

39
hook/singleton.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# shellcheck disable=SC2001
# 单例模式
# 示例
# # shellcheck disable=SC1091
# if [[ -d "/host/proc/1/" ]]; then source /apps/gitrce/hook/singleton.sh "$0"; fi
__singleton_pattern() {
# 实现单例模式,确保同一时刻只有一个脚本实例在运行
_script_path_this=$(realpath "$(ps -p $$ -o args= 2>/dev/null | awk '{print $2}')")
# 生成 PID 文件的路径
_pid_name="$(echo "$_script_path_this" | sed 's:^/::; s/\//#/g; s/\.sh$/.pid/')" # 还原 echo "$result" | sed 's:^:/:' | sed 's/#/\//g; s/\.pid$/.sh/'
_pid_file="/apps/pid/$_pid_name"
# 创建 PID 文件所在的目录
mkdir -p "${_pid_file%/*}"
# 从 PID 文件中读取存储的 PID
_pid_data=$(cat "$_pid_file" 2>/dev/null)
# 根据存储的 PID 获取对应脚本的绝对路径
_script_path_pid=$(realpath "$(ps -p "$_pid_data" -o args= 2>/dev/null | awk '{print $2}')" 2>/dev/null)
if [[ "$_script_path_this" != "$_script_path_pid" ]]; then
# 如果当前脚本路径与存储的脚本路径不同,则更新 PID 文件并继续执行
echo "$$" >"$_pid_file"
else
# 如果相同,则说明已有实例在运行,输出相关信息并退出
echo "脚本单例模式运行, 已存在运行实例,当前脚本已退出"
echo "_pid_file: $_pid_file"
echo "_pid_data: $_pid_data"
echo "_script_path_this: $_script_path_this"
echo "_script_path_pid : $_script_path_pid"
exit 0
fi
}
__singleton_pattern

9
hook/sleep.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
# 需要在执行脚本延时一段时间, 可以调用该脚本...
_t_sleep=$(shuf -i "$2"-"$3" -n 1)
echo "延时:$_t_sleep"
sleep "$_t_sleep"
# 示例
# if [[ "$1" == "sleep" ]]; then source /apps/gitrce/hook/sleep.sh "$0" 1 300; fi