mirror of https://github.com/ospab/ostp.git
Fix UAC diagnostics parity, gk alias, flutter version, and versioned CI channels
- GUI launch_as_admin now matches the CLI's UAC diagnosis: detects
ERROR_CANCELLED (1223, user declined the prompt) instead of silently
treating it as success, and reports GetLastError()+exe path for any other
ShellExecuteW failure, replacing the old single opaque "denied or missing"
message that made GUI/TUI failures impossible to tell apart.
- generate-key subcommand renamed to `gk` (kept `generate-key` as an alias).
- Fixed a real short-flag collision: GenerateKey's --count used short='c',
which collides with the global --config short (propagated into every
subcommand); clap validates the whole command tree on first parse(), so
this could break parsing for the entire CLI, not just generate-key/gk.
--count is now short='n'.
- ostp-flutter/pubspec.yaml version was stuck at 0.2.97+12; bumped to 0.4.1+13.
- release.yml: added a resolve-channel job that computes one release tag per
run instead of repeating the logic in five upload steps. Rolling channel
pushes now carry the actual Cargo.toml version instead of a bare channel
name: `{version}-nightly` for the nightly branch, `{version}-beta` for
pre-release. workflow_dispatch gained a `channel` input restricted to
nightly/beta only — a manual run can never accidentally publish a "stable"
release; that still requires an explicit vX.Y.Z tag push.
This commit is contained in:
parent
b2ee9eb010
commit
c330a0abe3
|
|
@ -10,6 +10,19 @@ on:
|
|||
- nightly
|
||||
- pre-release
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
channel:
|
||||
description: >-
|
||||
Manually build+release just this rolling channel. Stable releases
|
||||
are NEVER picked here on purpose — cut those only via a real
|
||||
"vX.Y.Z" tag push, so a manual dispatch can't accidentally publish
|
||||
a "stable" release.
|
||||
type: choice
|
||||
required: true
|
||||
default: nightly
|
||||
options:
|
||||
- nightly
|
||||
- beta
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
|
@ -21,6 +34,55 @@ env:
|
|||
RUST_BACKTRACE: short
|
||||
|
||||
jobs:
|
||||
# Computes ONE channel + release tag for this whole run, so every build
|
||||
# job (native matrix + all 3 GUI platforms + Android) uploads to the exact
|
||||
# same release under the exact same tag, instead of repeating this logic
|
||||
# (and risking it drifting out of sync) in five separate places.
|
||||
#
|
||||
# Tag shape:
|
||||
# - real "vX.Y.Z" / "vX.Y.Z-beta.N" tag push -> tag used as-is (stable promotion)
|
||||
# - push to `nightly` -> "{version}-nightly" (rolling, same tag every push)
|
||||
# - push to `pre-release` -> "{version}-beta" (rolling, same tag every push)
|
||||
# - workflow_dispatch -> forced by the `channel` input (nightly|beta only)
|
||||
resolve-channel:
|
||||
name: Resolve release channel
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
channel: ${{ steps.resolve.outputs.channel }}
|
||||
tag_name: ${{ steps.resolve.outputs.tag_name }}
|
||||
prerelease: ${{ steps.resolve.outputs.prerelease }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Resolve channel, version, and release tag
|
||||
id: resolve
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
BASE_VERSION=$(grep -m1 '^version' Cargo.toml | sed -E 's/version *= *"([^"]+)"/\1/')
|
||||
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
CHANNEL="stable"
|
||||
TAG="${{ github.ref_name }}"
|
||||
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
CHANNEL="${{ github.event.inputs.channel }}"
|
||||
elif [ "${{ github.ref_name }}" = "nightly" ]; then
|
||||
CHANNEL="nightly"
|
||||
elif [ "${{ github.ref_name }}" = "pre-release" ]; then
|
||||
CHANNEL="beta"
|
||||
else
|
||||
CHANNEL="nightly"
|
||||
fi
|
||||
|
||||
if [ "$CHANNEL" != "stable" ]; then
|
||||
TAG="${BASE_VERSION}-${CHANNEL}"
|
||||
fi
|
||||
|
||||
echo "Resolved channel=$CHANNEL tag=$TAG (base version $BASE_VERSION)"
|
||||
echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT"
|
||||
echo "tag_name=$TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "prerelease=$([ "$CHANNEL" = "stable" ] && echo false || echo true)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
check-and-test:
|
||||
name: Check & Test
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -58,7 +120,7 @@ jobs:
|
|||
|
||||
publish-release-matrix:
|
||||
name: Release for ${{ matrix.target }}
|
||||
needs: check-and-test
|
||||
needs: [check-and-test, resolve-channel]
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
@ -244,22 +306,19 @@ jobs:
|
|||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
# Version tags (v0.4.1, v0.4.1-beta.N) use their own name as the
|
||||
# release; branch pushes (nightly/pre-release) roll a release named
|
||||
# after the branch itself — no name remapping needed since
|
||||
# github.ref_name is already the tag OR the branch name as-is.
|
||||
tag_name: ${{ github.ref_name }}
|
||||
# Any branch push is a rolling prerelease; for real version tags,
|
||||
# a hyphenated suffix (-beta.N) marks it prerelease, a bare
|
||||
# semver tag (v0.4.1) is a stable release.
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') || contains(github.ref_name, '-') }}
|
||||
# Computed once in resolve-channel so every platform/job in this run
|
||||
# lands on the exact same tag: "{version}-nightly" / "{version}-beta"
|
||||
# for rolling channel pushes, or the pushed "vX.Y.Z" tag as-is for a
|
||||
# real stable release.
|
||||
tag_name: ${{ needs.resolve-channel.outputs.tag_name }}
|
||||
prerelease: ${{ needs.resolve-channel.outputs.prerelease }}
|
||||
files: ${{ matrix.release_name }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-windows-gui:
|
||||
name: Build Windows GUI (Tauri) - ${{ matrix.arch }}
|
||||
needs: check-and-test
|
||||
needs: [check-and-test, resolve-channel]
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -326,22 +385,19 @@ jobs:
|
|||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
# Version tags (v0.4.1, v0.4.1-beta.N) use their own name as the
|
||||
# release; branch pushes (nightly/pre-release) roll a release named
|
||||
# after the branch itself — no name remapping needed since
|
||||
# github.ref_name is already the tag OR the branch name as-is.
|
||||
tag_name: ${{ github.ref_name }}
|
||||
# Any branch push is a rolling prerelease; for real version tags,
|
||||
# a hyphenated suffix (-beta.N) marks it prerelease, a bare
|
||||
# semver tag (v0.4.1) is a stable release.
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') || contains(github.ref_name, '-') }}
|
||||
# Computed once in resolve-channel so every platform/job in this run
|
||||
# lands on the exact same tag: "{version}-nightly" / "{version}-beta"
|
||||
# for rolling channel pushes, or the pushed "vX.Y.Z" tag as-is for a
|
||||
# real stable release.
|
||||
tag_name: ${{ needs.resolve-channel.outputs.tag_name }}
|
||||
prerelease: ${{ needs.resolve-channel.outputs.prerelease }}
|
||||
files: ostp-windows-gui-${{ matrix.arch }}.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-linux-gui:
|
||||
name: Build Linux GUI (Tauri) - ${{ matrix.arch }}
|
||||
needs: check-and-test
|
||||
needs: [check-and-test, resolve-channel]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -394,22 +450,19 @@ jobs:
|
|||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
# Version tags (v0.4.1, v0.4.1-beta.N) use their own name as the
|
||||
# release; branch pushes (nightly/pre-release) roll a release named
|
||||
# after the branch itself — no name remapping needed since
|
||||
# github.ref_name is already the tag OR the branch name as-is.
|
||||
tag_name: ${{ github.ref_name }}
|
||||
# Any branch push is a rolling prerelease; for real version tags,
|
||||
# a hyphenated suffix (-beta.N) marks it prerelease, a bare
|
||||
# semver tag (v0.4.1) is a stable release.
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') || contains(github.ref_name, '-') }}
|
||||
# Computed once in resolve-channel so every platform/job in this run
|
||||
# lands on the exact same tag: "{version}-nightly" / "{version}-beta"
|
||||
# for rolling channel pushes, or the pushed "vX.Y.Z" tag as-is for a
|
||||
# real stable release.
|
||||
tag_name: ${{ needs.resolve-channel.outputs.tag_name }}
|
||||
prerelease: ${{ needs.resolve-channel.outputs.prerelease }}
|
||||
files: ostp-linux-gui-${{ matrix.arch }}.tar.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-macos-gui:
|
||||
name: Build macOS GUI (Tauri) - ${{ matrix.arch }}
|
||||
needs: check-and-test
|
||||
needs: [check-and-test, resolve-channel]
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -459,22 +512,19 @@ jobs:
|
|||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
# Version tags (v0.4.1, v0.4.1-beta.N) use their own name as the
|
||||
# release; branch pushes (nightly/pre-release) roll a release named
|
||||
# after the branch itself — no name remapping needed since
|
||||
# github.ref_name is already the tag OR the branch name as-is.
|
||||
tag_name: ${{ github.ref_name }}
|
||||
# Any branch push is a rolling prerelease; for real version tags,
|
||||
# a hyphenated suffix (-beta.N) marks it prerelease, a bare
|
||||
# semver tag (v0.4.1) is a stable release.
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') || contains(github.ref_name, '-') }}
|
||||
# Computed once in resolve-channel so every platform/job in this run
|
||||
# lands on the exact same tag: "{version}-nightly" / "{version}-beta"
|
||||
# for rolling channel pushes, or the pushed "vX.Y.Z" tag as-is for a
|
||||
# real stable release.
|
||||
tag_name: ${{ needs.resolve-channel.outputs.tag_name }}
|
||||
prerelease: ${{ needs.resolve-channel.outputs.prerelease }}
|
||||
files: ostp-macos-gui-${{ matrix.arch }}.tar.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-android:
|
||||
name: Build Android Client (Flutter) - ${{ matrix.arch }}
|
||||
needs: check-and-test
|
||||
needs: [check-and-test, resolve-channel]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -535,15 +585,12 @@ jobs:
|
|||
- name: Upload to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
# Version tags (v0.4.1, v0.4.1-beta.N) use their own name as the
|
||||
# release; branch pushes (nightly/pre-release) roll a release named
|
||||
# after the branch itself — no name remapping needed since
|
||||
# github.ref_name is already the tag OR the branch name as-is.
|
||||
tag_name: ${{ github.ref_name }}
|
||||
# Any branch push is a rolling prerelease; for real version tags,
|
||||
# a hyphenated suffix (-beta.N) marks it prerelease, a bare
|
||||
# semver tag (v0.4.1) is a stable release.
|
||||
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') || contains(github.ref_name, '-') }}
|
||||
# Computed once in resolve-channel so every platform/job in this run
|
||||
# lands on the exact same tag: "{version}-nightly" / "{version}-beta"
|
||||
# for rolling channel pushes, or the pushed "vX.Y.Z" tag as-is for a
|
||||
# real stable release.
|
||||
tag_name: ${{ needs.resolve-channel.outputs.tag_name }}
|
||||
prerelease: ${{ needs.resolve-channel.outputs.prerelease }}
|
||||
files: ostp-flutter/ostp-android-${{ matrix.arch }}.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 0.2.97+12
|
||||
version: 0.4.1+13
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.4
|
||||
|
|
|
|||
|
|
@ -762,6 +762,7 @@ fn launch_as_admin(exe: &std::path::PathBuf, token: &str, port: u16) -> anyhow::
|
|||
let params_str = format!("--port {} --token-file \"{}\"", port, token_file.display());
|
||||
let params_wstr: Vec<u16> = OsStr::new(¶ms_str).encode_wide().chain(Some(0)).collect();
|
||||
#[link(name = "shell32")] extern "system" { fn ShellExecuteW(h: *mut std::ffi::c_void, op: *const u16, f: *const u16, p: *const u16, d: *const u16, s: i32) -> isize; }
|
||||
#[link(name = "kernel32")] extern "system" { fn GetLastError() -> u32; }
|
||||
|
||||
// Use the GUI executable's directory as the working directory so dependencies are found
|
||||
let cwd_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("."));
|
||||
|
|
@ -769,7 +770,27 @@ fn launch_as_admin(exe: &std::path::PathBuf, token: &str, port: u16) -> anyhow::
|
|||
|
||||
let ret = unsafe { ShellExecuteW(null_mut(), verb_wstr.as_ptr(), exe_wstr.as_ptr(), params_wstr.as_ptr(), dir_wstr.as_ptr(), 0) };
|
||||
|
||||
if ret <= 32 { anyhow::bail!("UAC denied or helper missing."); }
|
||||
// ShellExecuteW's return is a pseudo-HINSTANCE: > 32 means the call itself
|
||||
// "succeeded" — but that range INCLUDES ERROR_CANCELLED (1223), which is
|
||||
// exactly what Windows returns when the user clicks "No" on the UAC prompt.
|
||||
// The old `ret <= 32` check alone treated a user-denied prompt as success,
|
||||
// silently starting nothing and reporting a single opaque "denied or
|
||||
// missing" message that could not distinguish "no prompt ever shown"
|
||||
// (missing exe, ret<=32) from "prompt shown and declined" (ret==1223) from
|
||||
// any other Win32 failure — exactly the ambiguity blocking diagnosis here.
|
||||
if ret == 1223 {
|
||||
anyhow::bail!("UAC elevation was denied. TUN mode requires administrator privileges.");
|
||||
}
|
||||
if ret <= 32 {
|
||||
let win_err = unsafe { GetLastError() };
|
||||
anyhow::bail!(
|
||||
"Failed to request UAC elevation for the TUN helper (ShellExecuteW ret={}, \
|
||||
GetLastError={}, path={}). If this keeps happening with no prompt ever appearing, \
|
||||
an unsigned binary can be silently blocked by SmartScreen/antivirus during \
|
||||
elevation — try running ostp-gui.exe as Administrator manually.",
|
||||
ret, win_err, exe.display()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ enum Commands {
|
|||
mode: String,
|
||||
},
|
||||
/// Generate a new secure access key
|
||||
#[command(name = "gk", alias = "generate-key")]
|
||||
GenerateKey {
|
||||
/// Format for generated key (hex, base64)
|
||||
#[arg(long, default_value = "hex")]
|
||||
|
|
|
|||
Loading…
Reference in New Issue