Compare commits

..
2 Commits
Author SHA1 Message Date
cinco b05c188477 🐛 fix: consolidate publish-github into single shell block
Multiple command blocks caused $RELEASE_ID to be lost between shells.
Using ${ARCH} with braces triggered Drone variable substitution before
the shell ran, resulting in an empty arch name. Fix: single | block
with all logic, iterate over full filenames (same pattern as publish),
and use ENVIRON["DRONE_TAG"] in awk to bypass Drone substitution.
2026-03-01 04:56:36 -03:00
cinco 75d66cd47c feat: publish releases to GitHub and update install source
- Self-update now queries the GitHub Releases API (parse_latest_tag extracted for testability)
- install.sh now fetches and downloads from GitHub Releases
- Drone CI release pipeline publishes to both Gitea and GitHub via GITHUB_TOKEN secret
- Bump version to 0.7.0
2026-03-01 04:50:52 -03:00
6 changed files with 97 additions and 22 deletions
+42
View File
@@ -109,3 +109,45 @@ steps:
depends_on:
- build-x86_64
- build-aarch64
- name: publish-github
image: alpine
environment:
GITHUB_TOKEN:
from_secret: GITHUB_TOKEN
GITHUB_REPO: cinco/tmuxido
commands:
- apk add --no-cache curl jq
- |
EXISTING=$(curl -fsSL \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/$GITHUB_REPO/releases/tags/$DRONE_TAG" | jq -r '.id // empty')
if [ -n "$EXISTING" ]; then
curl -s -X DELETE \
-H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$GITHUB_REPO/releases/$EXISTING"
fi
BODY=$(awk '
BEGIN { tag = ENVIRON["DRONE_TAG"] }
/^## \[/ { in_section = (index($0, "[" tag "]") > 0); next }
in_section && /^## \[/ { exit }
in_section { print }
' CHANGELOG.md)
RELEASE_ID=$(curl -fsSL -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/$GITHUB_REPO/releases" \
-d "{\"tag_name\":\"$DRONE_TAG\",\"name\":\"$DRONE_TAG\",\"body\":$(echo "$BODY" | jq -Rs .)}" \
| jq -r '.id')
for FILE in tmuxido-x86_64-linux tmuxido-aarch64-linux; do
curl -fsSL -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: application/octet-stream" \
"https://uploads.github.com/repos/$GITHUB_REPO/releases/$RELEASE_ID/assets?name=$FILE" \
--data-binary @"$FILE"
done
depends_on:
- build-x86_64
- build-aarch64
+7
View File
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.0] - 2026-03-01
### Changed
- `install.sh` now downloads from GitHub Releases
- Self-update now queries the GitHub Releases API for new versions
- Releases are published to both Gitea and GitHub
## [0.6.0] - 2026-03-01
### Added
Generated
+1 -1
View File
@@ -864,7 +864,7 @@ dependencies = [
[[package]]
name = "tmuxido"
version = "0.6.0"
version = "0.7.0"
dependencies = [
"anyhow",
"clap",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tmuxido"
version = "0.6.0"
version = "0.7.0"
edition = "2024"
[dev-dependencies]
+7 -4
View File
@@ -1,8 +1,9 @@
#!/bin/sh
set -e
REPO="cinco/Tmuxido"
BASE_URL="https://git.cincoeuzebio.com"
REPO="cinco/tmuxido"
BASE_URL="https://github.com"
API_URL="https://api.github.com"
INSTALL_DIR="$HOME/.local/bin"
arch=$(uname -m)
@@ -12,8 +13,10 @@ case "$arch" in
*) echo "Unsupported architecture: $arch" >&2; exit 1 ;;
esac
tag=$(curl -fsSL "$BASE_URL/api/v1/repos/$REPO/releases?limit=1&page=1" \
| grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4)
tag=$(curl -fsSL \
-H "Accept: application/vnd.github.v3+json" \
"$API_URL/repos/$REPO/releases/latest" \
| grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
[ -z "$tag" ] && { echo "Could not fetch latest release" >&2; exit 1; }
+39 -16
View File
@@ -3,8 +3,9 @@ use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::process::Command;
const REPO: &str = "cinco/Tmuxido";
const BASE_URL: &str = "https://git.cincoeuzebio.com";
const REPO: &str = "cinco/tmuxido";
const BASE_URL: &str = "https://github.com";
const API_BASE: &str = "https://api.github.com";
/// Check if running from cargo (development mode)
fn is_dev_build() -> bool {
@@ -26,12 +27,27 @@ fn detect_arch() -> Result<&'static str> {
}
}
/// Fetch latest release tag from Gitea API
/// Parse tag_name from a GitHub releases/latest JSON response
fn parse_latest_tag(response: &str) -> Result<String> {
let tag: serde_json::Value =
serde_json::from_str(response).context("Failed to parse release API response")?;
tag.get("tag_name")
.and_then(|t| t.as_str())
.map(|t| t.to_string())
.ok_or_else(|| anyhow::anyhow!("Could not extract tag_name from release"))
}
/// Fetch latest release tag from GitHub API
pub(crate) fn fetch_latest_tag() -> Result<String> {
let url = format!("{}/api/v1/repos/{}/releases?limit=1&page=1", BASE_URL, REPO);
let url = format!("{}/repos/{}/releases/latest", API_BASE, REPO);
let output = Command::new("curl")
.args(["-fsSL", &url])
.args([
"-fsSL",
"-H",
"Accept: application/vnd.github.v3+json",
&url,
])
.output()
.context("Failed to execute curl. Make sure curl is installed.")?;
@@ -42,17 +58,7 @@ pub(crate) fn fetch_latest_tag() -> Result<String> {
));
}
let response = String::from_utf8_lossy(&output.stdout);
// Parse JSON response to extract tag_name
let tag: serde_json::Value =
serde_json::from_str(&response).context("Failed to parse release API response")?;
tag.get(0)
.and_then(|r| r.get("tag_name"))
.and_then(|t| t.as_str())
.map(|t| t.to_string())
.ok_or_else(|| anyhow::anyhow!("Could not extract tag_name from release"))
parse_latest_tag(&String::from_utf8_lossy(&output.stdout))
}
/// Get path to current executable
@@ -211,6 +217,23 @@ mod tests {
);
}
#[test]
fn should_parse_tag_from_github_latest_release_response() {
let json = r#"{"tag_name":"0.7.0","name":"0.7.0","body":"release notes"}"#;
assert_eq!(parse_latest_tag(json).unwrap(), "0.7.0");
}
#[test]
fn should_return_error_when_tag_name_missing() {
let json = r#"{"name":"0.7.0","body":"no tag_name field"}"#;
assert!(parse_latest_tag(json).is_err());
}
#[test]
fn should_return_error_when_response_is_invalid_json() {
assert!(parse_latest_tag("not valid json").is_err());
}
#[test]
fn should_compare_versions_correctly() {
assert_eq!(