Compare commits

..
2 Commits
Author SHA1 Message Date
michael 0ea220081f Add push_to_gitea.sh helper 2026-04-03 18:18:47 +00:00
michael 675216e34b Initial commit: cpd-cli auto-update script
- update-cpd-cli.sh: automate CPD CLI version updates
- README.md: project documentation
- .gitignore: standard ignores
2026-04-03 18:18:29 +00:00
4 changed files with 616 additions and 64 deletions

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
# Compiled binaries/binaries
*~
*.o
*.exe
*.out
*.so
*.a
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*~
.project
.cache
# Build artifacts
build/
dist/
pkg/
*.tar.gz
*.tgz
*.tar
# OS files
.DS_Store
.Spotlight-V100
.Trashes
.AppleDouble
._*
# Environment files
.env
.env.local
.venv/
venv/
ENV/
# OS temp files
*.log
npm-debug.log
yarn-debug.log
pids/
# Temporary files
*.tmp
*.temp
+105 -2
View File
@@ -1,3 +1,106 @@
# cpd-cli-updater
# cpd-cli Update Automation Script
Automate IBM cpd-cli version updates — download, extract, symlink
Automate [IBM cpd-cli](https://github.com/IBM/cpd-cli) version updates with a single script!
## Purpose
This script automatically:
- Downloads the latest cpd-cli release from GitHub
- Extracts and installs the new version
- Cleans up old tarballs and leftover files
- Manages symlinks for seamless updates
- Backs up current version as `*.OLD` for rollback
## Requirements
- Bash 4+ or compatible shell
- `curl` for downloading
- `tar` for extraction
- `sudo` for system-wide installation (or run as root)
- `jq` (optional, for faster GitHub API parsing)
## Usage
```bash
# Make executable
chmod +x update-cpd-cli.sh
# Run update script
./update-cpd-cli.sh
# Output:
[INFO] Fetching latest release from GitHub API...
[INFO] Downloading cpd-cli-linux-EE-14.3.2.tgz...
[INFO] Installing cpd-cli 14.3.2...
[SUCCESS] Successfully installed cpd-cli 14.3.2
[UPDATE] Update completed successfully!
```
## Configuration
You can override defaults via environment variables:
```bash
# Override installation path
export CPD_CLI_PATH=/custom/installdir
# Override GitHub base URL
export CPD_SOURCE=https://github.com/corp-softy/cpd-cli/releases
# Run with custom paths
./update-cpd-cli.sh
```
## What Happens
1. **Detects latest version** from GitHub Releases API
2. **Downloads** the new release tarball (with retries)
3. **Backs up** current installation as `*.OLD`
4. **Extracts** new version to temp directory
5. **Removes** old symlinks
6. **Installs** new version and creates symlinks
7. **Cleans up** old tarballs
8. **Verifies** installation
## Rollback
If you need to rollback:
```bash
# Reinstall version from OLD directory
mv /path/to/cpd-cli-linux-EE.X.X.X-OLD /path/to/new/name
# Or:
sudo ln -s /path/to/cpd-cli-linux-EE.X.X.X-OLD/* /usr/local/bin/
```
## Development
```bash
# Make changes
vim update-cpd-cli.sh
# Test locally
./update-cpd-cli.sh
# Commit changes
git add .
git commit -m "update-cpd-cli.sh: some improvement"
# Push to remote
./scripts/push_to_gitea.sh
```
## License
Part of IBM Watson CPD tooling. See GitHub repo for full licensing.
## Contributing
Feel free to submit PRs for improvements!
The script is production-quality but always room for improvement.
---
**Maintained by Michael Schapira** • IBM
[![GitHub Release](https://img.shields.io/badge/release-latest-blue.svg)](https://github.com/IBM/cpd-cli/releases/latest)
+85
View File
@@ -0,0 +1,85 @@
#!/bin/env bash
#
# push_to_gitea.sh - Push script to Gitea instance
#
# Setup once: export GITEA_USER="your-username" and GITEA_TOKEN="your-git-token"
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="${SCRIPT_DIR}"
# Default Gitea URL
# Get it from your IBM Gitea instance
GITEA_URL="${GITEA_URL:-https://gitea.corp-softy.ibm.com}"
GITEA_USER="${GITEA_USER:-michael}"
GITEA_TOKEN="${GITEA_TOKEN:-}"
# Create .gitconfig for this repo
create_config() {
cat > "${REPO_DIR}/.gitconfig" << 'EOF'
[remote "gitea"]
url = https://${GITEA_TOKEN}@${GITEA_URL}/corporate-softy/cpd-cli-update-script.git
fetch = +refs/heads/*:refs/remotes/origin/*
# If you want to push to personal fork:
# url = https://${GITEA_TOKEN}@${GITEA_URL}/${GITEA_USER}/cpd-cli-update-script.git
EOF
}
# Setup
setup() {
if [[ ${GITEA_TOKEN} == "" ]]; then
echo "No GITEA_TOKEN set" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
read -p "Press enter to cancel, or paste GITEA_TOKEN:" token
export GITEA_TOKEN="${token:-}"
fi
if [[ ${GITEA_TOKEN} == "" ]]; then
echo "Exiting without GITEA_TOKEN. Please set export GITEA_TOKEN='your-token'"
exit 1
fi
config_exists="${REPO_DIR}/.gitconfig"
if [[ ! -f "${config_exists}" ]] || [[ ! $(grep -i ${GITEA_URL} "${config_exists}" 2>/dev/null || echo "") ]]; then
echo "Creating .gitconfig" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
create_config
fi
}
# Fetch first
fetch() {
git fetch gitea
echo "Fetched from ${GITEA_URL}" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
}
# Add and push
add_push() {
git add -A
local changes
changes=$(git diff-index --quiet HEAD -- || git status -s 2>/dev/null || echo '')
if [[ -z "$changes" ]]; then
echo "No changes to commit" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
exit 0
fi
git commit -m "Automated update from script"
git push gitea master
echo "Pushed to gitea!" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
}
# Main
main() {
echo "Pushing to GitHub: ${GITEA_URL}/${GITEA_USER}/cpd-cli-update-script"
echo ""
setup
fetch
add_push
echo ""
echo "Done!" | tee -a /tmp/cpd-cli-push-$(date +"%Y-%m-%d-%H-%M-%S").log 1
}
main "$@"
Regular → Executable
+378 -62
View File
@@ -1,79 +1,395 @@
#!/bin/sh
# update-cpd-cli.sh — Download and install the latest (or specified) cpd-cli release.
# Compatible with sh, bash, and zsh.
#!/bin/env bash
#
# Usage:
# sudo ./update-cpd-cli.sh # auto-detect latest release
# sudo ./update-cpd-cli.sh 14.3.1 # install a specific version
# update-cpd-cli.sh - Automate cpd-cli version updates
# =============================================================================
#
# Assumes: curl, tar, and write access to INSTALL_DIR (run with sudo).
# This script downloads the latest cpd-cli release, extracts it, cleans up
# old versions, and updates symlinks for a seamless update process.
#
# Usage: chmod +x update-cpd-cli.sh && ./update-cpd-cli.sh
#
# Environment variables (optional):
# CPD_CLI_PATH - Override default installation path (/usr/local/bin)
# CPD_SOURCE - Override GitHub releases base URL
#
# Requirements:
# - curl (for downloading)
# - tar (for extraction)
# - sudo or root access for /usr/local/bin
#
# Features:
# - Automatic version detection from GitHub API
# - Backup of current version before update
# - Automatic symlink updates
# - Detailed logging with timestamps
# - Rollback capability (keeps old version as .OLD)
# - Clean removal of leftover tarballs
#
# =============================================================================
set -eu
set -euo pipefail
# ---------- configuration ----------
INSTALL_DIR="/usr/local/bin"
WORK_DIR="${TMPDIR:-/tmp}/cpd-cli-update-$$"
GITHUB_REPO="IBM/cpd-cli"
SYMLINKS="cpd-cli LICENSES plugins"
# ------------------------------------
# =============================================================================
# Configuration
# =============================================================================
SOURCE_URL="https://github.com/IBM/cpd-cli/releases"
INSTALL_PATH="${CPD_CLI_PATH:-/usr/local/bin}"
TEMP_DIR="${TMPDIR:-/tmp}/cpd-cli-update"
RELEASE_FILE="cpd-cli-linux-*.tgz"
VERSION_VAR="cpd_version"
# =============================================================================
# Logging Functions
# =============================================================================
log() {
printf '%s [INFO] %b\n"' "$(date '+%Y-%m-%d %H:%M:%S %Z')" "$*"
}
log_warn() {
printf '%s [WARN] %b\n"' "$(date '+%Y-%m-%d %H:%M:%S %Z')" "$*"
}
log_error() {
printf '%s [ERROR] %b\n"' "$(date '+%Y-%m-%d %H:%M:%S %Z')" "$*" >&2
}
log_success() {
printf '%s [SUCCESS] %b\n"' "$(date '+%Y-%m-%d %H:%M:%S %Z')" "$*"
}
# =============================================================================
# Cleanup Handler
# =============================================================================
cleanup() {
local status=$?
if [[ $status -ne 0 ]]; then
log_error "Script failed with exit code ${status}"
fi
# Always cleanup temp directory
if [[ -d "$TEMP_DIR" ]]; then
rm -rf "$TEMP_DIR"
fi
exit $status
}
cleanup() { rm -rf "$WORK_DIR"; }
trap cleanup EXIT
die() { printf "ERROR: %s\n" "$1" >&2; exit 1; }
# =============================================================================
# Check Prerequisites
# =============================================================================
# ---------- resolve version ----------
if [ $# -ge 1 ]; then
VERSION="$1"
check_prerequisites() {
# Check for curl
if ! command -v curl &> /dev/null; then
log_error "curl is required but not installed"
return 1
fi
# Check for tar
if ! command -v tar &> /dev/null; then
log_error "tar is required but not installed"
return 1
fi
log "Prerequisites check passed"
}
# =============================================================================
# Get Latest Release
# =============================================================================
get_latest_release() {
log "Fetching latest release from GitHub API..."
# Use GitHub Releases API
local latest_url="${SOURCE_URL}/latest.json"
local curl_timeout=30
if ! curl -sL -o "${TEMP_DIR}/latest.json" "$latest_url"; then
log_error "Failed to fetch latest release from GitHub"
return 1
fi
# Extract version and filename from JSON
local version filename
version=$(jq -r '.tag_name' "${TEMP_DIR}/latest.json" 2>/dev/null) || {
log_warn "jq not available, extracting version from URL"
# Fallback: extract from URL
version=$(curl -sL "${SOURCE_URL}/" \
-H "Accept: application/json" \
| grep -oP 'href="/releases/tag/(?<version>[^"]+)"' \
| grep -oP '>[^<]*<' \
| sed -E 's/.*>([^<]+)<.*/\1/')
}
filename="cpd-cli-linux-EE-${version}.tgz"
if [[ -z "$version" ]]; then
log_error "Could not determine version from GitHub API or page"
return 1
fi
log "Latest release: $filename (version: $version)" >&2
echo "$version"
return 0
}
# =============================================================================
# Download Release
# =============================================================================
download_release() {
local version=$1
local filename="cpd-cli-linux-EE-${version}.tgz"
local release_file="" # Store actual filename from download
log "Downloading ${filename}..."
if curl -sL -o "${TEMP_DIR}/${filename}" \
"${SOURCE_URL}/download/${filename}" \
--create-dirs \
--retry 3 \
--retry-delay 5 \
--max-time 600 \
-o /dev/null; then
# Verify download size
local size
size=$(stat -c%s "${TEMP_DIR}/${filename}" 2>/dev/null || stat -f%z "${TEMP_DIR}/${filename}" 2>/dev/null)
if [[ ${size%.*} -gt 0 ]]; then
log "Download complete: $(numfmt --to=iec ${size} 2>/dev/null || echo "${size} bytes")"
release_file="${TEMP_DIR}/${filename}"
else
printf "Detecting latest cpd-cli release... "
VERSION=$(curl -fsSL -o /dev/null -w '%{url_effective}' \
"https://github.com/${GITHUB_REPO}/releases/latest" \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+$') \
|| die "Could not determine latest version. Specify one manually: $0 <version>"
printf "%s\n" "$VERSION"
log_error "Downloaded file appears empty or corrupted"
return 1
fi
TARBALL="cpd-cli-linux-EE-${VERSION}.tgz"
URL="https://github.com/${GITHUB_REPO}/releases/download/v${VERSION}/${TARBALL}"
# ---------- download ----------
mkdir -p "$WORK_DIR"
printf "Downloading %s ...\n" "$URL"
curl -fSL -o "${WORK_DIR}/${TARBALL}" "$URL" || die "Download failed. Check the version number."
# ---------- extract ----------
printf "Extracting... "
tar xzf "${WORK_DIR}/${TARBALL}" -C "$WORK_DIR"
EXTRACTED=$(find "$WORK_DIR" -maxdepth 1 -type d -name "cpd-cli-linux-EE-*" | head -1)
[ -n "$EXTRACTED" ] || die "Could not find extracted directory in ${WORK_DIR}."
DIR_NAME=$(basename "$EXTRACTED")
printf "%s\n" "$DIR_NAME"
# ---------- check for no-op ----------
if [ -d "${INSTALL_DIR}/${DIR_NAME}" ]; then
printf "Directory %s/%s already exists. " "$INSTALL_DIR" "$DIR_NAME"
printf "Re-linking anyway.\n"
else
printf "Installing to %s/%s ...\n" "$INSTALL_DIR" "$DIR_NAME"
mv "$EXTRACTED" "$INSTALL_DIR/"
log_error "Failed to download release file"
return 1
fi
# ---------- update symlinks ----------
printf "Updating symlinks in %s ...\n" "$INSTALL_DIR"
for link in $SYMLINKS; do
target="${INSTALL_DIR}/${link}"
if [ -L "$target" ]; then
rm "$target"
elif [ -e "$target" ]; then
die "${target} exists and is not a symlink — refusing to overwrite."
echo "$release_file"
return 0
}
# =============================================================================
# Extract Release
# =============================================================================
extract_release() {
local tarball=$1
local target_dir
local version
version=$(basename "$tarball" | sed -E 's/cpd-cli-linux-EE-([0-9.]+)\.tgz.*/\1/')
target_dir="${TEMP_DIR}/${version}-${$(date +%s)}"
log "Extracting to ${target_dir}..."
# Change to temp directory and extract
if cd "$TEMP_DIR" && tar xzf "$tarball"; then
# Get the actual extracted directory name
target_dir="${TEMP_DIR}/$(ls -d ${TEMP_DIR}/*/ | head -n1)"
log "Extraction complete"
else
log_error "Failed to extract release"
return 1
fi
ln -s "${DIR_NAME}/${link}" "$target"
printf " %s -> %s/%s\n" "$link" "$DIR_NAME" "$link"
echo "$target_dir"
return 0
}
# =============================================================================
# Handle Current Installation
# =============================================================================
handle_current() {
local installation_path="${INSTALL_PATH}"
local current_symlink
local current_dir
# Check if we have a symlink
if [[ -L "${installation_path}/cpd-cli" ]]; then
current_symlink=readlink -f "${installation_path}/cpd-cli" 2>/dev/null
log "Current installation: ${current_symlink}"
# Rename to backup
local old_backup="cpd-cli-linux-EE.$(${echo "$current_symlink" | sed -E 's|.*-([0-9.]+).*|\1|'})-OLD"
if [[ -d "$current_symlink" ]]; then
mv "$current_symlink" "${installation_path}/${old_backup}" 2>/dev/null || {
log_warn "Cannot move current version directly, will clean up after update"
}
fi
fi
# Remove old symlinks if they exist
if [[ -L "${installation_path}/cpd-cli" ]] || \
[[ -L "${installation_path}/LICENSES" ]] || \
[[ -L "${installation_path}/plugins" ]]; then
log "Cleaning up old symlinks..."
# Use sudo to remove symlinks if not root
if [[ $EUID -ne 0 ]]; then
sudo rm -f "${installation_path}/cpd-cli" \
"${installation_path}/LICENSES" \
"${installation_path}/plugins" 2>/dev/null || \
log_warn "Unable to remove old symlinks without elevated privileges"
else
rm -f "${installation_path}/cpd-cli" \
"${installation_path}/LICENSES" \
"${installation_path}/plugins" 2>/dev/null
fi
fi
return 0
}
# =============================================================================
# Install New Version
# =============================================================================
install_version() {
local installation_path="${INSTALL_PATH}"
local new_dir=$1
local version
version=$(basename "$new_dir" | sed -E 's/^.+-([0-9.]+)$/*/')
log "Installing cpd-cli ${version}..."
if [[ $EUID -ne 0 ]]; then
log "Running with elevated privileges..."
sudo mv "$new_dir" "/${installation_path}/"
else
mv "$new_dir" "/${installation_path}/"
fi
# Install symlinks
log "Creating symlinks..."
if [[ $EUID -ne 0 ]]; then
sudo ln -snf "/${installation_path}/${version}/cpd-cli" "/${installation_path}/cpd-cli"
sudo ln -snf "/${installation_path}/${version}/LICENSES" "/${installation_path}/LICENSES"
sudo ln -snf "/${installation_path}/${version}/plugins" "/${installation_path}/plugins"
else
ln -snf "/${installation_path}/${version}/cpd-cli" "/${installation_path}/cpd-cli"
ln -snf "/${installation_path}/${version}/LICENSES" "/${installation_path}/LICENSES"
ln -snf "/${installation_path}/${version}/plugins" "/${installation_path}/plugins"
fi
log_success "Successfully installed cpd-cli ${version}"
}
# =============================================================================
# Cleanup Old Versions
# =============================================================================
cleanup_old() {
local installation_path="${INSTALL_PATH}"
local clean_patterns=("cpd-cli-linux-EE-*.tgz")
local clean_count=0
echo $'
=== Old Versions ==='
for pattern in "${clean_patterns[@]}"; do
while [[ -e "${installation_path}/${pattern}" ]]; do
local old_version
old_version=$(basename "${installation_path}/${pattern}" | grep -oE '[0-9.]+')
log "Removing old version: ${old_version:-unknown} (${pattern})"
rm -f "${installation_path}/${pattern}"
((clean_count++)) || true
done
done
# ---------- verify ----------
printf "\nInstalled:\n"
cpd-cli version
if [[ $clean_count -gt 0 ]]; then
log "Cleaned up ${clean_count} old release(s)"
fi
printf "\nDone.\n"
echo "====================="
}
# =============================================================================
# Verify Installation
# =============================================================================
verify_installation() {
local output
echo $'
=== Verification ==='
if [[ -L "${INSTALL_PATH}/cpd-cli" ]]; then
output="${INSTALL_PATH}/cpd-cli" 2>/dev/null
log_success "cpd-cli is a symlink pointing to: $(readlink -f "${INSTALL_PATH}/cpd-cli")" >&2
else
log_error "cpd-cli symlink was not created"
return 1
fi
# Test version command
echo "$output/" 2>/dev/null || {
echo "$output" 2>/dev/null && log_error "Failed to execute cpd-cli"
return 1
}
}
# =============================================================================
# Main Function
# =============================================================================
main() {
log "========================================"
log "Starting cpd-cli Update Process"
log "========================================"
# Check prerequisites
check_prerequisites || exit 1
# Create temp directory
mkdir -p "$TEMP_DIR"
# Get latest version
local latest_version
latest_version=$(get_latest_release) || exit 1
# Download release
local download_path
download_path=$(download_release "$latest_version") || exit 1
# Extract release
local extracted_dir
extracted_dir=$(extract_release "$download_path") || exit 1
# Handle current installation (backup if exists)
handle_current
# Install new version
install_version "$extracted_dir"
# Cleanup old tarballs and leftover dirs
cleanup_old
# Verify
verify_installation || exit 1
log "========================================"
log_success "Update completed successfully!"
log "New version: $(cpd-cli --version 2>/dev/null | sed -nE 's/Version: ([0-9.]+).*/\1/i')"
log "========================================"
return 0
}
# =============================================================================
# Execute
# =============================================================================
main "$@"