Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
9b7aab1fc7 | |||
3c717097cb | |||
991de009be | |||
2fef329319 | |||
db7623968d | |||
7c80c88feb | |||
2c7153cd7a | |||
d730feecb4 | |||
8293b7fdae | |||
0ba415866e | |||
53b41ca166 | |||
8a75f78e63 | |||
d9692c6366 | |||
f4b0062dfc | |||
b9ffc82e53 | |||
78aaea6a45 | |||
3645fbf161 | |||
a1d0f22132 | |||
fa73b0f4bf | |||
3b76a8b839 | |||
667f42dcdc | |||
dfbe09860f | |||
9e20f9c26a | |||
f968d0cb82 | |||
2ad84f4379 | |||
c7aa16426f | |||
5987f8e3b5 | |||
3a8eb72de2 | |||
33abbdfd24 | |||
0703d6cbff | |||
10d2d14938 | |||
691cf1d8d6 | |||
d1decb8e58 | |||
7d04904109 | |||
a5acd3aa97 | |||
eea9a12560 | |||
0a4570b55c |
16
.github/workflows/Delete.yml
vendored
Normal file
16
.github/workflows/Delete.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Delete old workflow runs
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *'
|
||||
# Run monthly, at 00:00 on the 1st day of month.
|
||||
|
||||
jobs:
|
||||
del_runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Delete workflow runs
|
||||
uses: GitRML/delete-workflow-runs@main
|
||||
with:
|
||||
token: ${{ secrets.AUTH_PAT }}
|
||||
repository: ${{ github.repository }}
|
||||
retain_days: 30
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -268,18 +268,6 @@ jobs:
|
||||
generate_release_notes: true
|
||||
body_path: release.txt
|
||||
|
||||
- name: Git push assets to "release" branch
|
||||
run: |
|
||||
cd bin || exit 1
|
||||
git init
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -b release
|
||||
git add .
|
||||
git commit -m "${{ env.BUILDTIME }}"
|
||||
git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
|
||||
git push -f -u origin release
|
||||
|
||||
Upload-Release:
|
||||
permissions: write-all
|
||||
if: ${{ github.ref_type=='tag' }}
|
||||
|
16
.github/workflows/delete.yml
vendored
Normal file
16
.github/workflows/delete.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Delete old workflow runs
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *'
|
||||
# Run monthly, at 00:00 on the 1st day of month.
|
||||
|
||||
jobs:
|
||||
del_runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Delete workflow runs
|
||||
uses: GitRML/delete-workflow-runs@main
|
||||
with:
|
||||
token: ${{ secrets.AUTH_PAT }}
|
||||
repository: ${{ github.repository }}
|
||||
retain_days: 30
|
@ -30,8 +30,7 @@
|
||||
- Comprehensive HTTP RESTful API controller
|
||||
|
||||
## Wiki
|
||||
|
||||
Documentation and configuring examples are available on [Clash.Meta Wiki](https://clash-meta.wiki).
|
||||
Configuration examples can be found at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be found [Clash.Meta Wiki](https://clash-meta.wiki).
|
||||
|
||||
## Build
|
||||
|
||||
|
@ -42,6 +42,8 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
|
||||
if host == "" {
|
||||
if ip, err := netip.ParseAddr(h); err == nil {
|
||||
metadata.DstIP = ip
|
||||
} else {
|
||||
metadata.Host = h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn := inner.HandleTcp(address, urlRes.Hostname())
|
||||
conn := inner.HandleTcp(address, "")
|
||||
return conn, nil
|
||||
},
|
||||
TLSClientConfig: tls.GetDefaultTLSConfig(),
|
||||
|
@ -66,7 +66,7 @@ func (p *path) MMDB() string {
|
||||
// 目录则直接跳过
|
||||
continue
|
||||
} else {
|
||||
if strings.EqualFold(strings.ToLower(fi.Name()), "country.mmdb") {
|
||||
if strings.EqualFold(fi.Name(), "Country.mmdb") {
|
||||
GeoipName = fi.Name()
|
||||
return P.Join(p.homeDir, fi.Name())
|
||||
}
|
||||
@ -93,7 +93,7 @@ func (p *path) GeoIP() string {
|
||||
// 目录则直接跳过
|
||||
continue
|
||||
} else {
|
||||
if strings.EqualFold(strings.ToLower(fi.Name()), "geoip.dat") {
|
||||
if strings.EqualFold(fi.Name(), "GeoIP.dat") {
|
||||
GeoipName = fi.Name()
|
||||
return P.Join(p.homeDir, fi.Name())
|
||||
}
|
||||
@ -112,7 +112,7 @@ func (p *path) GeoSite() string {
|
||||
// 目录则直接跳过
|
||||
continue
|
||||
} else {
|
||||
if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") {
|
||||
if strings.EqualFold(fi.Name(), "GeoSite.dat") {
|
||||
GeositeName = fi.Name()
|
||||
return P.Join(p.homeDir, fi.Name())
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ func restart(w http.ResponseWriter, r *http.Request) {
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalln("restarting:: %s", err)
|
||||
log.Fatalln("restarting: %s", err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
|
@ -1,18 +1,12 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/Dreamacro/clash/hub/updater"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func upgradeRouter() http.Handler {
|
||||
@ -30,41 +24,5 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
execPath, err := os.Executable()
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err)))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, r, render.M{"status": "ok"})
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
|
||||
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180
|
||||
// The background context is used because the underlying functions wrap it
|
||||
// with timeout and shut down the server, which handles current request. It
|
||||
// also should be done in a separate goroutine for the same reason.
|
||||
go func() {
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.Command(execPath, os.Args[1:]...)
|
||||
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalln("restarting: %s", err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
|
||||
err = syscall.Exec(execPath, os.Args, os.Environ())
|
||||
if err != nil {
|
||||
log.Fatalln("restarting: %s", err)
|
||||
}
|
||||
}()
|
||||
restart(w, r)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package updater
|
||||
import (
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -11,7 +12,9 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
clashHttp "github.com/Dreamacro/clash/component/http"
|
||||
"github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
@ -19,30 +22,24 @@ import (
|
||||
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go
|
||||
// Updater is the Clash.Meta updater.
|
||||
var (
|
||||
client http.Client
|
||||
|
||||
goarch string
|
||||
goos string
|
||||
goarm string
|
||||
gomips string
|
||||
|
||||
workDir string
|
||||
versionCheckURL string
|
||||
workDir string
|
||||
|
||||
// mu protects all fields below.
|
||||
mu sync.RWMutex
|
||||
|
||||
// TODO(a.garipov): See if all of these fields actually have to be in
|
||||
// this struct.
|
||||
currentExeName string // 当前可执行文件
|
||||
updateDir string // 更新目录
|
||||
packageName string // 更新压缩文件
|
||||
backupDir string // 备份目录
|
||||
backupExeName string // 备份文件名
|
||||
updateExeName string // 更新后的可执行文件
|
||||
unpackedFile string
|
||||
|
||||
baseURL string = "https://ghproxy.com/https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta"
|
||||
baseURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta"
|
||||
versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt"
|
||||
packageURL string
|
||||
latestVersion string
|
||||
@ -59,9 +56,18 @@ func (e *updateError) Error() string {
|
||||
// Update performs the auto-updater. It returns an error if the updater failed.
|
||||
// If firstRun is true, it assumes the configuration file doesn't exist.
|
||||
func Update() (err error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
goos = runtime.GOOS
|
||||
goarch = runtime.GOARCH
|
||||
latestVersion = getLatestVersion()
|
||||
latestVersion, err = getLatestVersion()
|
||||
if err != nil {
|
||||
err := &updateError{Message: err.Error()}
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infoln("current version alpha-%s, latest version alpha-%s", constant.Version, latestVersion)
|
||||
|
||||
if latestVersion == constant.Version {
|
||||
err := &updateError{Message: "Already using latest version"}
|
||||
@ -69,10 +75,6 @@ func Update() (err error) {
|
||||
}
|
||||
|
||||
updateDownloadURL()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
log.Infoln("current version alpha-%s", constant.Version)
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
@ -88,7 +90,6 @@ func Update() (err error) {
|
||||
}
|
||||
|
||||
workDir = filepath.Dir(execPath)
|
||||
//log.Infoln("workDir %s", execPath)
|
||||
|
||||
err = prepare(execPath)
|
||||
if err != nil {
|
||||
@ -107,6 +108,11 @@ func Update() (err error) {
|
||||
return fmt.Errorf("unpacking: %w", err)
|
||||
}
|
||||
|
||||
err = backup()
|
||||
if err != nil {
|
||||
return fmt.Errorf("replacing: %w", err)
|
||||
}
|
||||
|
||||
err = replace()
|
||||
if err != nil {
|
||||
return fmt.Errorf("replacing: %w", err)
|
||||
@ -115,14 +121,6 @@ func Update() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionCheckURL returns the version check URL.
|
||||
func VersionCheckURL() (vcu string) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
|
||||
return versionCheckURL
|
||||
}
|
||||
|
||||
// prepare fills all necessary fields in Updater object.
|
||||
func prepare(exePath string) (err error) {
|
||||
updateDir = filepath.Join(workDir, "meta-update")
|
||||
@ -142,7 +140,7 @@ func prepare(exePath string) (err error) {
|
||||
updateExeName = "clash.meta" + "-" + goos + "-" + goarch
|
||||
}
|
||||
|
||||
log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName)
|
||||
log.Infoln("updateExeName: %s ", updateExeName)
|
||||
|
||||
backupExeName = filepath.Join(backupDir, filepath.Base(exePath))
|
||||
updateExeName = filepath.Join(updateDir, updateExeName)
|
||||
@ -168,13 +166,13 @@ func unpack() error {
|
||||
|
||||
log.Debugln("updater: unpacking package")
|
||||
if strings.HasSuffix(pkgNameOnly, ".zip") {
|
||||
unpackedFile, err = zipFileUnpack(packageName, updateDir)
|
||||
_, err = zipFileUnpack(packageName, updateDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf(".zip unpack failed: %w", err)
|
||||
}
|
||||
|
||||
} else if strings.HasSuffix(pkgNameOnly, ".gz") {
|
||||
unpackedFile, err = gzFileUnpack(packageName, updateDir)
|
||||
_, err = gzFileUnpack(packageName, updateDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf(".gz unpack failed: %w", err)
|
||||
}
|
||||
@ -186,25 +184,37 @@ func unpack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// backup makes a backup of the current configuration and supporting files. It
|
||||
// ignores the configuration file if firstRun is true.
|
||||
func backup() (err error) {
|
||||
log.Infoln("updater: backing up current Exefile")
|
||||
_ = os.Mkdir(backupDir, 0o755)
|
||||
|
||||
err = copyFile(currentExeName, backupExeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", currentExeName, backupExeName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// replace moves the current executable with the updated one and also copies the
|
||||
// supporting files.
|
||||
func replace() error {
|
||||
//err := copySupportingFiles(unpackedFiles, updateDir, workDir)
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err)
|
||||
//}
|
||||
var err error
|
||||
|
||||
log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
|
||||
err := os.Rename(currentExeName, backupExeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
|
||||
// err := os.Rename(currentExeName, backupExeName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if goos == "windows" {
|
||||
// rename fails with "File in use" error
|
||||
log.Infoln("copying:%s to %s", updateExeName, currentExeName)
|
||||
log.Infoln("copying: %s to %s", updateExeName, currentExeName)
|
||||
err = copyFile(updateExeName, currentExeName)
|
||||
} else {
|
||||
log.Infoln("copying: %s to %s", updateExeName, currentExeName)
|
||||
err = os.Rename(updateExeName, currentExeName)
|
||||
}
|
||||
if err != nil {
|
||||
@ -226,8 +236,11 @@ const MaxPackageFileSize = 32 * 1024 * 1024
|
||||
|
||||
// Download package file and save it to disk
|
||||
func downloadPackageFile() (err error) {
|
||||
var resp *http.Response
|
||||
resp, err = client.Get(packageURL)
|
||||
// var resp *http.Response
|
||||
// resp, err = client.Get(packageURL)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
|
||||
defer cancel()
|
||||
resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http request failed: %w", err)
|
||||
}
|
||||
@ -255,11 +268,11 @@ func downloadPackageFile() (err error) {
|
||||
log.Debugln("updateDir %s", updateDir)
|
||||
err = os.Mkdir(updateDir, 0o755)
|
||||
if err != nil {
|
||||
fmt.Errorf("mkdir error: %w", err)
|
||||
return fmt.Errorf("mkdir error: %w", err)
|
||||
}
|
||||
|
||||
log.Debugln("updater: saving package to file %s", packageName)
|
||||
err = os.WriteFile(packageName, body, 0o755)
|
||||
err = os.WriteFile(packageName, body, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("os.WriteFile() failed: %w", err)
|
||||
}
|
||||
@ -405,10 +418,12 @@ func copyFile(src, dst string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLatestVersion() string {
|
||||
resp, err := http.Get(versionURL)
|
||||
func getLatestVersion() (version string, err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
resp, err := clashHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil)
|
||||
if err != nil {
|
||||
return ""
|
||||
return "", fmt.Errorf("get Latest Version fail: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
closeErr := resp.Body.Close()
|
||||
@ -419,11 +434,10 @@ func getLatestVersion() string {
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ""
|
||||
return "", fmt.Errorf("get Latest Version fail: %w", err)
|
||||
}
|
||||
content := strings.TrimRight(string(body), "\n")
|
||||
log.Infoln("latest:%s", content)
|
||||
return content
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func updateDownloadURL() {
|
||||
|
Reference in New Issue
Block a user