mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-02 21:12:13 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
160bb4fe8e | ||
|
|
a5b22fa512 |
@@ -13,6 +13,7 @@
|
||||
- 修复 代理 页展开分组后不显示最新测速结果,需切换节点才刷新
|
||||
- 修复 代理 在全局模式下无法展开节点组
|
||||
- 修复 自定义 CSS 无法修改编辑器字体和字号的问题
|
||||
- 修复 WebDAV 设置清空后无法保存
|
||||
|
||||
**🍎 macOS**
|
||||
|
||||
|
||||
@@ -134,6 +134,31 @@ function fetchBranches() {
|
||||
)
|
||||
}
|
||||
|
||||
function discardClosedCandidate(branch, base, prefix) {
|
||||
const candidates = list(
|
||||
`pulls?state=all&base=${base}&head=${repository.split('/')[0]}:${branch}&per_page=100`,
|
||||
)
|
||||
const latest = candidates[0]
|
||||
if (!latest || candidates.some((pr) => pr.state === 'open'))
|
||||
throw new Error(
|
||||
`Branch ${branch} is reserved by an active or unknown candidate.`,
|
||||
)
|
||||
const pr = get(`pulls/${latest.number}`)
|
||||
requireBotPr(pr, base, prefix)
|
||||
if (pr.state !== 'closed' || pr.merged)
|
||||
throw new Error(
|
||||
`Close the unmerged candidate before retrying: ${pr.html_url}`,
|
||||
)
|
||||
updateRefs(pr.base.repo.node_id, [
|
||||
{
|
||||
name: `refs/heads/${branch}`,
|
||||
beforeOid: pr.head.sha,
|
||||
afterOid: '0'.repeat(40),
|
||||
},
|
||||
])
|
||||
summary(`Released the unused branch from closed PR: ${pr.html_url}`)
|
||||
}
|
||||
|
||||
function prepare() {
|
||||
let refresh
|
||||
if (env.GITHUB_EVENT_NAME === 'pull_request_target') {
|
||||
@@ -211,8 +236,15 @@ function prepare() {
|
||||
: last
|
||||
? `${base}-rc.${BigInt(last.match(rcPattern)[4] ?? 0) + 1n}`
|
||||
: `${base}-rc`)
|
||||
if (occupied.includes(tag))
|
||||
throw new Error(`Tag ${tag} already exists or is reserved.`)
|
||||
if (tags.includes(tag)) throw new Error(`Tag ${tag} already exists.`)
|
||||
for (const [prefix, target] of [
|
||||
['release/', 'dev'],
|
||||
['publish/', 'main'],
|
||||
]) {
|
||||
const branch = `${prefix}${tag}`
|
||||
if (refs.includes(`refs/heads/${branch}`) && branch !== refresh?.head.ref)
|
||||
discardClosedCandidate(branch, target, prefix)
|
||||
}
|
||||
const sha = git('rev-parse', 'HEAD')
|
||||
const body = releaseBody(tag, sha, tags, false)
|
||||
summary(body)
|
||||
@@ -453,7 +485,7 @@ function publishPr() {
|
||||
validateVersionCommit(tag, sha)
|
||||
const branch = `publish/${tag}`
|
||||
const existing = list(
|
||||
`pulls?state=all&base=main&head=${repository.split('/')[0]}:${branch}&per_page=100`,
|
||||
`pulls?state=open&base=main&head=${repository.split('/')[0]}:${branch}&per_page=100`,
|
||||
)
|
||||
if (existing.length) {
|
||||
const publication = existing[0]
|
||||
@@ -567,28 +599,31 @@ function publish() {
|
||||
requireApproval(pr, sha)
|
||||
const main = git('rev-parse', 'origin/main')
|
||||
git('merge-base', '--is-ancestor', main, sha)
|
||||
const object = api('git/tags', {
|
||||
git(
|
||||
'-c',
|
||||
'user.name=github-actions[bot]',
|
||||
'-c',
|
||||
'user.email=41898282+github-actions[bot]@users.noreply.github.com',
|
||||
'tag',
|
||||
'--annotate',
|
||||
'--no-sign',
|
||||
'--message',
|
||||
marker,
|
||||
tag,
|
||||
message: marker,
|
||||
object: sha,
|
||||
type: 'commit',
|
||||
})
|
||||
// The tag records run ownership in the same transaction as the fast-forward.
|
||||
updateRefs(pr.base.repo.node_id, [
|
||||
{ name: 'refs/heads/main', beforeOid: main, afterOid: sha, force: false },
|
||||
{
|
||||
name: `refs/heads/${pr.head.ref}`,
|
||||
beforeOid: sha,
|
||||
afterOid: sha,
|
||||
force: false,
|
||||
},
|
||||
{
|
||||
name: `refs/tags/${tag}`,
|
||||
beforeOid: '0'.repeat(40),
|
||||
afterOid: object.sha,
|
||||
force: false,
|
||||
},
|
||||
])
|
||||
sha,
|
||||
)
|
||||
// GraphQL updateRefs rejects annotated tag objects; Git preserves atomicity.
|
||||
git(
|
||||
'push',
|
||||
'--atomic',
|
||||
`--force-with-lease=refs/heads/main:${main}`,
|
||||
`--force-with-lease=refs/heads/${pr.head.ref}:${sha}`,
|
||||
`--force-with-lease=refs/tags/${tag}:`,
|
||||
'origin',
|
||||
`${sha}:refs/heads/main`,
|
||||
`${sha}:refs/heads/${pr.head.ref}`,
|
||||
`refs/tags/${tag}:refs/tags/${tag}`,
|
||||
)
|
||||
summary(`Fast-forwarded main and tagged ${tag} at approved commit ${sha}.`)
|
||||
}
|
||||
releaseOutput(tag, sha)
|
||||
|
||||
@@ -40,9 +40,9 @@ export const BackupConfigViewer = memo(
|
||||
|
||||
const { register, handleSubmit, watch } = useForm<IWebDavConfig>({
|
||||
defaultValues: {
|
||||
url: webdav_url!,
|
||||
username: webdav_username!,
|
||||
password: webdav_password!,
|
||||
url: webdav_url ?? '',
|
||||
username: webdav_username ?? '',
|
||||
password: webdav_password ?? '',
|
||||
},
|
||||
})
|
||||
const url = watch('url')
|
||||
@@ -50,9 +50,9 @@ export const BackupConfigViewer = memo(
|
||||
const password = watch('password')
|
||||
|
||||
const webdavChanged =
|
||||
webdav_url !== url ||
|
||||
webdav_username !== username ||
|
||||
webdav_password !== password
|
||||
(webdav_url ?? '') !== url ||
|
||||
(webdav_username ?? '') !== username ||
|
||||
(webdav_password ?? '') !== password
|
||||
|
||||
const webdavSignature = buildWebdavSignature(verge)
|
||||
const webdavStatus = getWebdavStatus(webdavSignature)
|
||||
@@ -69,9 +69,9 @@ export const BackupConfigViewer = memo(
|
||||
}, [webdav_url, webdav_username, webdav_password, onInit, shouldAutoInit])
|
||||
|
||||
const checkForm = () => {
|
||||
const username = usernameRef.current?.value
|
||||
const username = usernameRef.current?.value.trim()
|
||||
const password = passwordRef.current?.value
|
||||
const url = urlRef.current?.value
|
||||
const url = urlRef.current?.value.trim()
|
||||
|
||||
if (!url) {
|
||||
urlRef.current?.focus()
|
||||
@@ -95,14 +95,15 @@ export const BackupConfigViewer = memo(
|
||||
}
|
||||
|
||||
const save = useLockFn(async (data: IWebDavConfig) => {
|
||||
checkForm()
|
||||
const trimmedUrl = data.url.trim()
|
||||
const trimmedUsername = data.username.trim()
|
||||
const isClearing = !trimmedUrl && !trimmedUsername && !data.password
|
||||
if (!isClearing) checkForm()
|
||||
const signature = buildWebdavSignature({
|
||||
webdav_url: data.url,
|
||||
webdav_username: data.username,
|
||||
webdav_password: data.password,
|
||||
})
|
||||
const trimmedUrl = data.url.trim()
|
||||
const trimmedUsername = data.username.trim()
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
@@ -127,7 +128,7 @@ export const BackupConfigViewer = memo(
|
||||
)
|
||||
setWebdavStatus(signature, 'unknown')
|
||||
showNotice.success('settings.modals.backup.messages.webdavConfigSaved')
|
||||
await onSaveSuccess(signature)
|
||||
if (!isClearing) await onSaveSuccess(signature)
|
||||
} catch (error) {
|
||||
showNotice.error(
|
||||
'settings.modals.backup.messages.webdavConfigSaveFailed',
|
||||
@@ -223,9 +224,9 @@ export const BackupConfigViewer = memo(
|
||||
}}
|
||||
>
|
||||
{webdavChanged ||
|
||||
webdav_url === undefined ||
|
||||
webdav_username === undefined ||
|
||||
webdav_password === undefined ? (
|
||||
!webdav_url ||
|
||||
!webdav_username ||
|
||||
!webdav_password ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color={'primary'}
|
||||
|
||||
@@ -43,7 +43,11 @@ export const getWebdavStatus = (signature: string): WebdavStatus => {
|
||||
}
|
||||
|
||||
export const setWebdavStatus = (signature: string, status: WebdavStatus) => {
|
||||
if (!signature || !canUseStorage()) return
|
||||
if (!canUseStorage()) return
|
||||
if (!signature) {
|
||||
localStorage.removeItem(WEBDAV_STATUS_KEY)
|
||||
return
|
||||
}
|
||||
|
||||
const payload: WebdavStatusCache = {
|
||||
signature,
|
||||
|
||||
Reference in New Issue
Block a user