Skip to content

[Bug] Netlify 部署上传完成后不轮询状态,可能误报成功(实际仍在处理 / 已失败) #50

@Tespera

Description

@Tespera

Gridea Pro 版本

main 分支 HEAD (b84db26)

操作系统

macOS (Apple Silicon)

系统版本

Darwin 25.4.0(问题与平台无关)

优先级

P1(Netlify 部署正确性)

Bug 描述

backend/internal/deploy/netlify_deployer.go:100-119Deploy 流程:

  1. scanAndHashFiles 扫描本地文件。
  2. createDeploy 拿到 deployIdrequired
  3. uploadFiles 并发 PUT 缺失文件。
  4. 直接 logger(\"✅ Netlify 部署成功!\") 返回。

问题:上传完成只是"Netlify 收到了所有文件"的信号;实际进入 "Ready" 状态还要经过:

  • 内部校验 / CDN 分发;
  • Build plugin 执行(即便是静态站,Netlify 也会跑一些内置 plugin);
  • DNS / SSL 同步;
  • 遇到 build plugin 报错或 deploy 超限时会被置为 error 状态。

对于用户,state 可能还在 processing / uploading / enqueued,甚至最终变成 error。当前代码完全不 poll /api/v1/deploys/{id},用户 UI 永远看到"成功",打开站点却看到"Deploy failed"或旧版本。

此外 deployIdcreateDeploy 拿到之后从未使用(除了传给 uploadFiles URL),说明原本就设计漏了 poll 这一步。

复现步骤

  1. 配置一个 Netlify 站点,故意在 _redirects 里写一条错误规则。
  2. 发布。
  3. 所有文件正常上传,应用弹"✅ 成功"。
  4. 打开 Netlify 后台:该 deploy 状态实际是 Deploy failed
  5. 访问站点:依然是旧版本。

或更常见场景:

  1. 大站点上传完成后,Netlify 端还需 30s-2min 处理。
  2. 用户关闭应用准备休息。
  3. Netlify 最终成功或失败,用户毫不知情。

期望行为

  • 上传完成后 poll GET /api/v1/deploys/{id},直到 state ∈ {ready, error} 或达到超时(建议 5 分钟)。
  • 轮询期间通过结构化事件通知前端"Netlify 正在处理中..."(参见 issue [Bug] 前端完全未监听 deploy-log 事件,部署过程无任何进度反馈 #43 的结构化事件设计)。
  • ready → 成功 toast。
  • error → 读取 error_message / title 字段在 UI 展示。
  • 5 分钟仍未 ready → 显示"仍在处理中,请稍后到 Netlify 后台查看",但不要把这种情况当成失败;建议提供一个按钮打开该 deploy URL。

实际行为

上传一完就宣布成功,用户无法知晓 Netlify 后续处理结果。

截图 / 日志

无。

补充信息 — 最佳解决方案

新增 pollDeployStatus 步骤

type netlifyDeployStatus struct {
    Id          string `json:\"id\"`
    State       string `json:\"state\"`          // uploading / processing / ready / error
    DeployUrl   string `json:\"deploy_url\"`
    ErrorMsg    string `json:\"error_message\"`
    DeployTime  int    `json:\"deploy_time\"`
}

func (p *NetlifyProvider) pollDeployStatus(ctx context.Context, deployId, token string, logger LogFunc) (*netlifyDeployStatus, error) {
    apiURL := fmt.Sprintf(\"%s/deploys/%s\", netlifyAPIBase, deployId)
    deadline := time.Now().Add(5 * time.Minute)
    interval := 2 * time.Second

    for {
        if time.Now().After(deadline) {
            return nil, fmt.Errorf(\"等待 Netlify 处理超时最后状态未知\")
        }
        if err := ctx.Err(); err != nil {
            return nil, err
        }

        req, _ := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
        req.Header.Set(\"Authorization\", \"Bearer \"+token)
        resp, err := p.client.Do(req)
        if err != nil {
            // 短暂错误,继续 poll
            logger(fmt.Sprintf(\"poll 失败将重试): %v\", err))
        } else {
            bodyBytes, _ := io.ReadAll(resp.Body)
            resp.Body.Close()
            if resp.StatusCode == http.StatusOK {
                var st netlifyDeployStatus
                if json.Unmarshal(bodyBytes, &st) == nil {
                    logger(fmt.Sprintf(\"状态: %s\", st.State))
                    switch st.State {
                    case \"ready\":
                        return &st, nil
                    case \"error\":
                        return &st, fmt.Errorf(\"Netlify 处理失败: %s\", st.ErrorMsg)
                    }
                }
            }
        }

        select {
        case <-ctx.Done():
            return nil, ctx.Err()
        case <-time.After(interval):
        }
        // 指数退避到 10s 上限
        if interval < 10*time.Second {
            interval *= 2
        }
    }
}

Deploy 最后加:

logger(\"文件上传完成等待 Netlify 处理...\")
status, err := p.pollDeployStatus(ctx, deployId, token, logger)
if err != nil {
    return err
}
logger(fmt.Sprintf(\"Netlify 部署成功: %s\", status.DeployUrl))

结合其他 issue

Vercel 侧对照

Vercel 也有类似问题,但更复杂(还有 checks / preview)。可以在本 issue 落地后单独开一个"Vercel deploy 状态轮询"的 follow-up。

单测

  • Mock Netlify API 返回 uploading→processing→ready,验证 Deploy 成功。
  • Mock 返回 error,验证返回错误并带 error_message。
  • ctx 取消中途,立即返回。

影响文件:

  • backend/internal/deploy/netlify_deployer.go
  • 新增单测。

检查清单

  • 我已搜索过现有 Issue,确认此 Bug 尚未被报告过。
  • 我正在使用最新版本的 Gridea Pro。

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1中优先级 / 强烈建议处理bugSomething isn't workingtriage待分诊

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions