mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
148468eee3 | ||
|
79083e5664 | ||
|
c52d2bc0b6 | ||
|
54d4f2ec57 | ||
|
a25b5cb894 | ||
|
6caa8fcd84 | ||
|
66270eee77 |
26
.github/workflows/build.yaml
vendored
26
.github/workflows/build.yaml
vendored
@@ -68,7 +68,8 @@ jobs:
|
||||
- tarball
|
||||
strategy:
|
||||
matrix:
|
||||
os: ${{fromJson(needs.setup-matrix.outputs.matrix)}}
|
||||
# Only build gp on amd64, as the arm64 package will be built in release.yaml
|
||||
os: [{runner: ubuntu-latest, arch: amd64}]
|
||||
package: [deb, rpm, pkg, binary]
|
||||
runs-on: ${{ matrix.os.runner }}
|
||||
name: build-gp (${{ matrix.package }}, ${{ matrix.os.arch }})
|
||||
@@ -167,22 +168,23 @@ jobs:
|
||||
steps:
|
||||
- name: Prepare workspace
|
||||
run: rm -rf gh-release && mkdir gh-release
|
||||
|
||||
- name: Checkout GlobalProtect-openconnect
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
repository: yuezk/GlobalProtect-openconnect
|
||||
ref: ${{ github.ref }}
|
||||
path: gh-release/gp
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: gh-release
|
||||
path: gh-release/gp/.build/artifacts
|
||||
|
||||
- name: Create GH release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
RELEASE_TAG: ${{ github.ref == 'refs/heads/dev' && 'snapshot' || github.ref_name }}
|
||||
REPO: ${{ github.repository }}
|
||||
NOTES: ${{ github.ref == 'refs/heads/dev' && '**!!! DO NOT USE THIS RELEASE IN PRODUCTION !!!**' || format('Release {0}', github.ref_name) }}
|
||||
run: |
|
||||
gh -R "$REPO" release delete $RELEASE_TAG --yes --cleanup-tag || true
|
||||
gh -R "$REPO" release create $RELEASE_TAG \
|
||||
--title "$RELEASE_TAG" \
|
||||
--notes "$NOTES" \
|
||||
${{ github.ref == 'refs/heads/dev' && '--target dev' || '' }} \
|
||||
${{ github.ref == 'refs/heads/dev' && '--prerelease' || '' }} \
|
||||
gh-release/artifact-source/* \
|
||||
gh-release/artifact-gpgui-*/*
|
||||
cd gh-release/gp/scripts && ./gh-release.sh "$RELEASE_TAG"
|
||||
|
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -570,7 +570,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"is_executable",
|
||||
]
|
||||
@@ -898,24 +898,6 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy_macro"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0235d912a8c749f4e0c9f18ca253b4c28cfefc1d2518096016d6e3230b6424"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "1.0.9"
|
||||
@@ -1436,13 +1418,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gpapi"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.5",
|
||||
"chacha20poly1305",
|
||||
"clap",
|
||||
"dotenvy_macro",
|
||||
"log",
|
||||
"md5",
|
||||
"open",
|
||||
@@ -1470,7 +1451,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gpauth"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1491,7 +1472,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gpclient"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1513,7 +1494,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gpgui-helper"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1531,7 +1512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gpservice"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -2545,7 +2526,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openconnect"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"common",
|
||||
|
@@ -5,7 +5,7 @@ members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth", "apps/g
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.70"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
|
||||
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
|
||||
edition = "2021"
|
||||
@@ -41,7 +41,6 @@ uzers = "0.11"
|
||||
whoami = "1"
|
||||
thiserror = "1"
|
||||
redact-engine = "0.1"
|
||||
dotenvy_macro = "0.15"
|
||||
compile-time = "0.2"
|
||||
serde_urlencoded = "0.7"
|
||||
md5="0.7"
|
||||
|
@@ -19,7 +19,7 @@ pub(crate) struct SharedArgs {
|
||||
#[derive(Subcommand)]
|
||||
enum CliCommand {
|
||||
#[command(about = "Connect to a portal server")]
|
||||
Connect(ConnectArgs),
|
||||
Connect(Box<ConnectArgs>),
|
||||
#[command(about = "Disconnect from the server")]
|
||||
Disconnect,
|
||||
#[command(about = "Launch the GUI")]
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
use std::{collections::HashMap, env::temp_dir, fs, path::PathBuf};
|
||||
|
||||
use clap::Args;
|
||||
use directories::ProjectDirs;
|
||||
@@ -82,6 +82,11 @@ impl<'a> LaunchGuiHandler<'a> {
|
||||
|
||||
async fn feed_auth_data(auth_data: &str) -> anyhow::Result<()> {
|
||||
let _ = tokio::join!(feed_auth_data_gui(auth_data), feed_auth_data_cli(auth_data));
|
||||
|
||||
// Cleanup the temporary file
|
||||
let html_file = temp_dir().join("gpauth.html");
|
||||
let _ = std::fs::remove_file(html_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
11
changelog.md
11
changelog.md
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 2.3.2 - 2024-06-17
|
||||
|
||||
- Fix the CAS callback parsing issue (fix [#372](https://github.com/yuezk/GlobalProtect-openconnect/issues/372))
|
||||
- CLI: fix the `/tmp/gpauth.html` deletion issue (fix [#366](https://github.com/yuezk/GlobalProtect-openconnect/issues/366))
|
||||
- GUI: fix the license not working after reboot (fix [#376](https://github.com/yuezk/GlobalProtect-openconnect/issues/376))
|
||||
- GUI: add the license activation management link
|
||||
|
||||
## 2.3.1 - 2024-05-21
|
||||
|
||||
- Fix the `--sslkey` option not working
|
||||
|
||||
## 2.3.0 - 2024-05-20
|
||||
|
||||
- Support client certificate authentication (fix [#363](https://github.com/yuezk/GlobalProtect-openconnect/issues/363))
|
||||
|
@@ -25,7 +25,6 @@ chacha20poly1305 = { version = "0.10", features = ["std"] }
|
||||
redact-engine.workspace = true
|
||||
url.workspace = true
|
||||
regex.workspace = true
|
||||
dotenvy_macro.workspace = true
|
||||
uzers.workspace = true
|
||||
serde_urlencoded.workspace = true
|
||||
md5.workspace = true
|
||||
|
19
crates/gpapi/build.rs
Normal file
19
crates/gpapi/build.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let workspace_dir = Path::new(manifest_dir).ancestors().nth(2).unwrap();
|
||||
let gpgui_dir = workspace_dir.parent().unwrap().join("gpgui");
|
||||
|
||||
let gp_service_binary = workspace_dir.join("target/debug/gpservice");
|
||||
let gp_client_binary = workspace_dir.join("target/debug/gpclient");
|
||||
let gp_auth_binary = workspace_dir.join("target/debug/gpauth");
|
||||
let gp_gui_helper_binary = workspace_dir.join("target/debug/gpgui-helper");
|
||||
let gp_gui_binary = gpgui_dir.join("target/debug/gpgui");
|
||||
|
||||
println!("cargo:rustc-env=GP_SERVICE_BINARY={}", gp_service_binary.display());
|
||||
println!("cargo:rustc-env=GP_CLIENT_BINARY={}", gp_client_binary.display());
|
||||
println!("cargo:rustc-env=GP_AUTH_BINARY={}", gp_auth_binary.display());
|
||||
println!("cargo:rustc-env=GP_GUI_HELPER_BINARY={}", gp_gui_helper_binary.display());
|
||||
println!("cargo:rustc-env=GP_GUI_BINARY={}", gp_gui_binary.display());
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
|
||||
use log::{info, warn};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -68,23 +70,29 @@ impl SamlAuthData {
|
||||
if auth_data.starts_with("cas-as") {
|
||||
info!("Got CAS auth data from globalprotectcallback");
|
||||
|
||||
let auth_data: SamlAuthData = serde_urlencoded::from_str(auth_data).map_err(|e| {
|
||||
// Decode the auth data and use the original value if decoding fails
|
||||
let auth_data = urlencoding::decode(auth_data).unwrap_or_else(|err| {
|
||||
warn!("Failed to decode token auth data: {}", err);
|
||||
Cow::Borrowed(auth_data)
|
||||
});
|
||||
|
||||
let auth_data: SamlAuthData = serde_urlencoded::from_str(auth_data.borrow()).map_err(|e| {
|
||||
warn!("Failed to parse token auth data: {}", e);
|
||||
warn!("Auth data: {}", auth_data);
|
||||
AuthDataParseError::Invalid
|
||||
})?;
|
||||
|
||||
Ok(auth_data)
|
||||
} else {
|
||||
info!("Parsing SAML auth data...");
|
||||
|
||||
let auth_data = decode_to_string(auth_data).map_err(|e| {
|
||||
warn!("Failed to decode SAML auth data: {}", e);
|
||||
AuthDataParseError::Invalid
|
||||
})?;
|
||||
let auth_data = Self::from_html(&auth_data)?;
|
||||
|
||||
Ok(auth_data)
|
||||
return Ok(auth_data);
|
||||
}
|
||||
|
||||
info!("Parsing SAML auth data...");
|
||||
let auth_data = decode_to_string(auth_data).map_err(|e| {
|
||||
warn!("Failed to decode SAML auth data: {}", e);
|
||||
AuthDataParseError::Invalid
|
||||
})?;
|
||||
let auth_data = Self::from_html(&auth_data)?;
|
||||
|
||||
Ok(auth_data)
|
||||
}
|
||||
|
||||
pub fn username(&self) -> &str {
|
||||
@@ -142,6 +150,16 @@ mod tests {
|
||||
assert_eq!(auth_data.token(), Some("very_long_string"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_data_from_gpcallback_cas_urlencoded() {
|
||||
let auth_data = "globalprotectcallback:cas-as%3D1%26un%3Dxyz%40email.com%26token%3Dvery_long_string";
|
||||
|
||||
let auth_data = SamlAuthData::from_gpcallback(auth_data).unwrap();
|
||||
|
||||
assert_eq!(auth_data.username(), "xyz@email.com");
|
||||
assert_eq!(auth_data.token(), Some("very_long_string"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_data_from_gpcallback_non_cas() {
|
||||
let auth_data = "PGh0bWw+PCEtLSA8c2FtbC1hdXRoLXN0YXR1cz4xPC9zYW1sLWF1dGgtc3RhdHVzPjxwcmVsb2dpbi1jb29raWU+cHJlbG9naW4tY29va2llPC9wcmVsb2dpbi1jb29raWU+PHNhbWwtdXNlcm5hbWU+eHl6QGVtYWlsLmNvbTwvc2FtbC11c2VybmFtZT48c2FtbC1zbG8+bm88L3NhbWwtc2xvPjxzYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+PC9zYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+IC0tPjwvaHRtbD4=";
|
||||
|
@@ -29,12 +29,12 @@ pub const GP_GUI_HELPER_BINARY: &str = "/usr/bin/gpgui-helper";
|
||||
pub(crate) const GP_AUTH_BINARY: &str = "/usr/bin/gpauth";
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_CLIENT_BINARY: &str = dotenvy_macro::dotenv!("GP_CLIENT_BINARY");
|
||||
pub const GP_CLIENT_BINARY: &str = env!("GP_CLIENT_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_SERVICE_BINARY: &str = dotenvy_macro::dotenv!("GP_SERVICE_BINARY");
|
||||
pub const GP_SERVICE_BINARY: &str = env!("GP_SERVICE_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_GUI_BINARY: &str = dotenvy_macro::dotenv!("GP_GUI_BINARY");
|
||||
pub const GP_GUI_BINARY: &str = env!("GP_GUI_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_GUI_HELPER_BINARY: &str = dotenvy_macro::dotenv!("GP_GUI_HELPER_BINARY");
|
||||
pub const GP_GUI_HELPER_BINARY: &str = env!("GP_GUI_HELPER_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) const GP_AUTH_BINARY: &str = dotenvy_macro::dotenv!("GP_AUTH_BINARY");
|
||||
pub(crate) const GP_AUTH_BINARY: &str = env!("GP_AUTH_BINARY");
|
||||
|
@@ -1,4 +1,7 @@
|
||||
use std::{env::temp_dir, io::Write};
|
||||
use std::{env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt};
|
||||
|
||||
use anyhow::bail;
|
||||
use log::warn;
|
||||
|
||||
pub struct BrowserAuthenticator<'a> {
|
||||
auth_request: &'a str,
|
||||
@@ -14,8 +17,18 @@ impl BrowserAuthenticator<'_> {
|
||||
open::that_detached(self.auth_request)?;
|
||||
} else {
|
||||
let html_file = temp_dir().join("gpauth.html");
|
||||
let mut file = std::fs::File::create(&html_file)?;
|
||||
|
||||
// Remove the file and error if permission denied
|
||||
if let Err(err) = fs::remove_file(&html_file) {
|
||||
if err.kind() != std::io::ErrorKind::NotFound {
|
||||
warn!("Failed to remove the temporary file: {}", err);
|
||||
bail!("Please remove the file manually: {:?}", html_file);
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = fs::File::create(&html_file)?;
|
||||
|
||||
file.set_permissions(fs::Permissions::from_mode(0o600))?;
|
||||
file.write_all(self.auth_request.as_bytes())?;
|
||||
|
||||
open::that_detached(html_file)?;
|
||||
@@ -24,11 +37,3 @@ impl BrowserAuthenticator<'_> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BrowserAuthenticator<'_> {
|
||||
fn drop(&mut self) {
|
||||
// Cleanup the temporary file
|
||||
let html_file = temp_dir().join("gpauth.html");
|
||||
let _ = std::fs::remove_file(html_file);
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ static vpn_connected_callback on_vpn_connected;
|
||||
/* Validate the peer certificate */
|
||||
static int validate_peer_cert(__attribute__((unused)) void *_vpninfo, const char *reason)
|
||||
{
|
||||
INFO("Validating peer cert: %s", reason);
|
||||
INFO("Accepting the server certificate though %s", reason);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,9 @@ static void print_progress(__attribute__((unused)) void *_vpninfo, int level, co
|
||||
char *message = format_message(format, args);
|
||||
va_end(args);
|
||||
|
||||
if (message == NULL)
|
||||
{
|
||||
if (message == NULL) {
|
||||
ERROR("Failed to format log message");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
LOG(level, message);
|
||||
free(message);
|
||||
}
|
||||
@@ -63,16 +60,13 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
|
||||
INFO("OS: %s", options->os);
|
||||
INFO("CSD_USER: %d", options->csd_uid);
|
||||
INFO("CSD_WRAPPER: %s", options->csd_wrapper);
|
||||
INFO("CERTIFICATE: %s", options->certificate);
|
||||
INFO("SSLKEY: %s", options->sslkey);
|
||||
INFO("RECONNECT_TIMEOUT: %d", options->reconnect_timeout);
|
||||
INFO("MTU: %d", options->mtu);
|
||||
INFO("DISABLE_IPV6: %d", options->disable_ipv6);
|
||||
|
||||
vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL);
|
||||
|
||||
if (!vpninfo)
|
||||
{
|
||||
if (!vpninfo) {
|
||||
ERROR("openconnect_vpninfo_new failed");
|
||||
return 1;
|
||||
}
|
||||
@@ -82,25 +76,18 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
|
||||
openconnect_set_protocol(vpninfo, "gp");
|
||||
openconnect_set_hostname(vpninfo, options->server);
|
||||
openconnect_set_cookie(vpninfo, options->cookie);
|
||||
openconnect_set_client_cert(vpninfo, options->certificate, options->sslkey);
|
||||
|
||||
if (options->key_password) {
|
||||
openconnect_set_key_password(vpninfo, options->key_password);
|
||||
}
|
||||
|
||||
if (options->os) {
|
||||
openconnect_set_reported_os(vpninfo, options->os);
|
||||
}
|
||||
|
||||
if (options->certificate)
|
||||
{
|
||||
if (options->certificate) {
|
||||
INFO("Setting client certificate: %s", options->certificate);
|
||||
openconnect_set_client_cert(vpninfo, options->certificate, NULL);
|
||||
openconnect_set_client_cert(vpninfo, options->certificate, options->sslkey);
|
||||
}
|
||||
|
||||
if (options->servercert) {
|
||||
INFO("Setting server certificate: %s", options->servercert);
|
||||
openconnect_set_system_trust(vpninfo, 0);
|
||||
if (options->key_password) {
|
||||
openconnect_set_key_password(vpninfo, options->key_password);
|
||||
}
|
||||
|
||||
if (options->csd_wrapper) {
|
||||
@@ -117,38 +104,32 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
|
||||
}
|
||||
|
||||
g_cmd_pipe_fd = openconnect_setup_cmd_pipe(vpninfo);
|
||||
if (g_cmd_pipe_fd < 0)
|
||||
{
|
||||
if (g_cmd_pipe_fd < 0) {
|
||||
ERROR("openconnect_setup_cmd_pipe failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!uname(&utsbuf))
|
||||
{
|
||||
if (!uname(&utsbuf)) {
|
||||
openconnect_set_localname(vpninfo, utsbuf.nodename);
|
||||
}
|
||||
|
||||
// Essential step
|
||||
if (openconnect_make_cstp_connection(vpninfo) != 0)
|
||||
{
|
||||
if (openconnect_make_cstp_connection(vpninfo) != 0) {
|
||||
ERROR("openconnect_make_cstp_connection failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (openconnect_setup_dtls(vpninfo, 60) != 0)
|
||||
{
|
||||
if (openconnect_setup_dtls(vpninfo, 60) != 0) {
|
||||
openconnect_disable_dtls(vpninfo);
|
||||
}
|
||||
|
||||
// Essential step
|
||||
openconnect_set_setup_tun_handler(vpninfo, setup_tun_handler);
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (1) {
|
||||
int ret = openconnect_mainloop(vpninfo, options->reconnect_timeout, 10);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
if (ret) {
|
||||
INFO("openconnect_mainloop returned %d, exiting", ret);
|
||||
openconnect_vpninfo_free(vpninfo);
|
||||
return ret;
|
||||
@@ -165,8 +146,7 @@ void vpn_disconnect()
|
||||
|
||||
INFO("Stopping VPN connection: %d", g_cmd_pipe_fd);
|
||||
|
||||
if (write(g_cmd_pipe_fd, &cmd, 1) < 0)
|
||||
{
|
||||
if (write(g_cmd_pipe_fd, &cmd, 1) < 0) {
|
||||
ERROR("Failed to write to command pipe, VPN connection may not be stopped");
|
||||
}
|
||||
}
|
||||
|
51
scripts/gh-release.sh
Executable file
51
scripts/gh-release.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: ./scripts/gh-release.sh <tag>
|
||||
|
||||
set -e
|
||||
|
||||
REPO="yuezk/GlobalProtect-openconnect"
|
||||
TAG=$1
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
RELEASE_NOTES="Release $TAG"
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Usage: ./scripts/gh-release.sh <tag>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# For snapshot release, we don't create a release, just clear the existing assets and upload new ones.
|
||||
# This is to avoid notification spam.
|
||||
release_snapshot() {
|
||||
RELEASE_NOTES='**!!! DO NOT USE THIS RELEASE IN PRODUCTION !!!**'
|
||||
|
||||
# Get the existing assets
|
||||
gh -R "$REPO" release view "$TAG" --json assets --jq '.assets[].name' \
|
||||
| xargs -I {} gh -R "$REPO" release delete-asset "$TAG" {} --yes
|
||||
|
||||
echo "Uploading new assets..."
|
||||
gh -R "$REPO" release upload "$TAG" \
|
||||
"$PROJECT_DIR"/.build/artifacts/artifact-source/* \
|
||||
"$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/*
|
||||
}
|
||||
|
||||
release_tag() {
|
||||
echo "Removing existing release..."
|
||||
gh -R "$REPO" release delete $TAG --yes --cleanup-tag || true
|
||||
|
||||
echo "Creating release..."
|
||||
gh -R "$REPO" release create $TAG \
|
||||
--title "$TAG" \
|
||||
--notes "$RELEASE_NOTES" \
|
||||
"$PROJECT_DIR"/.build/artifacts/artifact-source/* \
|
||||
"$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/*
|
||||
}
|
||||
|
||||
if [[ $TAG == *"snapshot" ]]; then
|
||||
release_snapshot
|
||||
else
|
||||
release_tag
|
||||
fi
|
Reference in New Issue
Block a user