publish_osf.sh-working
1 #!/usr/bin/env bash 2 set -euo pipefail 3 4 # === Constants and Paths === 5 BASEDIR="$(pwd)" 6 OSF_YAML="$BASEDIR/osf.yaml" 7 GITFIELD_DIR="$BASEDIR/.gitfield" 8 mkdir -p "$GITFIELD_DIR" 9 10 SCAN_LOG_INIT="$GITFIELD_DIR/scan_log.json" 11 SCAN_LOG_PUSH="$GITFIELD_DIR/push_log.json" 12 TMP_JSON="$GITFIELD_DIR/tmp_project.json" 13 TOKEN_PATH="$HOME/.local/gitfieldlib/osf.token" 14 mkdir -p "$(dirname "$TOKEN_PATH")" 15 16 # === Dependency Check & Auto-Install === 17 require_yq() { 18 if ! command -v yq &>/dev/null || ! yq --version 2>/dev/null | grep -q 'version 4'; then 19 echo "⚠️ Correct 'yq' (Go version) not found. Installing from GitHub..." 20 YQ_BIN="/usr/local/bin/yq" 21 ARCH=$(uname -m) 22 case $ARCH in 23 x86_64) ARCH=amd64 ;; 24 aarch64) ARCH=arm64 ;; 25 *) echo "❌ Unsupported architecture: $ARCH" && exit 1 ;; 26 esac 27 YQ_VERSION="v4.43.1" 28 curl -Lo yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${ARCH}" \ 29 && chmod +x yq && sudo mv yq "$YQ_BIN" 30 echo "✅ 'yq' installed to $YQ_BIN" 31 fi 32 } 33 34 require_jq() { 35 if ! command -v jq &>/dev/null; then 36 echo "⚠️ 'jq' not found. Installing..." 37 sudo apt update && sudo apt install -y jq 38 echo "✅ 'jq' installed." 39 fi 40 } 41 42 require_yq 43 require_jq 44 45 # === Token Retrieval === 46 if [[ -z "${OSF_TOKEN:-}" ]]; then 47 if [[ -f "$TOKEN_PATH" ]]; then 48 OSF_TOKEN=$(<"$TOKEN_PATH") 49 else 50 echo -n "🔐 Enter your OSF_TOKEN (stored for future use): " 51 read -rs OSF_TOKEN 52 echo 53 echo "$OSF_TOKEN" > "$TOKEN_PATH" 54 chmod 600 "$TOKEN_PATH" 55 echo "📁 Token saved to $TOKEN_PATH" 56 fi 57 fi 58 59 # === INIT MODE === 60 init_mode() { 61 echo "🔍 Scanning project directory..." 62 63 mapfile -t ALL_FILES < <(find "$BASEDIR" -type f \( -name '*.md' -o -name '*.pdf' -o -name '*.tex' \) ! -path "*/.git/*" ! -path "*/.gitfield/*") 64 65 detect_file() { 66 local keywords=("$@") 67 for file in "${ALL_FILES[@]}"; do 68 for kw in "${keywords[@]}"; do 69 if [[ "${file,,}" == *"$kw"* ]]; then 70 echo "$file" 71 return 0 72 fi 73 done 74 done 75 } 76 77 WIKI_PATH=$(detect_file "wiki.md" "wiki") 78 README_PATH=$(detect_file "readme.md") 79 PAPER_PATH=$(detect_file "main.pdf" "theory.pdf" "paper.pdf") 80 81 ESSAYS=() 82 FILES=() 83 84 for f in "${ALL_FILES[@]}"; do 85 case "$f" in 86 "$WIKI_PATH"|"$README_PATH"|"$PAPER_PATH") continue ;; 87 *essays/*|*notes/*|*docs/*) ESSAYS+=("$f") ;; 88 *) FILES+=("$f") ;; 89 esac 90 done 91 92 echo "📝 Generating osf.yaml..." 93 94 { 95 echo "title: \"$(basename "$BASEDIR")\"" 96 echo "description: \"Auto-generated by GitField OSF publisher\"" 97 echo "category: \"project\"" 98 echo "public: false" 99 echo "tags: [gitfield, auto-generated]" 100 101 [[ -n "$WIKI_PATH" ]] && echo -e "\nwiki:\n path: \"${WIKI_PATH#$BASEDIR/}\"\n overwrite: true" 102 [[ -n "$README_PATH" ]] && echo -e "\nreadme:\n path: \"${README_PATH#$BASEDIR/}\"" 103 [[ -n "$PAPER_PATH" ]] && echo -e "\npaper:\n path: \"${PAPER_PATH#$BASEDIR/}\"\n name: \"$(basename "$PAPER_PATH")\"" 104 105 if ((${#ESSAYS[@]})); then 106 echo -e "\nessays:" 107 for essay in "${ESSAYS[@]}"; do 108 echo " - path: \"${essay#$BASEDIR/}\"" 109 echo " name: \"$(basename "$essay")\"" 110 done 111 fi 112 113 if ((${#FILES[@]})); then 114 echo -e "\nfiles:" 115 for file in "${FILES[@]}"; do 116 echo " - path: \"${file#$BASEDIR/}\"" 117 echo " name: \"$(basename "$file")\"" 118 done 119 fi 120 } > "$OSF_YAML" 121 122 jq -n \ 123 --argjson all "$(printf '%s\n' "${ALL_FILES[@]}" | jq -R . | jq -s .)" \ 124 --arg wiki "$WIKI_PATH" \ 125 --arg readme "$README_PATH" \ 126 --arg paper "$PAPER_PATH" \ 127 --argjson essays "$(printf '%s\n' "${ESSAYS[@]}" | jq -R . | jq -s .)" \ 128 --argjson files "$(printf '%s\n' "${FILES[@]}" | jq -R . | jq -s .)" \ 129 --arg osf_yaml "$OSF_YAML" \ 130 '{ 131 detected_files: $all, 132 classified: { 133 wiki: $wiki, 134 readme: $readme, 135 paper: $paper, 136 essays: $essays, 137 files: $files 138 }, 139 osf_yaml_path: $osf_yaml 140 }' > "$SCAN_LOG_INIT" 141 142 echo "✅ osf.yaml created at $OSF_YAML" 143 } 144 145 # === PUSH MODE === 146 push_mode() { 147 TITLE=$(yq e '.title' "$OSF_YAML") 148 DESCRIPTION=$(yq e '.description' "$OSF_YAML") 149 CATEGORY=$(yq e '.category' "$OSF_YAML") 150 PUBLIC=$(yq e '.public' "$OSF_YAML") 151 152 echo "🚀 Creating OSF project..." 153 154 RESPONSE=$(curl -s -w "%{http_code}" -o "$TMP_JSON" -X POST "https://api.osf.io/v2/nodes/" \ 155 -H "Authorization: Bearer $OSF_TOKEN" \ 156 -H "Content-Type: application/vnd.api+json" \ 157 -d @- <<EOF 158 { 159 "data": { 160 "type": "nodes", 161 "attributes": { 162 "title": "$TITLE", 163 "description": "$DESCRIPTION", 164 "category": "$CATEGORY", 165 "public": $PUBLIC 166 } 167 } 168 } 169 EOF 170 ) 171 172 STATUS="${RESPONSE: -3}" 173 if [[ "$STATUS" != "201" ]]; then 174 echo "❌ Project creation failed with status $STATUS" 175 echo "🧾 Response:" 176 cat "$TMP_JSON" 177 exit 1 178 fi 179 180 NODE_ID=$(jq -r '.data.id' "$TMP_JSON") 181 if [[ "$NODE_ID" == "null" || -z "$NODE_ID" ]]; then 182 echo "❌ No valid OSF project ID returned." 183 cat "$TMP_JSON" 184 exit 1 185 fi 186 187 echo "📡 Project created: $NODE_ID" 188 189 upload_file() { 190 local path="$1" 191 local name="$2" 192 echo "📁 Uploading $name from $path..." 193 UPLOAD_URL="https://files.osf.io/v1/resources/$NODE_ID/providers/osfstorage/?kind=file&name=$(basename "$name")" 194 curl -s -X PUT "$UPLOAD_URL" \ 195 -H "Authorization: Bearer $OSF_TOKEN" \ 196 -F "file=@$path" > /dev/null 197 } 198 199 upload_group() { 200 local section="$1" 201 local count 202 count=$(yq e ".${section} | length" "$OSF_YAML") 203 for ((i = 0; i < count; i++)); do 204 local path 205 path=$(yq e ".${section}[$i].path" "$OSF_YAML") 206 local name 207 name=$(yq e ".${section}[$i].name" "$OSF_YAML") 208 upload_file "$path" "$name" 209 done 210 } 211 212 [[ $(yq e '.readme.path' "$OSF_YAML") != "null" ]] && { 213 path=$(yq e '.readme.path' "$OSF_YAML") 214 upload_file "$path" "$(basename "$path")" 215 } 216 217 [[ $(yq e '.paper.path' "$OSF_YAML") != "null" ]] && { 218 path=$(yq e '.paper.path' "$OSF_YAML") 219 name=$(yq e '.paper.name' "$OSF_YAML") 220 upload_file "$path" "$name" 221 } 222 223 upload_group "files" 224 upload_group "essays" 225 226 if [[ $(yq e '.wiki.path' "$OSF_YAML") != "null" ]]; then 227 WIKI_PATH=$(yq e '.wiki.path' "$OSF_YAML") 228 echo "📜 Pushing wiki from $WIKI_PATH..." 229 CONTENT=$(jq -Rs . < "$WIKI_PATH") 230 curl -s -X PATCH "https://api.osf.io/v2/nodes/$NODE_ID/wikis/home/" \ 231 -H "Authorization: Bearer $OSF_TOKEN" \ 232 -H "Content-Type: application/vnd.api+json" \ 233 -d @- <<EOF > /dev/null 234 { 235 "data": { 236 "type": "wikis", 237 "attributes": { 238 "content": $CONTENT 239 } 240 } 241 } 242 EOF 243 fi 244 245 jq -n \ 246 --arg node_id "$NODE_ID" \ 247 --arg pushed_at "$(date -Iseconds)" \ 248 --arg token_path "$TOKEN_PATH" \ 249 '{ 250 project_id: $node_id, 251 pushed_at: $pushed_at, 252 token_used: $token_path 253 }' > "$SCAN_LOG_PUSH" 254 255 echo "✅ OSF Push Complete!" 256 echo "🌐 View project: https://osf.io/$NODE_ID/" 257 } 258 259 # === Dispatcher === 260 case "${1:-}" in 261 --init | init) init_mode ;; 262 --push | push) push_mode ;; 263 *) 264 echo "Usage: $0 [--init | --push]" 265 exit 1 266 ;; 267 esac