commit 6608a556f1e4e6240ae1bf8e6673ab60ecde2cc8 Author: Paul W. Date: Thu Jun 4 20:34:08 2026 +0000 chore: bootstrap shx-helper repo diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a252237 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.sh] +indent_size = 2 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..fb7ad83 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,23 @@ +name: ci + +on: + push: + branches: + - main + pull_request: + +jobs: + smoke: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: install deps + run: sudo apt-get update && sudo apt-get install -y jq + + - name: bash syntax + run: bash -n bin/shx-upload tests/smoke.sh + + - name: smoke test + run: ./tests/smoke.sh diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..4efd242 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,26 @@ +name: release + +on: + push: + tags: + - 'v*' + +jobs: + package: + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: stage artifact + run: | + mkdir -p dist + cp bin/shx-upload dist/shx-upload + chmod +x dist/shx-upload + tar -C dist -czf shx-helper-${GITHUB_REF_NAME}.tar.gz shx-upload + + - name: upload artifact + uses: actions/upload-artifact@v4 + with: + name: shx-helper-${{ github.ref_name }} + path: shx-helper-${{ github.ref_name }}.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7c9653 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# local env / secrets +.env +.env.* +*.local + +# editor / os +.DS_Store +Thumbs.db +.vscode/ +.idea/ +*.swp +*~ + +# test / temp +/tmp/ +artifacts/ +*.tmp +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..824dd08 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Paul W. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b54f99 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +.PHONY: test smoke lint + +test: smoke + +smoke: + ./tests/smoke.sh + +lint: + bash -n bin/shx-upload tests/smoke.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ecf23d --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# shx-helper + +tiny helper repo for uploads to the SHX Zipline instance. + +## what it includes + +- `bin/shx-upload` - upload one or more files to Zipline +- `.gitea/workflows/ci.yml` - basic CI for syntax + smoke tests +- `.gitea/workflows/release.yml` - tag build artifact packaging +- `tests/smoke.sh` - local smoke test with mocked network calls + +## requirements + +- `bash` +- `curl` +- `jq` for normal output mode + +## env + +loaded from `/root/.openclaw/.env` by default: + +- `SHX_URL=https://shx.reversed.dev` +- `SHX_TOKEN=...` ← required for uploads + +optional fallback: + +- `ZIPLINE_TOKEN=...` + +## usage + +```bash +/root/projects/shx-helper/bin/shx-upload ./file.gif +/root/projects/shx-helper/bin/shx-upload ./a.png ./b.mp4 +/root/projects/shx-helper/bin/shx-upload --json ./file.gif +/root/projects/shx-helper/bin/shx-upload --help +``` + +## local checks + +```bash +make lint +make test +``` + +## release flow + +push a tag like `v0.1.0` to trigger the release packaging workflow. + +## notes + +- username/password are for dashboard login only +- actual uploads use a Zipline API token +- docs: https://zipline.diced.sh/docs +- upload endpoint ref: https://v3.zipline.diced.sh/docs/api/upload +- shell uploader guide: https://zipline.diced.sh/docs/guides/uploaders/shell-script diff --git a/bin/shx-upload b/bin/shx-upload new file mode 100755 index 0000000..eff395c --- /dev/null +++ b/bin/shx-upload @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euo pipefail + +ENV_FILE="${SHX_ENV_FILE:-/root/.openclaw/.env}" +if [[ -f "$ENV_FILE" ]]; then + set -a + source "$ENV_FILE" + set +a +fi + +usage() { + cat <<'EOF' +Usage: + shx-upload [more files...] + shx-upload --json + +Env: + SHX_URL Base URL, e.g. https://shx.reversed.dev + SHX_TOKEN Zipline API token from the dashboard + +Notes: + - Username/password are only for dashboard login. + - Uploads use an API token. +EOF +} + +want_json=0 +if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then + usage + exit 0 +fi + +if [[ "${1:-}" == "--json" ]]; then + want_json=1 + shift +fi + +if [[ $# -lt 1 ]]; then + usage + exit 1 +fi + +: "${SHX_URL:=}" +: "${SHX_TOKEN:=${ZIPLINE_TOKEN:-}}" + +if [[ -z "$SHX_URL" ]]; then + echo "error: SHX_URL is not set" >&2 + exit 1 +fi + +if [[ -z "$SHX_TOKEN" ]]; then + cat >&2 <&2 + return 1 + fi + + local response + response="$(curl -fsS \ + -H "authorization: $SHX_TOKEN" \ + "$SHX_URL/api/upload" \ + -F "file=@$file")" + + if [[ $want_json -eq 1 ]]; then + printf '%s\n' "$response" + else + if ! command -v jq >/dev/null 2>&1; then + echo "error: jq is required unless --json is used" >&2 + return 1 + fi + printf '%s\n' "$response" | jq -r 'if .files then .files[] else . end' + fi +} + +status=0 +for file in "$@"; do + if ! upload_one "$file"; then + status=1 + fi +done + +exit $status diff --git a/tests/smoke.sh b/tests/smoke.sh new file mode 100755 index 0000000..785bff0 --- /dev/null +++ b/tests/smoke.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +workdir="$(mktemp -d)" +trap 'rm -rf "$workdir"' EXIT + +cat >"$workdir/.env" <<'EOF' +SHX_URL=https://shx.example +SHX_TOKEN=test-token +EOF + +mkdir -p "$workdir/bin" +cat >"$workdir/bin/curl" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail +printf '%s\n' '{"files":["https://shx.example/mock-file"]}' +EOF +chmod +x "$workdir/bin/curl" + +cat >"$workdir/bin/jq" <<'EOF' +#!/usr/bin/env python3 +import json, sys +payload = json.load(sys.stdin) +for item in payload.get("files", []): + print(item) +EOF +chmod +x "$workdir/bin/jq" + +touch "$workdir/demo.txt" + +PATH="$workdir/bin:$PATH" \ +SHX_ENV_FILE="$workdir/.env" \ +"$repo_root/bin/shx-upload" "$workdir/demo.txt" >"$workdir/out.txt" + +grep -Fx 'https://shx.example/mock-file' "$workdir/out.txt" + +echo 'smoke ok'