8 Commits

Author SHA1 Message Date
Markus F.J. Busche
fd5ff20743 Add disclaimer 2025-09-20 19:31:50 +02:00
Markus F.J. Busche
87449641f6 chore: remove scripts/upload_asset_direct.sh (deprecated) 2025-09-20 19:29:49 +02:00
Markus F.J. Busche
731cd207dd chore: stage remaining changes 2025-09-20 18:29:54 +02:00
Markus F.J. Busche
a5489de212 docs: korrigiere Screenshot-Dateiname auf .png 2025-09-20 18:24:47 +02:00
Markus F.J. Busche
c0d4448dc1 docs: symlink docs/ -> octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs 2025-09-20 18:24:24 +02:00
Markus F.J. Busche
0d43e181c8 docs: Screenshot der Funnel-Einstellungen in README verlinkt 2025-09-20 18:18:37 +02:00
Markus F.J. Busche
f60068b01f release: update scripts/release_gitea.sh to jq-based flow; skip existing assets 2025-09-20 18:13:22 +02:00
Markus F.J. Busche
4d484c5023 Bump to v0.1.6.2 2025-09-20 18:07:27 +02:00
6 changed files with 42 additions and 72 deletions

1
docs Symbolic link
View File

@@ -0,0 +1 @@
octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs

View File

@@ -2,6 +2,8 @@
This plugin makes your OctoPrint instance accessible from anywhere via Tailscale Funnel, without needing to configure port forwarding, dynamic DNS, or complex firewall settings. This plugin makes your OctoPrint instance accessible from anywhere via Tailscale Funnel, without needing to configure port forwarding, dynamic DNS, or complex firewall settings.
Disclaimer: *This plugin was partially vibe-coded*.
## Features ## Features
* Enable/disable Tailscale Funnel access directly from OctoPrint's settings * Enable/disable Tailscale Funnel access directly from OctoPrint's settings
@@ -9,6 +11,10 @@ This plugin makes your OctoPrint instance accessible from anywhere via Tailscale
* Display the public URL for accessing OctoPrint remotely * Display the public URL for accessing OctoPrint remotely
* Configure the port to expose via Funnel * Configure the port to expose via Funnel
## Screenshot
![Tailscale Funnel Settings](octoprint_tailscale_funnel/octoprint_tailscale_funnel/docs/screenshots/funnel-settings.png)
## Requirements ## Requirements
* OctoPrint 1.3.0 or higher * OctoPrint 1.3.0 or higher

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -14,7 +14,7 @@ plugin_package = "octoprint_tailscale_funnel"
plugin_name = "OctoPrint-Tailscale-Funnel" plugin_name = "OctoPrint-Tailscale-Funnel"
# The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module
plugin_version = "0.1.6.1" plugin_version = "0.1.6.2"
# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module # module

View File

@@ -1,4 +1,4 @@
{ {
"version": "0.1.6.1" "version": "0.1.6.2"
} }

View File

@@ -44,24 +44,21 @@ if [ -z "$TOKEN" ]; then
echo "GITEA_API_TOKEN not set (in .env)." >&2; exit 1 echo "GITEA_API_TOKEN not set (in .env)." >&2; exit 1
fi fi
# Derive asset name early for later use
ASSET_NAME="$(basename "$ASSET_PATH")"
BODY="Tailscale Funnel Plugin ${TAG}\n\nAutomated release." BODY="Tailscale Funnel Plugin ${TAG}\n\nAutomated release."
if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then if [ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ]; then
BODY=$(cat "$BODY_FILE") BODY=$(cat "$BODY_FILE")
fi fi
# Build JSON payload via python (robust quoting) # Try to fetch existing release by tag first
create_payload=$(REL_TAG="$TAG" REL_NAME="$NAME" REL_BODY_TXT="$BODY" python3 - <<'PY' get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}" || true)
import json, os rel_id=$(echo "$get_resp" | jq -r '.id // empty')
payload = {
"tag_name": os.environ["REL_TAG"], if [ -z "$rel_id" ]; then
"name": os.environ["REL_NAME"], # Build minimal JSON payload (use existing tag)
"body": os.environ["REL_BODY_TXT"], create_payload=$(jq -n --arg tag "$TAG" --arg name "$NAME" --arg body "$BODY" '{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:false}')
"draft": False,
"prerelease": False
}
print(json.dumps(payload))
PY
)
create_resp=$(curl -sS -X POST \ create_resp=$(curl -sS -X POST \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
@@ -69,29 +66,14 @@ create_resp=$(curl -sS -X POST \
-d "$create_payload" \ -d "$create_payload" \
"${API_URL}/repos/${OWNER}/${REPO}/releases" || true) "${API_URL}/repos/${OWNER}/${REPO}/releases" || true)
# Extract id; if missing, fetch by tag # Extract id from create response
rel_id=$(python3 - <<'PY' rel_id=$(echo "$create_resp" | jq -r '.id // empty')
import sys, json fi
data=sys.stdin.read().strip()
if not data:
print("")
else:
try:
obj=json.loads(data)
print(obj.get('id',''))
except Exception:
print("")
PY
<<<"$create_resp")
# Fallback: search releases list for matching tag if still empty
if [ -z "$rel_id" ]; then if [ -z "$rel_id" ]; then
get_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/tags/${TAG}") list_resp=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases?limit=100")
rel_id=$(python3 - <<'PY' rel_id=$(echo "$list_resp" | jq -r --arg tag "$TAG" '[.[] | select(.tag_name==$tag)][0].id // empty')
import sys, json
obj=json.loads(sys.stdin.read())
print(obj.get('id',''))
PY
<<<"$get_resp")
fi fi
if [ -z "$rel_id" ]; then if [ -z "$rel_id" ]; then
@@ -100,37 +82,18 @@ if [ -z "$rel_id" ]; then
exit 1 exit 1
fi fi
# Delete existing asset with same name (if any) # Check if asset exists (skip upload if present)
assets_json=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets") assets_json=$(curl -sS -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets")
asset_id=$(python3 - <<'PY' asset_exists=$(echo "$assets_json" | jq -r --arg name "$ASSET_NAME" 'any(.[]; .name==$name)')
import sys, json, os
name=os.environ['ASSET_NAME']
try:
arr=json.loads(sys.stdin.read())
for a in arr:
if a.get('name')==name:
print(a.get('id',''))
break
except Exception:
pass
PY
ASSET_NAME="$(basename "$ASSET_PATH")" <<<"$assets_json")
if [ -n "$asset_id" ]; then if [ "$asset_exists" = "true" ]; then
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets/${asset_id}" >/dev/null || true echo "Asset already exists, skipping upload: ${ASSET_NAME}"
else
echo "Uploading asset: ${ASSET_NAME}"
upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=${ASSET_NAME}")
html_url=$(echo "$upload_resp" | jq -r '.browser_download_url // empty')
echo "Asset uploaded: ${html_url}"
fi fi
# Upload asset echo "Release ${TAG} ready (id=${rel_id})."
upload_resp=$(curl -sS -H "Authorization: token ${TOKEN}" -F attachment=@"${ASSET_PATH}" "${API_URL}/repos/${OWNER}/${REPO}/releases/${rel_id}/assets?name=$(basename "$ASSET_PATH")")
html_url=$(python3 - <<'PY'
import sys, json
try:
print(json.loads(sys.stdin.read()).get('browser_download_url',''))
except Exception:
print("")
PY
<<<"$upload_resp")
echo "Release ${TAG} created (id=${rel_id}). Asset uploaded: ${html_url}"