chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:09:14 +08:00
commit 173d38e688
2005 changed files with 519775 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
name: 1Panel App Store
on:
workflow_dispatch:
inputs:
version:
description: "Version to submit (e.g. 0.5.3)"
required: true
jobs:
submit:
runs-on: ubuntu-latest
env:
REPO_OWNER: t8y2
steps:
- uses: actions/checkout@v5
- name: Submit to 1Panel App Store
env:
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
run: |
VERSION="${{ github.event.inputs.version }}"
git clone --depth 1 -b localApps \
"https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/appstore-1panel.git" \
appstore
cd appstore
APP_DIR="apps/dbx"
mkdir -p "${APP_DIR}/latest"
cp "${{ github.workspace }}/deploy/1panel/data.yml" "${APP_DIR}/data.yml"
cp "${{ github.workspace }}/deploy/1panel/README.md" "${APP_DIR}/README.md"
if [ -f "${{ github.workspace }}/deploy/1panel/logo.png" ]; then
cp "${{ github.workspace }}/deploy/1panel/logo.png" "${APP_DIR}/logo.png"
fi
cp -r "${{ github.workspace }}/deploy/1panel/latest/." "${APP_DIR}/latest/"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
BRANCH="app/dbx-${VERSION}"
git checkout -b "${BRANCH}"
git add "${APP_DIR}"
if git diff --cached --quiet; then
echo "1Panel app store is already up to date."
else
git commit -m "feat(dbx): release ${VERSION}"
git push origin "${BRANCH}"
gh pr create \
--repo "okxlin/appstore" \
--head "${REPO_OWNER}:${BRANCH}" \
--title "feat(dbx): release ${VERSION}" \
--body "Automated release of DBX v${VERSION}" \
--base localApps
echo "::notice::1Panel App Store PR created for ${VERSION}"
fi
+620
View File
@@ -0,0 +1,620 @@
name: Agents Release
on:
push:
tags: ["agents-v*"]
permissions:
contents: write
concurrency:
group: dbx-release-main
cancel-in-progress: false
jobs:
bump-versions:
runs-on: ubuntu-latest
outputs:
versions: ${{ steps.bump.outputs.versions }}
prev_versions: ${{ steps.bump.outputs.prev_versions }}
prev_tag: ${{ steps.bump.outputs.prev_tag }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changes and bump versions
id: bump
shell: bash
run: |
LEGACY_REPO="https://github.com/t8y2/dbx-agents.git"
LEGACY_BASE_TAG="v0.2.33"
PREV_TAG=$(git tag --sort=-creatordate | grep '^agents-v' | sed -n '2p')
SKIP_BUMP=false
MIGRATED_FIRST_RELEASE=false
PREV_VERSIONS_FILE=""
if [ -z "$PREV_TAG" ]; then
echo "No previous agents-v tag found; treating this as the first migrated agents release"
PREV_TAG=$(git rev-list --max-parents=0 HEAD)
TMP_LEGACY="$(mktemp -d)"
git clone --depth 1 --branch "$LEGACY_BASE_TAG" "$LEGACY_REPO" "$TMP_LEGACY"
PREV_VERSIONS_FILE="$(mktemp)"
cp "$TMP_LEGACY/versions.json" "$PREV_VERSIONS_FILE"
rm -rf "$TMP_LEGACY"
SKIP_BUMP=true
MIGRATED_FIRST_RELEASE=true
fi
echo "Comparing $PREV_TAG..HEAD"
ARGS=(--prev-tag "$PREV_TAG" --migrated-first-release "$MIGRATED_FIRST_RELEASE" --write)
if [ "$SKIP_BUMP" = "true" ]; then
ARGS+=(--skip-bump)
fi
if [ -n "$PREV_VERSIONS_FILE" ]; then
ARGS+=(--prev-versions-file "$PREV_VERSIONS_FILE")
fi
node .github/scripts/bump-agent-versions.mjs "${ARGS[@]}"
if ! git diff --quiet -- agents/versions.json; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add agents/versions.json
git commit -m "chore: bump module versions [skip ci]"
git fetch origin main --no-tags
git rebase origin/main
git push origin HEAD:main
fi
build-agents:
needs: [bump-versions]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: |
8
21
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew shadowJar --parallel
working-directory: agents
- run: python3 scripts/validate_agent_jars.py
working-directory: agents
- uses: actions/upload-artifact@v4
with:
name: agent-jars
path: "agents/drivers/*/build/libs/dbx-agent-*.jar"
build-oracle-native:
needs: [bump-versions]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22.x"
- name: Test Oracle native agent
working-directory: agents/drivers/oracle-go
run: go test ./...
- name: Cross-compile Oracle native agent
shell: bash
run: |
mkdir -p release-native
cd agents/drivers/oracle-go
declare -A TARGETS=(
["macos-aarch64"]="darwin/arm64"
["macos-x64"]="darwin/amd64"
["linux-aarch64"]="linux/arm64"
["linux-x64"]="linux/amd64"
["windows-aarch64"]="windows/arm64"
["windows-x64"]="windows/amd64"
)
for platform in "${!TARGETS[@]}"; do
IFS=/ read -r goos goarch <<< "${TARGETS[$platform]}"
output="../../../release-native/dbx-agent-oracle-${platform}"
if [[ "$goos" == "windows" ]]; then
output="${output}.exe"
fi
echo "Building $platform ($goos/$goarch)"
CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" go build -trimpath -ldflags="-s -w" -o "$output" .
done
ls -lh ../../../release-native
- uses: actions/upload-artifact@v4
with:
name: oracle-native
path: "release-native/dbx-agent-oracle-*"
build-xugu-native:
needs: [bump-versions]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.22.x"
- name: Test Xugu native agent
working-directory: agents/drivers/xugu
run: GONOSUMDB=gitee.com/XuguDB/go-xugu-driver go test ./...
- name: Cross-compile Xugu native agent
shell: bash
run: |
mkdir -p release-native
cd agents/drivers/xugu
declare -A TARGETS=(
["macos-aarch64"]="darwin/arm64"
["macos-x64"]="darwin/amd64"
["linux-aarch64"]="linux/arm64"
["linux-x64"]="linux/amd64"
["windows-aarch64"]="windows/arm64"
["windows-x64"]="windows/amd64"
)
for platform in "${!TARGETS[@]}"; do
IFS=/ read -r goos goarch <<< "${TARGETS[$platform]}"
output="../../../release-native/dbx-agent-xugu-${platform}"
if [[ "$goos" == "windows" ]]; then
output="${output}.exe"
fi
echo "Building $platform ($goos/$goarch)"
GONOSUMDB=gitee.com/XuguDB/go-xugu-driver CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" go build -trimpath -ldflags="-s -w" -o "$output" .
done
ls -lh ../../../release-native
- uses: actions/upload-artifact@v4
with:
name: xugu-native
path: "release-native/dbx-agent-xugu-*"
build-jre:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- jre-key: "21"
java-version: "21"
modules: "java.base,java.sql,java.sql.rowset,java.naming,java.management,java.desktop,java.security.jgss,java.security.sasl,jdk.security.auth,jdk.security.jgss,jdk.charsets,jdk.unsupported,java.scripting,java.compiler"
steps:
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ matrix.java-version }}
- name: Build JRE for all platforms
shell: bash
run: |
MODULES="${{ matrix.modules }}"
JLINK_OPTS="--strip-debug --no-header-files --no-man-pages --compress=zip-6"
JRE_KEY="${{ matrix.jre-key }}"
jlink --add-modules $MODULES $JLINK_OPTS --output dbx-jre
tar czf dbx-jre-${JRE_KEY}-linux-x64.tar.gz dbx-jre
rm -rf dbx-jre
ADOPTIUM="https://api.adoptium.net/v3/binary/latest/${{ matrix.java-version }}/ga"
declare -A PLATFORMS=(
["macos-aarch64"]="mac/aarch64"
["macos-x64"]="mac/x64"
["linux-aarch64"]="linux/aarch64"
["windows-x64"]="windows/x64"
)
for platform in "${!PLATFORMS[@]}"; do
os_arch="${PLATFORMS[$platform]}"
echo "=== Downloading JDK for $platform ($os_arch) ==="
if [[ "$platform" == windows-* ]]; then
curl -L -o jdk-$platform.zip "$ADOPTIUM/$os_arch/jdk/hotspot/normal/eclipse?project=jdk"
mkdir -p jdk-$platform
unzip -q jdk-$platform.zip -d jdk-$platform-tmp
mv jdk-$platform-tmp/*/* jdk-$platform/ || mv jdk-$platform-tmp/* jdk-$platform/
rm -rf jdk-$platform-tmp jdk-$platform.zip
else
curl -L -o jdk-$platform.tar.gz "$ADOPTIUM/$os_arch/jdk/hotspot/normal/eclipse?project=jdk"
mkdir -p jdk-$platform
tar xzf jdk-$platform.tar.gz -C jdk-$platform --strip-components=1
rm -f jdk-$platform.tar.gz
fi
JAVA_HOME_CROSS="jdk-$platform"
if [ -d "$JAVA_HOME_CROSS/Contents/Home/jmods" ]; then
JMODS="$JAVA_HOME_CROSS/Contents/Home/jmods"
else
JMODS="$JAVA_HOME_CROSS/jmods"
fi
LLVM_OBJCOPY=$(ls /usr/bin/llvm-objcopy-* 2>/dev/null | sort -V | tail -1)
if [ -z "$LLVM_OBJCOPY" ]; then LLVM_OBJCOPY=$(which llvm-objcopy 2>/dev/null || true); fi
CROSS_OPTS=""
if [ -n "$LLVM_OBJCOPY" ]; then
CROSS_OPTS="--strip-native-debug-symbols=objcopy=$LLVM_OBJCOPY"
else
CROSS_OPTS="--disable-plugin strip-native-debug-symbols"
fi
jlink --module-path "$JMODS" --add-modules $MODULES $JLINK_OPTS $CROSS_OPTS --output dbx-jre
tar czf dbx-jre-${JRE_KEY}-$platform.tar.gz dbx-jre
rm -rf dbx-jre jdk-$platform
done
ls -lh dbx-jre-*.tar.gz
- uses: actions/upload-artifact@v4
with:
name: jre-${{ matrix.jre-key }}
path: "dbx-jre-*.tar.gz"
release:
needs: [bump-versions, build-agents, build-oracle-native, build-xugu-native, build-jre]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Flatten artifacts
run: |
mkdir -p release
find artifacts/agent-jars -name '*.jar' -exec cp {} release/ \;
find artifacts/oracle-native -type f -name 'dbx-agent-oracle-*' -exec cp {} release/ \;
find artifacts/xugu-native -type f -name 'dbx-agent-xugu-*' -exec cp {} release/ \;
find artifacts -name 'dbx-jre-*.tar.gz' -exec cp {} release/ \;
ls -lh release/
- name: Generate agent-registry.json
env:
MODULE_VERSIONS: ${{ needs.bump-versions.outputs.versions }}
run: |
TAG="${GITHUB_REF_NAME}"
REPO="${GITHUB_REPOSITORY}"
RELEASE_VERSION="${TAG#agents-v}"
get_module_version() {
local name="$1"
echo "$MODULE_VERSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$name','${RELEASE_VERSION}'))"
}
generate_jar_entry() {
local name="$1" label="$2" file="$3" jre_key="$4" version="$5" external_driver="$6" native_json="$7"
local sha256=$(sha256sum "$file" | cut -d' ' -f1)
local size=$(stat -c%s "$file")
local url="https://github.com/${REPO}/releases/download/${TAG}/$(basename $file)"
local native_line=""
if [ -n "$native_json" ]; then
native_line=" \"native\": {${native_json}
},"
fi
cat <<ENTRY
"${name}": {
"version": "${version}",
"label": "${label}",
"min_app_version": "0.6.0",
"jre": "${jre_key}",
"external_driver_required": ${external_driver},
${native_line}
"jar": { "url": "${url}", "sha256": "${sha256}", "size": ${size} }
}
ENTRY
}
generate_native_entry() {
local name="$1" label="$2" jre_key="$3" version="$4" native_json="$5"
local legacy_jar_url="https://github.com/${REPO}/releases/download/${TAG}/dbx-agent-${name}-legacy-placeholder.jar"
cat <<ENTRY
"${name}": {
"version": "${version}",
"label": "${label}",
"min_app_version": "0.6.0",
"jre": "${jre_key}",
"external_driver_required": false,
"native": {${native_json}
},
"jar": { "url": "${legacy_jar_url}", "sha256": "", "size": 0 }
}
ENTRY
}
native_only_label() {
local name="$1"
case "$name" in
xugu) echo "虚谷 XuguDB" ;;
*) echo "$name" ;;
esac
}
generate_jre_platforms() {
local jre_key="$1"
local PLATFORMS=""
for f in release/dbx-jre-${jre_key}-*.tar.gz; do
[ ! -f "$f" ] && continue
p=$(basename "$f" .tar.gz | sed "s/dbx-jre-${jre_key}-//")
sha256=$(sha256sum "$f" | cut -d' ' -f1)
size=$(stat -c%s "$f")
url="https://github.com/${REPO}/releases/download/${TAG}/$(basename $f)"
[ -n "$PLATFORMS" ] && PLATFORMS="${PLATFORMS},"$'\n'
PLATFORMS="${PLATFORMS} \"${p}\": { \"url\": \"${url}\", \"sha256\": \"${sha256}\", \"size\": ${size} }"
done
echo "$PLATFORMS"
}
generate_native_platforms() {
local name="$1"
local PLATFORMS=""
for f in release/dbx-agent-${name}-*; do
[ ! -f "$f" ] && continue
[[ "$f" != *.jar ]] || continue
p=$(basename "$f" | sed "s/dbx-agent-${name}-//; s/\\.exe$//")
sha256=$(sha256sum "$f" | cut -d' ' -f1)
size=$(stat -c%s "$f")
url="https://github.com/${REPO}/releases/download/${TAG}/$(basename $f)"
[ -n "$PLATFORMS" ] && PLATFORMS="${PLATFORMS},"$'\n'
PLATFORMS="${PLATFORMS}"$'\n'" \"${p}\": { \"url\": \"${url}\", \"sha256\": \"${sha256}\", \"size\": ${size} }"
done
echo "$PLATFORMS"
}
detect_jre_key() {
local name="$1"
case "$name" in
*) echo "21" ;;
esac
}
JRE21_PLATFORMS=$(generate_jre_platforms "21")
DRIVERS=""
for f in release/dbx-agent-*.jar; do
name=$(basename "$f" .jar | sed 's/dbx-agent-//')
label=$(unzip -p "$f" META-INF/MANIFEST.MF | awk -F': ' 'BEGIN{IGNORECASE=1} /^Agent-Label:/ {sub(/\r$/, "", $2); print $2; exit}' || echo "$name")
[ -z "$label" ] && label="$name"
external_driver=$(unzip -p "$f" META-INF/MANIFEST.MF | awk -F': ' 'BEGIN{IGNORECASE=1} /^Agent-External-Driver:/ {sub(/\r$/, "", $2); print tolower($2); exit}' || true)
[ "$external_driver" = "true" ] || external_driver="false"
jre_key=$(detect_jre_key "$name")
version=$(get_module_version "$name")
native_json=$(generate_native_platforms "$name")
[ -n "$DRIVERS" ] && DRIVERS="${DRIVERS},"$'\n'
DRIVERS="${DRIVERS}$(generate_jar_entry "$name" "$label" "$f" "$jre_key" "$version" "$external_driver" "$native_json")"
done
for name in oracle xugu; do
[ -f "release/dbx-agent-${name}.jar" ] && continue
native_json=$(generate_native_platforms "$name")
[ -z "$native_json" ] && continue
label=$(native_only_label "$name")
jre_key=$(detect_jre_key "$name")
version=$(get_module_version "$name")
[ -n "$DRIVERS" ] && DRIVERS="${DRIVERS},"$'\n'
DRIVERS="${DRIVERS}$(generate_native_entry "$name" "$label" "$jre_key" "$version" "$native_json")"
done
cat > release/agent-registry.json <<EOF
{
"jres": {
"21": {
"version": "21.0.12+kerberos.1",
"platforms": {
${JRE21_PLATFORMS}
}
}
},
"drivers": {
${DRIVERS}
}
}
EOF
python3 -m json.tool release/agent-registry.json > /dev/null
echo "=== agent-registry.json ==="
cat release/agent-registry.json
- name: Build offline ZIP bundles
run: bash agents/scripts/build_offline_zip.sh release
- name: Generate release notes
env:
MODULE_VERSIONS: ${{ needs.bump-versions.outputs.versions }}
PREV_VERSIONS: ${{ needs.bump-versions.outputs.prev_versions }}
PREV_TAG: ${{ needs.bump-versions.outputs.prev_tag }}
run: |
NOTES=""
DRIVER_NAMES=()
for f in release/dbx-agent-*.jar; do
DRIVER_NAMES+=("$(basename "$f" .jar | sed 's/dbx-agent-//')")
done
for name in oracle xugu; do
[ -f "release/dbx-agent-${name}.jar" ] && continue
compgen -G "release/dbx-agent-${name}-*" > /dev/null && DRIVER_NAMES+=("$name")
done
native_only_label() {
local name="$1"
case "$name" in
oracle) echo "Oracle" ;;
xugu) echo "虚谷 XuguDB" ;;
*) echo "$name" ;;
esac
}
for name in "${DRIVER_NAMES[@]}"; do
old_ver=$(echo "$PREV_VERSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$name',''))")
new_ver=$(echo "$MODULE_VERSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$name',''))")
[ "$old_ver" = "$new_ver" ] && continue
jar_file="release/dbx-agent-${name}.jar"
if [ -f "$jar_file" ]; then
label=$(unzip -p "$jar_file" META-INF/MANIFEST.MF | awk -F': ' 'BEGIN{IGNORECASE=1} /^Agent-Label:/ {sub(/\r$/, "", $2); print $2; exit}' || echo "$name")
[ -z "$label" ] && label="$name"
else
label=$(native_only_label "$name")
fi
NOTES="${NOTES}### ${label} (${old_ver} → ${new_ver})"$'\n'
if [ "$name" = "oracle" ]; then
LOG_PATH="agents/drivers/oracle-go/"
elif [ -d "agents/drivers/$name" ]; then
LOG_PATH="agents/drivers/$name/"
else
LOG_PATH="agents/$name/"
fi
LOG_PATHS=("$LOG_PATH")
while IFS= read -r line; do
[ -n "$line" ] && NOTES="${NOTES}- ${line}"$'\n'
done < <(git log --oneline "$PREV_TAG"..HEAD -- "${LOG_PATHS[@]}" | sed 's/^[0-9a-f]* //')
# include common changes if any
while IFS= read -r line; do
[ -n "$line" ] && NOTES="${NOTES}- ${line}"$'\n'
done < <(git log --oneline "$PREV_TAG"..HEAD -- agents/common/ | sed 's/^[0-9a-f]* //')
NOTES="${NOTES}"$'\n'
done
if [ -z "$NOTES" ]; then
NOTES="Routine rebuild, no agent-specific changes."$'\n'
fi
printf "## 更新内容\n\n%s" "$NOTES" > RELEASE_NOTES.md
echo "=== Release Notes ==="
cat RELEASE_NOTES.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: release/*
body_path: RELEASE_NOTES.md
make_latest: false
- name: Update agents-latest GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git tag -f agents-latest "$GITHUB_SHA"
git push origin refs/tags/agents-latest --force
if gh release view agents-latest --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release upload agents-latest release/* --repo "$GITHUB_REPOSITORY" --clobber
gh release edit agents-latest \
--repo "$GITHUB_REPOSITORY" \
--title "Agents latest" \
--notes "Moving release for the latest DBX agent registry, driver artifacts, managed JRE archives, and offline bundles." \
--latest=false
else
gh release create agents-latest release/* \
--repo "$GITHUB_REPOSITORY" \
--title "Agents latest" \
--notes "Moving release for the latest DBX agent registry, driver artifacts, managed JRE archives, and offline bundles." \
--latest=false
fi
- name: Upload to R2
env:
MODULE_VERSIONS: ${{ needs.bump-versions.outputs.versions }}
PREV_VERSIONS: ${{ needs.bump-versions.outputs.prev_versions }}
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com"
R2_BUCKET: "s3://${{ secrets.R2_BUCKET_NAME }}"
run: |
# Always upload agent-registry.json
aws s3 cp release/agent-registry.json \
"$R2_BUCKET/agents/agent-registry.json" \
--endpoint-url "$R2_ENDPOINT"
echo "Uploaded agent-registry.json"
# Upload only changed driver JARs
UPLOADED=0
SKIPPED=0
for f in release/dbx-agent-*.jar; do
[ ! -f "$f" ] && continue
name=$(basename "$f" .jar | sed 's/dbx-agent-//')
old_ver=$(echo "$PREV_VERSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$name',''))" 2>/dev/null)
new_ver=$(echo "$MODULE_VERSIONS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$name',''))" 2>/dev/null)
if [ "$old_ver" = "$new_ver" ] && [ -n "$old_ver" ]; then
echo "Skip $(basename $f) (unchanged $old_ver)"
SKIPPED=$((SKIPPED + 1))
continue
fi
aws s3 cp "$f" \
"$R2_BUCKET/agents/drivers/$(basename $f)" \
--endpoint-url "$R2_ENDPOINT"
echo "Uploaded $(basename $f) ($old_ver → $new_ver)"
UPLOADED=$((UPLOADED + 1))
done
echo "Drivers: $UPLOADED uploaded, $SKIPPED skipped"
for f in release/dbx-agent-*; do
[ -f "$f" ] || continue
[[ "$f" != *.jar ]] || continue
aws s3 cp "$f" \
"$R2_BUCKET/agents/drivers/$(basename $f)" \
--endpoint-url "$R2_ENDPOINT"
echo "Uploaded $(basename $f)"
done
# Upload JRE — always overwrite to ensure latest modules
for f in release/dbx-jre-*.tar.gz; do
[ ! -f "$f" ] && continue
key="agents/jre/$(basename $f)"
aws s3 cp "$f" "$R2_BUCKET/$key" --endpoint-url "$R2_ENDPOINT"
echo "Uploaded $(basename $f)"
done
sync-release-to-cnb:
name: Sync agents releases to CNB
needs: release
if: ${{ always() && needs.release.result == 'success' }}
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
tag: ["${{ github.ref_name }}", "agents-latest"]
steps:
- name: Sync release assets to CNB
env:
TAG_NAME: ${{ matrix.tag }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_USERNAME: ${{ github.repository_owner }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CNB_REPOSITORY: dbxio.com/dbx
CNB_USERNAME: cnb
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
run: |
set -euo pipefail
wget -q https://cnb.cool/znb/mpgrm/-/releases/download/v0.1.0/mpgrm_linux_amd64.tar.gz
tar -xf mpgrm_linux_amd64.tar.gz
chmod +x mpgrm
./mpgrm releases sync \
--repo "https://github.com/${GITHUB_REPOSITORY}.git" \
--target-repo "https://cnb.cool/${CNB_REPOSITORY}.git" \
--tags "${TAG_NAME}"
sync-release-to-atomgit:
name: Sync agents releases to AtomGit
needs: release
if: ${{ always() && needs.release.result == 'success' }}
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
tag: ["${{ github.ref_name }}", "agents-latest"]
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Sync release assets to AtomGit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ matrix.tag }}
GITHUB_REPOSITORY: ${{ github.repository }}
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
ATOMGIT_REPOSITORY: t8y2/dbx
run: |
set -euo pipefail
git fetch origin "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
git remote add atomgit "https://t8y2:${ATOMGIT_TOKEN}@atomgit.com/t8y2/dbx.git"
git push atomgit "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
mkdir -p "$RUNNER_TEMP/github-release" "$RUNNER_TEMP/release-assets"
gh release view "$TAG_NAME" --repo "$GITHUB_REPOSITORY" \
--json tagName,name,body,targetCommitish,isPrerelease,isDraft,assets \
> "$RUNNER_TEMP/github-release/release.json"
gh release download "$TAG_NAME" --repo "$GITHUB_REPOSITORY" \
--dir "$RUNNER_TEMP/release-assets" --clobber
node .github/scripts/sync-atomgit-release.mjs \
--github-release "$RUNNER_TEMP/github-release/release.json" \
--assets-dir "$RUNNER_TEMP/release-assets" \
${{ matrix.tag == 'agents-latest' && '--replace-assets' || '' }}
+248
View File
@@ -0,0 +1,248 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUSTFLAGS: -C debuginfo=line-tables-only
jobs:
frontend:
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
cache: pnpm
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libsecret-1-dev
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- name: Frontend check
run: pnpm check
- name: Node package tests
run: pnpm test:packages
- name: Node package publish dry run
run: pnpm publish:dry-run
rust-fmt-clippy:
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/.ghcup || true
docker system prune -af || true
df -h
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libsecret-1-dev
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./ -> target"
shared-key: ci-rust-fmt-clippy-x86_64-unknown-linux-gnu
- name: Cargo fmt check
run: cargo fmt --check
- name: Cargo clippy
run: cargo clippy --workspace --locked --all-targets
rust-test:
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/.ghcup || true
docker system prune -af || true
df -h
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libsecret-1-dev
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./ -> target"
shared-key: ci-rust-test-x86_64-unknown-linux-gnu
- name: Cargo test
run: cargo test --workspace --locked
rust:
needs: [changes, rust-fmt-clippy, rust-test]
if: always() && needs.changes.outputs.rust == 'true'
runs-on: ubuntu-22.04
steps:
- name: Check Rust jobs
run: |
if [ "${{ needs.rust-fmt-clippy.result }}" != "success" ]; then
echo "rust-fmt-clippy result: ${{ needs.rust-fmt-clippy.result }}"
exit 1
fi
if [ "${{ needs.rust-test.result }}" != "success" ]; then
echo "rust-test result: ${{ needs.rust-test.result }}"
exit 1
fi
jdbc:
needs: changes
if: needs.changes.outputs.jdbc == 'true'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
cache: maven
- name: JDBC plugin version guard
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
run: |
if [ -z "$BASE_SHA" ] || echo "$BASE_SHA" | grep -Eq '^0+$'; then
BASE_SHA="HEAD~1"
fi
node .github/scripts/check-jdbc-plugin-version.mjs "$BASE_SHA" HEAD
- name: JDBC plugin package check
run: ./plugins/jdbc/package.sh
changes:
runs-on: ubuntu-22.04
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
rust: ${{ steps.filter.outputs.rust }}
jdbc: ${{ steps.filter.outputs.jdbc }}
agents: ${{ steps.filter.outputs.agents }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Detect changed areas
uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'apps/desktop/**'
- 'packages/**'
- 'pnpm-lock.yaml'
- 'package.json'
- '.oxfmtrc.json'
- 'scripts/run-check.mjs'
- '.github/workflows/ci.yml'
rust:
- 'crates/**'
- 'src-tauri/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain*'
- '.github/workflows/ci.yml'
jdbc:
- 'plugins/jdbc/**'
agents:
- 'agents/**'
agents:
needs: changes
if: needs.changes.outputs.agents == 'true'
runs-on: ubuntu-22.04
defaults:
run:
working-directory: agents
steps:
- uses: actions/checkout@v5
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
8
21
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22.x"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Agent script tests
run: python3 -m unittest discover -s scripts -p '*_test.py'
- name: Agent validation
run: python3 scripts/validate_agents.py
- name: Oracle native agent tests
run: go test ./...
working-directory: agents/drivers/oracle-go
- name: Xugu native agent tests
run: GONOSUMDB=gitee.com/XuguDB/go-xugu-driver go test ./...
working-directory: agents/drivers/xugu
- name: Oracle native agent build
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /tmp/dbx-agent-oracle-linux-x64 .
working-directory: agents/drivers/oracle-go
- name: Xugu native agent build
run: GONOSUMDB=gitee.com/XuguDB/go-xugu-driver CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /tmp/dbx-agent-xugu-linux-x64 .
working-directory: agents/drivers/xugu
- name: Java agent tests and packages
run: ./gradlew test shadowJar --continue
- name: Agent jar validation
run: python3 scripts/validate_agent_jars.py
+126
View File
@@ -0,0 +1,126 @@
name: Docker Dev
on:
workflow_dispatch:
env:
CNB_REPOSITORY: "dbxio.com/dbx"
CNB_REGISTRY: "docker.cnb.cool"
jobs:
docker:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform: [linux/amd64, linux/arm64]
steps:
- uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to CNB Registry
uses: docker/login-action@v3
with:
registry: ${{ env.CNB_REGISTRY }}
username: cnb
password: ${{ secrets.CNB_TOKEN }}
- name: Build and push Docker Hub by digest
id: build-dockerhub
uses: docker/build-push-action@v6
with:
context: .
file: deploy/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/dbx,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.platform }}-dev
cache-to: type=gha,scope=${{ matrix.platform }}-dev,mode=max
- name: Build and push CNB by digest
id: build-cnb
uses: docker/build-push-action@v6
with:
context: .
file: deploy/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.platform }}-dev
- name: Export digests
run: |
mkdir -p /tmp/digests/dockerhub /tmp/digests/cnb
dockerhub_digest="${{ steps.build-dockerhub.outputs.digest }}"
cnb_digest="${{ steps.build-cnb.outputs.digest }}"
touch "/tmp/digests/dockerhub/${dockerhub_digest#sha256:}"
touch "/tmp/digests/cnb/${cnb_digest#sha256:}"
- name: Upload Docker Hub digest
uses: actions/upload-artifact@v7
with:
name: dockerhub-digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: /tmp/digests/dockerhub/*
if-no-files-found: error
retention-days: 1
- name: Upload CNB digest
uses: actions/upload-artifact@v7
with:
name: cnb-digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: /tmp/digests/cnb/*
if-no-files-found: error
retention-days: 1
docker-manifest:
needs: docker
runs-on: ubuntu-latest
steps:
- name: Download Docker Hub digests
uses: actions/download-artifact@v8
with:
path: /tmp/digests/dockerhub
pattern: dockerhub-digests-*
merge-multiple: true
- name: Download CNB digests
uses: actions/download-artifact@v8
with:
path: /tmp/digests/cnb
pattern: cnb-digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to CNB Registry
uses: docker/login-action@v3
with:
registry: ${{ env.CNB_REGISTRY }}
username: cnb
password: ${{ secrets.CNB_TOKEN }}
- name: Create manifest list and push
run: |
docker buildx imagetools create \
-t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:dev \
$(cd /tmp/digests/dockerhub && printf '${{ secrets.DOCKERHUB_USERNAME }}/dbx@sha256:%s ' *)
docker buildx imagetools create \
-t ${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }}:dev \
$(cd /tmp/digests/cnb && printf '${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }}@sha256:%s ' *)
+35
View File
@@ -0,0 +1,35 @@
name: Deploy Docs
on:
push:
branches: [main]
paths: ["docs/**"]
workflow_run:
workflows: ["Publish Packages"]
types: [completed]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: pages
cancel-in-progress: true
jobs:
build:
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: 22
- uses: pnpm/action-setup@v6
- run: cd docs && pnpm install --frozen-lockfile --ignore-workspace && pnpm build
- name: Deploy to Cloudflare Workers
working-directory: docs
run: pnpm dlx wrangler@4 deploy
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+154
View File
@@ -0,0 +1,154 @@
name: I18n Autofill
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "apps/desktop/src/i18n/locales/**"
- ".github/scripts/i18n-autofill.mjs"
- ".github/workflows/i18n-autofill.yml"
permissions:
contents: write
issues: write
pull-requests: read
concurrency:
group: i18n-autofill-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
autofill:
if: github.event.pull_request.draft == false
runs-on: ubuntu-22.04
steps:
- name: Checkout trusted base script
uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.base.sha }}
- name: Copy trusted script
run: cp .github/scripts/i18n-autofill.mjs "$RUNNER_TEMP/i18n-autofill.mjs"
- name: Checkout pull request branch
uses: actions/checkout@v5
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ secrets.I18N_BOT_TOKEN || secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Fetch base branch
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
git remote add base "https://github.com/${{ github.repository }}.git"
git fetch --no-tags --depth=1 base "${BASE_REF}"
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
- name: Autofill missing i18n entries
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
DEEPSEEK_MODEL: ${{ vars.DEEPSEEK_MODEL || 'deepseek-v4-pro' }}
I18N_BASE_REF: base/${{ github.event.pull_request.base.ref }}
I18N_SUMMARY_FILE: ${{ runner.temp }}/i18n-summary.json
run: node "$RUNNER_TEMP/i18n-autofill.mjs" --write
- name: Commit and push translation patch
id: patch
env:
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
if git diff --quiet -- apps/desktop/src/i18n/locales; then
echo "No i18n autofill changes to commit"
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git config user.name "dbx-i18n-bot"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add apps/desktop/src/i18n/locales
git commit -m "chore(i18n): autofill new translations"
if git push origin "HEAD:${PR_HEAD_REF}"; then
echo "push_failed=false" >> "$GITHUB_OUTPUT"
else
echo "push_failed=true" >> "$GITHUB_OUTPUT"
git diff HEAD~1 HEAD -- apps/desktop/src/i18n/locales > "$RUNNER_TEMP/i18n-autofill.patch"
fi
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Comment autofill result
if: steps.patch.outputs.changed == 'true' && steps.patch.outputs.push_failed != 'true'
env:
GH_TOKEN: ${{ secrets.I18N_BOT_TOKEN || secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
SUMMARY_FILE: ${{ runner.temp }}/i18n-summary.json
BODY_FILE: ${{ runner.temp }}/i18n-autofill-comment.md
run: |
node <<'NODE'
const fs = require("node:fs");
const summary = JSON.parse(fs.readFileSync(process.env.SUMMARY_FILE, "utf8"));
const keyCount = summary.newKeys.length;
const localeRows = summary.updatedLocales
.map(({ locale, count }) => `| \`${locale}\` | ${count} |`)
.join("\n");
const body = [
"### 🌐 I18n autofill completed",
"",
`✅ Added translations for **${keyCount}** new \`${summary.sourceLocale}\` i18n ${keyCount === 1 ? "key" : "keys"}.`,
"",
"| Locale | Added entries |",
"| --- | ---: |",
localeRows,
].join("\n");
fs.writeFileSync(process.env.BODY_FILE, `${body}\n`);
NODE
gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body-file "$BODY_FILE"
- name: Comment push failure
if: steps.patch.outputs.changed == 'true' && steps.patch.outputs.push_failed == 'true'
env:
GH_TOKEN: ${{ secrets.I18N_BOT_TOKEN || secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
SUMMARY_FILE: ${{ runner.temp }}/i18n-summary.json
PATCH_FILE: ${{ runner.temp }}/i18n-autofill.patch
BODY_FILE: ${{ runner.temp }}/i18n-autofill-push-failed.md
run: |
node <<'NODE'
const fs = require("node:fs");
const summary = JSON.parse(fs.readFileSync(process.env.SUMMARY_FILE, "utf8"));
const patch = fs.readFileSync(process.env.PATCH_FILE, "utf8");
const keyCount = summary.newKeys.length;
const localeRows = summary.updatedLocales
.map(({ locale, count }) => `| \`${locale}\` | ${count} |`)
.join("\n");
const patchPreview = patch.length > 60000
? `${patch.slice(0, 60000)}\n\n[Patch truncated in comment. Please check the workflow artifact/log for the full diff.]`
: patch;
const body = [
"### ⚠️ I18n autofill needs manual apply",
"",
`I generated translations for **${keyCount}** new \`${summary.sourceLocale}\` i18n ${keyCount === 1 ? "key" : "keys"}, but could not push a patch to this PR branch.`,
"",
"| Locale | Missing entries |",
"| --- | ---: |",
localeRows,
"",
"#### Patch preview",
"```diff",
patchPreview,
"```",
].join("\n");
fs.writeFileSync(process.env.BODY_FILE, `${body}\n`);
NODE
gh pr comment "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --body-file "$BODY_FILE"
+30
View File
@@ -0,0 +1,30 @@
name: Issue Claim
on:
issue_comment:
types: [created]
concurrency:
group: issue-claim-${{ github.event.issue.number }}
cancel-in-progress: false
permissions:
contents: read
issues: write
jobs:
claim:
runs-on: ubuntu-latest
if: ${{ !github.event.issue.pull_request }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v5
- name: Handle /claim command
run: node .github/scripts/issue-claim.mjs
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_USER: ${{ github.event.comment.user.login }}
COMMENT_USER_TYPE: ${{ github.event.comment.user.type }}
@@ -0,0 +1,30 @@
name: Issue Triage Labels
on:
issues:
types: [opened, edited, reopened]
concurrency:
group: issue-database-label-${{ github.event.issue.number }}
cancel-in-progress: true
permissions:
contents: read
issues: write
jobs:
label:
runs-on: ubuntu-latest
if: ${{ !github.event.issue.pull_request }}
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
- name: Triage issue labels and title
run: node .github/scripts/label-issue-database.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+342
View File
@@ -0,0 +1,342 @@
name: Node Packages Release
on:
workflow_dispatch:
inputs:
version:
description: "Package version to publish, for example 0.4.4"
required: true
permissions:
contents: write
id-token: write
concurrency:
group: dbx-release-main
cancel-in-progress: false
jobs:
prepare:
name: Prepare package release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
registry-url: https://registry.npmjs.org
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Check npm token
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -z "${NODE_AUTH_TOKEN}" ]; then
echo "::error::NPM_TOKEN secret is required to publish DBX Node packages."
exit 1
fi
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libsecret-1-dev
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set package versions
id: version
env:
VERSION: ${{ github.event.inputs.version }}
run: |
node <<'NODE'
const fs = require("fs");
const version = process.env.VERSION.trim();
if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(version)) {
throw new Error(`Invalid semver version: ${version}`);
}
const readJson = (path) => JSON.parse(fs.readFileSync(path, "utf8"));
const writeJson = (path, data) => fs.writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`);
for (const path of [
"packages/node-core/package.json",
"packages/cli/package.json",
"packages/mcp-server/package.json",
]) {
const pkg = readJson(path);
pkg.version = version;
writeJson(path, pkg);
}
const serverPath = "packages/mcp-server/server.json";
const server = readJson(serverPath);
server.version = version;
for (const packageInfo of server.packages ?? []) {
if (packageInfo.registryType === "npm" && packageInfo.identifier === "@dbx-app/mcp-server") {
packageInfo.version = version;
}
}
writeJson(serverPath, server);
fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${version}\n`);
fs.appendFileSync(process.env.GITHUB_OUTPUT, `tag=packages-v${version}\n`);
NODE
- name: Run package tests
run: pnpm test:packages
- name: Build and pack packages
run: pnpm publish:dry-run
- name: Configure git author
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Commit package release version
run: |
VERSION="${{ steps.version.outputs.version }}"
git add packages/node-core/package.json packages/cli/package.json packages/mcp-server/package.json packages/mcp-server/server.json
if git diff --cached --quiet; then
echo "Package versions already committed for ${VERSION}."
else
git commit -m "chore(packages): release ${VERSION} [skip node-packages-release]"
fi
- name: Rebase package release commit and push
env:
RELEASE_TOKEN: ${{ secrets.MCP_RELEASE_TOKEN }}
run: |
TAG="${{ steps.version.outputs.tag }}"
if [ -n "${RELEASE_TOKEN}" ]; then
git remote set-url origin "https://x-access-token:${RELEASE_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
fi
git fetch origin main --no-tags
git rebase origin/main
REMOTE_TAG_SHA="$(git ls-remote --tags origin "refs/tags/${TAG}" | awk '{print $1}')"
HEAD_SHA="$(git rev-parse HEAD)"
if [ -n "${REMOTE_TAG_SHA}" ]; then
if [ "${REMOTE_TAG_SHA}" != "${HEAD_SHA}" ]; then
echo "::error::Remote tag ${TAG} already exists at ${REMOTE_TAG_SHA}, expected ${HEAD_SHA}."
exit 1
fi
echo "Remote tag ${TAG} already points at ${HEAD_SHA}."
else
git tag -f "${TAG}" "${HEAD_SHA}"
fi
git push origin HEAD:main
if [ -z "${REMOTE_TAG_SHA}" ]; then
git push origin "refs/tags/${TAG}:refs/tags/${TAG}"
fi
publish-node-core:
name: Publish node-core
runs-on: ubuntu-latest
needs: prepare
outputs:
published-or-existing: ${{ steps.publish.outputs.published_or_existing }}
steps:
- uses: actions/checkout@v5
with:
ref: ${{ needs.prepare.outputs.tag }}
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
registry-url: https://registry.npmjs.org
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libsecret-1-dev
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Publish Node core
id: publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
if npm view "@dbx-app/node-core@${VERSION}" version >/dev/null 2>&1; then
echo "@dbx-app/node-core@${VERSION} already exists on npm; skipping."
echo "published_or_existing=true" >> "$GITHUB_OUTPUT"
exit 0
fi
pnpm --filter @dbx-app/node-core publish --access public --provenance --no-git-checks
echo "published_or_existing=true" >> "$GITHUB_OUTPUT"
publish-leaf-packages:
name: Publish ${{ matrix.package-name }}
runs-on: ubuntu-latest
needs: [prepare, publish-node-core]
strategy:
fail-fast: false
matrix:
include:
- package-name: "@dbx-app/cli"
filter: "@dbx-app/cli"
- package-name: "@dbx-app/mcp-server"
filter: "@dbx-app/mcp-server"
steps:
- uses: actions/checkout@v5
with:
ref: ${{ needs.prepare.outputs.tag }}
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
registry-url: https://registry.npmjs.org
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- name: Install native build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libsecret-1-dev
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build workspace dependencies
run: pnpm --filter @dbx-app/node-core build
- name: Publish package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.prepare.outputs.version }}
PACKAGE_NAME: ${{ matrix.package-name }}
PACKAGE_FILTER: ${{ matrix.filter }}
run: |
if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${VERSION} already exists on npm; skipping."
exit 0
fi
pnpm --filter "${PACKAGE_FILTER}" publish --access public --provenance --no-git-checks
publish-homebrew-formula:
name: Publish Homebrew formula
runs-on: ubuntu-latest
needs: [prepare, publish-leaf-packages]
steps:
- name: Download CLI npm tarball and compute SHA256
id: cli-hash
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
VERSION="${VERSION}"
NPM_TARBALL="cli-${VERSION}.tgz"
NPM_URL="https://registry.npmjs.org/@dbx-app/cli/-/${NPM_TARBALL}"
# Poll npm until the package is available (CDN propagation delay)
for i in $(seq 1 12); do
if curl -fsSLI "${NPM_URL}" >/dev/null 2>&1; then
echo "Package found on attempt ${i}"
break
fi
echo "Waiting for npm CDN (attempt ${i}/12)..."
sleep 10
done
curl -fsSL -o "${NPM_TARBALL}" "${NPM_URL}"
CLI_SHA256=$(sha256sum "${NPM_TARBALL}" | cut -d ' ' -f 1)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "sha256=${CLI_SHA256}" >> "$GITHUB_OUTPUT"
echo " cli version: ${VERSION}"
echo " sha256: ${CLI_SHA256}"
- name: Push formula to homebrew-tap
env:
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
CLI_VERSION: ${{ steps.cli-hash.outputs.version }}
CLI_SHA256: ${{ steps.cli-hash.outputs.sha256 }}
run: |
git clone --depth 1 \
"https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/t8y2/homebrew-tap.git" \
homebrew-tap
cd homebrew-tap
mkdir -p Formula
cat > Formula/dbx-cli.rb <<'RUBY_EOF'
class DbxCli < Formula
desc "Command-line interface for DBX database connections, schema, and safe queries"
homepage "https://github.com/t8y2/dbx"
url "https://registry.npmjs.org/@dbx-app/cli/-/cli-__CLI_VERSION__.tgz"
sha256 "__CLI_SHA256__"
license "Apache-2.0"
depends_on "node"
on_linux do
depends_on "pkgconf" => :build
depends_on "libsecret"
end
def install
system "npm", "install", *std_npm_args
bin.install_symlink libexec.glob("bin/*")
# Rebuild better-sqlite3 and keytar native bindings for the current platform.
# prebuild-install is blocked by the Homebrew sandbox during npm install,
# so we must rebuild them explicitly via node-gyp.
node_modules = libexec/"lib/node_modules/@dbx-app/cli/node_modules"
cd node_modules/"better-sqlite3" do
system "npm", "run", "build-release"
end
cd node_modules/"keytar" do
rm_r "prebuilds" if File.directory?("prebuilds")
system "npm", "run", "build"
end
end
test do
assert_path_exists bin/"dbx"
system bin/"dbx", "doctor"
end
end
RUBY_EOF
sed -i 's/^ //' Formula/dbx-cli.rb
sed -i "s/__CLI_VERSION__/${CLI_VERSION}/g" Formula/dbx-cli.rb
sed -i "s/__CLI_SHA256__/${CLI_SHA256}/g" Formula/dbx-cli.rb
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/dbx-cli.rb
if git diff --cached --quiet; then
echo "CLI Homebrew formula is already up to date."
else
git commit -m "dbx-cli ${CLI_VERSION}"
git push
echo "::notice::CLI Homebrew formula updated to ${CLI_VERSION}"
fi
+200
View File
@@ -0,0 +1,200 @@
name: Notifications
on:
# issues:
# types: [opened]
pull_request_target:
types: [opened, closed]
release:
types: [published, released]
jobs:
notify:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'release' || (!github.event.release.draft && !github.event.release.prerelease) }}
steps:
- name: Build message
id: msg
env:
EVENT_NAME: ${{ github.event_name }}
EVENT_ACTION: ${{ github.event.action }}
# ISSUE_TITLE: ${{ github.event.issue.title }}
# ISSUE_URL: ${{ github.event.issue.html_url }}
# ISSUE_USER: ${{ github.event.issue.user.login }}
# ISSUE_BODY: ${{ github.event.issue.body }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_USER: ${{ github.event.pull_request.user.login }}
PR_MERGED: ${{ github.event.pull_request.merged }}
PR_MERGED_BY: ${{ github.event.pull_request.merged_by.login }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
RELEASE_NAME: ${{ github.event.release.name }}
RELEASE_URL: ${{ github.event.release.html_url }}
RELEASE_BODY: ${{ github.event.release.body }}
run: |
python3 - <<'PYEOF'
import os
event = os.environ["EVENT_NAME"]
action = os.environ.get("EVENT_ACTION", "")
msg = None
# if event == "issues":
# title = os.environ["ISSUE_TITLE"]
# url = os.environ["ISSUE_URL"]
# user = os.environ["ISSUE_USER"]
# msg = "\n".join([
# "📋 新 Issue",
# f"标题: {title}",
# f"提交者: {user}",
# f"链接: {url}",
# ])
#
# elif event == "release":
if event == "release":
tag = os.environ["RELEASE_TAG"]
name = os.environ["RELEASE_NAME"]
url = os.environ["RELEASE_URL"]
body = os.environ.get("RELEASE_BODY", "")[:800]
import re
body = re.sub(r'#{1,6}\s*', '', body)
body = re.sub(r'\*\*(.+?)\*\*', r'\1', body)
body = re.sub(r'\*(.+?)\*', r'\1', body)
body = re.sub(r'`(.+?)`', r'\1', body)
body = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', body)
body = re.sub(r'\n{3,}', '\n\n', body)
body = body.strip()
msg = "\n".join([
f"🚀 新版本发布 {tag}",
"",
body,
"",
f"下载: {url}",
])
elif event in ("pull_request", "pull_request_target"):
title = os.environ["PR_TITLE"]
url = os.environ["PR_URL"]
if action == "opened":
user = os.environ["PR_USER"]
msg = "\n".join([
"🔀 新 Pull Request",
f"标题: {title}",
f"提交者: {user}",
f"链接: {url}",
])
elif action == "closed" and os.environ.get("PR_MERGED") == "true":
user = os.environ.get("PR_MERGED_BY", "unknown")
msg = "\n".join([
"✅ PR 已合并",
f"标题: {title}",
f"合并者: {user}",
f"链接: {url}",
])
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
if msg:
f.write(f"skip=false\n")
delim = "EOF_MSG"
f.write(f"msg<<{delim}\n{msg}\n{delim}\n")
else:
f.write(f"skip=true\n")
PYEOF
# - name: AI Analysis for Issue
# id: ai
# if: ${{ github.event_name == 'issues' && steps.msg.outputs.skip != 'true' }}
# env:
# ISSUE_TITLE: ${{ github.event.issue.title }}
# ISSUE_BODY: ${{ github.event.issue.body }}
# ISSUE_USER: ${{ github.event.issue.user.login }}
# DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
# run: |
# python3 - <<'PYEOF'
# import os, json, urllib.request
#
# title = os.environ["ISSUE_TITLE"]
# body = (os.environ.get("ISSUE_BODY") or "(无描述)")[:800]
# user = os.environ.get("ISSUE_USER", "unknown")
# api_key = os.environ.get("DEEPSEEK_API_KEY", "")
#
# if not api_key:
# with open(os.environ["GITHUB_OUTPUT"], "a") as f:
# f.write("analysis=\n")
# exit(0)
#
# prompt = f"""分析这个 GitHub Issue,用中文回复。格式要求(严格遵守,不要多余内容):
#
# 总结: [一句话概括核心问题]
# 优先级: [P0紧急/P1重要/P2普通/P3低优]
# 合理性: [有效问题/重复报告/信息不足/不是bug]
# 建议: [一句话给出处理建议或关联方向]
#
# ---
# 标题: {title}
# 提交者: {user}
# 内容: {body}"""
#
# req = urllib.request.Request(
# "https://api.deepseek.com/v1/chat/completions",
# data=json.dumps({
# "model": "deepseek-v4-pro",
# "max_tokens": 300,
# "messages": [{"role": "user", "content": prompt}],
# }).encode(),
# headers={
# "Authorization": f"Bearer {api_key}",
# "Content-Type": "application/json",
# },
# )
#
# try:
# resp = urllib.request.urlopen(req, timeout=20)
# data = json.loads(resp.read())
# text = data["choices"][0]["message"]["content"]
# except Exception as e:
# text = f"(AI 分析暂时不可用: {e})"
#
# with open(os.environ["GITHUB_OUTPUT"], "a") as f:
# delim = "EOF_ANALYSIS"
# f.write(f"analysis<<{delim}\n{text}\n{delim}\n")
# PYEOF
- name: Send to Feishu
if: ${{ steps.msg.outputs.skip != 'true' && github.event_name != 'release' }}
env:
FEISHU_WEBHOOK_URL: ${{ secrets.FEISHU_WEBHOOK_URL }}
MSG_CONTENT: ${{ steps.msg.outputs.msg }}
run: |
curl -s -X POST "$FEISHU_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg text "$MSG_CONTENT" '{msg_type: "text", content: {text: $text}}')"
- name: Send to QQ group
if: steps.msg.outputs.skip != 'true'
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
HOST: ${{ secrets.DEPLOY_HOST }}
ASTRBOT_API_KEY: ${{ secrets.ASTRBOT_API_KEY }}
MSG_CONTENT: ${{ steps.msg.outputs.msg }}
# AI_ANALYSIS: ${{ steps.ai.outputs.analysis }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null
# if [ -n "$AI_ANALYSIS" ]; then
# MSG_CONTENT="${MSG_CONTENT}
#
# 🤖 AI 分析:
# ${AI_ANALYSIS}"
# fi
MSG=$(echo "$MSG_CONTENT" | jq -Rs .)
ssh "root@${HOST}" "curl -s -X POST http://localhost:6185/api/v1/im/message \
-H 'Authorization: Bearer ${ASTRBOT_API_KEY}' \
-H 'Content-Type: application/json' \
-d '{\"umo\": \"default:GroupMessage:1087880322\", \"message\": ${MSG}}'"
+48
View File
@@ -0,0 +1,48 @@
name: Project Triage
on:
issues:
types: [opened, reopened, labeled, unlabeled, assigned, unassigned]
workflow_dispatch:
inputs:
backfill:
description: Sync all open issues into DBX Issue Triage
required: false
default: "false"
permissions:
contents: read
issues: read
jobs:
sync-project-triage:
runs-on: ubuntu-latest
env:
PROJECT_TOKEN: ${{ secrets.PROJECT_AUTOMATION_TOKEN }}
PROJECT_OWNER: t8y2
PROJECT_NUMBER: "1"
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22.13.0
- name: Validate project token
run: |
if [ -z "$PROJECT_TOKEN" ]; then
echo "Missing PROJECT_AUTOMATION_TOKEN secret"
exit 1
fi
- name: Sync current issue
if: ${{ github.event_name == 'issues' }}
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_EVENT_ACTION: ${{ github.event.action }}
run: node .github/scripts/sync-project-triage.mjs --issue-number "$ISSUE_NUMBER" --event-action "$ISSUE_EVENT_ACTION"
- name: Backfill open issues
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.backfill == 'true' }}
run: node .github/scripts/sync-project-triage.mjs --backfill true
+382
View File
@@ -0,0 +1,382 @@
name: Publish Packages
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g. v0.3.10)"
required: true
permissions:
contents: write
env:
APP_NAME: dbx
PRODUCT_NAME: DBX
REPO_OWNER: t8y2
jobs:
publish:
name: Publish to Homebrew & Scoop
runs-on: ubuntu-latest
steps:
- name: Extract version from tag
id: version
run: |
TAG="${{ github.event.inputs.tag }}"
TAG="$(echo "${TAG}" | xargs)"
VERSION="${TAG#v}"
if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
echo "::error::Tag '${TAG}' does not look like a semver version."
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Download release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p artifacts
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
ASSETS=(
"${PRODUCT_NAME}_${VERSION}_aarch64.dmg"
"${PRODUCT_NAME}_${VERSION}_x64.dmg"
"${PRODUCT_NAME}_${VERSION}_x64-setup.exe"
"${PRODUCT_NAME}_${VERSION}_arm64-setup.exe"
)
for ASSET in "${ASSETS[@]}"; do
echo "::group::Downloading ${ASSET}"
gh release download "${TAG}" \
--repo "${{ github.repository }}" \
--pattern "${ASSET}" \
--dir artifacts \
--clobber
echo "::endgroup::"
done
ls -lh artifacts/
- name: Compute SHA256 hashes
id: hashes
run: |
VERSION="${{ steps.version.outputs.version }}"
compute_hash() {
local file="$1" name="$2"
if [[ ! -f "${file}" ]]; then
echo "::error::Missing artifact: ${file}"
exit 1
fi
local hash
hash=$(sha256sum "${file}" | cut -d ' ' -f 1)
echo "${name}=${hash}" >> "$GITHUB_OUTPUT"
echo " ${name}: ${hash}"
}
echo "SHA256 hashes:"
compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_aarch64.dmg" "sha_dmg_arm64"
compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64.dmg" "sha_dmg_x64"
compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64-setup.exe" "sha_exe_x64"
compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_arm64-setup.exe" "sha_exe_arm64"
- name: Publish Homebrew Cask
env:
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA_ARM64="${{ steps.hashes.outputs.sha_dmg_arm64 }}"
SHA_X64="${{ steps.hashes.outputs.sha_dmg_x64 }}"
git clone --depth 1 \
"https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/homebrew-tap.git" \
homebrew-tap
cd homebrew-tap
mkdir -p Casks
cat > Casks/dbx.rb <<'RUBY_EOF'
cask "dbx" do
arch arm: "aarch64", intel: "x64"
version "__VERSION__"
sha256 arm: "__SHA_ARM64__",
intel: "__SHA_X64__"
url "https://github.com/__OWNER__/__APP__/releases/download/v#{version}/__PRODUCT___#{version}_#{arch}.dmg",
verified: "github.com/__OWNER__/__APP__/"
name "DBX"
desc "Database management tool"
homepage "https://dbxio.com/"
depends_on macos: :big_sur
app "DBX.app"
zap trash: [
"~/Library/Application Support/com.dbx.app",
"~/Library/Caches/com.dbx.app",
"~/Library/Logs/com.dbx.app",
"~/Library/Preferences/com.dbx.app.plist",
]
end
RUBY_EOF
sed -i 's/^ //' Casks/dbx.rb
sed -i "s/__VERSION__/${VERSION}/g" Casks/dbx.rb
sed -i "s/__OWNER__/${REPO_OWNER}/g" Casks/dbx.rb
sed -i "s/__APP__/${APP_NAME}/g" Casks/dbx.rb
sed -i "s/__PRODUCT__/${PRODUCT_NAME}/g" Casks/dbx.rb
sed -i "s/__SHA_ARM64__/${SHA_ARM64}/g" Casks/dbx.rb
sed -i "s/__SHA_X64__/${SHA_X64}/g" Casks/dbx.rb
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/dbx.rb
if git diff --cached --quiet; then
echo "Homebrew Cask is already up to date."
else
git commit -m "dbx ${VERSION}"
git push
echo "::notice::Homebrew Cask updated to ${VERSION}"
fi
- name: Publish Scoop manifest
env:
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA_EXE_X64="${{ steps.hashes.outputs.sha_exe_x64 }}"
SHA_EXE_ARM64="${{ steps.hashes.outputs.sha_exe_arm64 }}"
git clone --depth 1 \
"https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/scoop-bucket.git" \
scoop-bucket
cd scoop-bucket
cat > dbx.json <<JSON_EOF
{
"version": "${VERSION}",
"description": "Open-source database management tool",
"homepage": "https://github.com/${REPO_OWNER}/${APP_NAME}",
"license": "Apache-2.0",
"architecture": {
"64bit": {
"url": "https://github.com/${REPO_OWNER}/${APP_NAME}/releases/download/v${VERSION}/${PRODUCT_NAME}_${VERSION}_x64-setup.exe",
"hash": "${SHA_EXE_X64}"
},
"arm64": {
"url": "https://github.com/${REPO_OWNER}/${APP_NAME}/releases/download/v${VERSION}/${PRODUCT_NAME}_${VERSION}_arm64-setup.exe",
"hash": "${SHA_EXE_ARM64}"
}
},
"innosetup": false,
"installer": {
"args": ["/S", "/D=\$dir"]
},
"uninstaller": {
"file": "uninstall.exe",
"args": ["/S"]
},
"checkver": {
"github": "https://github.com/${REPO_OWNER}/${APP_NAME}"
},
"autoupdate": {
"architecture": {
"64bit": {
"url": "https://github.com/${REPO_OWNER}/${APP_NAME}/releases/download/v\$version/${PRODUCT_NAME}_\$version_x64-setup.exe"
},
"arm64": {
"url": "https://github.com/${REPO_OWNER}/${APP_NAME}/releases/download/v\$version/${PRODUCT_NAME}_\$version_arm64-setup.exe"
}
}
}
}
JSON_EOF
sed -i 's/^ //' dbx.json
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add dbx.json
if git diff --cached --quiet; then
echo "Scoop manifest is already up to date."
else
git commit -m "dbx ${VERSION}"
git push
echo "::notice::Scoop manifest updated to ${VERSION}"
fi
upload-r2:
name: Upload to R2
runs-on: ubuntu-latest
steps:
- name: Extract version from tag
id: version
run: |
TAG="${{ github.event.inputs.tag }}"
TAG="$(echo "${TAG}" | xargs)"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Download release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p assets
gh release download "${{ steps.version.outputs.tag }}" \
--repo "${{ github.repository }}" \
--dir assets \
--clobber
ls -lh assets/
- name: Rewrite latest.json download URLs to R2 CDN
run: |
TAG="${{ steps.version.outputs.tag }}"
LATEST_JSON="assets/latest.json"
if [ ! -f "$LATEST_JSON" ]; then
echo "::warning::latest.json not found, skipping URL rewrite"
exit 0
fi
echo "Before:"
jq '.platforms[]?.url // empty' "$LATEST_JSON"
jq --arg tag "$TAG" '
(.platforms[]?.url?) |= (
sub("https://github\\.com/t8y2/dbx/releases/download/" + $tag + "/"; "https://dl.dbxio.com/releases/latest/")
+ "?v=" + $tag
)
' "$LATEST_JSON" > "$LATEST_JSON.tmp"
mv "$LATEST_JSON.tmp" "$LATEST_JSON"
echo "After:"
jq '.platforms[]?.url // empty' "$LATEST_JSON"
- name: Upload to R2 (versioned)
run: |
aws s3 sync ./assets "s3://${{ secrets.R2_BUCKET_NAME }}/releases/${{ steps.version.outputs.tag }}/" \
--endpoint-url "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
- name: Upload to R2 (latest)
run: |
aws s3 sync ./assets "s3://${{ secrets.R2_BUCKET_NAME }}/releases/latest/" \
--endpoint-url "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com" \
--cache-control "no-cache" \
--delete
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
- name: Purge Cloudflare cache
run: |
set -euo pipefail
zone_id="${CLOUDFLARE_ZONE_ID:-}"
if [ -z "$zone_id" ]; then
zone_id="$(curl -fsSL \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones?name=dbxio.com" \
| jq -r '.result[0].id // empty')"
fi
if [ -z "$zone_id" ]; then
echo "::error::Cloudflare zone dbxio.com not found. Set CLOUDFLARE_ZONE_ID or grant Zone:Read to CLOUDFLARE_API_TOKEN."
exit 1
fi
curl -fsSL -X POST \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}' \
"https://api.cloudflare.com/client/v4/zones/${zone_id}/purge_cache" \
| jq -e '.success == true'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
promote-release:
name: Promote to Release
needs: [publish, upload-r2]
runs-on: ubuntu-latest
steps:
- name: Promote prerelease to release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ github.event.inputs.tag }}"
gh release edit "${TAG}" \
--repo "${{ github.repository }}" \
--prerelease=false \
--latest
echo "::notice::Release ${TAG} promoted from prerelease to latest release"
notify-qq:
name: QQ Notify
needs: [promote-release]
runs-on: ubuntu-latest
steps:
- name: Build message
id: msg
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.event.inputs.tag }}
run: |
RELEASE_JSON=$(gh release view "${TAG}" --repo "${{ github.repository }}" --json tagName,name,url,body)
export RELEASE_TAG=$(echo "$RELEASE_JSON" | jq -r '.tagName')
export RELEASE_URL=$(echo "$RELEASE_JSON" | jq -r '.url')
export RELEASE_BODY=$(echo "$RELEASE_JSON" | jq -r '.body')
python3 - <<'PYEOF'
import os, re
tag = os.environ["RELEASE_TAG"]
url = os.environ["RELEASE_URL"]
body = os.environ.get("RELEASE_BODY", "")[:800]
body = re.sub(r'#{1,6}\s*', '', body)
body = re.sub(r'\*\*(.+?)\*\*', r'\1', body)
body = re.sub(r'\*(.+?)\*', r'\1', body)
body = re.sub(r'`(.+?)`', r'\1', body)
body = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', body)
body = re.sub(r'\n{3,}', '\n\n', body)
body = body.strip()
msg = "\n".join([
"━━━━━━━━━━━━━━━",
f"🚀 新版本发布 {tag}",
"━━━━━━━━━━━━━━━",
"",
body,
"",
f"下载: {url}",
"━━━━━━━━━━━━━━━",
])
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
delim = "EOF_MSG"
f.write(f"msg<<{delim}\n{msg}\n{delim}\n")
PYEOF
- name: Send to QQ group
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
HOST: ${{ secrets.DEPLOY_HOST }}
ASTRBOT_API_KEY: ${{ secrets.ASTRBOT_API_KEY }}
MSG_CONTENT: ${{ steps.msg.outputs.msg }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null
PAYLOAD=$(jq -nc --arg msg "$MSG_CONTENT" \
'{"umo": "default:GroupMessage:1087880322", "message": $msg}')
printf '%s' "$PAYLOAD" | ssh "root@${HOST}" "curl -fsS -X POST http://localhost:6185/api/v1/im/message \
-H 'Authorization: Bearer ${ASTRBOT_API_KEY}' \
-H 'Content-Type: application/json' \
--data-binary @-"
+557
View File
@@ -0,0 +1,557 @@
name: Release
on:
push:
tags:
- "v*"
permissions:
contents: write
env:
CNB_REPOSITORY: "dbxio.com/dbx"
CNB_USERNAME: "cnb"
CNB_REGISTRY: "docker.cnb.cool"
jobs:
bump-jdbc-plugin-version:
runs-on: ubuntu-latest
outputs:
changed: ${{ steps.bump.outputs.changed }}
old_version: ${{ steps.bump.outputs.old_version }}
new_version: ${{ steps.bump.outputs.new_version }}
prev_tag: ${{ steps.prev-tag.outputs.prev_tag }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Find previous release tag
id: prev-tag
shell: bash
run: |
PREV_TAG=$(git tag --sort=-creatordate | grep '^v' | sed -n '2p')
if [ -z "$PREV_TAG" ]; then
PREV_TAG=$(git rev-list --max-parents=0 HEAD)
fi
echo "prev_tag=${PREV_TAG}" >> "$GITHUB_OUTPUT"
echo "Comparing ${PREV_TAG}..HEAD"
- name: Detect JDBC plugin changes and bump version
id: bump
shell: bash
run: |
BUMP_OUTPUT="$(node .github/scripts/bump-jdbc-plugin-version.mjs "${{ steps.prev-tag.outputs.prev_tag }}" HEAD --write)"
echo "$BUMP_OUTPUT"
echo "$BUMP_OUTPUT" >> "$GITHUB_OUTPUT"
- name: Commit JDBC plugin version bump
if: steps.bump.outputs.changed == 'true'
shell: bash
run: |
TMP_DIR="$(mktemp -d)"
cp plugins/jdbc/pom.xml "$TMP_DIR/pom.xml"
cp plugins/jdbc/manifest.json "$TMP_DIR/manifest.json"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch origin main
git switch -C jdbc-plugin-version-bump origin/main
cp "$TMP_DIR/pom.xml" plugins/jdbc/pom.xml
cp "$TMP_DIR/manifest.json" plugins/jdbc/manifest.json
git add plugins/jdbc/pom.xml plugins/jdbc/manifest.json
if git diff --cached --quiet; then
echo "JDBC plugin version ${{ steps.bump.outputs.new_version }} is already on main."
exit 0
fi
git commit -m "chore(jdbc): bump plugin version [skip ci]"
git push origin HEAD:main
build:
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
target: aarch64-apple-darwin
- platform: macos-15-intel
target: x86_64-apple-darwin
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: ubuntu-22.04-arm
target: aarch64-unknown-linux-gnu
- platform: windows-2022
target: x86_64-pc-windows-msvc
arch: x64
- platform: windows-2022
target: aarch64-pc-windows-msvc
arch: arm64
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v6
- name: Install frontend dependencies
run: pnpm install
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Compute Rust dependency hash
id: deps-hash
shell: bash
run: |
{
find . -name Cargo.toml -not -path './target/*' -print0 \
| sort -z \
| xargs -0 sed -E '/^version = /d'
grep -v '^version = ' Cargo.lock
} | sha256sum | cut -d' ' -f1 | {
read -r hash
echo "hash=${hash:0:20}" >> "$GITHUB_OUTPUT"
}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./ -> target"
shared-key: release-${{ matrix.target }}-${{ steps.deps-hash.outputs.hash }}
add-rust-environment-hash-key: false
cache-on-failure: true
- name: Install Apple certificate (macOS)
if: startsWith(matrix.platform, 'macos')
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH
security create-keychain -p "" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "" $KEYCHAIN_PATH
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "" $KEYCHAIN_PATH
security list-keychains -d user -s $KEYCHAIN_PATH login.keychain-db
- name: Install system dependencies (Linux)
if: startsWith(matrix.platform, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev xdg-utils
- name: Setup Tauri signing key
shell: bash
run: |
echo "${{ secrets.TAURI_SIGNING_PRIVATE_KEY_BASE64 }}" | base64 --decode > "$RUNNER_TEMP/updater.key"
KEY_B64=$(base64 < "$RUNNER_TEMP/updater.key" | tr -d '\r\n')
echo "TAURI_SIGNING_PRIVATE_KEY=$KEY_B64" >> "$GITHUB_ENV"
if [ -n "${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}" ]; then
echo "TAURI_SIGNING_PRIVATE_KEY_PASSWORD=${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}" >> "$GITHUB_ENV"
fi
- name: Generate release notes
id: release-notes
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BODY="$(gh api "repos/${GITHUB_REPOSITORY}/releases/generate-notes" \
-f tag_name="${GITHUB_REF_NAME}" \
--jq '.body')"
if [ -z "$BODY" ]; then
BODY="DBX ${GITHUB_REF_NAME}"
fi
DELIM="RELEASE_NOTES_$(date +%s%N)"
{
echo "body<<$DELIM"
printf '%s\n' "$BODY"
echo "$DELIM"
} >> "$GITHUB_OUTPUT"
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_SIGNING_IDENTITY: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_SIGNING_IDENTITY || '' }}
APPLE_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_ID || '' }}
APPLE_PASSWORD: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_PASSWORD || '' }}
APPLE_TEAM_ID: ${{ startsWith(matrix.platform, 'macos') && secrets.APPLE_TEAM_ID || '' }}
with:
tagName: ${{ github.ref_name }}
releaseName: "DBX ${{ github.ref_name }}"
releaseBody: ${{ steps.release-notes.outputs.body }}
releaseDraft: true
prerelease: false
args: --target ${{ matrix.target }}
- name: Upload Windows portable ZIP
if: matrix.arch == 'x64' || matrix.arch == 'arm64'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$version = "${env:GITHUB_REF_NAME}".TrimStart("v")
$arch = "${{ matrix.arch }}"
$portableRoot = "portable"
$portableDir = Join-Path $portableRoot "DBX_${version}_${arch}"
$zipName = "DBX_${version}_${arch}-portable.zip"
$exePath = "target/${{ matrix.target }}/release/dbx.exe"
if (!(Test-Path $exePath)) {
Write-Error "Missing Windows executable: $exePath"
exit 1
}
New-Item -ItemType Directory -Force -Path $portableDir | Out-Null
Copy-Item $exePath (Join-Path $portableDir "DBX.exe") -Force
Copy-Item "LICENSE" (Join-Path $portableDir "LICENSE") -Force
Copy-Item "README.md" (Join-Path $portableDir "README.md") -Force
Set-Content -Path (Join-Path $portableDir "portable.dbx") -Value "" -NoNewline
Compress-Archive -Path (Join-Path $portableDir "*") -DestinationPath $zipName -Force
gh release upload "${env:GITHUB_REF_NAME}" $zipName --repo "${env:GITHUB_REPOSITORY}" --clobber
- name: Upload Windows WebView2 offline installer
if: matrix.arch == 'x64' || matrix.arch == 'arm64'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$version = "${env:GITHUB_REF_NAME}".TrimStart("v")
$arch = "${{ matrix.arch }}"
$bundleDir = "target/${{ matrix.target }}/release/bundle/nsis"
$offlineName = "DBX_${version}_${arch}-webview2-offline-setup.exe"
$before = @{}
if (Test-Path $bundleDir) {
Get-ChildItem $bundleDir -Filter "*.exe" | ForEach-Object { $before[$_.FullName] = $_.LastWriteTimeUtc }
}
pnpm tauri bundle --bundles nsis --target ${{ matrix.target }} --config src-tauri/tauri.webview2-offline.conf.json
$installer = Get-ChildItem $bundleDir -Filter "*.exe" |
Where-Object { !$before.ContainsKey($_.FullName) -or $_.LastWriteTimeUtc -gt $before[$_.FullName] } |
Sort-Object LastWriteTimeUtc -Descending |
Select-Object -First 1
if (!$installer) {
Write-Error "Missing WebView2 offline installer in ${bundleDir}"
exit 1
}
Copy-Item $installer.FullName $offlineName -Force
gh release upload "${env:GITHUB_REF_NAME}" $offlineName --repo "${env:GITHUB_REPOSITORY}" --clobber
cleanup-release-signatures:
needs: build
runs-on: ubuntu-latest
steps:
- name: Remove standalone updater signature assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
mapfile -t SIG_ASSETS < <(
gh release view "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--json assets \
--jq '.assets[].name | select(endswith(".sig"))'
)
if [ "${#SIG_ASSETS[@]}" -eq 0 ]; then
echo "No standalone .sig release assets found."
exit 0
fi
for ASSET in "${SIG_ASSETS[@]}"; do
echo "Deleting release asset: ${ASSET}"
gh release delete-asset "${GITHUB_REF_NAME}" "${ASSET}" \
--repo "${GITHUB_REPOSITORY}" \
--yes
done
jdbc-plugin:
needs: [cleanup-release-signatures, bump-jdbc-plugin-version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
cache: maven
- name: Apply automatic JDBC plugin version bump
shell: bash
run: node .github/scripts/bump-jdbc-plugin-version.mjs "${{ needs.bump-jdbc-plugin-version.outputs.prev_tag }}" HEAD --write
- name: Read JDBC plugin version
id: jdbc-plugin
shell: bash
run: |
VERSION="$(grep -m1 '<version>' plugins/jdbc/pom.xml | sed -E 's/.*<version>([^<]+)<.*/\1/')"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Package JDBC plugin
run: ./plugins/jdbc/package.sh
- name: Upload JDBC plugin asset
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${GITHUB_REF_NAME}" \
"plugins/jdbc/dist/dbx-jdbc-plugin-${{ steps.jdbc-plugin.outputs.version }}.zip" \
"plugins/jdbc/dist/dbx-jdbc-plugin-latest.zip" \
--repo "${GITHUB_REPOSITORY}" \
--clobber
- name: Add JDBC plugin metadata to latest.json
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
mkdir -p "$RUNNER_TEMP/latest-json"
gh release download "${GITHUB_REF_NAME}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern latest.json \
--dir "$RUNNER_TEMP/latest-json"
node .github/scripts/augment-latest-json-jdbc-plugin.mjs \
"$RUNNER_TEMP/latest-json/latest.json" \
"${{ steps.jdbc-plugin.outputs.version }}" \
"1" \
"https://github.com/t8y2/dbx/releases/latest/download/dbx-jdbc-plugin-latest.zip"
gh release upload "${GITHUB_REF_NAME}" \
"$RUNNER_TEMP/latest-json/latest.json" \
--repo "${GITHUB_REPOSITORY}" \
--clobber
publish:
needs: [cleanup-release-signatures, docker-manifest, jdbc-plugin]
runs-on: ubuntu-latest
steps:
- name: Publish draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release edit ${{ github.ref_name }} --repo ${{ github.repository }} --draft=false --prerelease
sync-release-to-cnb:
name: Sync release to CNB
needs: publish
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Download GitHub release assets
env:
TAG_NAME: ${{ github.ref_name }}
GITHUB_REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/github-release" "$RUNNER_TEMP/release-assets"
gh release view "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--json tagName,name,body,targetCommitish,isPrerelease,isDraft,assets \
> "$RUNNER_TEMP/github-release/release.json"
gh release download "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--dir "$RUNNER_TEMP/release-assets" \
--clobber
- name: Sync release assets to CNB
env:
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
CNB_UPLOAD_CONCURRENCY: "3"
run: |
node .github/scripts/sync-cnb-release.mjs \
--github-release "$RUNNER_TEMP/github-release/release.json" \
--assets-dir "$RUNNER_TEMP/release-assets"
sync-release-to-atomgit:
name: Sync release to AtomGit
needs: publish
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Ensure AtomGit tag exists
env:
TAG_NAME: ${{ github.ref_name }}
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
run: |
set -euo pipefail
git fetch origin "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
git remote add atomgit "https://t8y2:${ATOMGIT_TOKEN}@atomgit.com/t8y2/dbx.git"
git push atomgit "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
- name: Download GitHub release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.ref_name }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/github-release" "$RUNNER_TEMP/release-assets"
gh release view "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--json tagName,name,body,targetCommitish,isPrerelease,isDraft,assets \
> "$RUNNER_TEMP/github-release/release.json"
gh release download "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--dir "$RUNNER_TEMP/release-assets" \
--clobber
- name: Sync release assets to AtomGit
env:
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
ATOMGIT_REPOSITORY: "t8y2/dbx"
# AtomGit throttles concurrent large-asset uploads, which can expire their temporary upload URLs.
ATOMGIT_UPLOAD_CONCURRENCY: "1"
run: |
set -euo pipefail
node .github/scripts/sync-atomgit-release.mjs \
--github-release "$RUNNER_TEMP/github-release/release.json" \
--assets-dir "$RUNNER_TEMP/release-assets"
docker:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform: [linux/amd64, linux/arm64]
steps:
- uses: actions/checkout@v5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to CNB Registry
uses: docker/login-action@v3
with:
registry: ${{ env.CNB_REGISTRY }}
username: cnb
password: ${{ secrets.CNB_TOKEN }}
- name: Build and push Docker Hub by digest
id: build-dockerhub
uses: docker/build-push-action@v6
with:
context: .
file: deploy/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/dbx,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.platform }}
cache-to: type=gha,scope=${{ matrix.platform }},mode=max
- name: Build and push CNB by digest
id: build-cnb
uses: docker/build-push-action@v6
with:
context: .
file: deploy/Dockerfile
platforms: ${{ matrix.platform }}
outputs: type=image,name=${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.platform }}
- name: Export digests
run: |
mkdir -p /tmp/digests/dockerhub /tmp/digests/cnb
dockerhub_digest="${{ steps.build-dockerhub.outputs.digest }}"
cnb_digest="${{ steps.build-cnb.outputs.digest }}"
touch "/tmp/digests/dockerhub/${dockerhub_digest#sha256:}"
touch "/tmp/digests/cnb/${cnb_digest#sha256:}"
- name: Upload Docker Hub digest
uses: actions/upload-artifact@v7
with:
name: dockerhub-digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: /tmp/digests/dockerhub/*
if-no-files-found: error
retention-days: 1
- name: Upload CNB digest
uses: actions/upload-artifact@v7
with:
name: cnb-digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: /tmp/digests/cnb/*
if-no-files-found: error
retention-days: 1
docker-manifest:
needs: docker
runs-on: ubuntu-latest
steps:
- name: Download Docker Hub digests
uses: actions/download-artifact@v8
with:
path: /tmp/digests/dockerhub
pattern: dockerhub-digests-*
merge-multiple: true
- name: Download CNB digests
uses: actions/download-artifact@v8
with:
path: /tmp/digests/cnb
pattern: cnb-digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to CNB Registry
uses: docker/login-action@v3
with:
registry: ${{ env.CNB_REGISTRY }}
username: cnb
password: ${{ secrets.CNB_TOKEN }}
- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Create manifest list and push
run: |
docker buildx imagetools create \
-t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:${{ steps.version.outputs.version }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/dbx:latest \
$(cd /tmp/digests/dockerhub && printf '${{ secrets.DOCKERHUB_USERNAME }}/dbx@sha256:%s ' *)
docker buildx imagetools create \
-t ${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }}:${{ steps.version.outputs.version }} \
-t ${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }}:latest \
$(cd /tmp/digests/cnb && printf '${{ env.CNB_REGISTRY }}/${{ env.CNB_REPOSITORY }}@sha256:%s ' *)
+41
View File
@@ -0,0 +1,41 @@
name: Spam Comment Guard
on:
issue_comment:
types: [created]
# 用 PROJECT_AUTOMATION_TOKENowner PAT)调 DELETEGITHUB_TOKEN 本身无需写权限
permissions:
contents: read
jobs:
delete-malicious-link:
runs-on: ubuntu-latest
# 跳过机器人与仓库 owner,避免误删维护者的正当评论
if: ${{ github.event.comment.user.type != 'Bot' && github.event.comment.user.login != github.repository_owner }}
env:
# owner PATGITHUB_TOKEN 非 admin 删不掉别人的评论,必须用 owner 令牌
GH_TOKEN: ${{ secrets.PROJECT_AUTOMATION_TOKEN }}
COMMENT_ID: ${{ github.event.comment.id }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_USER: ${{ github.event.comment.user.login }}
ISSUE_URL: ${{ github.event.issue.html_url }}
REPO: ${{ github.repository }}
steps:
- name: Delete comment containing malicious attachment link
run: |
set -euo pipefail
# 命中外部恶意附件链接即删除评论。两条分支:
# 1) 外部 http(s) 直链 .zip(排除 GitHub 自有域名的 release/archive/raw/attachments
# 2) markdown 链接文本以 .zip/.exe/.scr/.7z 结尾且指向外部域名——
# 规避手法常把 .zip 放链接文本、URL 用 // 协议相对或 %2E 编码域名
# 如需扩展扩展名,修改下方两处列表即可。
if ! printf '%s' "$COMMENT_BODY" | grep -qiP 'https?://(?!(?:github\.com|[^/]*\.githubusercontent\.com)/)[^[:space:]<>"]+\.zip([?#][^[:space:]<>"]*)?|\[[^\]]*\.(zip|7z|exe|scr)\]\((?!(?:https?:)?//(?:github\.com|[^/]*\.githubusercontent\.com)/)[^)]*\)'; then
echo "No malicious attachment link detected, skipping"
exit 0
fi
echo "Detected malicious attachment link in comment by @$COMMENT_USER on $ISSUE_URL — deleting"
# 物理删除评论,内容对所有人不可见。DELETE 返回 204 无响应体。
gh api -X DELETE "repos/${REPO}/issues/comments/${COMMENT_ID}"
@@ -0,0 +1,62 @@
name: Sync AtomGit Release Assets
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag to sync"
required: true
type: string
permissions:
contents: read
env:
ATOMGIT_REPOSITORY: "t8y2/dbx"
jobs:
sync-release-to-atomgit:
name: Sync release to AtomGit
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Ensure AtomGit tag exists
env:
TAG_NAME: ${{ inputs.tag }}
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
run: |
set -euo pipefail
git fetch origin "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
git remote add atomgit "https://t8y2:${ATOMGIT_TOKEN}@atomgit.com/t8y2/dbx.git"
git push atomgit "refs/tags/${TAG_NAME}:refs/tags/${TAG_NAME}" --force
- name: Download GitHub release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ inputs.tag }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/github-release" "$RUNNER_TEMP/release-assets"
gh release view "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--json tagName,name,body,targetCommitish,isPrerelease,isDraft,assets \
> "$RUNNER_TEMP/github-release/release.json"
gh release download "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--dir "$RUNNER_TEMP/release-assets" \
--clobber
- name: Sync release assets to AtomGit
env:
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
ATOMGIT_REPOSITORY: ${{ env.ATOMGIT_REPOSITORY }}
run: |
set -euo pipefail
node .github/scripts/sync-atomgit-release.mjs \
--github-release "$RUNNER_TEMP/github-release/release.json" \
--assets-dir "$RUNNER_TEMP/release-assets"
+46
View File
@@ -0,0 +1,46 @@
name: Sync Changelog to R2
on:
workflow_run:
workflows: ["Publish Packages"]
types: [completed]
workflow_dispatch:
jobs:
sync:
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v6
with:
node-version: 22
- name: Generate changelog JSON
run: node .github/scripts/sync-changelog.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
- name: Upload to R2
run: |
pip install awscli --quiet
aws s3 cp releases-cn.json "s3://${R2_BUCKET_NAME}/changelog/releases-cn.json" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
--content-type application/json
if [ -f releases-en.json ]; then
aws s3 cp releases-en.json "s3://${R2_BUCKET_NAME}/changelog/releases-en.json" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
--content-type application/json
fi
if [ -f latest-en.json ]; then
aws s3 cp latest-en.json "s3://${R2_BUCKET_NAME}/changelog/latest-en.json" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
--content-type application/json
fi
env:
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}
@@ -0,0 +1,40 @@
name: Sync CNB Release Assets
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag to sync"
required: true
type: string
permissions:
contents: read
env:
CNB_REPOSITORY: "dbxio.com/dbx"
jobs:
sync-release-to-cnb:
name: Sync release to CNB
runs-on: ubuntu-latest
steps:
- name: Sync release assets to CNB
env:
TAG_NAME: ${{ inputs.tag }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_USERNAME: ${{ github.repository_owner }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CNB_USERNAME: cnb
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
run: |
set -euo pipefail
wget -q https://cnb.cool/znb/mpgrm/-/releases/download/v0.1.0/mpgrm_linux_amd64.tar.gz
tar -xf mpgrm_linux_amd64.tar.gz
chmod +x mpgrm
./mpgrm releases sync \
--repo "https://github.com/${GITHUB_REPOSITORY}.git" \
--target-repo "https://cnb.cool/${CNB_REPOSITORY}.git" \
--tags "${TAG_NAME}"
+80
View File
@@ -0,0 +1,80 @@
name: Sync Mirrors
on:
push:
branches:
- main
tags:
- "**"
delete:
workflow_dispatch:
# Mirror providers reject concurrent forced updates to the same ref. Serialize all
# branch, tag, and delete events so rapid GitHub pushes cannot race each other.
concurrency:
group: sync-mirrors
cancel-in-progress: false
jobs:
sync-cnb:
name: Sync to CNB
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Push to CNB
env:
CNB_TOKEN: ${{ secrets.CNB_TOKEN }}
run: |
set -euo pipefail
git remote add cnb "https://cnb:${CNB_TOKEN}@cnb.cool/dbxio.com/dbx.git"
git push cnb --all --force
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
git push cnb ":refs/tags/${GITHUB_REF_NAME}" || true
fi
git push cnb --tags --force
sync-atomgit:
name: Sync to AtomGit
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Push to AtomGit
env:
ATOMGIT_TOKEN: ${{ secrets.ATOMGIT_TOKEN }}
run: |
set -euo pipefail
git remote add atomgit "https://t8y2:${ATOMGIT_TOKEN}@atomgit.com/t8y2/dbx.git"
git push atomgit --all --force
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
git push atomgit ":refs/tags/${GITHUB_REF_NAME}" || true
fi
git push atomgit --tags --force
sync-gitee:
name: Sync to Gitee
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Push to Gitee
env:
GITEE_TOKEN: ${{ secrets.GITEE_TOKEN }}
run: |
set -euo pipefail
git remote add gitee "https://codetty:${GITEE_TOKEN}@gitee.com/codetty/dbx.git"
git push gitee --all --force
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
git push gitee ":refs/tags/${GITHUB_REF_NAME}" || true
fi
git push gitee --tags --force