Cube 文档静态资源上传指南:upload-asset.sh 与 cube-dev-websites-shared S3 桶的完整使用规范
【免费下载链接】cube📊 Cube Core is open-source semantic layer for AI, BI and embedded analytics项目地址: https://gitcode.com/gh_mirrors/cu/cube
本指南围绕 Cube 文档仓库(docs-mintlify/)中维护文档静态资源(图片、Logo、架构图等)的标准流程展开,核心工具是docs-mintlify/scripts/upload-asset.sh:它负责将本地静态资源上传到cube-dev-websites-sharedS3 桶,并输出可直接写入.mdx文档的https://static.cube.dev/<key>URL。读完本文,你将掌握该脚本的安装配置、调用方式、路径命名约定、不可变(immutable)对象存储策略,以及如何在 Mintlify 文档中正确引用这些资源。
背景:为什么文档图片不能直接提交到仓库
Cube 文档站点基于 Mintlify 构建(参见 docs-mintlify/package.json,依赖mintlify ^4),其本地开发与构建命令如下:
cd docs-mintlify yarn dev # 启动 Mintlify 开发服务器(端口 3002) yarn build # 构建文档站点关于图片与二进制文件的处理,docs-mintlify/CLAUDE.md 明确了一条硬性规则:不要把图片或其他二进制文件提交到仓库。截屏、示意图、Logo、视频等编辑性媒体统一上传到cube-dev-websites-sharedS3 桶,由https://static.cube.dev/<key>提供访问,文档.mdx中只引用该 URL。仓库中的docs-mintlify/images/目录仅存放少数遗留旧资源,官方说明不再向其中新增内容。
这样的设计带来几个好处:
- 仓库只包含文本内容(
.mdx),体积小、diff 清晰、审查容易; - 同一资源可被多篇文档复用,URL 与文档页面解耦;
- 对象携带长效缓存头(
Cache-Control: public, max-age=31536000, immutable),CDN 层可以放心缓存,站点加载更快。
脚本概览:upload-asset.sh 的职责与调用方式
upload-asset.sh 是一个约 116 行的 Bash 脚本(以#!/usr/bin/env bash开头,启用set -euo pipefail),核心流程是:
- 校验参数与本地文件是否存在;
- 校验目标 key 是否合法(必须是相对路径、不允许包含
..或/开头); - 根据文件扩展名推断
Content-Type; - 若未传
--force,先通过aws s3api head-object检查 key 是否已存在,已存在则拒绝覆盖; - 用
aws s3 cp上传,携带固定的content-type与cache-control; - 打印最终 URL,并在 macOS 上通过
pbcopy复制到剪贴板。
脚本的常量定义如下(可在脚本头部看到):
BUCKET="cube-dev-websites-shared" REGION="us-west-2" PUBLIC_BASE="https://static.cube.dev" PROFILE="${AWS_PROFILE:-cube-static}"也就是说:桶名固定为cube-dev-websites-shared,区域为us-west-2,公网基址为https://static.cube.dev,AWS 凭证 profile 默认取cube-static(可用环境变量AWS_PROFILE覆盖)。脚本还内置了usage函数,会在参数不足时打印帮助信息后退出(退出码 1)。
基本调用
在docs-mintlify/目录下执行:
./scripts/upload-asset.sh <local-file> <dest-key> [--force]参数含义:
| 参数 | 说明 |
|---|---|
<local-file> | 本地待上传文件的路径(相对docs-mintlify/),必须真实存在,否则脚本报error: source file not found |
<dest-key> | 上传到桶内的目标 key(即 URL 的<key>部分),必须是相对路径,不允许以/开头或包含..,否则脚本报错拒绝 |
--force | 可选。跳过"key 已存在则拒绝"的检查,强制覆盖(详见下文"不可变性"一节) |
官方示例:
./scripts/upload-asset.sh ./snowflake.svg icons/snowflake.svg ./scripts/upload-asset.sh ./architecture.png docs/getting-started/architecture.png ./scripts/upload-asset.sh ./flow.svg diagrams/pre-aggregations-flow.svg成功上传后的输出形如:
→ bucket: s3://cube-dev-websites-shared/icons/snowflake.svg → region: us-west-2 → profile: cube-static → content-type: image/svg+xml → cache: public, max-age=31536000, immutable ✓ uploaded https://static.cube.dev/icons/snowflake.svg (copied to clipboard)把输出的 URL 粘贴到对应的.mdx文件中并提交即可。
注意:docs-mintlify/CLAUDE.md 特别提醒,本仓库直接使用./scripts/upload-asset.sh,而不是pnpm upload-asset——后者是 landing 仓库的封装,不适用于此处。
一次性环境配置(One-time setup)
1. 安装 AWS CLI
brew install awscli2. 配置本地 profile
建议将 profile 命名为cube-static,这样脚本会自动选中它(脚本的默认PROFILE="${AWS_PROFILE:-cube-static}"会优先读取AWS_PROFILE环境变量,其次回退到cube-static;如果你用了别的名字,运行时需先export AWS_PROFILE=<name>)。
aws configure --profile cube-static # AWS Access Key ID: <your key> # AWS Secret Access Key: <your secret> # Default region name: us-west-2 # Default output format: json所需凭证必须具有对cube-dev-websites-shared桶执行s3:PutObject与s3:HeadObject的权限——前者用于上传,后者用于不可变检查。如果没有凭证,应向 Cube 的 AWS 账号管理者申请,切勿自行猜测或捏造凭证(CLAUDE.md 中明确强调"ask — don't guess credentials")。
3. 验证配置
aws sts get-caller-identity --profile cube-static aws s3 ls s3://cube-dev-websites-shared/icons/ --profile cube-static | head第一条命令返回当前身份信息,确认凭证有效;第二条列出桶内icons/前缀下的对象,确认对桶的读权限与路径可用。
路径约定:按内容域组织资源
为保证同一资源可跨页面复用,资源按内容域分组存放,且文件名统一使用 kebab-case(小写连字符)。完整的前缀表如下:
| 前缀 | 用途 |
|---|---|
icons/<slug>.svg | Provider / integration / vendor 的 Logo,用于<Card>组件 |
icons/<slug>-light.svg | Logo 的浅色变体(用于深色背景) |
icons/<slug>-dark.svg | Logo 的深色变体(用于浅色背景) |
docs/<section>/<slug>/<file> | 特定文档页面的截屏与配图 |
diagrams/<slug>.svg | 架构图 / 流程图 |
recipes/<slug>/<file> | 与 recipe 相关的截屏 |
文件格式建议:
- Provider Logo 优先使用 SVG(矢量、可缩放、体积小);
- UI 截屏优先使用 PNG,大图使用 WebP;
- 上传前务必压缩——该桶被激进缓存(
max-age=31536000),一旦 CDN 缓存后,体积过大的资源会持续拖慢页面。
这一约定与 docs-mintlify/CLAUDE.md 的 "Images and screenshots" 一节保持一致,后者给出了同样的四个前缀(docs/<section>/<slug>/<file>、icons/<slug>.svg、diagrams/<slug>.svg、recipes/<slug>/<file>),并补充说明:截屏请用 Mintlify 的<Frame>组件包裹;暂时没有截屏时,先用 MDX 注释占位:{/* TODO: screenshot — ... */}。
在仓库实际文档中,可以找到大量按此约定组织的引用示例,例如:
- docs-mintlify/admin/connect-to-data/visualization-tools/index.mdx 中的
<Card>图标,如https://static.cube.dev/icons/quicksight.svg、https://static.cube.dev/icons/hashboard.svg、https://static.cube.dev/icons/metabase.svg等,以及深色/浅色变体icons/klipfolio-light.svg、icons/hashboard.svg、icons/hightouch-dark.svg; - docs-mintlify/admin/deployment/continuous-deployment.mdx 中的页面截屏,如
https://static.cube.dev/docs/admin/deployment/continuous-deployment/build-deploy-tab.png; - docs-mintlify/admin/deployment/dedicated/aws/private-api-connectivity.mdx 中的架构图,如
https://static.cube.dev/diagrams/private-api-connectivity-aws-v2.png。
这些真实用例印证了icons/、docs/<section>/、diagrams/三类前缀的实际落地形态。
不可变性约定:路径不可覆盖
对象路径按约定不可变。脚本默认拒绝覆盖已存在的 key:非--force模式下,它先调用
aws s3api head-object --bucket "$BUCKET" --key "$KEY" --region "$REGION" --profile "$PROFILE"若对象已存在,脚本会打印错误并退出(错误信息同时提示:约定是不可变路径,请换一个新 key,例如加-v2后缀,或确有必要时传--force)。
如果资源需要更新,标准流程是:
- 上传带版本后缀的新 key,例如
snowflake-v2.svg; - 在同一个 PR中更新
.mdx里的引用。
这样做有两层收益:
- 保证
Cache-Control: public, max-age=31536000, immutable的安全性——旧 URL 内容永不变化,CDN 缓存永远有效; - 回滚极其简单——只需还原 Markdown 里的 URL 引用即可,无需动 S3 上的对象。
什么时候可以用 --force
如果确实需要覆盖(例如同一会话中上传了损坏文件、且 CDN 尚未缓存),可以传--force:
./scripts/upload-asset.sh ./fixed.svg icons/snowflake.svg --force但请避免对任何已上线的资源使用--force。原因从脚本源码可见:上传时固定携带--cache-control "public, max-age=31536000, immutable",对象会在一整年内被视为不可变而被缓存。覆盖已上线的 key 意味着新内容可能要在缓存中滞留长达一年才能被用户看到——这正是"路径不可变"约定的出发点。
从脚本源码看底层实现细节
upload-asset.sh 中有几个值得关注的实现细节,有助于理解脚本行为:
Content-Type 推断(guess_content_type函数):脚本将文件名转小写后按扩展名映射 MIME 类型——.svg → image/svg+xml、.png → image/png、.jpg/.jpeg → image/jpeg、.gif → image/gif、.webp → image/webp、.avif → image/avif、.ico → image/x-icon、.mp4 → video/mp4、.webm → video/webm、.pdf → application/pdf、.json → application/json、.txt/.md → text/plain; charset=utf-8;未知扩展名则回退到file --mime-type -b探测,探测失败时兜底为application/octet-stream。这意味着 SVG 与 PNG 之外的文件类型(如 WebP、PDF、视频)也能被正确处理。
上传命令:最终通过
aws s3 cp "$SRC" "s3://${BUCKET}/${KEY}" \ --region "$REGION" \ --profile "$PROFILE" \ --content-type "$CONTENT_TYPE" \ --cache-control "$CACHE_CONTROL"完成上传,content-type与cache-control均显式指定,保证 CDN 与浏览器按预期解析资源。
macOS 剪贴板:上传成功后若检测到pbcopy(macOS 自带),会将 URL 写入剪贴板,方便直接粘贴到文档中。
上传后的验证与质量把关
docs-mintlify/CLAUDE.md 强烈建议:在编辑任何.mdx之前,先验证每次上传。验证命令:
curl -sI https://static.cube.dev/<key>期望响应:
- HTTP 状态码
200; content-type正确;content-length与本地文件一致。
这样可以在重写大量文档之前就发现坏上传,成本远低于事后排查。另外,static.cube.dev是原样透传(pass-through)的——博客的图片优化器只会重写 Uploadcare 的 URL,不会对static.cube.dev上的资源做任何缩放或压缩,因此上传前必须自行压缩:高分辨率截屏(例如 CleanShot 直出的 Retina 图)常常超过 3000px、数 MB 大小,需要先缩放;UI 截屏优先 PNG,大图用 WebP,Logo 用 SVG。
与其它文档脚本的分工
docs-mintlify/scripts/目录下还有若干配套脚本(完整清单见 docs-mintlify/scripts),与upload-asset.sh分工不同:
- check_links.py:校验 Mintlify 文档中所有内部链接是否指向真实文件(支持行内链接
text、引用式链接[ref]: /path、href/url属性),可传入文档根目录运行,--verbose输出详情; - extract-api.mjs、extract-changelog.mjs、extract-chat.js、extract-core-data.js:从上游(API 定义、变更日志等)提取内容生成文档;
- rewrite_links.py、transform_components.py、update_frontmatter.py:批量改写链接、转换组件、更新 frontmatter。
其中extract-api.mjs与extract-changelog.mjs已接入 docs-mintlify/package.json 的脚本命令(api:extract、api:changelog、api:sync、api:check)。upload-asset.sh是其中唯一负责媒体资源上传与发布的核心脚本。
小结:标准工作流
将以上内容串成一条完整的日常工作流:
- 在
docs-mintlify/下准备好本地资源文件(Logo 用 SVG,截屏用 PNG/WebP,先压缩); - 按路径约定确定目标 key(
icons/、docs/<section>/<slug>/、diagrams/或recipes/,kebab-case 命名); - 执行
./scripts/upload-asset.sh <local-file> <dest-key>,脚本输出 URL 并(macOS 上)复制到剪贴板; - 用
curl -sI https://static.cube.dev/<key>验证200、content-type与content-length; - 将 URL 粘贴进对应的
.mdx(截屏用<Frame>包裹),同一个 PR 内提交引用变更; - 资源需要更新时,上传带版本后缀的新 key(如
-v2)并更新引用,避免对已上线资源使用--force。
这套流程保证了 Cube 文档仓库保持轻量、资源 URL 稳定、CDN 缓存高效且可随时回滚,是维护 docs-mintlify 文档站点内容时的标准做法。
【免费下载链接】cube📊 Cube Core is open-source semantic layer for AI, BI and embedded analytics项目地址: https://gitcode.com/gh_mirrors/cu/cube
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考