diff --git a/.github/workflows/sync-node-exporter.yml b/.github/workflows/sync-node-exporter.yml new file mode 100644 index 0000000..bdc67c6 --- /dev/null +++ b/.github/workflows/sync-node-exporter.yml @@ -0,0 +1,204 @@ +name: Sync node_exporter 1.9.* / 1.8.* (matrix) + +on: + workflow_dispatch: + inputs: + tag: + description: "Release version without 'v' (e.g., 1.9.1). Defaults to 1.9.1" + required: false + type: string + schedule: + - cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00,请改为 "0 17 * * *" + +permissions: + contents: read + +concurrency: + group: sync-node-exporter-1x + cancel-in-progress: false + +jobs: + prep: + name: Resolve version & remote check + runs-on: ubuntu-latest + env: + GH_REPO: prometheus/node_exporter + GH_TOKEN: ${{ github.token }} + RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }} + RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }} + VPS_HOST: ${{ secrets.VPS_HOST }} + REMOTE_ROOT: /data/update-server/prometheus/node_exporter/ + DEFAULT_TAG: 1.9.1 # <-- 无 v + ALLOWED_SERIES: "^(1\\.9|1\\.8)\\.[0-9]+\\.[0-9]+$" + outputs: + tag: ${{ steps.resolve.outputs.tag }} # v1.9.1 + version: ${{ steps.resolve.outputs.version }} # 1.9.1 + exists: ${{ steps.remotecheck.outputs.exists }} + steps: + - uses: actions/checkout@v4 + + - name: Ensure GitHub CLI & deps + run: | + set -euo pipefail + sudo apt-get update -y + sudo apt-get install -y gh jq rsync + gh --version + jq --version + rsync --version | head -n1 + + - name: Resolve version (use input or default) & validate (1.9.* / 1.8.*) + id: resolve + run: | + set -euo pipefail + VERSION_INPUT='${{ github.event.inputs.tag }}' + if [ -n "$VERSION_INPUT" ]; then + VERSION="$VERSION_INPUT" + else + VERSION="$DEFAULT_TAG" + fi + + if ! echo "$VERSION" | grep -Eq '${{ env.ALLOWED_SERIES }}'; then + echo "Invalid or disallowed version: $VERSION. Allowed: 1.9.* or 1.8.*" >&2 + exit 1 + fi + + REL_TAG="v${VERSION}" # GitHub release tag 有 v 前缀 + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=$REL_TAG" >> "$GITHUB_OUTPUT" + echo "Use version: $VERSION (release tag: $REL_TAG)" + + - name: Init SSH + run: | + set -euo pipefail + mkdir -p ~/.ssh + echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts + + - name: Check remote existing version dir + id: remotecheck + env: + VERSION: ${{ steps.resolve.outputs.version }} + run: | + set -euo pipefail + REMOTE_DIR="${REMOTE_ROOT}/${VERSION}" + if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Remote already has ${REMOTE_DIR}, skip whole sync." + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Remote does not have ${REMOTE_DIR}, will sync." + fi + + sync-one: + name: Sync ${{ matrix.asset_suffix }} for ${{ needs.prep.outputs.version }} + needs: prep + if: needs.prep.outputs.exists == 'false' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + asset_suffix: + - "linux-amd64.tar.gz" + - "linux-arm64.tar.gz" + env: + GH_REPO: prometheus/node_exporter + GH_TOKEN: ${{ github.token }} + RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }} + RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }} + VPS_HOST: ${{ secrets.VPS_HOST }} + REMOTE_ROOT: /data/update-server/prometheus/node_exporter + TAG: ${{ needs.prep.outputs.tag }} # v1.9.1 + VERSION: ${{ needs.prep.outputs.version }} # 1.9.1 + steps: + - uses: actions/checkout@v4 + + - name: Ensure GitHub CLI & deps + run: | + set -euo pipefail + sudo apt-get update -y + sudo apt-get install -y gh jq rsync + gh --version + + - name: Check asset exists via GitHub CLI + id: has_asset + run: | + set -euo pipefail + # 文件名:node_exporter-${VERSION}.linux-amd64.tar.gz / linux-arm64.tar.gz + ASSET="node_exporter-${VERSION}.${{ matrix.asset_suffix }}" + echo "Checking asset $ASSET for tag ${TAG}" + if gh release view "${TAG}" --repo "${GH_REPO}" --json assets \ + | jq -r '.assets[].name' | grep -Fxq "$ASSET"; then + echo "asset=$ASSET" >> "$GITHUB_OUTPUT" + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "Asset $ASSET not found for ${TAG}, will skip." + fi + + - name: Download asset + if: steps.has_asset.outputs.exists == 'true' + run: | + set -euo pipefail + mkdir -p "releases/${VERSION}" + gh release download "${TAG}" \ + --repo "${GH_REPO}" \ + --pattern "${{ steps.has_asset.outputs.asset }}" \ + --dir "releases/${VERSION}" + + - name: Init SSH + if: steps.has_asset.outputs.exists == 'true' + run: | + set -euo pipefail + mkdir -p ~/.ssh + echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts + + - name: Rsync this asset to remote + if: steps.has_asset.outputs.exists == 'true' + run: | + set -euo pipefail + REMOTE_DIR="${REMOTE_ROOT}/${VERSION}" + ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'" + echo "Rsync releases/${VERSION}/${{ steps.has_asset.outputs.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/" + rsync -av -e "ssh -i ~/.ssh/id_rsa" \ + "releases/${VERSION}/${{ steps.has_asset.outputs.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/" + + retention: + name: Remote retention (keep latest 10) + needs: [prep, sync-one] + if: needs.prep.outputs.exists == 'false' + runs-on: ubuntu-latest + env: + RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }} + RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }} + VPS_HOST: ${{ secrets.VPS_HOST }} + REMOTE_ROOT: /data/update-server/prometheus/node_exporter/ + steps: + - name: Init SSH + run: | + set -euo pipefail + mkdir -p ~/.ssh + echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts + + - name: Prune old versions on remote (keep 10) + run: | + set -euo pipefail + ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc ' + set -euo pipefail + cd "'"${REMOTE_ROOT}"'" || exit 0 + keep=10 + mapfile -t all < <(ls -1 | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$" | sort -V -r || true) + if [ "${#all[@]}" -le "$keep" ]; then + echo "Nothing to prune. Count=${#all[@]}" + exit 0 + fi + to_delete=("${all[@]:keep}") + echo "Pruning old versions: ${to_delete[*]}" + for d in "${to_delete[@]}"; do + rm -rf -- "$d" + done + '