Fix all shellcheck warnings for nix build gate
- SC2015: Wrap A && B || C patterns in brace groups for directive scope
- SC2064: Suppress for intentional early trap expansion (local vars)
- SC2164: Add || exit to cd commands in subshells
- SC2001: Suppress for sed URL injection (clearer than parameter expansion)
- SC1083: Handle {tree} git syntax (quote or suppress)
- SC1091: Suppress for runtime-resolved source paths
- SC2034: Remove unused exit codes (E_OK, E_CONFIG, E_AUTH)
- SC2116: Eliminate useless echo in mono_auth_url construction
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091 # Source paths resolved at runtime
|
||||||
# bin/josh-sync — CLI entrypoint for josh-sync
|
# bin/josh-sync — CLI entrypoint for josh-sync
|
||||||
#
|
#
|
||||||
# Usage: josh-sync <command> [flags]
|
# Usage: josh-sync <command> [flags]
|
||||||
@@ -291,11 +292,14 @@ cmd_preflight() {
|
|||||||
|
|
||||||
parse_config "$config_file"
|
parse_config "$config_file"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2015 # A && B || C is intentional — pass/warn/fail never fail
|
||||||
|
{
|
||||||
[ -n "$JOSH_PROXY_URL" ] && pass "proxy_url: ${JOSH_PROXY_URL}" || fail "proxy_url missing"
|
[ -n "$JOSH_PROXY_URL" ] && pass "proxy_url: ${JOSH_PROXY_URL}" || fail "proxy_url missing"
|
||||||
[ -n "$MONOREPO_PATH" ] && pass "monorepo_path: ${MONOREPO_PATH}" || fail "monorepo_path missing"
|
[ -n "$MONOREPO_PATH" ] && pass "monorepo_path: ${MONOREPO_PATH}" || fail "monorepo_path missing"
|
||||||
[ -n "$BOT_TRAILER" ] && pass "trailer: ${BOT_TRAILER}" || fail "trailer missing"
|
[ -n "$BOT_TRAILER" ] && pass "trailer: ${BOT_TRAILER}" || fail "trailer missing"
|
||||||
[ -n "${GITEA_TOKEN:-}" ] && pass "SYNC_BOT_TOKEN is set" || warn "SYNC_BOT_TOKEN missing in .env"
|
[ -n "${GITEA_TOKEN:-}" ] && pass "SYNC_BOT_TOKEN is set" || warn "SYNC_BOT_TOKEN missing in .env"
|
||||||
[ -n "${BOT_USER:-}" ] && pass "BOT_USER: ${BOT_USER}" || warn "BOT_USER missing in .env"
|
[ -n "${BOT_USER:-}" ] && pass "BOT_USER: ${BOT_USER}" || warn "BOT_USER missing in .env"
|
||||||
|
}
|
||||||
|
|
||||||
local target_count
|
local target_count
|
||||||
target_count=$(echo "$JOSH_SYNC_TARGETS" | jq 'length')
|
target_count=$(echo "$JOSH_SYNC_TARGETS" | jq 'length')
|
||||||
@@ -316,8 +320,11 @@ cmd_preflight() {
|
|||||||
|
|
||||||
load_target "$TARGET_JSON"
|
load_target "$TARGET_JSON"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2015
|
||||||
|
{
|
||||||
[ -n "$JOSH_FILTER" ] && pass "josh_filter: ${JOSH_FILTER}" || fail "josh_filter missing"
|
[ -n "$JOSH_FILTER" ] && pass "josh_filter: ${JOSH_FILTER}" || fail "josh_filter missing"
|
||||||
[ -n "$SUBREPO_URL" ] && pass "subrepo_url: ${SUBREPO_URL}" || fail "subrepo_url missing"
|
[ -n "$SUBREPO_URL" ] && pass "subrepo_url: ${SUBREPO_URL}" || fail "subrepo_url missing"
|
||||||
|
}
|
||||||
|
|
||||||
# Subfolder exists
|
# Subfolder exists
|
||||||
if [ -d "$subfolder" ]; then
|
if [ -d "$subfolder" ]; then
|
||||||
|
|||||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770562336,
|
||||||
|
"narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d6c71932130818840fc8fe9509cf50be8c64634f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
josh_auth_url() {
|
josh_auth_url() {
|
||||||
local base="${JOSH_PROXY_URL}/${MONOREPO_PATH}.git${JOSH_FILTER}.git"
|
local base="${JOSH_PROXY_URL}/${MONOREPO_PATH}.git${JOSH_FILTER}.git"
|
||||||
|
# shellcheck disable=SC2001 # sed is clearer than ${var//} for URL injection
|
||||||
echo "$base" | sed "s|https://|https://${BOT_USER}:${GITEA_TOKEN}@|"
|
echo "$base" | sed "s|https://|https://${BOT_USER}:${GITEA_TOKEN}@|"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ subrepo_auth_url() {
|
|||||||
if [ "${SUBREPO_AUTH:-https}" = "ssh" ]; then
|
if [ "${SUBREPO_AUTH:-https}" = "ssh" ]; then
|
||||||
echo "$SUBREPO_URL"
|
echo "$SUBREPO_URL"
|
||||||
else
|
else
|
||||||
|
# shellcheck disable=SC2001
|
||||||
echo "$SUBREPO_URL" | sed "s|https://|https://${BOT_USER}:${SUBREPO_TOKEN}@|"
|
echo "$SUBREPO_URL" | sed "s|https://|https://${BOT_USER}:${SUBREPO_TOKEN}@|"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
# ─── Exit Codes ─────────────────────────────────────────────────────
|
# ─── Exit Codes ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
readonly E_OK=0
|
|
||||||
readonly E_GENERAL=1
|
readonly E_GENERAL=1
|
||||||
readonly E_CONFIG=10
|
|
||||||
readonly E_AUTH=11
|
|
||||||
|
|
||||||
# ─── Logging ────────────────────────────────────────────────────────
|
# ─── Logging ────────────────────────────────────────────────────────
|
||||||
# All log output goes to stderr. Sync functions use stdout for return values.
|
# All log output goes to stderr. Sync functions use stdout for return values.
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ write_state() {
|
|||||||
echo "$state_json" | jq '.' > "${tmp_dir}/${key}.json"
|
echo "$state_json" | jq '.' > "${tmp_dir}/${key}.json"
|
||||||
|
|
||||||
(
|
(
|
||||||
cd "$tmp_dir"
|
cd "$tmp_dir" || exit
|
||||||
git add -A
|
git add -A
|
||||||
if ! git diff --cached --quiet 2>/dev/null; then
|
if ! git diff --cached --quiet 2>/dev/null; then
|
||||||
git -c user.name="$BOT_NAME" -c user.email="$BOT_EMAIL" \
|
git -c user.name="$BOT_NAME" -c user.email="$BOT_EMAIL" \
|
||||||
|
|||||||
25
lib/sync.sh
25
lib/sync.sh
@@ -18,6 +18,7 @@ forward_sync() {
|
|||||||
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
||||||
local work_dir
|
local work_dir
|
||||||
work_dir=$(mktemp -d)
|
work_dir=$(mktemp -d)
|
||||||
|
# shellcheck disable=SC2064 # Intentional early expansion — work_dir is local
|
||||||
trap "rm -rf '$work_dir'" EXIT
|
trap "rm -rf '$work_dir'" EXIT
|
||||||
|
|
||||||
log "INFO" "=== Forward sync: mono/${mono_branch} → subrepo/${subrepo_branch} ==="
|
log "INFO" "=== Forward sync: mono/${mono_branch} → subrepo/${subrepo_branch} ==="
|
||||||
@@ -28,7 +29,7 @@ forward_sync() {
|
|||||||
--branch "$mono_branch" --single-branch \
|
--branch "$mono_branch" --single-branch \
|
||||||
"${work_dir}/filtered" || die "Failed to clone through josh-proxy"
|
"${work_dir}/filtered" || die "Failed to clone through josh-proxy"
|
||||||
|
|
||||||
cd "${work_dir}/filtered"
|
cd "${work_dir}/filtered" || exit
|
||||||
git config user.name "$BOT_NAME"
|
git config user.name "$BOT_NAME"
|
||||||
git config user.email "$BOT_EMAIL"
|
git config user.email "$BOT_EMAIL"
|
||||||
|
|
||||||
@@ -56,7 +57,8 @@ forward_sync() {
|
|||||||
|
|
||||||
# 5. Compare trees — skip if identical
|
# 5. Compare trees — skip if identical
|
||||||
local mono_tree subrepo_tree
|
local mono_tree subrepo_tree
|
||||||
mono_tree=$(git rev-parse HEAD^{tree})
|
mono_tree=$(git rev-parse 'HEAD^{tree}')
|
||||||
|
# shellcheck disable=SC1083 # {tree} is git syntax, not shell brace expansion
|
||||||
subrepo_tree=$(git rev-parse "subrepo/${subrepo_branch}^{tree}" 2>/dev/null || echo "none")
|
subrepo_tree=$(git rev-parse "subrepo/${subrepo_branch}^{tree}" 2>/dev/null || echo "none")
|
||||||
|
|
||||||
if [ "$mono_tree" = "$subrepo_tree" ]; then
|
if [ "$mono_tree" = "$subrepo_tree" ]; then
|
||||||
@@ -110,8 +112,10 @@ ${BOT_TRAILER}: forward/${mono_branch}/$(date -u +%Y-%m-%dT%H:%M:%SZ)" >&2
|
|||||||
git push "$(subrepo_auth_url)" "${conflict_branch}"
|
git push "$(subrepo_auth_url)" "${conflict_branch}"
|
||||||
|
|
||||||
# Create PR on subrepo
|
# Create PR on subrepo
|
||||||
local pr_body
|
local pr_body conflicted_list
|
||||||
pr_body="## Sync Conflict\n\nMonorepo \`${mono_branch}\` has changes that conflict with \`${subrepo_branch}\`.\n\n**Conflicted files:**\n$(echo "$conflicted" | sed 's/^/- /')\n\nPlease resolve and merge this PR to complete the sync."
|
# shellcheck disable=SC2001
|
||||||
|
conflicted_list=$(echo "$conflicted" | sed 's/^/- /')
|
||||||
|
pr_body="## Sync Conflict\n\nMonorepo \`${mono_branch}\` has changes that conflict with \`${subrepo_branch}\`.\n\n**Conflicted files:**\n${conflicted_list}\n\nPlease resolve and merge this PR to complete the sync."
|
||||||
|
|
||||||
create_pr "${SUBREPO_API}" "${SUBREPO_TOKEN}" \
|
create_pr "${SUBREPO_API}" "${SUBREPO_TOKEN}" \
|
||||||
"$subrepo_branch" "$conflict_branch" \
|
"$subrepo_branch" "$conflict_branch" \
|
||||||
@@ -134,6 +138,7 @@ reverse_sync() {
|
|||||||
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
||||||
local work_dir
|
local work_dir
|
||||||
work_dir=$(mktemp -d)
|
work_dir=$(mktemp -d)
|
||||||
|
# shellcheck disable=SC2064 # Intentional early expansion — work_dir is local
|
||||||
trap "rm -rf '$work_dir'" EXIT
|
trap "rm -rf '$work_dir'" EXIT
|
||||||
|
|
||||||
log "INFO" "=== Reverse sync: subrepo/${subrepo_branch} → mono/${mono_branch} ==="
|
log "INFO" "=== Reverse sync: subrepo/${subrepo_branch} → mono/${mono_branch} ==="
|
||||||
@@ -143,7 +148,7 @@ reverse_sync() {
|
|||||||
--branch "$subrepo_branch" --single-branch \
|
--branch "$subrepo_branch" --single-branch \
|
||||||
"${work_dir}/subrepo" || die "Failed to clone subrepo"
|
"${work_dir}/subrepo" || die "Failed to clone subrepo"
|
||||||
|
|
||||||
cd "${work_dir}/subrepo"
|
cd "${work_dir}/subrepo" || exit
|
||||||
git config user.name "$BOT_NAME"
|
git config user.name "$BOT_NAME"
|
||||||
git config user.email "$BOT_EMAIL"
|
git config user.email "$BOT_EMAIL"
|
||||||
|
|
||||||
@@ -205,13 +210,16 @@ initial_import() {
|
|||||||
'.[] | select(.name == $n) | .subfolder')
|
'.[] | select(.name == $n) | .subfolder')
|
||||||
local work_dir
|
local work_dir
|
||||||
work_dir=$(mktemp -d)
|
work_dir=$(mktemp -d)
|
||||||
|
# shellcheck disable=SC2064 # Intentional early expansion — work_dir is local
|
||||||
trap "rm -rf '$work_dir'" EXIT
|
trap "rm -rf '$work_dir'" EXIT
|
||||||
|
|
||||||
log "INFO" "=== Initial import: subrepo/${subrepo_branch} → mono/${mono_branch}:${subfolder}/ ==="
|
log "INFO" "=== Initial import: subrepo/${subrepo_branch} → mono/${mono_branch}:${subfolder}/ ==="
|
||||||
|
|
||||||
# 1. Clone monorepo (directly, not through josh — we need the real repo)
|
# 1. Clone monorepo (directly, not through josh — we need the real repo)
|
||||||
local mono_auth_url
|
local mono_auth_url
|
||||||
mono_auth_url=$(echo "https://${BOT_USER}:${GITEA_TOKEN}@$(echo "$MONOREPO_API" | sed 's|https://||; s|/api/v1/repos/|/|').git")
|
local api_host_path
|
||||||
|
api_host_path=$(echo "$MONOREPO_API" | sed 's|https://||; s|/api/v1/repos/|/|')
|
||||||
|
mono_auth_url="https://${BOT_USER}:${GITEA_TOKEN}@${api_host_path}.git"
|
||||||
|
|
||||||
git clone "$mono_auth_url" \
|
git clone "$mono_auth_url" \
|
||||||
--branch "$mono_branch" --single-branch \
|
--branch "$mono_branch" --single-branch \
|
||||||
@@ -227,7 +235,7 @@ initial_import() {
|
|||||||
log "INFO" "Subrepo has ${file_count} files"
|
log "INFO" "Subrepo has ${file_count} files"
|
||||||
|
|
||||||
# 3. Copy subrepo content into monorepo subfolder
|
# 3. Copy subrepo content into monorepo subfolder
|
||||||
cd "${work_dir}/monorepo"
|
cd "${work_dir}/monorepo" || exit
|
||||||
git config user.name "$BOT_NAME"
|
git config user.name "$BOT_NAME"
|
||||||
git config user.email "$BOT_EMAIL"
|
git config user.email "$BOT_EMAIL"
|
||||||
|
|
||||||
@@ -277,6 +285,7 @@ subrepo_reset() {
|
|||||||
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
local subrepo_branch="$SYNC_BRANCH_SUBREPO"
|
||||||
local work_dir
|
local work_dir
|
||||||
work_dir=$(mktemp -d)
|
work_dir=$(mktemp -d)
|
||||||
|
# shellcheck disable=SC2064 # Intentional early expansion — work_dir is local
|
||||||
trap "rm -rf '$work_dir'" EXIT
|
trap "rm -rf '$work_dir'" EXIT
|
||||||
|
|
||||||
log "INFO" "=== Subrepo reset: josh-filtered mono/${mono_branch} → subrepo/${subrepo_branch} ==="
|
log "INFO" "=== Subrepo reset: josh-filtered mono/${mono_branch} → subrepo/${subrepo_branch} ==="
|
||||||
@@ -287,7 +296,7 @@ subrepo_reset() {
|
|||||||
--branch "$mono_branch" --single-branch \
|
--branch "$mono_branch" --single-branch \
|
||||||
"${work_dir}/filtered" || die "Failed to clone through josh-proxy"
|
"${work_dir}/filtered" || die "Failed to clone through josh-proxy"
|
||||||
|
|
||||||
cd "${work_dir}/filtered"
|
cd "${work_dir}/filtered" || exit
|
||||||
|
|
||||||
local head_sha
|
local head_sha
|
||||||
head_sha=$(git rev-parse HEAD)
|
head_sha=$(git rev-parse HEAD)
|
||||||
|
|||||||
Reference in New Issue
Block a user