diff --git a/.github/workflows/aur.yml b/.github/workflows/aur.yml index 3cd3999..5d5244b 100644 --- a/.github/workflows/aur.yml +++ b/.github/workflows/aur.yml @@ -15,6 +15,8 @@ permissions: jobs: publish-aur: + # Не публикуем dev/pre-release теги в AUR + if: "!contains(github.ref_name, '-')" runs-on: ubuntu-latest environment: aur steps: @@ -133,4 +135,4 @@ jobs: git add PKGBUILD .SRCINFO git commit -m "dcr ${{ steps.meta.outputs.pkgver }}" - git push + git push \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 076ce19..37ac546 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,11 @@ on: push: tags: - "v*" + workflow_dispatch: + inputs: + tag: + description: "Tag to build and upload assets for (e.g. v0.6.5)" + required: true permissions: contents: write @@ -13,12 +18,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + # При ручном запуске чекаутим нужный тег + ref: ${{ github.event.inputs.tag || github.ref }} - name: Prepare Release Notes shell: bash run: | set -euo pipefail - tag="${GITHUB_REF_NAME#v}" + # При ручном запуске берём версию из инпута, иначе из тега + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + tag="${{ github.event.inputs.tag }}" + tag="${tag#v}" + else + tag="${GITHUB_REF_NAME#v}" + fi notes_file=".github/release-notes.md" if grep -q "^## \\[${tag}\\]" CHANGELOG.md; then awk -v ver="$tag" ' @@ -42,83 +56,74 @@ jobs: fail-fast: false matrix: include: - # GNU/Linux - # x86_64 + # ── Linux GNU ────────────────────────────────────────────────────── - os: ubuntu-latest target: x86_64-unknown-linux-gnu - asset_name: dcr-x86_64-unknown-linux-gnu - # ARM64 - os: ubuntu-latest target: aarch64-unknown-linux-gnu - asset_name: dcr-aarch64-unknown-linux-gnu + apt: gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + env_cc: CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc + env_linker: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc - # Musl/Linux - # x86_64 + # ── Linux Musl ───────────────────────────────────────────────────── - os: ubuntu-latest target: x86_64-unknown-linux-musl - asset_name: dcr-x86_64-unknown-linux-musl + apt: musl-tools + env_cc: CC_x86_64_unknown_linux_musl=musl-gcc + env_linker: CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc - # ARM64 - os: ubuntu-latest target: aarch64-unknown-linux-musl - asset_name: dcr-aarch64-unknown-linux-musl + musl_cross: aarch64-linux-musl-cross + env_cc: CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc + env_linker: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc - # MacOS - # x86_64 (Intel Mac, macOS 10.12+) + # ── macOS ────────────────────────────────────────────────────────── - os: macos-15-intel target: x86_64-apple-darwin - asset_name: dcr-x86_64-apple-darwin - # ARM64 (Apple Silicon Mac, macOS 11+) - os: macos-latest target: aarch64-apple-darwin - asset_name: dcr-aarch64-apple-darwin - # Windows MSVC - # x86_64 + # ── Windows MSVC ─────────────────────────────────────────────────── - os: windows-latest target: x86_64-pc-windows-msvc - asset_name: dcr-x86_64-pc-windows-msvc.exe - # ARM64 - os: windows-latest target: aarch64-pc-windows-msvc - asset_name: dcr-aarch64-pc-windows-msvc.exe - # Windows GNU - # x86_64 + # ── Windows GNU ──────────────────────────────────────────────────── - os: windows-latest target: x86_64-pc-windows-gnu - asset_name: dcr-x86_64-pc-windows-gnu.exe - # Windows LLVM - # x86_64 - - os: windows-latest - target: x86_64-pc-windows-gnullvm - asset_name: dcr-x86_64-pc-windows-gnullvm.exe - - - # ARM64 - - os: windows-latest - target: aarch64-pc-windows-gnullvm - asset_name: dcr-aarch64-pc-windows-gnullvm.exe + # gnullvm исключён: LLVM MinGW не предустановлен на GitHub Actions runs-on: ${{ matrix.os }} needs: notes steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.tag || github.ref }} + - name: Download Release Notes uses: actions/download-artifact@v4 with: name: release-notes path: .github - - name: Install Linux Cross Compilation Tools - if: runner.os == 'Linux' + - name: Install apt packages + if: runner.os == 'Linux' && matrix.apt != '' run: | sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu musl-tools + sudo apt-get install -y ${{ matrix.apt }} + + - name: Install musl.cc cross-toolchain + if: matrix.musl_cross != '' + run: | + curl -fsSL "https://musl.cc/${{ matrix.musl_cross }}.tgz" | \ + sudo tar -xz -C /opt + echo "/opt/${{ matrix.musl_cross }}/bin" >> "$GITHUB_PATH" - name: Setup Rust uses: dtolnay/rust-toolchain@stable @@ -126,22 +131,56 @@ jobs: targets: ${{ matrix.target }} - name: Build - run: cargo build --release --target ${{ matrix.target }} + shell: bash + env: + MATRIX_ENV_CC: ${{ matrix.env_cc }} + MATRIX_ENV_LINKER: ${{ matrix.env_linker }} + run: | + if [[ -n "${MATRIX_ENV_CC:-}" ]]; then + export "${MATRIX_ENV_CC?}" + fi + if [[ -n "${MATRIX_ENV_LINKER:-}" ]]; then + export "${MATRIX_ENV_LINKER?}" + fi + cargo build --release --target ${{ matrix.target }} + + - name: Derive version string + shell: bash + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + RAW_TAG="${{ github.event.inputs.tag }}" + else + RAW_TAG="${GITHUB_REF_NAME}" + fi + echo "RELEASE_TAG=${RAW_TAG}" >> "$GITHUB_ENV" + echo "RELEASE_VERSION=${RAW_TAG#v}" >> "$GITHUB_ENV" + + - name: Derive asset name (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + echo "ASSET_NAME=dcr-${{ matrix.target }}-${RELEASE_VERSION}" >> "$GITHUB_ENV" + + - name: Derive asset name (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + "ASSET_NAME=dcr-${{ matrix.target }}-$($env:RELEASE_VERSION).exe" >> $env:GITHUB_ENV - name: Prepare Artifact (Unix) if: runner.os != 'Windows' shell: bash - run: | - cp target/${{ matrix.target }}/release/dcr ${{ matrix.asset_name }} + run: cp target/${{ matrix.target }}/release/dcr "$ASSET_NAME" - name: Prepare Artifact (Windows) if: runner.os == 'Windows' shell: pwsh - run: | - Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "${{ matrix.asset_name }}" + run: Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "$env:ASSET_NAME" - name: Upload Release Asset uses: softprops/action-gh-release@v2 with: - files: ${{ matrix.asset_name }} + tag_name: ${{ env.RELEASE_TAG }} + files: ${{ env.ASSET_NAME }} body_path: .github/release-notes.md + prerelease: ${{ contains(env.RELEASE_TAG, '-') }} \ No newline at end of file diff --git a/install.ps1 b/install.ps1 index 2fcb632..20eeb2a 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,15 +1,16 @@ $ErrorActionPreference = 'Stop' -$RepoUrl = 'https://github.com/dexoron/dcr' -$ApiUrl = 'https://api.github.com/repos/dexoron/dcr/releases/latest' +$RepoUrl = 'https://github.com/dexoron/dcr' +$ApiLatest = 'https://api.github.com/repos/dexoron/dcr/releases/latest' +$ApiAll = 'https://api.github.com/repos/dexoron/dcr/releases' $InstallPath = Join-Path $env:LOCALAPPDATA 'dcr' -$BinPath = Join-Path $env:USERPROFILE '.local\bin' -$BinaryPath = Join-Path $InstallPath 'dcr.exe' +$BinPath = Join-Path $env:USERPROFILE '.local\bin' +$BinaryPath = Join-Path $InstallPath 'dcr.exe' -function Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Cyan } -function Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } -function Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } -function Fail($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red; exit 1 } +function Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Cyan } +function Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } +function Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function Fail($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red; exit 1 } function Require-Cmd($name) { if (-not (Get-Command $name -ErrorAction SilentlyContinue)) { @@ -17,6 +18,20 @@ function Require-Cmd($name) { } } +function Select-Channel { + Write-Host "Choose channel:" + Write-Host " 1) Latest stable release (default)" + Write-Host " 2) Latest dev (pre-release)" + $choice = Read-Host "Enter 1 or 2 [1]" + if ([string]::IsNullOrWhiteSpace($choice)) { $choice = '1' } + + switch ($choice) { + '1' { return 'stable' } + '2' { return 'dev' } + default { Fail 'Unknown option' } + } +} + function Select-Mode { Write-Host "Choose installation mode:" Write-Host " 1) Download prebuilt binary from GitHub Release (recommended)" @@ -26,7 +41,7 @@ function Select-Mode { switch ($choice) { '1' { return 'release' } - '2' { return 'build' } + '2' { return 'build' } default { Fail 'Unknown option' } } } @@ -38,18 +53,35 @@ function Get-Target { return 'x86_64-pc-windows-msvc' } +function Fetch-ReleaseJson($channel) { + if ($channel -eq 'dev') { + Info "Looking for latest dev (pre-release)..." + $releases = Invoke-RestMethod -Method Get -Uri $ApiAll + $pre = $releases | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1 + if (-not $pre) { Fail "No dev (pre-release) found on GitHub" } + return $pre + } else { + return Invoke-RestMethod -Method Get -Uri $ApiLatest + } +} + function Install-FromRelease { - param([string]$target) + param([string]$target, [string]$channel) New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null - $assetName = "dcr-$target.exe" - Info "Fetching latest release..." - $release = Invoke-RestMethod -Method Get -Uri $ApiUrl + $release = Fetch-ReleaseJson $channel + $tag = $release.tag_name + $version = $tag.TrimStart('v') + + # Имя бинарника: dcr--.exe + $assetName = "dcr-$target-$version.exe" + + Info "Fetching release $tag (channel: $channel)..." $asset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 if (-not $asset) { - Fail "Asset $assetName not found in release $($release.tag_name)" + Fail "Asset $assetName not found in release $tag" } Info "Downloading $assetName..." @@ -58,7 +90,7 @@ function Install-FromRelease { } function Install-FromSource { - param([string]$target) + param([string]$target, [string]$channel) Require-Cmd 'git' Require-Cmd 'cargo' @@ -67,7 +99,15 @@ function Install-FromSource { if (Test-Path $tmp) { Remove-Item -Recurse -Force $tmp } Info 'Cloning repository...' - git clone --depth 1 $RepoUrl $tmp | Out-Null + if ($channel -eq 'dev') { + # Пробуем ветку dev + $result = git clone --depth 1 --branch dev $RepoUrl $tmp 2>&1 + if ($LASTEXITCODE -ne 0) { + git clone --depth 1 $RepoUrl $tmp | Out-Null + } + } else { + git clone --depth 1 $RepoUrl $tmp | Out-Null + } Info 'Building release binary...' Push-Location $tmp @@ -100,14 +140,15 @@ function Setup-Path { } Info 'Starting DCR installation' -$mode = Select-Mode -$target = Get-Target +$channel = Select-Channel +$mode = Select-Mode +$target = Get-Target if ($mode -eq 'build') { - Install-FromSource -target $target + Install-FromSource -target $target -channel $channel } else { - Install-FromRelease -target $target + Install-FromRelease -target $target -channel $channel } Setup-Path -Ok 'Installation completed successfully' +Ok 'Installation completed successfully' \ No newline at end of file diff --git a/install.sh b/install.sh index 79a70d2..61ff798 100755 --- a/install.sh +++ b/install.sh @@ -6,7 +6,8 @@ INSTALL_PATH="$HOME/.local/share/dcr" BINPATH="$HOME/.local/bin" LOGFILE="$HOME/.cache/dcr-install.log" REPO_URL="https://github.com/dexoron/dcr" -GITHUB_API="https://api.github.com/repos/dexoron/dcr/releases/latest" +GITHUB_API_LATEST="https://api.github.com/repos/dexoron/dcr/releases/latest" +GITHUB_API_ALL="https://api.github.com/repos/dexoron/dcr/releases" RED='\033[0;31m' GREEN='\033[0;32m' @@ -17,13 +18,16 @@ NC='\033[0m' mkdir -p "$(dirname "$LOGFILE")" exec > >(tee -a "$LOGFILE") 2>&1 -log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"; } +log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"; } success() { echo -e "${GREEN}✔ $1${NC}"; } -warn() { echo -e "${YELLOW}⚠ $1${NC}"; } -error() { echo -e "${RED}✖ $1${NC}"; } +warn() { echo -e "${YELLOW}⚠ $1${NC}"; } +error() { echo -e "${RED}✖ $1${NC}"; } trap 'error "Error on line $LINENO"; exit 1' ERR +INSTALL_MODE="" +CHANNEL="" + check_os() { case "$(uname -s)" in Linux|Darwin) ;; @@ -37,13 +41,11 @@ detect_target() { arch="$(uname -m)" case "$os:$arch" in - Linux:x86_64) TARGET_TRIPLE="x86_64-unknown-linux-gnu" ;; - Darwin:x86_64) TARGET_TRIPLE="x86_64-apple-darwin" ;; + Linux:x86_64) TARGET_TRIPLE="x86_64-unknown-linux-gnu" ;; + Darwin:x86_64) TARGET_TRIPLE="x86_64-apple-darwin" ;; Darwin:arm64|Darwin:aarch64) TARGET_TRIPLE="aarch64-apple-darwin" ;; *) error "Unsupported platform: $os/$arch"; exit 1 ;; esac - - ASSET_NAME="dcr-${TARGET_TRIPLE}" } check_common_dependencies() { @@ -51,14 +53,106 @@ check_common_dependencies() { } check_build_dependencies() { - command -v git >/dev/null 2>&1 || { error "git is not installed"; exit 1; } + command -v git >/dev/null 2>&1 || { error "git is not installed"; exit 1; } command -v cargo >/dev/null 2>&1 || { error "cargo is not installed"; exit 1; } } +select_channel() { + echo "Choose channel:" + echo " 1) Latest stable release (default)" + echo " 2) Latest dev (pre-release)" + read -r -p "Enter 1 or 2 [1]: " choice + + case "${choice:-1}" in + 1) CHANNEL="stable" ;; + 2) CHANNEL="dev" ;; + *) error "Unknown option"; exit 1 ;; + esac +} + +select_install_mode() { + echo "Choose installation mode:" + echo " 1) Download prebuilt binary from GitHub Release (recommended)" + echo " 2) Build from git" + read -r -p "Enter 1 or 2 [1]: " choice + + case "${choice:-1}" in + 1) INSTALL_MODE="release" ;; + 2) INSTALL_MODE="build" ;; + *) error "Unknown option"; exit 1 ;; + esac +} + +# Возвращает JSON нужного релиза в stdout +fetch_release_json() { + if [[ "$CHANNEL" == "dev" ]]; then + log "Looking for latest dev (pre-release)..." + local json + json="$(curl -fsSL "$GITHUB_API_ALL")" + # Первый pre-release в списке + local result + result="$(printf '%s' "$json" | python3 - <<'EOF' +import sys, json +releases = json.load(sys.stdin) +pre = [r for r in releases if r.get("prerelease", False)] +if not pre: + print("{}", end="") +else: + print(json.dumps(pre[0]), end="") +EOF +)" + if [[ -z "$result" || "$result" == "{}" ]]; then + error "No dev (pre-release) found on GitHub" + exit 1 + fi + printf '%s' "$result" + else + curl -fsSL "$GITHUB_API_LATEST" + fi +} + +download_binary() { + local release_json tag version asset_name download_url + + release_json="$(fetch_release_json)" + + tag="$(printf '%s\n' "$release_json" | \ + sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p' | head -n1)" + if [[ -z "$tag" ]]; then + error "Failed to determine release version" + exit 1 + fi + + # Версия без ведущего 'v' + version="${tag#v}" + # Имя бинарника: dcr-- + asset_name="dcr-${TARGET_TRIPLE}-${version}" + + log "Fetching release ${tag} (channel: ${CHANNEL})..." + + download_url="$(printf '%s\n' "$release_json" | \ + sed -n "s#.*\"browser_download_url\": \"\([^\"]*/${asset_name}\)\".*#\1#p" | head -n1)" + + if [[ -z "$download_url" ]]; then + error "Asset ${asset_name} not found in release ${tag}" + exit 1 + fi + + mkdir -p "$INSTALL_PATH" + curl -fL "$download_url" -o "$INSTALL_PATH/dcr" + chmod +x "$INSTALL_PATH/dcr" + success "Binary ${asset_name} downloaded (${tag})" +} + prepare_sources() { log "Fetching sources..." rm -rf "$TMPDIR" git clone --depth 1 "$REPO_URL" "$TMPDIR" + if [[ "$CHANNEL" == "dev" ]]; then + # Клонируем dev ветку, если она есть + git clone --depth 1 --branch dev "$REPO_URL" "$TMPDIR" 2>/dev/null || \ + git clone --depth 1 "$REPO_URL" "$TMPDIR" + fi success "Sources fetched" } @@ -68,34 +162,6 @@ build_binary() { success "Build completed" } -fetch_latest_release_json() { - curl -fsSL "$GITHUB_API" -} - -download_binary() { - log "Fetching latest release..." - - local release_json download_url tag - release_json="$(fetch_latest_release_json)" - - tag="$(printf '%s\n' "$release_json" | sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p' | head -n1)" - if [[ -z "$tag" ]]; then - error "Failed to determine release version" - exit 1 - fi - - download_url="$(printf '%s\n' "$release_json" | sed -n "s#.*\"browser_download_url\": \"\([^\"]*${ASSET_NAME}\)\".*#\1#p" | head -n1)" - if [[ -z "$download_url" ]]; then - error "Asset ${ASSET_NAME} not found in release ${tag}" - exit 1 - fi - - mkdir -p "$INSTALL_PATH" - curl -fL "$download_url" -o "$INSTALL_PATH/dcr" - chmod +x "$INSTALL_PATH/dcr" - success "Binary ${ASSET_NAME} downloaded (${tag})" -} - install_built_binary() { mkdir -p "$INSTALL_PATH" cp "$TMPDIR/target/release/dcr" "$INSTALL_PATH/dcr" @@ -122,25 +188,13 @@ cleanup() { rm -rf "$TMPDIR" 2>/dev/null || true } -select_install_mode() { - echo "Choose installation mode:" - echo " 1) Download prebuilt binary from GitHub Release (recommended)" - echo " 2) Build from git" - read -r -p "Enter 1 or 2 [1]: " choice - - case "${choice:-1}" in - 1) INSTALL_MODE="release" ;; - 2) INSTALL_MODE="build" ;; - *) error "Unknown option"; exit 1 ;; - esac -} - main() { log "Starting DCR installation" check_os detect_target check_common_dependencies + select_channel select_install_mode cleanup @@ -161,4 +215,4 @@ main() { log "Log saved to $LOGFILE" } -main "$@" +main "$@" \ No newline at end of file