chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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
|
||||
@@ -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' || '' }}
|
||||
@@ -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
|
||||
@@ -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 ' *)
|
||||
@@ -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 }}
|
||||
@@ -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"
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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}}'"
|
||||
@@ -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
|
||||
@@ -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 @-"
|
||||
@@ -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 ' *)
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Spam Comment Guard
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
# 用 PROJECT_AUTOMATION_TOKEN(owner PAT)调 DELETE,GITHUB_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 PAT:GITHUB_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"
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user