#!/bin/sh set -eu log() { printf '[bump-version] %s\n' "$*" } error() { printf '[bump-version] %s\n' "$*" >&2 } die() { error "${1:-fatal error}" exit "${2:-1}" } var_value() { # shellcheck disable=SC2039 eval "printf '%s' \"\${$1:-}\"" } normalize_host() { value="$1" if [ -z "${value}" ]; then printf '%s' "" return fi printf '%s' "${value}" | sed -E ' s#^[[:alpha:]][[:alnum:]+.-]*://##; s#^[^@]*@##; s#[:/].*$##; ' } extract_proto() { url="$1" if printf '%s' "${url}" | grep -Eq '^[[:alpha:]][[:alnum:]+.-]*://'; then printf '%s' "${url}" | sed -E 's#^([[:alpha:]][[:alnum:]+.-]*://).*#\1#' | tr -d ':/' return fi if printf '%s' "${url}" | grep -Eq '^[^@]+@[^:]+:'; then printf 'ssh' return fi printf 'https' } discover_env_credentials() { DEFAULT_GIT_USER="${GIT_AUTH_DEFAULT_USER:-woodpecker}" while IFS=: read -r user_var pass_var; do [ -n "${pass_var}" ] || continue pass="$(var_value "${pass_var}")" [ -n "${pass}" ] || continue if [ "${user_var}" = "-" ] || [ -z "${user_var}" ]; then user="" else user="$(var_value "${user_var}")" fi if [ -z "${user}" ]; then user="${DEFAULT_GIT_USER}" fi GIT_AUTH_USER="${user}" GIT_AUTH_PASS="${pass}" GIT_AUTH_SOURCE="env:${user_var:--}/${pass_var}" return 0 done <<'EOF' CI_GIT_USERNAME:CI_GIT_PASSWORD CI_GIT_USERNAME:CI_GIT_TOKEN CI_GIT_USERNAME:CI_GIT_PASS CI_GIT_USER:CI_GIT_PASSWORD CI_GIT_USER:CI_GIT_TOKEN CI_NETRC_USERNAME:CI_NETRC_PASSWORD CI_NETRC_LOGIN:CI_NETRC_PASSWORD WOODPECKER_GIT_USERNAME:WOODPECKER_GIT_PASSWORD WOODPECKER_GIT_USERNAME:WOODPECKER_GIT_TOKEN WOODPECKER_NETRC_USERNAME:WOODPECKER_NETRC_PASSWORD WOODPECKER_NETRC_LOGIN:WOODPECKER_NETRC_PASSWORD DRONE_GIT_USERNAME:DRONE_GIT_PASSWORD DRONE_GIT_USERNAME:DRONE_GIT_TOKEN DRONE_NETRC_USERNAME:DRONE_NETRC_PASSWORD DRONE_NETRC_LOGIN:DRONE_NETRC_PASSWORD GIT_USERNAME:GIT_PASSWORD GIT_USER:GIT_PASS GIT_AUTH_USERNAME:GIT_AUTH_PASSWORD -:GIT_PASSWORD -:GIT_PASS EOF for token_var in CI_GIT_TOKEN WOODPECKER_GIT_TOKEN DRONE_GIT_TOKEN GIT_TOKEN GITEA_TOKEN; do token="$(var_value "${token_var}")" if [ -n "${token}" ]; then user="$(var_value CI_GIT_USERNAME)" [ -n "${user}" ] || user="$(var_value WOODPECKER_GIT_USERNAME)" [ -n "${user}" ] || user="$(var_value DRONE_GIT_USERNAME)" [ -n "${user}" ] || user="${DEFAULT_GIT_USER}" GIT_AUTH_USER="${user}" GIT_AUTH_PASS="${token}" GIT_AUTH_SOURCE="env:${token_var}" return 0 fi done return 1 } discover_vault_credentials() { VAULT_MOUNT="${GIT_CREDENTIALS_VAULT_MOUNT:-kv}" VAULT_PATH="${GIT_CREDENTIALS_VAULT_PATH:-${CI_GIT_VAULT_PATH:-}}" [ -n "${VAULT_PATH}" ] || return 1 VLT_BIN="${GIT_VAULT_HELPER:-./ci/vlt}" if [ ! -x "${VLT_BIN}" ]; then return 1 fi user_field="${GIT_CREDENTIALS_VAULT_USER_FIELD:-username}" pass_field="${GIT_CREDENTIALS_VAULT_PASSWORD_FIELD:-password}" if ! GIT_AUTH_USER="$("${VLT_BIN}" kv_get "${VAULT_MOUNT}" "${VAULT_PATH}" "${user_field}" 2>/dev/null)"; then return 1 fi if ! GIT_AUTH_PASS="$("${VLT_BIN}" kv_get "${VAULT_MOUNT}" "${VAULT_PATH}" "${pass_field}" 2>/dev/null)"; then return 1 fi GIT_AUTH_SOURCE="vault:${VAULT_MOUNT}/${VAULT_PATH}" return 0 } write_netrc() { host="$1" user="$2" pass="$3" file="${HOME:-/root}/.netrc" { printf 'machine %s\n' "${host}" printf 'login %s\n' "${user}" printf 'password %s\n' "${pass}" } > "${file}" chmod 600 "${file}" } setup_https_credentials() { host="$1" netrc="${HOME:-/root}/.netrc" if [ -s "${netrc}" ] && awk -v host="${host}" ' $1 == "machine" && $2 == host { found=1; exit } END { exit found ? 0 : 1 } ' "${netrc}"; then log "reusing credentials already present in ${netrc}" return 0 fi if discover_env_credentials; then masked="$(printf '%s' "${GIT_AUTH_USER}" | cut -c1-2)***" log "using ${GIT_AUTH_SOURCE} for ${host} (user ${masked})" write_netrc "${host}" "${GIT_AUTH_USER}" "${GIT_AUTH_PASS}" return 0 fi if discover_vault_credentials; then masked="$(printf '%s' "${GIT_AUTH_USER}" | cut -c1-2)***" log "using ${GIT_AUTH_SOURCE} for ${host} (user ${masked})" write_netrc "${host}" "${GIT_AUTH_USER}" "${GIT_AUTH_PASS}" return 0 fi return 1 } START_DIR="$(pwd)" log "invoked from ${START_DIR}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="" if command -v git >/dev/null 2>&1; then REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)" fi if [ -z "${REPO_ROOT}" ]; then REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" fi log "repo root resolved to ${REPO_ROOT}" cd "${REPO_ROOT}" VERSION_FILE="./version" if [ ! -f "${VERSION_FILE}" ]; then if git cat-file -e "HEAD:version" 2>/dev/null; then error "version file missing in workspace, restoring from HEAD" git show "HEAD:version" > "${VERSION_FILE}" else die "version file not found: ${VERSION_FILE}" fi fi CURRENT_VERSION="$(cat "${VERSION_FILE}")" NEXT_VERSION="$(printf '%s' "${CURRENT_VERSION}" | awk -F. -v OFS=. ' function pad(value, width, result, i) { result=value "" if (length(result) >= width) { return result } i = width - length(result) while (i-- > 0) { result = "0" result } return result } NF==1 { print ++$NF; next } { last = $NF + 1 $NF = pad(last, length($NF)) print }')" printf '%s\n' "${NEXT_VERSION}" > "${VERSION_FILE}" log "${CURRENT_VERSION} -> ${NEXT_VERSION}" git add "${VERSION_FILE}" if git diff --cached --quiet; then log "no changes staged, skipping commit" exit 0 fi AUTHOR_NAME="${GIT_AUTHOR_NAME:-woodpecker}" AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-ci@sendico.io}" git config user.name "${AUTHOR_NAME}" git config user.email "${AUTHOR_EMAIL}" git commit -m "chore(ci): bump version to ${NEXT_VERSION}" BRANCH="${WOODPECKER_BRANCH:-}" if [ -z "${BRANCH}" ] || [ "${BRANCH}" = "HEAD" ]; then BRANCH="$(git rev-parse --abbrev-ref HEAD)" fi REMOTE_URL="${CI_REPO_REMOTE:-${WOODPECKER_GIT_REMOTE:-${DRONE_REMOTE_URL:-}}}" if [ -z "${REMOTE_URL}" ]; then REMOTE_URL="$(git config --get remote.origin.url 2>/dev/null || true)" fi [ -n "${REMOTE_URL}" ] || die "unable to determine git remote url" REMOTE_PROTO="$(extract_proto "${REMOTE_URL}")" REMOTE_HOST="$(normalize_host "${REMOTE_URL}")" [ -n "${REMOTE_HOST}" ] || die "unable to determine remote host from ${REMOTE_URL}" log "using remote ${REMOTE_URL}" git remote set-url origin "${REMOTE_URL}" case "${REMOTE_PROTO}" in ssh) if [ -n "${GIT_SSH_KEY_B64:-}" ]; then SSH_KEY_FILE="${HOME:-/root}/.ssh/id_ci" mkdir -p "$(dirname "${SSH_KEY_FILE}")" printf '%s' "${GIT_SSH_KEY_B64}" | base64 -d > "${SSH_KEY_FILE}" chmod 600 "${SSH_KEY_FILE}" export GIT_SSH_COMMAND="ssh -i ${SSH_KEY_FILE} -o StrictHostKeyChecking=no" log "configured SSH key for git push" else log "no SSH key provided, relying on existing agent configuration" fi ;; http|https) if ! setup_https_credentials "${REMOTE_HOST}"; then die "no git credentials detected (set CI_GIT_USERNAME/CI_GIT_PASSWORD, WOODPECKER_GIT_USERNAME/WOODPECKER_GIT_PASSWORD, or configure GIT_CREDENTIALS_VAULT_PATH)" fi ;; *) log "unknown git protocol ${REMOTE_PROTO}, attempting push without extra credentials" ;; esac git push origin "HEAD:${BRANCH}"