mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
feat: add environment-driven publish scripts for single-branch workflow
Add generic publish pipeline that supports both internal and external publishing via environment variables, without leaking any internal URLs or tool references into the public repository.
This commit is contained in:
parent
9a4c966b6d
commit
ce9511eed6
5 changed files with 384 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,9 +9,10 @@ dist/
|
|||
CLAUDE.md
|
||||
pages/package-lock.json
|
||||
scripts/hooks/
|
||||
scripts/publish/.env.internal
|
||||
CHANGELOG.md
|
||||
|
||||
verdor/
|
||||
vendor/
|
||||
temp/
|
||||
|
||||
# Ignore downloaded Go binary, but keep JS entry point
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Makefile
|
|||
go.mod
|
||||
go.sum
|
||||
scripts/hooks/
|
||||
scripts/publish/
|
||||
.claude/
|
||||
.git/
|
||||
.idea/
|
||||
|
|
|
|||
20
scripts/publish/.env.example
Normal file
20
scripts/publish/.env.example
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Copy to .env.internal (gitignored) and fill in actual values:
|
||||
# cp scripts/publish/.env.example scripts/publish/.env.internal
|
||||
#
|
||||
# Then publish:
|
||||
# source scripts/publish/.env.internal && ./scripts/publish/publish.sh
|
||||
|
||||
# Override package name
|
||||
OCR_PKG_NAME=
|
||||
|
||||
# npm registry URL
|
||||
OCR_PUBLISH_REGISTRY=
|
||||
|
||||
# Binary download URL template (supports {version} {os} {arch} placeholders)
|
||||
OCR_URL_PATTERN=
|
||||
|
||||
# Checksum file URL template
|
||||
OCR_CHECKSUM_PATTERN=
|
||||
|
||||
# [Optional] Git repo for binary uploads (set to enable upload step)
|
||||
OCR_RELEASE_GIT_REPO=
|
||||
64
scripts/publish/_common.sh
Executable file
64
scripts/publish/_common.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env bash
|
||||
# _common.sh — Shared utilities for the publish pipeline.
|
||||
# Sourced by other scripts, not executed directly.
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────────────
|
||||
if [ -t 1 ]; then
|
||||
RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m' BOLD='\033[1m' RESET='\033[0m'
|
||||
else
|
||||
RED='' GREEN='' YELLOW='' BLUE='' BOLD='' RESET=''
|
||||
fi
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────────────
|
||||
info() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
||||
success() { echo -e "${GREEN}[OK]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
||||
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
die() { error "$*"; exit 1; }
|
||||
|
||||
# ── Path resolution ──────────────────────────────────────────────────────────
|
||||
resolve_project_root() {
|
||||
local script_dir
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
(cd "$script_dir/../.." && pwd)
|
||||
}
|
||||
|
||||
# ── Version helpers ──────────────────────────────────────────────────────────
|
||||
normalize_version() {
|
||||
local v="$1"
|
||||
[[ "$v" != v* ]] && v="v${v}"
|
||||
echo "$v"
|
||||
}
|
||||
|
||||
npm_version_from_tag() {
|
||||
echo "${1#v}"
|
||||
}
|
||||
|
||||
# ── Confirmation prompt ──────────────────────────────────────────────────────
|
||||
confirm() {
|
||||
local message="${1:-Proceed?}"
|
||||
if [ "${OCR_FORCE_YES:-0}" = "1" ]; then
|
||||
return 0
|
||||
fi
|
||||
printf "\n%b%s (y/n): ${RESET}" "$BOLD" "$message"
|
||||
read -r answer
|
||||
case "$answer" in
|
||||
[yY]|[yY][eE][sS]) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── Step execution with timing ───────────────────────────────────────────────
|
||||
run_step() {
|
||||
local step_name="$1"
|
||||
local step_cmd="$2"
|
||||
info "--- ${step_name} ---"
|
||||
local start_time
|
||||
start_time=$(date +%s)
|
||||
eval "$step_cmd" || die "Step '${step_name}' failed."
|
||||
local elapsed
|
||||
elapsed=$(( $(date +%s) - start_time ))
|
||||
success "${step_name} completed (${elapsed}s)"
|
||||
echo ""
|
||||
}
|
||||
297
scripts/publish/publish.sh
Executable file
297
scripts/publish/publish.sh
Executable file
|
|
@ -0,0 +1,297 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# publish.sh — Environment-driven publish pipeline.
|
||||
#
|
||||
# Builds binaries, optionally uploads to a git-based release repo,
|
||||
# patches package.json with override values, and publishes to npm.
|
||||
#
|
||||
# Usage:
|
||||
# source scripts/publish/.env.internal && ./scripts/publish/publish.sh
|
||||
#
|
||||
# Environment variables (all optional — defaults come from package.json):
|
||||
# OCR_PKG_NAME Override package name
|
||||
# OCR_PUBLISH_REGISTRY Override npm registry URL
|
||||
# OCR_URL_PATTERN Override ocrConfig.urlPattern
|
||||
# OCR_CHECKSUM_PATTERN Override ocrConfig.checksumPattern
|
||||
# OCR_RELEASE_GIT_REPO Git repo URL for binary uploads (skip if unset)
|
||||
# OCR_VERSION_OVERRIDE Use this version instead of latest git tag
|
||||
# OCR_FORCE_YES Skip confirmation prompts (CI mode)
|
||||
# OCR_SKIP_BUILD Skip build step (use existing dist/)
|
||||
# OCR_SKIP_UPLOAD Skip git repo upload step
|
||||
# OCR_SKIP_PUBLISH Skip npm publish step
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
PROJECT_ROOT="$(resolve_project_root)"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# ── Configuration ─────────────────────────────────────────────────────────────
|
||||
SKIP_BUILD="${OCR_SKIP_BUILD:-0}"
|
||||
SKIP_UPLOAD="${OCR_SKIP_UPLOAD:-0}"
|
||||
SKIP_PUBLISH="${OCR_SKIP_PUBLISH:-0}"
|
||||
|
||||
# ── Banner ────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${BOLD}============================================${RESET}"
|
||||
echo -e "${BOLD} OpenCodeReview — Publish Pipeline ${RESET}"
|
||||
echo -e "${BOLD}============================================${RESET}"
|
||||
echo ""
|
||||
|
||||
# ── Resolve version from git tag ─────────────────────────────────────────────
|
||||
VERSION_TAG="${OCR_VERSION_OVERRIDE:-}"
|
||||
if [ -z "$VERSION_TAG" ]; then
|
||||
VERSION_TAG=$(git describe --tags --abbrev=0 2>/dev/null || die "No git tag found. Create one: git tag vX.Y.Z && git push origin --tags")
|
||||
fi
|
||||
VERSION_TAG="$(normalize_version "$VERSION_TAG")"
|
||||
NPM_VERSION="$(npm_version_from_tag "$VERSION_TAG")"
|
||||
export VERSION_TAG NPM_VERSION
|
||||
|
||||
info "Release plan:"
|
||||
info " Git tag: ${VERSION_TAG}"
|
||||
info " npm version: ${NPM_VERSION}"
|
||||
[ -n "${OCR_PKG_NAME:-}" ] && info " Package: ${OCR_PKG_NAME}"
|
||||
[ -n "${OCR_PUBLISH_REGISTRY:-}" ] && info " Registry: ${OCR_PUBLISH_REGISTRY}"
|
||||
[ -n "${OCR_RELEASE_GIT_REPO:-}" ] && info " Upload repo: ${OCR_RELEASE_GIT_REPO}"
|
||||
echo ""
|
||||
|
||||
# ── Pre-flight checks ────────────────────────────────────────────────────────
|
||||
preflight() {
|
||||
local missing=0
|
||||
info "Checking prerequisites..."
|
||||
for cmd in go jq npm git shasum; do
|
||||
if command -v "$cmd" >/dev/null 2>&1; then
|
||||
info " ${cmd}: found"
|
||||
else
|
||||
error " ${cmd}: NOT FOUND"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
[ "$missing" -eq 0 ] || die "Missing required tools."
|
||||
success "All tools available"
|
||||
echo ""
|
||||
|
||||
local status
|
||||
status=$(git status --porcelain 2>/dev/null || true)
|
||||
if [ -n "$status" ]; then
|
||||
warn "Working tree has uncommitted changes:"
|
||||
echo "$status"
|
||||
die "Please commit or stash changes before publishing."
|
||||
fi
|
||||
success "Git working tree is clean"
|
||||
}
|
||||
|
||||
run_step "Pre-flight checks" "preflight"
|
||||
|
||||
# ── Confirm ───────────────────────────────────────────────────────────────────
|
||||
if ! confirm "Ready to publish ${VERSION_TAG}. Continue?"; then
|
||||
info "Aborted by user."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Build ─────────────────────────────────────────────────────────────────────
|
||||
if [ "$SKIP_BUILD" = "1" ]; then
|
||||
warn "Skipping build (OCR_SKIP_BUILD=1)"
|
||||
else
|
||||
run_step "Building binaries (make dist)" \
|
||||
"make -C \"$PROJECT_ROOT\" dist"
|
||||
fi
|
||||
|
||||
# ── Sync package.json version ─────────────────────────────────────────────────
|
||||
sync_version() {
|
||||
local pkg="$PROJECT_ROOT/package.json"
|
||||
local current
|
||||
current=$(jq -r '.version' "$pkg")
|
||||
if [ "$NPM_VERSION" = "$current" ]; then
|
||||
info "package.json version already matches ${NPM_VERSION}."
|
||||
else
|
||||
info "Updating package.json version: ${current} → ${NPM_VERSION}"
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
jq --arg v "$NPM_VERSION" '.version = $v' "$pkg" > "$tmp"
|
||||
mv "$tmp" "$pkg"
|
||||
success "package.json version updated"
|
||||
fi
|
||||
}
|
||||
|
||||
run_step "Syncing version" "sync_version"
|
||||
|
||||
# ── Upload to git-based release repo (optional) ──────────────────────────────
|
||||
upload_to_git_repo() {
|
||||
local repo_url="$OCR_RELEASE_GIT_REPO"
|
||||
local dist_dir="$PROJECT_ROOT/dist"
|
||||
local tmp_repo
|
||||
tmp_repo=$(mktemp -d -t "ocr-release-repo.XXXXXX")
|
||||
|
||||
info "Cloning release repo (sparse)..."
|
||||
git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_repo"
|
||||
(cd "$tmp_repo" && git sparse-checkout set --no-cone VERSION)
|
||||
success "Clone ready"
|
||||
|
||||
local bin_dir="${tmp_repo}/bin/${VERSION_TAG}"
|
||||
mkdir -p "$bin_dir"
|
||||
|
||||
local count=0
|
||||
for f in "$dist_dir"/opencodereview-${VERSION_TAG}-*; do
|
||||
[ -f "$f" ] || continue
|
||||
local base target
|
||||
base=$(basename "$f")
|
||||
target="${base/opencodereview-${VERSION_TAG}-/opencodereview-}"
|
||||
info " Copying ${target}"
|
||||
cp "$f" "$bin_dir/${target}"
|
||||
count=$((count + 1))
|
||||
done
|
||||
if [ "$count" -eq 0 ]; then
|
||||
rm -rf "$tmp_repo"
|
||||
die "No binaries found matching opencodereview-${VERSION_TAG}-*"
|
||||
fi
|
||||
success "Copied ${count} binaries"
|
||||
|
||||
(cd "$bin_dir" && shasum -a 256 opencodereview-* | sort > sha256sum.txt)
|
||||
success "Checksums generated"
|
||||
|
||||
local version_file="${tmp_repo}/VERSION"
|
||||
if [ -f "$version_file" ]; then
|
||||
local last
|
||||
last=$(tail -1 "$version_file" 2>/dev/null || true)
|
||||
[ "$last" = "$VERSION_TAG" ] || printf "%s\n" "$VERSION_TAG" >> "$version_file"
|
||||
else
|
||||
printf "%s\n" "$VERSION_TAG" > "$version_file"
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$tmp_repo"
|
||||
git add --sparse -A
|
||||
if ! git diff --cached --quiet; then
|
||||
git config user.name "open-code-review-bot"
|
||||
git config user.email "bot@open-code-review.local"
|
||||
git commit -m "release: ${VERSION_TAG}"
|
||||
git push
|
||||
success "Pushed to release repo"
|
||||
else
|
||||
info "No changes to push — binaries already exist."
|
||||
fi
|
||||
)
|
||||
|
||||
rm -rf "$tmp_repo"
|
||||
}
|
||||
|
||||
if [ "$SKIP_UPLOAD" = "1" ]; then
|
||||
warn "Skipping upload (OCR_SKIP_UPLOAD=1)"
|
||||
elif [ -n "${OCR_RELEASE_GIT_REPO:-}" ]; then
|
||||
run_step "Uploading to release repo" "upload_to_git_repo"
|
||||
else
|
||||
info "No OCR_RELEASE_GIT_REPO set, skipping binary upload."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Patch package.json for publish ────────────────────────────────────────────
|
||||
PACKAGE_PATCHED=0
|
||||
PACKAGE_BACKUP=""
|
||||
|
||||
patch_package_json() {
|
||||
local pkg="$PROJECT_ROOT/package.json"
|
||||
|
||||
local changed=0
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
cp "$pkg" "$tmp"
|
||||
|
||||
if [ -n "${OCR_PKG_NAME:-}" ]; then
|
||||
jq --arg n "$OCR_PKG_NAME" '.name = $n' "$tmp" > "${tmp}.new" && mv "${tmp}.new" "$tmp"
|
||||
info " name → ${OCR_PKG_NAME}"
|
||||
changed=1
|
||||
fi
|
||||
|
||||
if [ -n "${OCR_PUBLISH_REGISTRY:-}" ]; then
|
||||
jq --arg r "$OCR_PUBLISH_REGISTRY" '.publishConfig.registry = $r | .publishConfig.access = "public"' "$tmp" > "${tmp}.new" && mv "${tmp}.new" "$tmp"
|
||||
info " publishConfig.registry → ${OCR_PUBLISH_REGISTRY}"
|
||||
changed=1
|
||||
fi
|
||||
|
||||
if [ -n "${OCR_URL_PATTERN:-}" ]; then
|
||||
jq --arg u "$OCR_URL_PATTERN" '.ocrConfig.urlPattern = $u' "$tmp" > "${tmp}.new" && mv "${tmp}.new" "$tmp"
|
||||
info " ocrConfig.urlPattern → ${OCR_URL_PATTERN}"
|
||||
changed=1
|
||||
fi
|
||||
|
||||
if [ -n "${OCR_CHECKSUM_PATTERN:-}" ]; then
|
||||
jq --arg c "$OCR_CHECKSUM_PATTERN" '.ocrConfig.checksumPattern = $c' "$tmp" > "${tmp}.new" && mv "${tmp}.new" "$tmp"
|
||||
info " ocrConfig.checksumPattern → ${OCR_CHECKSUM_PATTERN}"
|
||||
changed=1
|
||||
fi
|
||||
|
||||
if [ "$changed" -eq 1 ]; then
|
||||
PACKAGE_BACKUP=$(mktemp)
|
||||
cp "$pkg" "$PACKAGE_BACKUP"
|
||||
mv "$tmp" "$pkg"
|
||||
PACKAGE_PATCHED=1
|
||||
success "package.json patched for publish"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
info "No overrides configured, publishing with default package.json"
|
||||
fi
|
||||
}
|
||||
|
||||
run_step "Patching package.json" "patch_package_json"
|
||||
|
||||
# ── Publish ───────────────────────────────────────────────────────────────────
|
||||
do_publish() {
|
||||
local registry_args=()
|
||||
if [ -n "${OCR_PUBLISH_REGISTRY:-}" ]; then
|
||||
registry_args=(--registry "$OCR_PUBLISH_REGISTRY")
|
||||
fi
|
||||
|
||||
local pkg_name
|
||||
pkg_name=$(jq -r '.name' "$PROJECT_ROOT/package.json")
|
||||
local already
|
||||
already=$(npm view "${pkg_name}@${NPM_VERSION}" version ${registry_args[@]+"${registry_args[@]}"} 2>/dev/null || true)
|
||||
if [ "$already" = "$NPM_VERSION" ]; then
|
||||
warn "Version ${NPM_VERSION} already published!"
|
||||
if confirm "Skip publish and continue?"; then
|
||||
return 0
|
||||
else
|
||||
die "Aborted."
|
||||
fi
|
||||
fi
|
||||
|
||||
info "Publishing ${pkg_name}@${NPM_VERSION} ..."
|
||||
npm publish ${registry_args[@]+"${registry_args[@]}"}
|
||||
success "Published ${pkg_name}@${NPM_VERSION}"
|
||||
}
|
||||
|
||||
if [ "$SKIP_PUBLISH" = "1" ]; then
|
||||
warn "Skipping publish (OCR_SKIP_PUBLISH=1)"
|
||||
else
|
||||
run_step "Publishing to registry" "do_publish"
|
||||
fi
|
||||
|
||||
# ── Restore package.json ─────────────────────────────────────────────────────
|
||||
if [ "$PACKAGE_PATCHED" -eq 1 ] && [ -n "$PACKAGE_BACKUP" ]; then
|
||||
cp "$PACKAGE_BACKUP" "$PROJECT_ROOT/package.json"
|
||||
rm -f "$PACKAGE_BACKUP"
|
||||
success "package.json restored (version bump preserved)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Commit version bump (if version was synced) ──────────────────────────────
|
||||
git add package.json 2>/dev/null || true
|
||||
if ! git diff --cached --quiet 2>/dev/null; then
|
||||
git commit -m "chore(release): bump version to ${VERSION_TAG}"
|
||||
success "Committed version bump"
|
||||
else
|
||||
info "No version changes to commit."
|
||||
fi
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD}============================================${RESET}"
|
||||
echo -e "${GREEN}${BOLD} PUBLISH COMPLETE ${RESET}"
|
||||
echo -e "${GREEN}${BOLD}============================================${RESET}"
|
||||
echo ""
|
||||
echo -e " Version: ${BOLD}${VERSION_TAG}${RESET}"
|
||||
[ -n "${OCR_PKG_NAME:-}" ] && echo -e " Package: ${OCR_PKG_NAME}" || echo -e " Package: $(jq -r '.name' "$PROJECT_ROOT/package.json")"
|
||||
echo ""
|
||||
Loading…
Add table
Add a link
Reference in a new issue