Fix Gist setup and first-run bootstrap
This commit is contained in:
@@ -18,7 +18,7 @@ macOS/Linux:
|
|||||||
curl -fsSL https://git.okk.cool/purp1e/zed-sync/raw/branch/master/bootstrap-from-gist.sh | sh
|
curl -fsSL https://git.okk.cool/purp1e/zed-sync/raw/branch/master/bootstrap-from-gist.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
脚本会处理 GitHub 登录、询问 Gist URL/ID、从 Gist 下载单个 `zed-settings-sync.json`、备份当前 Zed 配置并完成安装。安装后重启 Zed。
|
脚本会处理 GitHub 登录并询问 Gist URL/ID。输入已有 Gist 会下载其配置;直接留空会从本仓库种子配置自动创建新的 Secret Gist。随后脚本会备份当前 Zed 配置并完成安装,安装后重启 Zed。
|
||||||
|
|
||||||
For first-machine setup, new-machine onboarding, daily Gist synchronization, and conflict handling, see [`SYNC-MANUAL.zh-CN.md`](./SYNC-MANUAL.zh-CN.md).
|
For first-machine setup, new-machine onboarding, daily Gist synchronization, and conflict handling, see [`SYNC-MANUAL.zh-CN.md`](./SYNC-MANUAL.zh-CN.md).
|
||||||
|
|
||||||
@@ -38,12 +38,12 @@ For first-machine setup, new-machine onboarding, daily Gist synchronization, and
|
|||||||
Open the Task Picker and search for `Zed Settings`:
|
Open the Task Picker and search for `Zed Settings`:
|
||||||
|
|
||||||
- `Export JSON...` and `Import JSON...` show a native file picker.
|
- `Export JSON...` and `Import JSON...` show a native file picker.
|
||||||
- `Configure GitHub Gist...` accepts a Gist ID or complete URL and remembers it on this machine.
|
- `Configure GitHub Gist...` accepts an existing Gist ID or complete URL and remembers it on this machine.
|
||||||
- `Show GitHub Gist Status` compares the current, remote, and last synchronized SHA-256 hashes.
|
- `Show GitHub Gist Status` compares the current, remote, and last synchronized SHA-256 hashes.
|
||||||
- `Push to GitHub Gist` and `Pull from GitHub Gist` use the remembered Gist and ask before overwriting divergent settings.
|
- `Push to GitHub Gist` creates a new Secret Gist when none is configured; later Push/Pull operations use the remembered Gist and ask before overwriting divergent settings.
|
||||||
- `Authenticate GitHub...` runs `gh auth login` in the terminal.
|
- `Authenticate GitHub...` runs `gh auth login` in the terminal.
|
||||||
|
|
||||||
GitHub CLI (`gh`) must be installed. The Gist must already exist and may contain other files; synchronization only creates or replaces `zed-settings-sync.json` inside it.
|
GitHub CLI (`gh`) must be installed and authenticated. An existing Gist may contain other files; synchronization only creates or replaces `zed-settings-sync.json` inside it.
|
||||||
|
|
||||||
The synchronized JSON is deliberately flat. Its top-level keys are paths such as `settings.json`, `keymap.json`, and `scripts/file-exclusions.json`, whose values are the complete file contents. No version, metadata, `files` wrapper, or hash is embedded. SHA-256 values are calculated at runtime and only the last synchronized value is retained locally.
|
The synchronized JSON is deliberately flat. Its top-level keys are paths such as `settings.json`, `keymap.json`, and `scripts/file-exclusions.json`, whose values are the complete file contents. No version, metadata, `files` wrapper, or hash is embedded. SHA-256 values are calculated at runtime and only the last synchronized value is retained locally.
|
||||||
|
|
||||||
|
|||||||
+9
-10
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
## 2. 准备 Gist
|
## 2. 准备 Gist
|
||||||
|
|
||||||
1. 登录 GitHub,打开 <https://gist.github.com/>。
|
不需要预先创建 Gist:第一次执行 Push 时,如果本机尚未配置 Gist,系统会询问是否自动创建新的 Secret Gist。
|
||||||
2. 创建一个 Secret Gist。GitHub 不允许创建完全空白的 Gist,可以先放一个 `README.md`。
|
|
||||||
3. 复制完整 Gist URL 或末尾的 Gist ID。
|
已有 Gist 时,也可以登录 GitHub、打开 <https://gist.github.com/>,复制完整 Gist URL 或末尾的 Gist ID,再通过 `Configure GitHub Gist...` 关联。
|
||||||
|
|
||||||
Gist ID 不是访问令牌。身份认证由 GitHub CLI `gh` 管理。
|
Gist ID 不是访问令牌。身份认证由 GitHub CLI `gh` 管理。
|
||||||
|
|
||||||
@@ -58,13 +58,12 @@ Zed Settings: Authenticate GitHub...
|
|||||||
gh auth login
|
gh auth login
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 保存 Gist
|
### 3.2 创建或关联 Gist
|
||||||
|
|
||||||
1. 按 `Mod+R`。
|
1. 按 `Mod+R`。
|
||||||
2. 运行 `Zed Settings: Configure GitHub Gist...`。
|
2. 没有 Gist 时直接运行 `Zed Settings: Push to GitHub Gist`,确认自动创建。
|
||||||
3. 粘贴 Gist URL 或 Gist ID。
|
3. 已有 Gist 时先运行 `Zed Settings: Configure GitHub Gist...` 并粘贴 URL/ID,再执行 Push。
|
||||||
4. 运行 `Zed Settings: Push to GitHub Gist`。
|
4. 运行 `Zed Settings: Show GitHub Gist Status`,确认状态为 `Synchronized`。
|
||||||
5. 运行 `Zed Settings: Show GitHub Gist Status`,确认状态为 `Synchronized`。
|
|
||||||
|
|
||||||
Push 只会在该 Gist 中创建或更新 `zed-settings-sync.json`,不会删除 Gist 中的其他文件。
|
Push 只会在该 Gist 中创建或更新 `zed-settings-sync.json`,不会删除 Gist 中的其他文件。
|
||||||
|
|
||||||
@@ -126,8 +125,8 @@ curl -fsSL https://<Gitea域名>/<用户>/<仓库>/raw/branch/main/bootstrap-fro
|
|||||||
|
|
||||||
1. 检查 Node.js 和 `gh`。
|
1. 检查 Node.js 和 `gh`。
|
||||||
2. 在尚未登录时运行 `gh auth login`。
|
2. 在尚未登录时运行 `gh auth login`。
|
||||||
3. 询问 Gist URL 或 ID。
|
3. 询问 Gist URL 或 ID;留空时自动创建新的 Secret Gist。
|
||||||
4. 从 GitHub API 读取 `zed-settings-sync.json`。
|
4. 输入已有 Gist 时从 GitHub API 读取配置;留空时使用本仓库的 `zed-settings-sync.json` 种子配置。
|
||||||
5. 验证顶层文件路径,防止写出 Zed 配置目录。
|
5. 验证顶层文件路径,防止写出 Zed 配置目录。
|
||||||
6. 备份新机器已有配置。
|
6. 备份新机器已有配置。
|
||||||
7. 安装设置、键位、Tasks、snippets、themes 和辅助脚本。
|
7. 安装设置、键位、Tasks、snippets、themes 和辅助脚本。
|
||||||
|
|||||||
+19
-3
@@ -48,18 +48,20 @@ function Write-Atomic([string]$Path, [string]$Contents) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Require-Command "node" "winget install OpenJS.NodeJS.LTS"
|
Require-Command "node" "winget install OpenJS.NodeJS.LTS"
|
||||||
|
if (-not $env:ZED_SETTINGS_BUNDLE_PATH) {
|
||||||
Require-Command "gh" "winget install GitHub.cli"
|
Require-Command "gh" "winget install GitHub.cli"
|
||||||
|
Remove-Item Env:GH_TOKEN,Env:GITHUB_TOKEN -ErrorAction SilentlyContinue
|
||||||
& gh auth status *> $null
|
& gh auth status *> $null
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "GitHub authentication is required."
|
Write-Host "GitHub authentication is required."
|
||||||
& gh auth login
|
& gh auth login
|
||||||
if ($LASTEXITCODE -ne 0) { throw "GitHub authentication failed." }
|
if ($LASTEXITCODE -ne 0) { throw "GitHub authentication failed." }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$entered = if ($env:ZED_SETTINGS_GIST_ID) { $env:ZED_SETTINGS_GIST_ID } else { Read-Host "GitHub Gist ID or full URL" }
|
$entered = if ($env:ZED_SETTINGS_GIST_ID) { $env:ZED_SETTINGS_GIST_ID } else { Read-Host "GitHub Gist ID or URL (leave blank to create a new Secret Gist)" }
|
||||||
$gistId = Get-GistId $entered
|
|
||||||
if ($env:ZED_SETTINGS_BUNDLE_PATH) {
|
if ($env:ZED_SETTINGS_BUNDLE_PATH) {
|
||||||
|
$gistId = Get-GistId $entered
|
||||||
$bundleSource = [IO.File]::ReadAllText($env:ZED_SETTINGS_BUNDLE_PATH, [Text.Encoding]::UTF8)
|
$bundleSource = [IO.File]::ReadAllText($env:ZED_SETTINGS_BUNDLE_PATH, [Text.Encoding]::UTF8)
|
||||||
} else {
|
} else {
|
||||||
$token = (& gh auth token).Trim()
|
$token = (& gh auth token).Trim()
|
||||||
@@ -70,12 +72,26 @@ if ($env:ZED_SETTINGS_BUNDLE_PATH) {
|
|||||||
"User-Agent" = "zed-settings-bootstrap"
|
"User-Agent" = "zed-settings-bootstrap"
|
||||||
"X-GitHub-Api-Version" = "2022-11-28"
|
"X-GitHub-Api-Version" = "2022-11-28"
|
||||||
}
|
}
|
||||||
|
if ([string]::IsNullOrWhiteSpace($entered)) {
|
||||||
|
$seedUrl = "https://git.okk.cool/purp1e/zed-sync/raw/branch/master/zed-settings-sync.json"
|
||||||
|
$bundleSource = (Invoke-WebRequest -UseBasicParsing -Uri $seedUrl).Content
|
||||||
|
$payload = @{
|
||||||
|
description = "Zed settings sync"
|
||||||
|
public = $false
|
||||||
|
files = @{ $bundleName = @{ content = $bundleSource } }
|
||||||
|
} | ConvertTo-Json -Depth 5
|
||||||
|
$created = Invoke-RestMethod -Method Post -Uri "https://api.github.com/gists" -Headers $headers -ContentType "application/json" -Body ([Text.Encoding]::UTF8.GetBytes($payload))
|
||||||
|
$gistId = $created.id
|
||||||
|
Write-Host "Created Secret Gist: $($created.html_url)"
|
||||||
|
} else {
|
||||||
|
$gistId = Get-GistId $entered
|
||||||
$gist = Invoke-RestMethod -Uri "https://api.github.com/gists/$gistId" -Headers $headers
|
$gist = Invoke-RestMethod -Uri "https://api.github.com/gists/$gistId" -Headers $headers
|
||||||
$remoteFile = $gist.files.$bundleName
|
$remoteFile = $gist.files.$bundleName
|
||||||
if (-not $remoteFile) { throw "Gist $gistId does not contain $bundleName." }
|
if (-not $remoteFile) { throw "Gist $gistId does not contain $bundleName." }
|
||||||
if ($remoteFile.truncated) { throw "$bundleName is too large for the Gist API response." }
|
if ($remoteFile.truncated) { throw "$bundleName is too large for the Gist API response." }
|
||||||
$bundleSource = [string]$remoteFile.content
|
$bundleSource = [string]$remoteFile.content
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$bundle = $bundleSource | ConvertFrom-Json
|
$bundle = $bundleSource | ConvertFrom-Json
|
||||||
|
|||||||
+40
-8
@@ -5,22 +5,51 @@ command -v node >/dev/null 2>&1 || {
|
|||||||
printf '%s\n' 'Node.js is required. Install it with your system package manager.' >&2
|
printf '%s\n' 'Node.js is required. Install it with your system package manager.' >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
if [ -z "${ZED_SETTINGS_BUNDLE_PATH:-}" ]; then
|
||||||
command -v gh >/dev/null 2>&1 || {
|
command -v gh >/dev/null 2>&1 || {
|
||||||
printf '%s\n' 'GitHub CLI (gh) is required: https://cli.github.com/' >&2
|
printf '%s\n' 'GitHub CLI (gh) is required: https://cli.github.com/' >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
unset GH_TOKEN GITHUB_TOKEN
|
||||||
if ! gh auth status >/dev/null 2>&1; then
|
if ! gh auth status >/dev/null 2>&1; then
|
||||||
printf '%s\n' 'GitHub authentication is required.'
|
printf '%s\n' 'GitHub authentication is required.'
|
||||||
gh auth login </dev/tty >/dev/tty
|
gh auth login </dev/tty >/dev/tty
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "${ZED_SETTINGS_GIST_ID:-}" ]; then
|
if [ -n "${ZED_SETTINGS_GIST_ID:-}" ]; then
|
||||||
entered=$ZED_SETTINGS_GIST_ID
|
entered=$ZED_SETTINGS_GIST_ID
|
||||||
else
|
else
|
||||||
printf 'GitHub Gist ID or full URL: ' >/dev/tty
|
printf 'GitHub Gist ID or URL (leave blank to create a new Secret Gist): ' >/dev/tty
|
||||||
IFS= read -r entered </dev/tty
|
IFS= read -r entered </dev/tty
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
temporary_dir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$temporary_dir"' EXIT INT TERM
|
||||||
|
response="$temporary_dir/gist.json"
|
||||||
|
bundle_path=${ZED_SETTINGS_BUNDLE_PATH:-}
|
||||||
|
|
||||||
|
if [ -n "$bundle_path" ]; then
|
||||||
|
gist_id=$(printf '%s' "$entered" | sed -nE 's#.*[^a-fA-F0-9]([a-fA-F0-9]{8,})/?$#\1#p')
|
||||||
|
elif [ -z "$entered" ]; then
|
||||||
|
bundle_path="$temporary_dir/zed-settings-sync.json"
|
||||||
|
payload="$temporary_dir/create-gist.json"
|
||||||
|
curl -fsSL 'https://git.okk.cool/purp1e/zed-sync/raw/branch/master/zed-settings-sync.json' -o "$bundle_path"
|
||||||
|
node - "$bundle_path" "$payload" <<'NODE_CREATE'
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const [bundlePath, payloadPath] = process.argv.slice(2);
|
||||||
|
const content = fs.readFileSync(bundlePath, "utf8");
|
||||||
|
fs.writeFileSync(payloadPath, JSON.stringify({
|
||||||
|
description: "Zed settings sync",
|
||||||
|
public: false,
|
||||||
|
files: { "zed-settings-sync.json": { content } },
|
||||||
|
}));
|
||||||
|
NODE_CREATE
|
||||||
|
gh api --method POST gists --input "$payload" > "$response"
|
||||||
|
gist_id=$(node -e 'const g=require(process.argv[1]); process.stdout.write(g.id)' "$response")
|
||||||
|
gist_url=$(node -e 'const g=require(process.argv[1]); process.stdout.write(g.html_url)' "$response")
|
||||||
|
printf 'Created Secret Gist: %s\n' "$gist_url"
|
||||||
|
else
|
||||||
gist_id=$(printf '%s' "$entered" | sed -nE 's#.*[^a-fA-F0-9]([a-fA-F0-9]{8,})/?$#\1#p')
|
gist_id=$(printf '%s' "$entered" | sed -nE 's#.*[^a-fA-F0-9]([a-fA-F0-9]{8,})/?$#\1#p')
|
||||||
if [ -z "$gist_id" ]; then
|
if [ -z "$gist_id" ]; then
|
||||||
gist_id=$(printf '%s' "$entered" | sed -nE 's#^([a-fA-F0-9]{8,})$#\1#p')
|
gist_id=$(printf '%s' "$entered" | sed -nE 's#^([a-fA-F0-9]{8,})$#\1#p')
|
||||||
@@ -29,15 +58,18 @@ if [ -z "$gist_id" ]; then
|
|||||||
printf '%s\n' 'Enter a valid GitHub Gist ID or URL.' >&2
|
printf '%s\n' 'Enter a valid GitHub Gist ID or URL.' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
temporary_dir=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$temporary_dir"' EXIT INT TERM
|
|
||||||
response="$temporary_dir/gist.json"
|
|
||||||
if [ -z "${ZED_SETTINGS_BUNDLE_PATH:-}" ]; then
|
|
||||||
gh api "gists/$gist_id" > "$response"
|
gh api "gists/$gist_id" > "$response"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
node - "$response" "$gist_id" "${ZED_SETTINGS_BUNDLE_PATH:-}" <<'NODE'
|
if [ -z "${gist_id:-}" ]; then
|
||||||
|
gist_id=$(printf '%s' "$entered" | sed -nE 's#^([a-fA-F0-9]{8,})$#\1#p')
|
||||||
|
fi
|
||||||
|
if [ -z "$gist_id" ]; then
|
||||||
|
printf '%s\n' 'Enter a valid GitHub Gist ID or URL.' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
node - "$response" "$gist_id" "$bundle_path" <<'NODE'
|
||||||
const { createHash } = require("node:crypto");
|
const { createHash } = require("node:crypto");
|
||||||
const fs = require("node:fs");
|
const fs = require("node:fs");
|
||||||
const os = require("node:os");
|
const os = require("node:os");
|
||||||
|
|||||||
@@ -22,4 +22,7 @@ foreach ($script in @(
|
|||||||
}
|
}
|
||||||
[IO.File]::WriteAllText((Join-Path $target "tasks.json"), $tasks, $utf8)
|
[IO.File]::WriteAllText((Join-Path $target "tasks.json"), $tasks, $utf8)
|
||||||
|
|
||||||
|
node (Join-Path $source "scripts\zed-settings-sync.mjs") export (Join-Path $target "zed-settings-sync.json")
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Failed to generate zed-settings-sync.json" }
|
||||||
|
|
||||||
Write-Host "Exported Zed configuration from $source"
|
Write-Host "Exported Zed configuration from $source"
|
||||||
|
|||||||
@@ -19,4 +19,6 @@ const tasks = fs.readFileSync(source, "utf8")
|
|||||||
fs.writeFileSync(target, tasks);
|
fs.writeFileSync(target, tasks);
|
||||||
NODE
|
NODE
|
||||||
|
|
||||||
|
node "$source/scripts/zed-settings-sync.mjs" export "$target/zed-settings-sync.json"
|
||||||
|
|
||||||
printf 'Exported Zed configuration from %s\n' "$source"
|
printf 'Exported Zed configuration from %s\n' "$source"
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ function runPowerShell(command, extraEnv = {}) {
|
|||||||
const result = spawnSync("powershell.exe", ["-NoProfile", "-STA", "-Command", command], {
|
const result = spawnSync("powershell.exe", ["-NoProfile", "-STA", "-Command", command], {
|
||||||
encoding: "utf8",
|
encoding: "utf8",
|
||||||
env: { ...process.env, ...extraEnv },
|
env: { ...process.env, ...extraEnv },
|
||||||
windowsHide: true,
|
windowsHide: false,
|
||||||
});
|
});
|
||||||
if (result.status !== 0) throw new Error(result.stderr.trim() || "Windows dialog failed");
|
if (result.status !== 0) throw new Error(result.stderr.trim() || "Windows dialog failed");
|
||||||
return result.stdout.trim();
|
return result.stdout.trim();
|
||||||
@@ -199,10 +199,10 @@ function runPowerShell(command, extraEnv = {}) {
|
|||||||
|
|
||||||
function nativeFileDialog(mode) {
|
function nativeFileDialog(mode) {
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
const common = "[Console]::OutputEncoding=[Text.Encoding]::UTF8; Add-Type -AssemblyName System.Windows.Forms; ";
|
const common = "[Console]::OutputEncoding=[Text.Encoding]::UTF8; Add-Type -AssemblyName System.Windows.Forms; $o=New-Object System.Windows.Forms.Form; $o.TopMost=$true; $o.ShowInTaskbar=$false; $o.Opacity=0; $o.Show(); $o.Activate(); ";
|
||||||
const command = mode === "save"
|
const command = mode === "save"
|
||||||
? `${common}$d=New-Object System.Windows.Forms.SaveFileDialog; $d.Filter='Zed settings (*.json)|*.json'; $d.FileName='${BUNDLE_NAME}'; $d.AddExtension=$true; if($d.ShowDialog() -eq 'OK'){[Console]::Write($d.FileName)}`
|
? `${common}$d=New-Object System.Windows.Forms.SaveFileDialog; $d.Filter='Zed settings (*.json)|*.json'; $d.FileName='${BUNDLE_NAME}'; $d.AddExtension=$true; if($d.ShowDialog($o) -eq 'OK'){[Console]::Write($d.FileName)}; $o.Close()`
|
||||||
: `${common}$d=New-Object System.Windows.Forms.OpenFileDialog; $d.Filter='Zed settings (*.json)|*.json'; $d.FileName='${BUNDLE_NAME}'; if($d.ShowDialog() -eq 'OK'){[Console]::Write($d.FileName)}`;
|
: `${common}$d=New-Object System.Windows.Forms.OpenFileDialog; $d.Filter='Zed settings (*.json)|*.json'; $d.FileName='${BUNDLE_NAME}'; if($d.ShowDialog($o) -eq 'OK'){[Console]::Write($d.FileName)}; $o.Close()`;
|
||||||
return runPowerShell(command);
|
return runPowerShell(command);
|
||||||
}
|
}
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
@@ -220,7 +220,42 @@ function nativeFileDialog(mode) {
|
|||||||
|
|
||||||
function nativeInput(prompt, title, defaultValue = "") {
|
function nativeInput(prompt, title, defaultValue = "") {
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
const command = "[Console]::OutputEncoding=[Text.Encoding]::UTF8; Add-Type -AssemblyName Microsoft.VisualBasic; [Console]::Write([Microsoft.VisualBasic.Interaction]::InputBox($env:ZED_SYNC_PROMPT,$env:ZED_SYNC_TITLE,$env:ZED_SYNC_DEFAULT))";
|
const command = `
|
||||||
|
[Console]::OutputEncoding=[Text.Encoding]::UTF8
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
$f=New-Object System.Windows.Forms.Form
|
||||||
|
$f.Text=$env:ZED_SYNC_TITLE
|
||||||
|
$f.Size=New-Object System.Drawing.Size(620,190)
|
||||||
|
$f.StartPosition='CenterScreen'
|
||||||
|
$f.TopMost=$true
|
||||||
|
$f.ShowInTaskbar=$true
|
||||||
|
$f.FormBorderStyle='FixedDialog'
|
||||||
|
$f.MaximizeBox=$false
|
||||||
|
$f.MinimizeBox=$false
|
||||||
|
$l=New-Object System.Windows.Forms.Label
|
||||||
|
$l.Text=$env:ZED_SYNC_PROMPT
|
||||||
|
$l.AutoSize=$false
|
||||||
|
$l.Location=New-Object System.Drawing.Point(14,14)
|
||||||
|
$l.Size=New-Object System.Drawing.Size(575,42)
|
||||||
|
$t=New-Object System.Windows.Forms.TextBox
|
||||||
|
$t.Text=$env:ZED_SYNC_DEFAULT
|
||||||
|
$t.Location=New-Object System.Drawing.Point(14,62)
|
||||||
|
$t.Size=New-Object System.Drawing.Size(575,24)
|
||||||
|
$ok=New-Object System.Windows.Forms.Button
|
||||||
|
$ok.Text='OK'
|
||||||
|
$ok.DialogResult=[System.Windows.Forms.DialogResult]::OK
|
||||||
|
$ok.Location=New-Object System.Drawing.Point(427,105)
|
||||||
|
$cancel=New-Object System.Windows.Forms.Button
|
||||||
|
$cancel.Text='Cancel'
|
||||||
|
$cancel.DialogResult=[System.Windows.Forms.DialogResult]::Cancel
|
||||||
|
$cancel.Location=New-Object System.Drawing.Point(514,105)
|
||||||
|
$f.Controls.AddRange(@($l,$t,$ok,$cancel))
|
||||||
|
$f.AcceptButton=$ok
|
||||||
|
$f.CancelButton=$cancel
|
||||||
|
$f.Add_Shown({$f.Activate();$t.Focus();$t.SelectAll()})
|
||||||
|
if($f.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){[Console]::Write($t.Text)}
|
||||||
|
`;
|
||||||
return runPowerShell(command, {
|
return runPowerShell(command, {
|
||||||
ZED_SYNC_PROMPT: prompt,
|
ZED_SYNC_PROMPT: prompt,
|
||||||
ZED_SYNC_TITLE: title,
|
ZED_SYNC_TITLE: title,
|
||||||
@@ -239,7 +274,7 @@ function nativeInput(prompt, title, defaultValue = "") {
|
|||||||
|
|
||||||
function nativeMessage(message, title = "Zed Settings Sync", error = false) {
|
function nativeMessage(message, title = "Zed Settings Sync", error = false) {
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
const command = "Add-Type -AssemblyName System.Windows.Forms; [void][System.Windows.Forms.MessageBox]::Show($env:ZED_SYNC_MESSAGE,$env:ZED_SYNC_TITLE,'OK',$env:ZED_SYNC_ICON)";
|
const command = "Add-Type -AssemblyName System.Windows.Forms; $o=New-Object System.Windows.Forms.Form; $o.TopMost=$true; $o.ShowInTaskbar=$false; $o.Opacity=0; $o.Show(); $o.Activate(); [void][System.Windows.Forms.MessageBox]::Show($o,$env:ZED_SYNC_MESSAGE,$env:ZED_SYNC_TITLE,'OK',$env:ZED_SYNC_ICON); $o.Close()";
|
||||||
runPowerShell(command, {
|
runPowerShell(command, {
|
||||||
ZED_SYNC_MESSAGE: message,
|
ZED_SYNC_MESSAGE: message,
|
||||||
ZED_SYNC_TITLE: title,
|
ZED_SYNC_TITLE: title,
|
||||||
@@ -257,7 +292,7 @@ function nativeMessage(message, title = "Zed Settings Sync", error = false) {
|
|||||||
|
|
||||||
function nativeConfirm(message, title = "Zed Settings Sync") {
|
function nativeConfirm(message, title = "Zed Settings Sync") {
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
const command = "Add-Type -AssemblyName System.Windows.Forms; [Console]::Write([System.Windows.Forms.MessageBox]::Show($env:ZED_SYNC_MESSAGE,$env:ZED_SYNC_TITLE,'YesNo','Warning'))";
|
const command = "Add-Type -AssemblyName System.Windows.Forms; $o=New-Object System.Windows.Forms.Form; $o.TopMost=$true; $o.ShowInTaskbar=$false; $o.Opacity=0; $o.Show(); $o.Activate(); [Console]::Write([System.Windows.Forms.MessageBox]::Show($o,$env:ZED_SYNC_MESSAGE,$env:ZED_SYNC_TITLE,'YesNo','Warning')); $o.Close()";
|
||||||
return runPowerShell(command, { ZED_SYNC_MESSAGE: message, ZED_SYNC_TITLE: title }) === "Yes";
|
return runPowerShell(command, { ZED_SYNC_MESSAGE: message, ZED_SYNC_TITLE: title }) === "Yes";
|
||||||
}
|
}
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
@@ -297,6 +332,7 @@ function normalizeGistId(value) {
|
|||||||
|
|
||||||
async function promptForGist() {
|
async function promptForGist() {
|
||||||
const state = await readSyncConfiguration();
|
const state = await readSyncConfiguration();
|
||||||
|
console.log("Opening Gist configuration dialog...");
|
||||||
const entered = nativeInput(
|
const entered = nativeInput(
|
||||||
"Paste the GitHub Gist ID or full Gist URL. The previous value is remembered on this machine.",
|
"Paste the GitHub Gist ID or full Gist URL. The previous value is remembered on this machine.",
|
||||||
"Zed Settings Gist",
|
"Zed Settings Gist",
|
||||||
@@ -313,7 +349,10 @@ async function promptForGist() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function runGh(args, input) {
|
function runGh(args, input) {
|
||||||
const result = spawnSync("gh", args, { encoding: "utf8", input, windowsHide: true });
|
const env = { ...process.env };
|
||||||
|
delete env.GH_TOKEN;
|
||||||
|
delete env.GITHUB_TOKEN;
|
||||||
|
const result = spawnSync("gh", args, { encoding: "utf8", env, input, windowsHide: true });
|
||||||
if (result.error?.code === "ENOENT") {
|
if (result.error?.code === "ENOENT") {
|
||||||
throw new Error("GitHub CLI (gh) is not installed. Install it, then run: gh auth login");
|
throw new Error("GitHub CLI (gh) is not installed. Install it, then run: gh auth login");
|
||||||
}
|
}
|
||||||
@@ -334,6 +373,15 @@ function updateRemoteGist(gistId, contents) {
|
|||||||
return JSON.parse(runGh(["api", "--method", "PATCH", `gists/${gistId}`, "--input", "-"], payload));
|
return JSON.parse(runGh(["api", "--method", "PATCH", `gists/${gistId}`, "--input", "-"], payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRemoteGist(contents) {
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
description: "Zed settings sync",
|
||||||
|
public: false,
|
||||||
|
files: { [BUNDLE_NAME]: { content: contents } },
|
||||||
|
});
|
||||||
|
return JSON.parse(runGh(["api", "--method", "POST", "gists", "--input", "-"], payload));
|
||||||
|
}
|
||||||
|
|
||||||
function shortHash(hash) {
|
function shortHash(hash) {
|
||||||
return hash ? hash.slice(0, 12) : "none";
|
return hash ? hash.slice(0, 12) : "none";
|
||||||
}
|
}
|
||||||
@@ -345,9 +393,21 @@ async function configureGist() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function pushGist() {
|
async function pushGist() {
|
||||||
const state = await promptForGist();
|
const state = await readSyncConfiguration();
|
||||||
if (!state) return;
|
|
||||||
const localBundle = await createBundle();
|
const localBundle = await createBundle();
|
||||||
|
if (!state.config.gist_id) {
|
||||||
|
console.log("No Gist is configured. Asking to create one...");
|
||||||
|
const proceed = nativeConfirm(
|
||||||
|
"No GitHub Gist is configured. Create a new Secret Gist from this machine's current Zed settings?",
|
||||||
|
);
|
||||||
|
if (!proceed) return;
|
||||||
|
const created = createRemoteGist(localBundle.contents);
|
||||||
|
state.config.gist_id = created.id;
|
||||||
|
state.config.last_synced_hash = localBundle.hash;
|
||||||
|
await saveSyncConfiguration(state.path, state.config);
|
||||||
|
nativeMessage(`Created and uploaded Secret Gist:\n${created.html_url || created.id}\n\nSHA-256: ${localBundle.hash}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const remote = readRemoteGist(state.config.gist_id);
|
const remote = readRemoteGist(state.config.gist_id);
|
||||||
const remoteHash = remote.parsed?.hash || "";
|
const remoteHash = remote.parsed?.hash || "";
|
||||||
const lastHash = state.config.last_synced_hash;
|
const lastHash = state.config.last_synced_hash;
|
||||||
|
|||||||
+12
-8
@@ -107,8 +107,8 @@
|
|||||||
"gist-configure"
|
"gist-configure"
|
||||||
],
|
],
|
||||||
"allow_concurrent_runs": false,
|
"allow_concurrent_runs": false,
|
||||||
"reveal": "never",
|
"reveal": "no_focus",
|
||||||
"hide": "always",
|
"hide": "never",
|
||||||
"show_summary": false,
|
"show_summary": false,
|
||||||
"show_command": false,
|
"show_command": false,
|
||||||
"save": "all"
|
"save": "all"
|
||||||
@@ -121,8 +121,8 @@
|
|||||||
"gist-status"
|
"gist-status"
|
||||||
],
|
],
|
||||||
"allow_concurrent_runs": false,
|
"allow_concurrent_runs": false,
|
||||||
"reveal": "never",
|
"reveal": "no_focus",
|
||||||
"hide": "always",
|
"hide": "never",
|
||||||
"show_summary": false,
|
"show_summary": false,
|
||||||
"show_command": false,
|
"show_command": false,
|
||||||
"save": "all"
|
"save": "all"
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
],
|
],
|
||||||
"allow_concurrent_runs": false,
|
"allow_concurrent_runs": false,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"hide": "on_success",
|
"hide": "never",
|
||||||
"show_summary": false,
|
"show_summary": false,
|
||||||
"show_command": false,
|
"show_command": false,
|
||||||
"save": "all"
|
"save": "all"
|
||||||
@@ -150,15 +150,19 @@
|
|||||||
],
|
],
|
||||||
"allow_concurrent_runs": false,
|
"allow_concurrent_runs": false,
|
||||||
"reveal": "no_focus",
|
"reveal": "no_focus",
|
||||||
"hide": "on_success",
|
"hide": "never",
|
||||||
"show_summary": false,
|
"show_summary": false,
|
||||||
"show_command": false,
|
"show_command": false,
|
||||||
"save": "all"
|
"save": "all"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Zed Settings: Authenticate GitHub...",
|
"label": "Zed Settings: Authenticate GitHub...",
|
||||||
"command": "gh",
|
"command": "powershell",
|
||||||
"args": ["auth", "login"],
|
"args": [
|
||||||
|
"-NoExit",
|
||||||
|
"-Command",
|
||||||
|
"Remove-Item Env:GH_TOKEN,Env:GITHUB_TOKEN -ErrorAction SilentlyContinue; gh auth login"
|
||||||
|
],
|
||||||
"allow_concurrent_runs": false,
|
"allow_concurrent_runs": false,
|
||||||
"reveal": "always",
|
"reveal": "always",
|
||||||
"hide": "never",
|
"hide": "never",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user