90eaf04a84
Closes the persistent "fatal: could not read Username for 'https://cnb.cool'" failure that bit about half of recent sync-cnb runs. Root causes from the failure log of run 25705666413 (2026-05-12) and 25697452832 (2026-05-11): 1. The previous tencentcom/git-sync Docker action discovered every local ref via fetch-depth: 0 and tried to push them all — including dependabot/* branches that GitHub had locally. Those follow-on pushes ran without the configured credential helper in scope and failed with the basic-auth prompt error above. 2. No concurrency guard meant the back-to-back `main` push and `v*` tag push that auto-tag.yml fires within ~15s of each other raced. Two workflow runs would attempt to push to the same CNB remote simultaneously; one would lose. Rewrite: * Hand-rolled `git push` with the CNB token URL-encoded inline so the credential is always in scope. No Docker action dependency. * `concurrency: group: cnb-sync, cancel-in-progress: false` so queued runs serialize cleanly rather than racing or dropping. * Only push the ref that triggered the run — `main` on branch push (with --force-with-lease for safety), the specific tag on tag push. Feature branches and dependabot refs no longer mirror. * 3-attempt retry with linear backoff (5s, 10s) for transient failures (CNB rate-limit, DNS blips, etc.). * `workflow_dispatch: {}` trigger so the maintainer can re-run against a specific ref manually if the automated run fails. Adds docs/CNB_MIRROR.md with: the verification steps after a release, the manual fallback procedure (one-time `git remote add cnb`, then `git push cnb vX.Y.Z`), the token-rotation procedure, and a note on why binary release assets aren't on CNB today. Cross-links from docs/RELEASE_RUNBOOK.md so the verify-after-release step doesn't get forgotten.
105 lines
4.1 KiB
YAML
105 lines
4.1 KiB
YAML
name: Sync to CNB
|
|
|
|
# Mirror commits and release tags to cnb.cool/deepseek-tui.com/DeepSeek-TUI
|
|
# so users behind GitHub-blocking networks can fetch the source and tagged
|
|
# releases from the Tencent-hosted mirror.
|
|
#
|
|
# Triggers:
|
|
# * push to main → mirrors that commit to CNB main
|
|
# * tag matching v* → mirrors that tag to CNB
|
|
# * workflow_dispatch → manual fallback if either of the above fails
|
|
#
|
|
# Why the rewrite (v0.8.31):
|
|
# The previous implementation used the opaque tencentcom/git-sync Docker
|
|
# action, which discovered every local ref via fetch-depth: 0 and tried
|
|
# to push them all — including dependabot/* branches that GitHub had
|
|
# locally. Those follow-on pushes ran without the configured credential
|
|
# helper in scope and failed with
|
|
# `fatal: could not read Username for 'https://cnb.cool'`
|
|
# No concurrency block meant the main-push and tag-push workflow runs
|
|
# that auto-tag.yml fires within seconds of each other raced. About
|
|
# half of recent runs failed for those two reasons combined.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ['v*']
|
|
workflow_dispatch: {}
|
|
|
|
# Serialize runs so the back-to-back main-push + tag-push from auto-tag.yml
|
|
# don't race each other rebasing onto CNB. cancel-in-progress: false so
|
|
# every commit actually arrives — we'd rather queue than drop.
|
|
concurrency:
|
|
group: cnb-sync
|
|
cancel-in-progress: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
sync:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Push triggering ref to CNB
|
|
env:
|
|
CNB_TOKEN: ${{ secrets.CNB_GIT_TOKEN }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [ -z "${CNB_TOKEN:-}" ]; then
|
|
echo "::error::CNB_GIT_TOKEN secret is not set; cannot push to CNB." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# URL-encode any '%' in the token so basic-auth doesn't break on
|
|
# special characters. CNB tokens are typically alphanumeric so
|
|
# this is belt-and-suspenders.
|
|
ENCODED_TOKEN="$(printf '%s' "${CNB_TOKEN}" | python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read(), safe=""))')"
|
|
REMOTE_URL="https://cnb:${ENCODED_TOKEN}@cnb.cool/deepseek-tui.com/DeepSeek-TUI.git"
|
|
# Use a masked alias so the token never appears in log lines.
|
|
git remote add cnb "${REMOTE_URL}"
|
|
|
|
# Push with retry on transient failures (CNB rate-limits, DNS
|
|
# blips, etc.). Args after `kind` are forwarded to `git push`
|
|
# so callers can pass `--force-with-lease`, multiple refspecs,
|
|
# etc. without quoting them into one string.
|
|
push_with_retry() {
|
|
local kind="$1"
|
|
shift
|
|
local attempt
|
|
for attempt in 1 2 3; do
|
|
echo "Attempt ${attempt}: pushing ${kind} to CNB"
|
|
if git push cnb "$@" 2>&1; then
|
|
echo "Successfully pushed ${kind} to CNB"
|
|
return 0
|
|
fi
|
|
if [ "${attempt}" -lt 3 ]; then
|
|
sleep $((attempt * 5))
|
|
fi
|
|
done
|
|
echo "::error::Failed to push ${kind} to CNB after 3 attempts" >&2
|
|
return 1
|
|
}
|
|
|
|
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
|
TAG="${GITHUB_REF#refs/tags/}"
|
|
push_with_retry "tag ${TAG}" "refs/tags/${TAG}:refs/tags/${TAG}"
|
|
elif [[ "${GITHUB_REF}" == refs/heads/main ]]; then
|
|
# --force-with-lease so an unexpected diverged state on CNB
|
|
# surfaces as a failure (rather than silently overwriting).
|
|
# The mirror is one-way; if CNB diverges, that's a bug worth
|
|
# investigating manually before pushing again.
|
|
push_with_retry "main" HEAD:refs/heads/main --force-with-lease
|
|
else
|
|
# workflow_dispatch from a non-main branch — push that branch
|
|
# too, but never force. Useful for testing the mirror against
|
|
# a feature branch before merging.
|
|
BRANCH="${GITHUB_REF#refs/heads/}"
|
|
push_with_retry "branch ${BRANCH}" "HEAD:refs/heads/${BRANCH}"
|
|
fi
|