diff --git a/.github/r2-cors.json b/.github/r2-cors.json new file mode 100644 index 000000000..d5a9929a1 --- /dev/null +++ b/.github/r2-cors.json @@ -0,0 +1,11 @@ +{ + "CORSRules": [ + { + "AllowedHeaders": ["*"], + "AllowedMethods": ["GET", "HEAD"], + "AllowedOrigins": ["*"], + "ExposeHeaders": ["ETag"], + "MaxAgeSeconds": 3600 + } + ] +} diff --git a/.github/workflows/sync-changelog.yml b/.github/workflows/sync-changelog.yml index a31e101cb..dcc00c0b3 100644 --- a/.github/workflows/sync-changelog.yml +++ b/.github/workflows/sync-changelog.yml @@ -3,10 +3,14 @@ name: Sync Changelog to R2 on: release: types: [published] + workflow_run: + workflows: ['Release'] + types: [completed] workflow_dispatch: jobs: sync: + if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -24,6 +28,10 @@ jobs: - name: Upload to R2 run: | pip install awscli --quiet + aws s3api put-bucket-cors \ + --bucket "${R2_BUCKET_NAME}" \ + --cors-configuration file://.github/r2-cors.json \ + --endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" aws s3 cp releases-cn.json "s3://${R2_BUCKET_NAME}/changelog/releases-cn.json" \ --endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \ --content-type application/json diff --git a/docs/app/[lang]/changelog/page.tsx b/docs/app/[lang]/changelog/page.tsx index 022f9217f..04d855b9a 100644 --- a/docs/app/[lang]/changelog/page.tsx +++ b/docs/app/[lang]/changelog/page.tsx @@ -1,6 +1,6 @@ -import { fetchChangelog } from '@/lib/changelog'; import { LandingNav } from '@/components/landing/LandingNav'; -import { ChangelogList } from '@/components/landing/ChangelogList'; +import { ChangelogRuntime } from '@/components/landing/ChangelogRuntime'; +import { fetchChangelog } from '@/lib/changelog'; const i18n = { en: { @@ -17,7 +17,7 @@ export default async function ChangelogPage({ params }: { params: Promise<{ lang const { lang } = await params; const l = lang === 'cn' ? 'cn' : 'en'; const t = i18n[l]; - const data = await fetchChangelog(l); + const initialData = await fetchChangelog(l); return (
@@ -29,11 +29,7 @@ export default async function ChangelogPage({ params }: { params: Promise<{ lang
- {data.releases.length === 0 ? ( -

No releases found.

- ) : ( - - )} +
); diff --git a/docs/app/[lang]/page.tsx b/docs/app/[lang]/page.tsx index 872db0022..78c104888 100644 --- a/docs/app/[lang]/page.tsx +++ b/docs/app/[lang]/page.tsx @@ -5,11 +5,14 @@ import { InfiniteMovingCards } from '@/components/aceternity/InfiniteMovingCards import { Spotlight } from '@/components/aceternity/Spotlight'; import { LandingNav } from '@/components/landing/LandingNav'; import { InstallTabs } from '@/components/landing/InstallTabs'; +import { LandingLatestUpdates } from '@/components/landing/LandingLatestUpdates'; import { RevealSection } from '@/components/landing/RevealSection'; +import { getAppVersion } from '@/lib/appVersion'; +import { fetchChangelog } from '@/lib/changelog'; +import { fetchLatestReleaseInfo } from '@/lib/latestRelease'; import { ArrowRight, Bot, - CheckCircle2, Database, FileCode, GitCompare, @@ -166,33 +169,6 @@ const capabilities = { ], }; -const latestUpdates = { - en: { - version: 'v0.5.4', - title: 'Latest updates', - desc: 'Mirrored from the latest GitHub release notes.', - link: 'Read the changelog', - items: [ - 'JDBC SSH tunnels and proxy support', - 'Grouped object browser with context menus', - 'Redis batch operations and command runner', - 'LIKE / NOT LIKE filters in the data grid', - ], - }, - cn: { - version: 'v0.5.4', - title: '最近更新', - desc: '同步 GitHub 最新 Release Notes。', - link: '查看更新日志', - items: [ - 'JDBC SSH 隧道和代理支持', - '对象浏览器分组与右键菜单', - 'Redis 批量操作和命令行', - '数据表格 LIKE / NOT LIKE 过滤', - ], - }, -}; - const testimonials = { en: [ { @@ -402,7 +378,9 @@ export default async function LandingPage({ const capabilityItems = capabilities[l]; const starLabel = await getGitHubStarLabel(); const metricItems = metrics(starLabel)[l]; - const latest = latestUpdates[l]; + const appVersion = getAppVersion(); + const [initialChangelog, initialLatestRelease] = await Promise.all([fetchChangelog(l), fetchLatestReleaseInfo()]); + const initialDownloadVersion = initialLatestRelease?.version ?? appVersion; const testimonialItems = testimonials[l]; return ( @@ -422,7 +400,7 @@ export default async function LandingPage({ {t.heroSubtitle}

- +
@@ -524,25 +502,7 @@ export default async function LandingPage({ {/* Updates */} - -
{latest.version}
-
-

{latest.title}

-

{latest.desc}

-
- - - {latest.link} - - -
+ {/* Final CTA */} diff --git a/docs/components/landing/ChangelogRuntime.tsx b/docs/components/landing/ChangelogRuntime.tsx new file mode 100644 index 000000000..3ba1fc1a4 --- /dev/null +++ b/docs/components/landing/ChangelogRuntime.tsx @@ -0,0 +1,49 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { ChangelogList } from '@/components/landing/ChangelogList'; +import { fetchChangelog, type ChangelogRelease } from '@/lib/changelog'; + +type ChangelogRuntimeProps = { + lang: 'en' | 'cn'; + initialReleases?: ChangelogRelease[]; +}; + +const text = { + en: { + loading: 'Loading releases...', + empty: 'No releases found.', + }, + cn: { + loading: '正在加载版本记录...', + empty: '暂无版本记录。', + }, +}; + +export function ChangelogRuntime({ lang, initialReleases = [] }: ChangelogRuntimeProps) { + const [releases, setReleases] = useState(initialReleases); + + useEffect(() => { + let active = true; + + fetchChangelog(lang).then((data) => { + if (active && data.releases.length > 0) { + setReleases(data.releases); + } + }); + + return () => { + active = false; + }; + }, [lang]); + + if (releases === null) { + return

{text[lang].loading}

; + } + + if (releases.length === 0) { + return

{text[lang].empty}

; + } + + return ; +} diff --git a/docs/components/landing/InstallTabs.tsx b/docs/components/landing/InstallTabs.tsx index ae4ac2063..a600dccb1 100644 --- a/docs/components/landing/InstallTabs.tsx +++ b/docs/components/landing/InstallTabs.tsx @@ -2,32 +2,12 @@ import { ChevronDown, Download, Server } from 'lucide-react'; import { useEffect, useMemo, useState } from 'react'; +import { createInstallOptions, type InstallOption } from '@/lib/downloadLinks'; +import { fetchLatestReleaseInfo } from '@/lib/latestRelease'; type InstallTabsProps = { lang: 'en' | 'cn'; -}; - -type InstallOption = { - id: string; - label: string; - href: string; -}; - -const allOptions: Record = { - en: [ - { id: 'macos-arm', label: 'For macOS (Apple Silicon)', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_aarch64.dmg' }, - { id: 'macos-intel', label: 'For macOS (Intel)', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_x64.dmg' }, - { id: 'windows', label: 'For Windows', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_x64-setup.exe' }, - { id: 'linux', label: 'For Linux x64', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_amd64.AppImage' }, - { id: 'linux-arm', label: 'For Linux ARM64', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_aarch64.AppImage' }, - ], - cn: [ - { id: 'macos-arm', label: '适用于 macOS (Apple Silicon)', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_aarch64.dmg' }, - { id: 'macos-intel', label: '适用于 macOS (Intel)', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_x64.dmg' }, - { id: 'windows', label: '适用于 Windows', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_x64-setup.exe' }, - { id: 'linux', label: '适用于 Linux x64', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_amd64.AppImage' }, - { id: 'linux-arm', label: '适用于 Linux ARM64', href: 'https://dl.dbxio.com/releases/latest/DBX_0.5.9_aarch64.AppImage' }, - ], + version: string; }; const downloadLabel = { en: 'Download DBX', cn: '下载 DBX' }; @@ -63,8 +43,9 @@ function PlatformIcon({ id, size, variant }: { id: string; size: number; variant return ; } -export function InstallTabs({ lang }: InstallTabsProps) { - const options = allOptions[lang]; +export function InstallTabs({ lang, version }: InstallTabsProps) { + const [downloadVersion, setDownloadVersion] = useState(version); + const options = useMemo(() => createInstallOptions(lang, downloadVersion), [lang, downloadVersion]); const [open, setOpen] = useState(false); const [platformId, setPlatformId] = useState('macos-arm'); @@ -72,6 +53,21 @@ export function InstallTabs({ lang }: InstallTabsProps) { setPlatformId(detectPlatformId()); }, []); + useEffect(() => { + let active = true; + + setDownloadVersion(version); + fetchLatestReleaseInfo().then((release) => { + if (active && release?.version) { + setDownloadVersion(release.version); + } + }); + + return () => { + active = false; + }; + }, [version]); + const primary = useMemo(() => options.find((o) => o.id === platformId) ?? options[0], [options, platformId]); const menuOptions = useMemo(() => options.filter((o) => o.id !== platformId), [options, platformId]); diff --git a/docs/components/landing/LandingLatestUpdates.tsx b/docs/components/landing/LandingLatestUpdates.tsx new file mode 100644 index 000000000..8da05f521 --- /dev/null +++ b/docs/components/landing/LandingLatestUpdates.tsx @@ -0,0 +1,57 @@ +'use client'; + +import Link from 'next/link'; +import { ArrowRight, CheckCircle2 } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { RevealSection } from '@/components/landing/RevealSection'; +import { fetchChangelog, type ChangelogRelease } from '@/lib/changelog'; +import { buildLandingLatestUpdates } from '@/lib/landingLatest'; +import { fetchLatestReleaseInfo, type LatestReleaseInfo } from '@/lib/latestRelease'; + +type LandingLatestUpdatesProps = { + lang: 'en' | 'cn'; + fallbackVersion: string; + initialRelease?: ChangelogRelease; + initialLatestRelease?: LatestReleaseInfo | null; +}; + +export function LandingLatestUpdates({ lang, fallbackVersion, initialRelease, initialLatestRelease }: LandingLatestUpdatesProps) { + const [latest, setLatest] = useState(() => buildLandingLatestUpdates(lang, initialRelease, fallbackVersion, initialLatestRelease)); + + useEffect(() => { + let active = true; + + setLatest(buildLandingLatestUpdates(lang, initialRelease, fallbackVersion, initialLatestRelease)); + Promise.all([fetchLatestReleaseInfo(), fetchChangelog(lang)]).then(([releaseInfo, data]) => { + if (!active) return; + + setLatest(buildLandingLatestUpdates(lang, data.releases[0] ?? initialRelease, fallbackVersion, releaseInfo ?? initialLatestRelease)); + }); + + return () => { + active = false; + }; + }, [lang, fallbackVersion, initialRelease, initialLatestRelease]); + + return ( + +
{latest.version}
+
+

{latest.title}

+

{latest.desc}

+
+
    + {latest.items.map((item) => ( +
  • + + {item} +
  • + ))} +
+ + {latest.link} + + +
+ ); +} diff --git a/docs/content/docs/changelog.cn.mdx b/docs/content/docs/changelog.cn.mdx index e3542eabd..a4d6f3238 100644 --- a/docs/content/docs/changelog.cn.mdx +++ b/docs/content/docs/changelog.cn.mdx @@ -3,195 +3,6 @@ title: 更新日志 description: 按版本查看 DBX 的新增功能、体验改进和问题修复,内容同步自 GitHub Releases。 --- -这里整理 DBX 的版本变化,按版本号从新到旧排列。内容以 GitHub Releases 为准,保留功能、改进和修复条目,下载安装说明请查看对应 GitHub Release 页面。 +更新日志会直接读取 R2 上的最新 Release 数据,因此发布后无需重新构建文档站也能刷新。 -## v0.5.4 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.4) - -### 新功能 - -- **JDBC SSH 隧道和代理支持** — JDBC 连接新增 SSH 隧道和代理支持,自动解析和改写 JDBC URL 中的 host:port,通过隧道安全访问远程数据库 -- **对象浏览器分组与右键菜单** — 对象浏览器按类型分组显示数据库对象,新增右键上下文菜单操作 -- **Redis 批量操作和命令行** — Redis 新增批量键操作和命令执行器 -- **数据库代理支持** — 新增数据库连接和 AI 的代理 (Proxy) 支持 -- **数据表格 LIKE/NOT LIKE 过滤** — 右键菜单新增 LIKE、NOT LIKE、大于、小于等过滤条件 -- **数据库对象源码编辑** — 支持查看和编辑数据库对象(存储过程、函数、视图等)的源码 -- **Connection URL 动态占位符** — 连接对话框根据数据库类型动态显示对应的 URL 格式示例 (contributed by @Abeautifulsnow) -- **AI Thinking 开关** — AI 设置新增 thinking 开关,可关闭本地模型(如 Ollama/vLLM)的思考输出以节省 token (contributed by @rarnu) -- **PostgreSQL 新类型支持** — 新增 PostgreSQL 整数数组和 float4 类型支持 (contributed by @xKrah) -- **西班牙语支持** — 新增西班牙语本地化,重构语言切换为下拉菜单 (contributed by @Max29xD) - -### 改进 - -- **设置页面优化** — AI 模型设置移至全局设置页面,优化设置页面布局和选中项样式 (contributed by @rarnu) -- **单元格值不再截断** — 修复详情面板和复制/导出操作中单元格值被截断为 256 字符的问题,现在显示完整内容 (contributed by @Abeautifulsnow) -- **虚拟滚动优化** — 优化数据表格虚拟滚动,减少快速滚动时的白屏闪烁 -- **数据表格工具栏** — 优化数据表格工具栏布局 -- **Redis 键浏览器国际化** — Redis 键浏览器新增国际化支持 (contributed by @xKrah) -- **设置页面链接** — 改进设置对话框中的链接显示 - -### 修复 - -- **Oracle RAC/SCAN 连接** — 修复 Oracle RAC/SCAN 连接重定向问题 (closes #209) -- **Oracle 表数据加载与编辑** — 修复 Oracle 表数据加载和编辑功能异常 -- **Oracle Schema 切换** — 简化编辑器选择器并支持 schema 切换 (closes #205) -- **Oracle 非英文 Windows 连接错误** — 修复 Oracle 在非英文 Windows 系统上的连接错误检测 (closes #208) -- **Oracle Schema Diff** — 加速 Oracle schema diff 并增加注释同步 (closes #198) -- **侧边栏性能** — 修复 100+ 数据库时侧边栏 UI 卡顿 (closes #201) -- **侧边栏搜索** — 修复侧边栏搜索无法找到被对象浏览器隐藏的表 -- **侧边栏刷新** — 修复展开节点刷新时子节点缓存未清除的问题 -- **WHERE 过滤持久化** — 修复 WHERE 过滤条件在 tab 切换时丢失的问题 (closes #210) -- **数据表格分页** — 修复保存数据后分页大小被重置的问题 -- **Enter 键补全** — 修复 Enter 键优先选中自动补全建议项 (closes #202) -- **SQL Server 对象源码** — 修复 SQL Server 对象源码定义保存问题 -- **构建修复** — 修复单引号导致构建失败的问题 (contributed by @xKrah) -- **侧边栏双击** — 修复侧边栏需要双击才能打开对象浏览器的问题 -- **虚拟树优化** — 修复非空树的虚拟化判断函数 - -## v0.5.3 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.3) - -### 新功能 - -- **AI SQL 智能执行** — AI 助手支持自动执行 SQL 语句,内置安全防护策略,根据语句类型自动判断是否允许执行 -- **MongoDB 表视图** — 新增 MongoDB 集合的表格视图,支持内联编辑数据 -- **导入 DBeaver 连接** — 支持从 DBeaver 导入数据库连接配置 -- **导入 Navicat 连接** — 支持从 Navicat NCX 文件导入数据库连接配置 -- **存储过程/函数浏览** — 对象浏览器支持显示存储过程和函数 -- **虚拟对象浏览器** — 新增虚拟对象浏览器,统一展示数据库对象 -- **数据库活动历史** — 支持追踪和查看数据库操作历史记录 -- **外部表格数据源基础** — 新增外部表格数据源的通用底座架构,为后续接入 CSV/XLSX/在线表格等数据源奠定基础 (contributed by @BlueSkyXN) -- **数据表格加载指示器** — 数据表格新增加载遮罩层和耗时计时器,查询执行状态一目了然 - -### 改进 - -- **编辑器增强** — 编辑器功能增强、DataGrid 重构和侧边栏交互改进 -- **XLSX 导出** — 表格右键菜单新增 XLSX 导出选项 -- **全栈性能优化** — 全栈性能优化和安全加固 -- **Oracle 加载提速** — 优化 Oracle 数据库初始表加载速度 -- **DuckDB 编译优化** — DuckDB 捆绑编译改为可选,减小构建体积 - -### 修复 - -- **侧边栏树状态** — 修复侧边栏树状态显示问题 -- **GitHub 链接** — 修复设置页面 About 中 GitHub 链接不可点击的问题 -- **Web 连接池** — 修复带前缀的 Web 连接池被意外断开的问题 -- **连接配置缓存** — 修复 Web 连接配置缓存同步问题 -- **Oracle Schema 缓存** — 修复 Oracle Schema 缓存刷新和表列表不完整的问题 -- **Oracle NCHAR 解码** — 修复 Oracle NCHAR 值解码不正确的问题 -- **侧边栏搜索折叠** — 修复侧边栏搜索结果无法折叠的问题 - -## v0.5.2 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.2) - -### 新功能 - -- **JDBC 插件支持** — 新增可选的 JDBC 插件模块,支持通过 JDBC 驱动连接更多数据库类型 -- **SQL 代码库** — 支持保存和管理常用 SQL 语句,存储在应用数据库中,随时复用 -- **DataGrip 风格列过滤器** — 数据表格支持类似 DataGrip 的列过滤功能,快速筛选数据 -- **数据对比与结构同步** — 新增数据对比和表结构同步功能 -- **连接 URL 和 XLSX 导出** — 支持通过连接 URL 快速创建连接,支持导出数据为 XLSX 格式 -- **连接颜色标识** — 连接支持自定义颜色标识,方便区分不同环境 -- **表格剪贴板快捷键** — 数据表格支持复制粘贴等剪贴板快捷键操作 -- **布局偏好设置** — 支持自定义界面布局偏好 -- **SSH 超时配置** — SSH 隧道连接支持配置超时时间 - -### 改进 - -- **界面外观优化** — 优化面板、标签页和应用整体外观布局 - -### 修复 - -- **Redis 键表列对齐** — 修复 Redis 键表格列宽未对齐的问题 -- **连接状态同步** — 修复展开缓存树时连接状态未同步的问题 -- **连接对话框错误** — 修复连接对话框的错误提示问题 -- **SQL 注释后查询检测** — 修复 SQL 注释后的结果查询未被正确检测的问题 -- **列过滤弹窗优化** — 优化列过滤弹窗的交互体验 -- **查询标签页持久化** — 修复未保存的查询标签页丢失的问题 -- **隐藏系统数据库对象** — 修复侧栏显示系统数据库对象的问题 -- **文件选择器行为** — 修复 Web 版文件选择器的行为不一致问题 - -## v0.5.1 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.1) - -### 新功能 - -- **设置页关于标签** — 设置对话框新增"关于"标签页,方便查看版本信息 -- **数据库对象列表缓存** — 侧栏数据库对象列表支持缓存,减少重复查询,加速切换 -- **从分组菜单创建连接** — 侧栏分组右键菜单支持直接创建新连接 -- **编辑器 Tab 缩进** — SQL 编辑器支持 Tab 键缩进 -- **Windows 绿色版压缩包** — 新增 Windows 便携版(ZIP),无需安装即可使用 - -### 改进 - -- **查询结果流式加载** — 查询结果使用流式传输并限制行数,提升大数据量查询的响应速度 -- **侧栏搜索性能优化** — 侧栏搜索匹配算法优化,过滤速度更快 -- **表格右键菜单增强** — 数据表格右键菜单功能增强 -- **数据表工具栏条件刷新** — 修复表数据编辑的工具栏,条件过滤和刷新不再丢失状态 (contributed by @rarnu) -- **AI 对话历史可切换** — AI 助手对话历史支持开关控制 - -### 修复 - -- **SQL Server 编辑支持** — 修复 SQL Server 数据编辑相关问题 -- **更新后重启** — 修复应用更新后重启失败的问题 -- **表格翻页滚动位置** — 修复翻页后表格滚动位置未重置的问题 -- **MySQL 事务编辑** — 修复 MySQL 事务编辑使用原始 SQL 执行的问题 -- **侧栏刷新保持展开** — 修复侧栏刷新后树节点展开状态丢失的问题 -- **标题栏双击切换** — 修复标题栏双击时窗口状态重复切换的问题 (#179) -- **默认库过滤数据库树** — 修复默认数据库配置过滤数据库树显示的问题 (#175) -- **标签文字被箭头遮挡** — 修复标签页滚动箭头与文字重叠的问题 -- **表格与弹窗布局** — 优化表格与 SQL 文件弹窗的布局显示 -- **事务状态位置** — 事务状态指示移至操作按钮旁边,更直观 -- **WHERE/ORDER BY 条件** — 修复 WHERE 和 ORDER BY 输入框条件重复和状态丢失的问题 -- **默认数据库处理** — 统一默认数据库的处理逻辑 - -## v0.5.0 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.0) - -### 新功能 - -- **Redis 浏览器全面重构** — 键浏览器升级为 Navicat 风格表格视图,支持键值编辑、分页浏览和全库键扫描 (contributed by @Bacon2994) -- **自定义标题栏** — 窗口标题栏改为自定义样式,集成窗口控制按钮 -- **数据表格 WHERE/ORDER BY 过滤** — 数据表格新增独立的 WHERE 和 ORDER BY 输入框,支持自定义条件过滤和排序 (#165) -- **结果集数据过滤** — 查询结果集内支持对已加载数据进行实时过滤 (contributed by @rarnu) -- **单元格值编辑器** — 单元格详情面板支持直接编辑值和设置 NULL -- **列宽自适应** — 数据表格自动根据表头文字和数据内容调整最佳列宽 -- **复制为 INSERT** — 数据表格右键菜单新增"复制为 INSERT 语句"功能 (#166) -- **创建/删除数据库和 Schema** — 侧栏右键菜单支持创建和删除数据库及 Schema -- **SQL Server CROSS APPLY 补全** — SQL 编辑器支持 CROSS APPLY / OUTER APPLY 语法补全 (#168) -- **OceanBase Oracle 模式** — 自动检测 OceanBase Oracle 模式,使用 Oracle 风格的 Schema 查询 (#155) -- **鼠标中键关闭标签** — 支持鼠标中键点击关闭标签页 (#162) -- **排序提示图标** — 鼠标悬停列标题时显示可排序提示图标 -- **窗口状态记忆** — 窗口位置和大小在关闭后自动保存,重新打开时恢复 -- **MCP 连接管理** — MCP 工具新增 `dbx_add_connection` 和 `dbx_remove_connection`,操作后自动通知桌面端刷新 - -### 改进 - -- **DataGrid 工具栏增强** — 数据表格工具栏和数据集交互优化 (contributed by @rarnu) -- **JSON/JSONB 渲染性能** — 预序列化 JSON/JSONB 值并截断单元格显示,大幅提升大数据量渲染性能 -- **信息栏合并** — 数据表底部信息栏和事务工具栏合并为一行,节省屏幕空间 -- **标签页样式优化** — 标签模式切换改用图标,标签标题改为 Navicat 风格 - -### 修复 - -- **标签页右键菜单** — 修复关闭所有/关闭其他/关闭右侧等标签页右键菜单不可用的问题 -- **查询结果表头和排序** — 修复查询结果表头提示和全量排序问题 (contributed by @Bacon2994) -- **MCP 连接配置** — 修复 MCP 写入的 config_json 与 Rust ConnectionConfig 结构不匹配的问题,改为从 SQLite 读取连接 -- **Tailwind 样式丢失** — 修复 tmpfs 环境下 Tailwind 样式缺失的问题 (#167) -- **数据表格滚动抖动** — 禁用数据表格过度滚动回弹,防止表头和数据错位 (#156) -- **GaussDB Schema 列表** — 移除 GaussDB list_schemas 的 EXISTS 过滤以保持一致性 (#154) -- **侧栏数据库过滤** — 连接配置指定数据库时,侧栏只显示该数据库 (#160) -- **Oracle 11g 兼容性** — 修复 Oracle 11g 连接兼容性问题和侧栏改进 -- **MySQL 全 NULL 显示** — 增加 MySQL 类型回退,防止缺少类型映射时所有列值显示为 NULL (#40) -- **AI 执行 SQL** — 修复 AI 执行时传递过期 SQL 状态的问题 (#153) -- **JSONB 编辑显示** — 修复 JSONB 值在编辑模式和详情面板中未正确显示为 JSON 字符串的问题 (#152) -- **MongoDB 副本集 SSH** — 修复 MongoDB 副本集通过 SSH 隧道连接失败的问题 (#151) -- **Redis 二进制误判** — 修复反斜杠字符串被误判为二进制数据的问题 (contributed by @Bacon2994) -- **Toast 通知层级** — 修复 Toast 通知被对话框遮挡的问题 -- **DDL 面板文本选择** — 修复 DDL 面板无法选中文本的问题 -- **原生右键菜单** — 禁用浏览器原生右键菜单,新增侧栏刷新按钮 (#159) +[打开实时更新日志](/cn/changelog) diff --git a/docs/content/docs/changelog.mdx b/docs/content/docs/changelog.mdx index 9aa0d02e1..94e328eb5 100644 --- a/docs/content/docs/changelog.mdx +++ b/docs/content/docs/changelog.mdx @@ -3,195 +3,6 @@ title: Changelog description: Track DBX release notes by version, mirrored from GitHub Releases. --- -This page mirrors the user-facing release notes from GitHub Releases. It keeps feature, improvement, and fix entries in the docs site; download tables and installer details remain on each GitHub Release page. +The changelog is loaded from the latest release data on R2, so it can refresh after a release without rebuilding the docs site. -## v0.5.4 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.4) - -### Added - -- **JDBC SSH tunnels and proxy support** — JDBC connections can use SSH tunnels and proxies, with host:port values parsed and rewritten from JDBC URLs. -- **Grouped object browser and context menus** — Database objects are grouped by type and expose context-menu actions. -- **Redis batch operations and command runner** — Redis adds batch key operations and a command executor. -- **Database proxy support** — Database connections and AI settings now support proxy configuration. -- **Data grid LIKE / NOT LIKE filters** — Grid context menus add LIKE, NOT LIKE, greater-than, and less-than filters. -- **Database object source editing** — View and edit source for database objects such as procedures, functions, and views. -- **Dynamic Connection URL placeholders** — The connection dialog shows URL examples that match the selected database type (contributed by @Abeautifulsnow). -- **AI Thinking toggle** — AI settings can hide thinking output from local models such as Ollama or vLLM to save tokens (contributed by @rarnu). -- **PostgreSQL type support** — Adds support for integer arrays and float4 (contributed by @xKrah). -- **Spanish localization** — Adds Spanish UI text and refactors language switching into a dropdown (contributed by @Max29xD). - -### Improved - -- **Settings page** — Moves AI model settings into global settings and improves the settings layout (contributed by @rarnu). -- **Full cell values** — Details, copy, and export flows no longer truncate cell values to 256 characters (contributed by @Abeautifulsnow). -- **Virtual scrolling** — Reduces blank flashing during fast grid scrolling. -- **Data grid toolbar** — Refines the grid toolbar layout. -- **Redis browser i18n** — Adds localization support to the Redis key browser (contributed by @xKrah). -- **Settings links** — Improves link presentation in the settings dialog. - -### Fixed - -- Fixes Oracle RAC/SCAN redirects (closes #209). -- Fixes Oracle table loading and editing. -- Simplifies Oracle selector behavior and supports schema switching (closes #205). -- Fixes Oracle connection error detection on non-English Windows systems (closes #208). -- Speeds up Oracle schema diff and adds comment synchronization (closes #198). -- Fixes sidebar lag with 100+ databases (closes #201). -- Fixes sidebar search missing tables hidden by the object browser. -- Fixes stale child caches when refreshing expanded sidebar nodes. -- Preserves WHERE filters across tab switches (closes #210). -- Keeps data grid page size after saving data. -- Makes Enter prefer autocomplete suggestions when available (closes #202). -- Fixes SQL Server object source saving. -- Fixes a build failure caused by single quotes (contributed by @xKrah). -- Fixes object browser items requiring double-click to open. -- Fixes virtual tree detection for non-empty trees. - -## v0.5.3 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.3) - -### Added - -- **AI SQL smart execution** — The AI assistant can execute SQL with built-in statement safety checks. -- **MongoDB table view** — MongoDB collections can be viewed and edited in a grid. -- **DBeaver import** — Import database connection configs from DBeaver. -- **Navicat import** — Import database connections from Navicat NCX files. -- **Procedure and function browsing** — The object browser can show procedures and functions. -- **Virtual object browser** — A unified browser for database objects. -- **Database activity history** — Track and review database operation history. -- **External table data source foundation** — Adds the shared architecture for future CSV, XLSX, and online sheet sources (contributed by @BlueSkyXN). -- **Data grid loading indicator** — Adds a loading mask and elapsed-time display for query execution. - -### Improved - -- Editor features, DataGrid internals, and sidebar interactions. -- XLSX export from the table context menu. -- Full-stack performance and safety hardening. -- Faster initial Oracle table loading. -- Optional DuckDB bundling to reduce build size. - -### Fixed - -- Sidebar tree state display. -- GitHub link clickability in Settings > About. -- Unexpected disconnection of prefixed web connection pools. -- Web connection config cache synchronization. -- Oracle schema cache refresh and incomplete table lists. -- Oracle NCHAR decoding. -- Sidebar search result collapsing. - -## v0.5.2 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.2) - -### Added - -- Optional JDBC plugin support for connecting to more database types through JDBC drivers. -- Saved SQL library for reusable SQL snippets. -- DataGrip-style column filters in the data grid. -- Data comparison and table structure synchronization. -- Connection URL creation and XLSX export. -- Custom connection colors for distinguishing environments. -- Clipboard shortcuts in the data grid. -- Layout preference settings. -- SSH tunnel timeout configuration. - -### Improved - -- Panels, tabs, and general application layout. - -### Fixed - -- Redis key table column alignment. -- Connection state synchronization when expanding cached trees. -- Connection dialog error messages. -- Result-query detection after SQL comments. -- Column filter popup interactions. -- Unsaved query tab persistence. -- Hidden system database objects in the sidebar. -- Web file picker inconsistencies. - -## v0.5.1 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.1) - -### Added - -- About tab in Settings. -- Cached database object lists in the sidebar. -- Create new connections from group context menus. -- Tab indentation in the SQL editor. -- Windows portable ZIP builds. - -### Improved - -- Streaming query results with row limits for large result sets. -- Faster sidebar search filtering. -- More capable grid context menus. -- Better toolbar state retention for data editing filters and refresh (contributed by @rarnu). -- Toggleable AI conversation history. - -### Fixed - -- SQL Server data editing issues. -- Restart after app updates. -- Grid scroll position after pagination. -- MySQL transaction editing through raw SQL. -- Sidebar expanded state after refresh. -- Repeated title-bar double-click state toggles (#179). -- Default database filtering in the database tree (#175). -- Tab text overlapping scroll arrows. -- Table and SQL file dialog layouts. -- Transaction status placement. -- Duplicate or lost WHERE / ORDER BY state. -- Default database handling consistency. - -## v0.5.0 - -[GitHub Release](https://github.com/t8y2/dbx/releases/tag/v0.5.0) - -### Added - -- Fully rebuilt Redis browser with a Navicat-style table view, value editing, pagination, and full keyspace scanning (contributed by @Bacon2994). -- Custom window title bar with integrated window controls. -- Dedicated WHERE and ORDER BY inputs for data-grid filtering and sorting (#165). -- In-result filtering for loaded query data (contributed by @rarnu). -- Editable cell details panel with explicit NULL support. -- Auto-fit column widths based on headers and data. -- Copy selected rows as INSERT statements (#166). -- Create and drop databases and schemas from the sidebar. -- SQL Server CROSS APPLY / OUTER APPLY completion (#168). -- OceanBase Oracle mode detection (#155). -- Middle-click tab close (#162). -- Sort hint icons on column hover. -- Window position and size persistence. -- MCP connection management with `dbx_add_connection` and `dbx_remove_connection`. - -### Improved - -- DataGrid toolbar and dataset interactions (contributed by @rarnu). -- JSON / JSONB rendering performance for large data sets. -- Combined data-grid information and transaction toolbar row. -- Navicat-style tab presentation. - -### Fixed - -- Tab context-menu actions such as close all, close others, and close right. -- Query result headers and full sorting (contributed by @Bacon2994). -- MCP config_json compatibility with Rust `ConnectionConfig`. -- Tailwind styles missing under tmpfs (#167). -- Data-grid overscroll causing header and data misalignment (#156). -- GaussDB schema-list filtering (#154). -- Sidebar database filtering when a connection specifies a database (#160). -- Oracle 11g compatibility and sidebar behavior. -- MySQL fallback type mapping to avoid all-NULL columns (#40). -- AI execution receiving stale SQL state (#153). -- JSONB display in edit mode and the details panel (#152). -- MongoDB replica-set connections through SSH tunnels (#151). -- Redis backslash strings being misclassified as binary data (contributed by @Bacon2994). -- Toast notifications hidden behind dialogs. -- Text selection in the DDL panel. -- Browser-native context menu conflicts and adds a sidebar refresh button (#159). +[Open the live changelog](/en/changelog) diff --git a/docs/lib/appVersion.ts b/docs/lib/appVersion.ts new file mode 100644 index 000000000..17721b937 --- /dev/null +++ b/docs/lib/appVersion.ts @@ -0,0 +1,5 @@ +import appPackage from '../../package.json'; + +export function getAppVersion() { + return appPackage.version; +} diff --git a/docs/lib/changelog.ts b/docs/lib/changelog.ts index 524e36bbb..d5889c7d2 100644 --- a/docs/lib/changelog.ts +++ b/docs/lib/changelog.ts @@ -1,3 +1,5 @@ +import { requestJson } from '@/lib/httpJson'; + export type ChangelogItem = { title: string; desc: string; @@ -21,17 +23,111 @@ export type ChangelogData = { releases: ChangelogRelease[]; }; -const BASE_URL = process.env.CHANGELOG_BASE_URL || 'https://dl.dbxio.com/changelog'; +type GitHubRelease = { + tag_name: string; + name: string | null; + published_at: string | null; + body: string | null; + draft: boolean; + prerelease: boolean; +}; + +const DEFAULT_BASE_URL = 'https://dl.dbxio.com/changelog'; +const GITHUB_RELEASES_URL = 'https://api.github.com/repos/t8y2/dbx/releases?per_page=30'; + +const SECTION_MAP: Record = { + 新功能: 'added', + Added: 'added', + 改进: 'improved', + Improved: 'improved', + 修复: 'fixed', + Fixed: 'fixed', + 变更: 'changed', + Changed: 'changed', + 移除: 'removed', + Removed: 'removed', +}; + +export function changelogUrl(lang: 'en' | 'cn') { + const baseUrl = + (typeof process !== 'undefined' && + (process.env.NEXT_PUBLIC_CHANGELOG_BASE_URL || process.env.CHANGELOG_BASE_URL)) || + DEFAULT_BASE_URL; + + return `${baseUrl}/releases-${lang}.json`; +} export async function fetchChangelog(lang: 'en' | 'cn'): Promise { - const url = `${BASE_URL}/releases-${lang}.json`; + const url = changelogUrl(lang); try { - const res = await fetch(url, { next: { revalidate: 3600 } }); - if (!res.ok) { - return { updatedAt: '', releases: [] }; + return await requestJson(url); + } catch { + return fetchGitHubChangelog(); + } +} + +function stripDownloadSection(body: string) { + const markers = ['### 下载安装', '### Download', '### 系统要求', '### System Requirements']; + let idx = body.length; + + for (const marker of markers) { + const markerIndex = body.indexOf(marker); + if (markerIndex !== -1 && markerIndex < idx) { + idx = markerIndex; } - return res.json() as Promise; + } + + return body.slice(0, idx).trim(); +} + +function parseReleaseBody(body: string) { + const sections: ChangelogSection[] = []; + let current: ChangelogSection | null = null; + + for (const line of stripDownloadSection(body).split('\n')) { + const headerMatch = line.match(/^###\s+(.+)/); + if (headerMatch) { + const title = headerMatch[1].trim(); + current = { type: SECTION_MAP[title] || 'other', title, items: [] }; + sections.push(current); + continue; + } + + if (!current) continue; + + const itemMatch = line.match(/^-\s+\*\*(.+?)\*\*\s*[—–-]\s*(.+)/); + if (itemMatch) { + current.items.push({ title: itemMatch[1].trim(), desc: itemMatch[2].trim() }); + continue; + } + + const plainMatch = line.match(/^-\s+(.+)/); + if (plainMatch) { + current.items.push({ title: plainMatch[1].trim(), desc: '' }); + } + } + + return sections.filter((section) => section.items.length > 0); +} + +export async function fetchGitHubChangelog(): Promise { + try { + const releases = await requestJson(GITHUB_RELEASES_URL, { + headers: { Accept: 'application/vnd.github+json' }, + }); + + return { + updatedAt: new Date().toISOString(), + releases: releases + .filter((release) => !release.draft && !release.prerelease) + .map((release) => ({ + tag: release.tag_name, + name: release.name || release.tag_name, + date: (release.published_at || '').slice(0, 10), + sections: parseReleaseBody(release.body || ''), + })), + }; } catch { return { updatedAt: '', releases: [] }; } diff --git a/docs/lib/downloadLinks.ts b/docs/lib/downloadLinks.ts new file mode 100644 index 000000000..41f47e99f --- /dev/null +++ b/docs/lib/downloadLinks.ts @@ -0,0 +1,51 @@ +export type InstallLang = 'en' | 'cn'; + +export type InstallOption = { + id: string; + label: string; + href: string; +}; + +type DownloadArtifact = { + id: string; + labels: Record; + suffix: string; +}; + +const DOWNLOAD_BASE_URL = 'https://dl.dbxio.com/releases/latest'; + +const downloadArtifacts: DownloadArtifact[] = [ + { + id: 'macos-arm', + labels: { en: 'For macOS (Apple Silicon)', cn: '适用于 macOS (Apple Silicon)' }, + suffix: 'aarch64.dmg', + }, + { + id: 'macos-intel', + labels: { en: 'For macOS (Intel)', cn: '适用于 macOS (Intel)' }, + suffix: 'x64.dmg', + }, + { + id: 'windows', + labels: { en: 'For Windows', cn: '适用于 Windows' }, + suffix: 'x64-setup.exe', + }, + { + id: 'linux', + labels: { en: 'For Linux x64', cn: '适用于 Linux x64' }, + suffix: 'amd64.AppImage', + }, + { + id: 'linux-arm', + labels: { en: 'For Linux ARM64', cn: '适用于 Linux ARM64' }, + suffix: 'aarch64.AppImage', + }, +]; + +export function createInstallOptions(lang: InstallLang, version: string): InstallOption[] { + return downloadArtifacts.map((artifact) => ({ + id: artifact.id, + label: artifact.labels[lang], + href: `${DOWNLOAD_BASE_URL}/DBX_${version}_${artifact.suffix}`, + })); +} diff --git a/docs/lib/httpJson.ts b/docs/lib/httpJson.ts new file mode 100644 index 000000000..49d14d0ba --- /dev/null +++ b/docs/lib/httpJson.ts @@ -0,0 +1,34 @@ +function requestJsonWithXhr(url: string): Promise { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.onload = () => { + if (xhr.status < 200 || xhr.status >= 300) { + reject(new Error(`Request failed with status ${xhr.status}`)); + return; + } + + resolve((xhr.response ?? JSON.parse(xhr.responseText)) as T); + }; + xhr.onerror = () => reject(new Error('Request failed')); + xhr.send(); + }); +} + +export async function requestJson(url: string, init?: RequestInit): Promise { + if (typeof fetch === 'function') { + const res = await fetch(url, init); + if (!res.ok) { + throw new Error(`Request failed with status ${res.status}`); + } + return res.json() as Promise; + } + + if (typeof XMLHttpRequest === 'function') { + return requestJsonWithXhr(url); + } + + throw new Error('No browser request API is available'); +} diff --git a/docs/lib/landingLatest.ts b/docs/lib/landingLatest.ts new file mode 100644 index 000000000..35cfb14e5 --- /dev/null +++ b/docs/lib/landingLatest.ts @@ -0,0 +1,63 @@ +import type { ChangelogRelease } from '@/lib/changelog'; +import type { LatestReleaseInfo } from '@/lib/latestRelease'; + +type LandingLatestUpdates = { + version: string; + title: string; + desc: string; + link: string; + items: string[]; +}; + +const text = { + en: { + title: 'Latest updates', + desc: 'Mirrored from the latest GitHub release notes.', + link: 'Read the changelog', + fallbackItems: [ + 'Desktop and Docker release assets', + 'Database workflow improvements', + 'Bug fixes and reliability updates', + 'Documentation and packaging updates', + ], + }, + cn: { + title: '最近更新', + desc: '同步 GitHub 最新 Release Notes。', + link: '查看更新日志', + fallbackItems: [ + '桌面版与 Docker 发布资产', + '数据库工作流改进', + '问题修复与稳定性更新', + '文档与打包流程更新', + ], + }, +}; + +function releaseItems(release: ChangelogRelease, lang: 'en' | 'cn') { + const separator = lang === 'cn' ? ',' : ': '; + + return release.sections + .flatMap((section) => section.items) + .map((item) => (item.desc ? `${item.title}${separator}${item.desc}` : item.title)) + .slice(0, 4); +} + +export function buildLandingLatestUpdates( + lang: 'en' | 'cn', + release: ChangelogRelease | undefined, + appVersion: string, + latestRelease?: LatestReleaseInfo | null, +): LandingLatestUpdates { + const t = text[lang]; + const items = release ? releaseItems(release, lang) : []; + const version = latestRelease?.version ? `v${latestRelease.version}` : release?.tag ?? `v${appVersion}`; + + return { + version, + title: t.title, + desc: t.desc, + link: t.link, + items: items.length > 0 ? items : t.fallbackItems, + }; +} diff --git a/docs/lib/latestRelease.ts b/docs/lib/latestRelease.ts new file mode 100644 index 000000000..3c689be54 --- /dev/null +++ b/docs/lib/latestRelease.ts @@ -0,0 +1,45 @@ +import { requestJson } from '@/lib/httpJson'; + +export type LatestReleaseInfo = { + version: string; + notes?: string; + pub_date?: string; +}; + +type GitHubLatestRelease = { + tag_name: string; + body: string | null; + published_at: string | null; +}; + +const LATEST_RELEASE_URL = 'https://dl.dbxio.com/releases/latest/latest.json'; +const GITHUB_LATEST_RELEASE_URL = 'https://api.github.com/repos/t8y2/dbx/releases/latest'; + +function normalizeVersion(version: string) { + return version.replace(/^v/, ''); +} + +export async function fetchLatestReleaseInfo(): Promise { + try { + const release = await requestJson(LATEST_RELEASE_URL); + return release.version ? { ...release, version: normalizeVersion(release.version) } : null; + } catch { + return fetchGitHubLatestReleaseInfo(); + } +} + +export async function fetchGitHubLatestReleaseInfo(): Promise { + try { + const release = await requestJson(GITHUB_LATEST_RELEASE_URL, { + headers: { Accept: 'application/vnd.github+json' }, + }); + + return { + version: normalizeVersion(release.tag_name), + notes: release.body || undefined, + pub_date: release.published_at || undefined, + }; + } catch { + return null; + } +} diff --git a/tests/docsReleaseIntegration.test.ts b/tests/docsReleaseIntegration.test.ts new file mode 100644 index 000000000..1f72bd876 --- /dev/null +++ b/tests/docsReleaseIntegration.test.ts @@ -0,0 +1,87 @@ +import { readFileSync } from "node:fs"; +import { strict as assert } from "node:assert"; +import test from "node:test"; +import appPackage from "../package.json" with { type: "json" }; +import { createInstallOptions } from "../docs/lib/downloadLinks.ts"; + +test("docs install links are generated from the app package version", () => { + const options = createInstallOptions("en", appPackage.version); + const hrefs = options.map((option) => option.href); + + assert.equal(hrefs.length, 5); + assert.ok(hrefs.every((href) => href.includes(`/DBX_${appPackage.version}_`))); + assert.equal(hrefs.some((href) => href.includes("0.5.9")), false); +}); + +test("docs landing page reads latest release data instead of hard-coding the old latest update", () => { + const source = readFileSync("docs/app/[lang]/page.tsx", "utf8"); + + assert.equal(source.includes("version: 'v0.5.4'"), false); + assert.match(source, /LandingLatestUpdates/); + assert.match(source, /fetchLatestReleaseInfo/); + assert.match(source, /initialLatestRelease/); +}); + +test("docs install widget reads the latest release version from latest.json at runtime", () => { + const source = readFileSync("docs/components/landing/InstallTabs.tsx", "utf8"); + const latestRelease = readFileSync("docs/lib/latestRelease.ts", "utf8"); + + assert.equal(source.includes("fetchChangelog"), false); + assert.match(source, /fetchLatestReleaseInfo/); + assert.match(latestRelease, /latest\.json/); +}); + +test("homepage update badge keeps latest.json version ahead of changelog tags", () => { + const source = readFileSync("docs/components/landing/LandingLatestUpdates.tsx", "utf8"); + + assert.match(source, /Promise\.all\(\[fetchLatestReleaseInfo\(\), fetchChangelog\(lang\)\]\)/); + assert.match(source, /releaseInfo \?\? initialLatestRelease/); +}); + +test("docs changelog page loads releases in the browser from R2", () => { + const source = readFileSync("docs/app/[lang]/changelog/page.tsx", "utf8"); + + assert.match(source, /initialData/); + assert.match(source, /ChangelogRuntime/); +}); + +test("legacy docs changelog pages do not keep stale release entries", () => { + const en = readFileSync("docs/content/docs/changelog.mdx", "utf8"); + const cn = readFileSync("docs/content/docs/changelog.cn.mdx", "utf8"); + + assert.equal(en.includes("## v0.5.4"), false); + assert.equal(cn.includes("## v0.5.4"), false); + assert.match(en, /\/en\/changelog/); + assert.match(cn, /\/cn\/changelog/); +}); + +test("docs deploy does not rerun just to refresh R2 changelog data", () => { + const source = readFileSync(".github/workflows/docs.yml", "utf8"); + + assert.equal(source.includes("workflow_run:"), false); + assert.equal(source.includes("Sync Changelog to R2"), false); +}); + +test("changelog sync configures R2 CORS for browser runtime reads", () => { + const workflow = readFileSync(".github/workflows/sync-changelog.yml", "utf8"); + const cors = JSON.parse(readFileSync(".github/r2-cors.json", "utf8")) as { + CORSRules: Array<{ + AllowedMethods: string[]; + AllowedOrigins: string[]; + }>; + }; + const rule = cors.CORSRules[0]; + + assert.match(workflow, /put-bucket-cors/); + assert.deepEqual(rule.AllowedOrigins, ["*"]); + assert.ok(rule.AllowedMethods.includes("GET")); + assert.ok(rule.AllowedMethods.includes("HEAD")); +}); + +test("changelog sync also runs after release workflow publishes with GITHUB_TOKEN", () => { + const workflow = readFileSync(".github/workflows/sync-changelog.yml", "utf8"); + + assert.match(workflow, /workflow_run:/); + assert.match(workflow, /workflows: \['Release'\]/); + assert.match(workflow, /github\.event\.workflow_run\.conclusion == 'success'/); +});