diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3b405ea --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - os: macos-latest + target: aarch64-apple-darwin + - os: macos-latest + target: x86_64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Build + run: cargo build --release --locked --target ${{ matrix.target }} + + - name: Package (Unix) + if: runner.os != 'Windows' + shell: bash + env: + VERSION: ${{ github.ref_name }} + TARGET: ${{ matrix.target }} + run: | + set -euxo pipefail + STAGING="staging/taskwal-${VERSION}-${TARGET}" + mkdir -p "$STAGING" + cp README.md "$STAGING/" + strip "target/${TARGET}/release/tw" 2>/dev/null || true + cp "target/${TARGET}/release/tw" "$STAGING/" + chmod +x "$STAGING/tw" + mkdir -p dist + tar -czvf "dist/taskwal-${VERSION}-${TARGET}.tar.gz" -C staging "taskwal-${VERSION}-${TARGET}" + + - name: Package (Windows) + if: runner.os == 'Windows' + shell: pwsh + env: + VERSION: ${{ github.ref_name }} + TARGET: ${{ matrix.target }} + run: | + $ErrorActionPreference = "Stop" + $staging = "staging/taskwal-$env:VERSION-$env:TARGET" + New-Item -ItemType Directory -Force -Path $staging | Out-Null + Copy-Item README.md $staging\ + Copy-Item "target/$env:TARGET/release/tw.exe" $staging\ + New-Item -ItemType Directory -Force -Path dist | Out-Null + Compress-Archive -Path $staging -DestinationPath "dist/taskwal-$env:VERSION-$env:TARGET.zip" -Force + + - uses: actions/upload-artifact@v4 + with: + name: dist-${{ matrix.target }} + path: dist/taskwal-*.* + + release: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + pattern: dist-* + path: assets + merge-multiple: true + + - name: SHA256 checksums + run: | + cd assets + sha256sum taskwal-* > SHA256SUMS + cat SHA256SUMS + + - uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: | + assets/* diff --git a/README.md b/README.md index 283c9bd..2a0ff28 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,36 @@ For every subcommand, flag, and TUI key, see the full guides: **[docs/USAGE.md]( ## Installation +### Prebuilt binaries + +You do **not** need Rust or Cargo to run TaskWAL. Prebuilt `tw` binaries are attached to [GitHub Releases](https://github.com/enesify/taskwal/releases) for: + +- Linux x86_64 (`x86_64-unknown-linux-gnu`, typical glibc-based distros) +- macOS Intel and Apple Silicon (`x86_64-apple-darwin`, `aarch64-apple-darwin`) +- Windows x86_64 (`x86_64-pc-windows-msvc`) + +**Linux / macOS — install script** (default install dir: `~/.local/bin`): + +```bash +curl -fsSL https://raw.githubusercontent.com/enesify/taskwal/master/scripts/install-tw.sh | bash +``` + +System-wide install (e.g. `/usr/local/bin`): + +```bash +curl -fsSL https://raw.githubusercontent.com/enesify/taskwal/master/scripts/install-tw.sh | TASKWAL_PREFIX=/usr/local bash +``` + +Optional: `TASKWAL_REPO=owner/repo`, `TASKWAL_VERSION=v0.1.0` to pin a release. + +**Windows — manual install:** download `taskwal--x86_64-pc-windows-msvc.zip` from Releases, verify `SHA256SUMS`, extract `tw.exe`, and place it on your `PATH`. + +**Manual (any platform):** download the matching `taskwal--.tar.gz` or `.zip`, verify checksums in `SHA256SUMS`, extract the `tw` binary, and move it to a directory on your `PATH`. + +**Maintainers:** create a release by pushing a git tag `vX.Y.Z` that matches the `version` in [`Cargo.toml`](Cargo.toml) (for example tag `v0.1.0` for `version = "0.1.0"`). The release workflow builds and uploads archives plus `SHA256SUMS`. + +### Build from source + Requires [Rust](https://rustup.rs/) 1.70+. **Build** (binary at `target/release/tw`): diff --git a/docs/KULLANIM.md b/docs/KULLANIM.md index 170c3e2..84c8ce8 100644 --- a/docs/KULLANIM.md +++ b/docs/KULLANIM.md @@ -23,7 +23,21 @@ TaskWAL, görevlerinizi **yerel bilgisayarınızda** tutan bir iş takip aracıd ## Kurulum ve `tw` komutu -Projeyi derledikten sonra: +### Önceden derlenmiş ikili (Rust gerekmez) + +Sürümler [GitHub Releases](https://github.com/enesify/taskwal/releases) üzerindedir; her sürümde hedefe göre arşivler ve `SHA256SUMS` dosyası bulunur. + +**Linux / macOS** — isteğe bağlı kurulum betiği (depodaki `scripts/install-tw.sh`): + +```bash +curl -fsSL https://raw.githubusercontent.com/enesify/taskwal/master/scripts/install-tw.sh | bash +``` + +**Windows** — `taskwal--x86_64-pc-windows-msvc.zip` dosyasını indirin; `tw.exe` dosyasını `PATH` üzerinde bir konuma çıkarın. + +### Kaynak koddan (Cargo) + +Projeyi klonladıktan sonra: ```bash cargo install --path /proje/yolu --force diff --git a/docs/USAGE.md b/docs/USAGE.md index 617d168..ec8d8a7 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -19,7 +19,21 @@ Override with environment variable: ## Install and the `tw` command -After building the project: +### Prebuilt binary (no Rust) + +Download a release from [GitHub Releases](https://github.com/enesify/taskwal/releases). Each release includes archives per target and a `SHA256SUMS` file. + +**Linux / macOS** — optional install script (see repository `scripts/install-tw.sh`): + +```bash +curl -fsSL https://raw.githubusercontent.com/enesify/taskwal/master/scripts/install-tw.sh | bash +``` + +**Windows** — use the `taskwal--x86_64-pc-windows-msvc.zip` asset; extract `tw.exe` onto your `PATH`. + +### From source (Cargo) + +After cloning the project: ```bash cargo install --path /path/to/taskwal --force diff --git a/scripts/install-tw.sh b/scripts/install-tw.sh new file mode 100755 index 0000000..503a42a --- /dev/null +++ b/scripts/install-tw.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# Install the `tw` binary from GitHub Releases (no Rust/Cargo required). +# Usage: +# curl -fsSL https://raw.githubusercontent.com/OWNER/REPO/BRANCH/scripts/install-tw.sh | bash +# Optional environment: +# TASKWAL_REPO=owner/repo (default: enesify/taskwal) +# TASKWAL_VERSION=v0.1.0 (default: latest GitHub release) +# TASKWAL_PREFIX=/usr/local (install to $PREFIX/bin; default: ~/.local) + +set -euo pipefail + +TASKWAL_REPO="${TASKWAL_REPO:-enesify/taskwal}" +TASKWAL_VERSION="${TASKWAL_VERSION:-}" +TASKWAL_PREFIX="${TASKWAL_PREFIX:-}" + +die() { + echo "install-tw.sh: $*" >&2 + exit 1 +} + +detect_target_triple() { + local os arch + os="$(uname -s)" + arch="$(uname -m)" + case "$os" in + Linux) + case "$arch" in + x86_64) echo "x86_64-unknown-linux-gnu" ;; + aarch64 | arm64) die "no prebuilt binary for Linux $arch yet; build from source with Rust" ;; + *) die "unsupported Linux architecture: $arch" ;; + esac + ;; + Darwin) + case "$arch" in + x86_64) echo "x86_64-apple-darwin" ;; + arm64) echo "aarch64-apple-darwin" ;; + *) die "unsupported macOS architecture: $arch" ;; + esac + ;; + *) + die "unsupported OS: $os (install the Windows zip from Releases, or build from source)" + ;; + esac +} + +require_cmd() { + command -v "$1" >/dev/null 2>&1 || die "required command not found: $1" +} + +download_to() { + local url="$1" out="$2" + if command -v curl >/dev/null 2>&1; then + curl -fsSL -o "$out" "$url" + elif command -v wget >/dev/null 2>&1; then + wget -q -O "$out" "$url" + else + die "need curl or wget to download" + fi +} + +fetch_latest_tag() { + local api_base="$1" json + json="$(curl -fsSL "${api_base}/releases/latest")" + if command -v python3 >/dev/null 2>&1; then + printf '%s' "$json" | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])" + return + fi + require_cmd grep + require_cmd sed + printf '%s' "$json" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1 +} + +main() { + require_cmd uname + local triple + triple="$(detect_target_triple)" + + local api_base="https://api.github.com/repos/${TASKWAL_REPO}" + local tag asset_name tmpdir install_dir + + if [[ -n "$TASKWAL_VERSION" ]]; then + tag="$TASKWAL_VERSION" + else + tag="$(fetch_latest_tag "$api_base")" + [[ -n "$tag" ]] || die "could not determine latest release tag" + fi + + asset_name="taskwal-${tag}-${triple}.tar.gz" + + if [[ -n "$TASKWAL_PREFIX" ]]; then + install_dir="${TASKWAL_PREFIX%/}/bin" + else + install_dir="${HOME}/.local/bin" + fi + + tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/taskwal-install.XXXXXX")" + trap 'rm -rf "$tmpdir"' EXIT + + local sums_url="https://github.com/${TASKWAL_REPO}/releases/download/${tag}/SHA256SUMS" + local asset_url="https://github.com/${TASKWAL_REPO}/releases/download/${tag}/${asset_name}" + + echo "Downloading ${asset_name} ..." + download_to "$sums_url" "${tmpdir}/SHA256SUMS" + download_to "$asset_url" "${tmpdir}/${asset_name}" + + ( + cd "$tmpdir" + grep -F "${asset_name}" SHA256SUMS | sha256sum -c + tar -xzf "${asset_name}" + local inner="taskwal-${tag}-${triple}/tw" + [[ -f "$inner" ]] || die "expected ${inner} inside archive" + chmod +x "$inner" + mkdir -p "$install_dir" + mv "$inner" "${install_dir}/tw" + ) + + echo "Installed tw to ${install_dir}/tw" + case ":${PATH}:" in + *":${install_dir}:"*) ;; + *) + echo "Add ${install_dir} to your PATH, e.g.:" + echo " export PATH=\"${install_dir}:\$PATH\"" + ;; + esac +} + +main "$@"