/ dev / publish_osf.sh-working
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