✨ 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
This commit is contained in:
parent
4caca0b26b
commit
c073a7c6f9
40
.drone.yml
40
.drone.yml
@ -109,3 +109,43 @@ steps:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- build-x86_64
|
- build-x86_64
|
||||||
- build-aarch64
|
- 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
|
||||||
|
- TAG=${DRONE_TAG}
|
||||||
|
- |
|
||||||
|
NOTES=$(awk "/^## \[$TAG\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md)
|
||||||
|
- |
|
||||||
|
EXISTING=$(curl -fsSL \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
"https://api.github.com/repos/$GITHUB_REPO/releases/tags/$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
|
||||||
|
- |
|
||||||
|
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\":\"$TAG\",\"name\":\"$TAG\",\"body\":$(echo "$NOTES" | jq -Rs .)}" \
|
||||||
|
| jq -r '.id')
|
||||||
|
- |
|
||||||
|
for ARCH in x86_64 aarch64; 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=tmuxido-${ARCH}-linux" \
|
||||||
|
--data-binary @"tmuxido-${ARCH}-linux"
|
||||||
|
done
|
||||||
|
depends_on:
|
||||||
|
- build-x86_64
|
||||||
|
- build-aarch64
|
||||||
|
|||||||
@ -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/).
|
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
|
## [0.6.0] - 2026-03-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -864,7 +864,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tmuxido"
|
name = "tmuxido"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tmuxido"
|
name = "tmuxido"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
11
install.sh
11
install.sh
@ -1,8 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
REPO="cinco/Tmuxido"
|
REPO="cinco/tmuxido"
|
||||||
BASE_URL="https://git.cincoeuzebio.com"
|
BASE_URL="https://github.com"
|
||||||
|
API_URL="https://api.github.com"
|
||||||
INSTALL_DIR="$HOME/.local/bin"
|
INSTALL_DIR="$HOME/.local/bin"
|
||||||
|
|
||||||
arch=$(uname -m)
|
arch=$(uname -m)
|
||||||
@ -12,8 +13,10 @@ case "$arch" in
|
|||||||
*) echo "Unsupported architecture: $arch" >&2; exit 1 ;;
|
*) echo "Unsupported architecture: $arch" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
tag=$(curl -fsSL "$BASE_URL/api/v1/repos/$REPO/releases?limit=1&page=1" \
|
tag=$(curl -fsSL \
|
||||||
| grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4)
|
-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; }
|
[ -z "$tag" ] && { echo "Could not fetch latest release" >&2; exit 1; }
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,9 @@ use std::os::unix::fs::PermissionsExt;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
const REPO: &str = "cinco/Tmuxido";
|
const REPO: &str = "cinco/tmuxido";
|
||||||
const BASE_URL: &str = "https://git.cincoeuzebio.com";
|
const BASE_URL: &str = "https://github.com";
|
||||||
|
const API_BASE: &str = "https://api.github.com";
|
||||||
|
|
||||||
/// Check if running from cargo (development mode)
|
/// Check if running from cargo (development mode)
|
||||||
fn is_dev_build() -> bool {
|
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> {
|
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")
|
let output = Command::new("curl")
|
||||||
.args(["-fsSL", &url])
|
.args([
|
||||||
|
"-fsSL",
|
||||||
|
"-H",
|
||||||
|
"Accept: application/vnd.github.v3+json",
|
||||||
|
&url,
|
||||||
|
])
|
||||||
.output()
|
.output()
|
||||||
.context("Failed to execute curl. Make sure curl is installed.")?;
|
.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_latest_tag(&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"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get path to current executable
|
/// 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]
|
#[test]
|
||||||
fn should_compare_versions_correctly() {
|
fn should_compare_versions_correctly() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user