站点首页此前显示的是 Dashy 上游的 logo —— index.html 里那几条
<link rel=icon href=/web-icons/favicon-*.png> 优先级高于我们的门户图标,
而扩展的 PNG 是占位图(128px 只有 9 种颜色、alpha 通道还是乱的)。
- portal/favicon.svg:纸飞机(圆角渐变底 + 白色折纸机头朝右上 + 两道尾迹),
几何收在 8..56 安全区,避免贴边被圆角裁切
- 由该 SVG 派生 favicon.ico(16/32/48)、apple-touch-icon(180)、
web-icons/favicon-{16,32,64}、扩展 icon{16,48,96,128}
- patch-index.py:删掉上游 icon link 并显式指向 /favicon.svg(幂等,
重复执行 unchanged);上游 web-icons 那批也覆盖一份作双保险
- Dockerfile 与 zd-host.sh 都 COPY 这些图标进镜像
- 验证:nginx 沙箱实测 /favicon.svg、/web-icons/*、/apple-touch-icon.png 均 200;
16px 下人眼仍可辨识为纸飞机
- .amo-upload-uuid(web-ext 生成的 UUID)改为忽略
116 lines
5.1 KiB
Bash
Executable File
116 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Firefox 扩展:构建 → lint → AMO unlisted 签名 → 发布到数据卷
|
||
#
|
||
# 为什么需要这个脚本(实测踩坑,2026-10-03):
|
||
# 1. 正式版 Firefox 要求扩展经 AMO 签名,未签名一律拒绝并报成「插件损坏」。
|
||
# 2. 签名包一旦生成就与内容绑定 —— 改一行代码就必须重新签名,
|
||
# 所以「签名」必须跟着「改代码」这一步自动发生,不能靠人记得手动做。
|
||
# 3. 签名是 AMO 生成的产物,不该进版本库(签名与源码脱节 + 是二进制),
|
||
# 因此落在数据卷里,跨部署存活;nginx 优先供给它(见 nginx.conf)。
|
||
#
|
||
# 凭据:只从环境变量读,绝不写进本仓库(AGENTS.md §6 脱敏铁律)
|
||
# WEB_EXT_API_KEY / WEB_EXT_API_SECRET —— AMO 开发者页面「API 密钥」里的
|
||
# JWT issuer:secret。属于程序化签名凭据,与账号密码无关。
|
||
# 先在 https://addons.mozilla.org/developers/ → 你的扩展 → 设置 → API 密钥 获取。
|
||
#
|
||
# 用法:
|
||
# WEB_EXT_API_KEY=... WEB_EXT_API_SECRET=... deploy/sign-firefox.sh # 签名并发布
|
||
# deploy/sign-firefox.sh --check # 只跑 lint,不签名
|
||
set -euo pipefail
|
||
|
||
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
EXT_DIR="$SRC/extension/firefox"
|
||
WEB_EXT="${WEB_EXT:-npx --yes web-ext@8}"
|
||
|
||
CHECK_ONLY=0
|
||
[ "${1:-}" = "--check" ] && CHECK_ONLY=1
|
||
|
||
echo "== 1/4 语法与规范检查 =="
|
||
node --check "$EXT_DIR/background.js"
|
||
node --check "$EXT_DIR/options.js"
|
||
node --check "$EXT_DIR/detect.js"
|
||
# lint 必须 0 error:AMO 用的是同一个 addons-linter,error 会被直接拒收。
|
||
# 注意 notice 不阻塞上传,但要能看见(提示 FUTURE/MV3 之类,不静默吞掉)。
|
||
LINT_OUT="$(mktemp)"
|
||
trap 'rm -f "$LINT_OUT"' EXIT
|
||
(cd "$EXT_DIR/.." && $WEB_EXT lint --source-dir=firefox --output text) >"$LINT_OUT" 2>&1 || {
|
||
cat "$LINT_OUT"; echo "!! lint 执行失败" >&2; exit 1; }
|
||
grep -E '^(errors|warnings|notices)' "$LINT_OUT" | sed 's/^/ /'
|
||
grep -qE '^errors +0' "$LINT_OUT" || { cat "$LINT_OUT"; echo "!! 有 error,AMO 会拒收" >&2; exit 1; }
|
||
grep -E '^(NOTICES|WARNINGS):' -A6 "$LINT_OUT" | grep -vE '^--$' | sed 's/^/ /' || true
|
||
|
||
VER="$(python3 -c "import json;print(json.load(open('$EXT_DIR/manifest.json'))['version'])")"
|
||
echo " 版本: $VER"
|
||
|
||
if [ "$CHECK_ONLY" = "1" ]; then
|
||
echo "--check 模式:跳过签名与发布。"
|
||
exit 0
|
||
fi
|
||
|
||
echo "== 2/4 AMO 签名(unlisted 自分发)=="
|
||
[ -n "${WEB_EXT_API_KEY:-}" ] && [ -n "${WEB_EXT_API_SECRET:-}" ] || {
|
||
cat >&2 <<'EOF'
|
||
!! 缺少 AMO 凭据。请设置环境变量后重试:
|
||
WEB_EXT_API_KEY='user:<id>:<num>' \
|
||
WEB_EXT_API_SECRET='<jwt secret>' \
|
||
deploy/sign-firefox.sh
|
||
凭据在 https://addons.mozilla.org/developers/ → 你的扩展 → 设置 → API 密钥 获取。
|
||
不要把凭据写进仓库或任何会被提交的文件(AGENTS.md §6)。
|
||
EOF
|
||
exit 2; }
|
||
|
||
ART="$(mktemp -d)"
|
||
trap 'rm -rf "$ART" "$LINT_OUT"' EXIT
|
||
(cd "$EXT_DIR/.." && $WEB_EXT sign --source-dir=firefox \
|
||
--artifacts-dir="$ART" --channel=unlisted --no-input) 2>&1 \
|
||
| grep -vE 'DeprecationWarning|trace-deprecation|no version information' | sed 's/^/ /'
|
||
|
||
XPI="$(ls "$ART"/*.xpi 2>/dev/null | head -1 || true)"
|
||
[ -n "$XPI" ] || { echo "!! 未产出签名包" >&2; exit 1; }
|
||
|
||
echo "== 3/4 校验签名包 =="
|
||
# 不能只看「web-ext 说成功」:签名条目缺失的包装上去照样报「插件损坏」。
|
||
python3 - "$XPI" <<'PY'
|
||
import json, sys, zipfile
|
||
z = zipfile.ZipFile(sys.argv[1])
|
||
names = z.namelist()
|
||
need = ['META-INF/mozilla.rsa', 'META-INF/mozilla.sf', 'META-INF/cose.manifest']
|
||
missing = [n for n in need if n not in names]
|
||
if missing:
|
||
sys.exit(f'!! 签名条目缺失: {missing}')
|
||
m = json.loads(z.read('manifest.json'))
|
||
g = m['browser_specific_settings']['gecko']
|
||
print(f" 签名条目齐备 版本={m['version']} id={g['id']}")
|
||
print(f" data_collection={g.get('data_collection_permissions')}")
|
||
PY
|
||
|
||
echo "== 4/4 发布到数据卷 =="
|
||
# 没给 NAVI_HOST 就只把包落到本地,供人工上传。
|
||
if [ -z "${NAVI_HOST:-}" ]; then
|
||
OUT="${1:-$SRC}/navi-firefox-signed.xpi"
|
||
cp "$XPI" "$OUT"
|
||
echo " 未设 NAVI_HOST,仅本地输出: $OUT"
|
||
echo " (线上发布:NAVI_HOST=user@host NAVI_SSH_PASS=... deploy/sign-firefox.sh)"
|
||
exit 0
|
||
fi
|
||
SSH_PASS="${NAVI_SSH_PASS:-}"
|
||
if [ -n "$SSH_PASS" ]; then
|
||
SSH=(sshpass -p "$SSH_PASS" ssh -o StrictHostKeyChecking=no "$NAVI_HOST")
|
||
SCP=(sshpass -p "$SSH_PASS" scp -q -o StrictHostKeyChecking=no)
|
||
else
|
||
SSH=(ssh -o StrictHostKeyChecking=no "$NAVI_HOST")
|
||
SCP=(scp -q -o StrictHostKeyChecking=no)
|
||
fi
|
||
DATA_DIR="${NAVI_DATA_DIR:-/vol1/docker/navi-data}"
|
||
STAMP="$(date +%Y%m%d%H%M%S)"
|
||
|
||
"${SCP[@]}" "$XPI" "$NAVI_HOST:$DATA_DIR/extension/.navi-firefox.xpi.new"
|
||
# 先备份再原子替换:中途失败不会让线上退化成半个文件
|
||
"${SSH[@]}" "set -e
|
||
cd $DATA_DIR/extension
|
||
[ -f navi-firefox.xpi ] && cp -a navi-firefox.xpi navi-firefox.xpi.bak-\$(date +%s)
|
||
mv .navi-firefox.xpi.new navi-firefox.xpi
|
||
echo \" 已发布: \$(sha256sum navi-firefox.xpi | cut -d' ' -f1)\"" | sed 's/^/ /'
|
||
|
||
echo "SIGN_OK version=$VER"
|