惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Palo Alto Networks Blog
Martin Fowler
Martin Fowler
T
Threatpost
D
Docker
S
Schneier on Security
M
MIT News - Artificial intelligence
G
Google Developers Blog
L
LINUX DO - 热门话题
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
人人都是产品经理
人人都是产品经理
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
CERT Recently Published Vulnerability Notes
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
MongoDB | Blog
MongoDB | Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Heimdal Security Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
SecWiki News
SecWiki News
P
Privacy International News Feed
P
Proofpoint News Feed
O
OpenAI News
B
Blog
腾讯CDC
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
T
Tor Project blog
H
Hacker News: Front Page
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
C
Cisco Blogs
S
Security Affairs

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 分享一下笔者的 Mac 装机必备软件 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
如何用 GitHub Actions 实现 Steam 自动化发布
Newbe36524 · 2026-04-16 · via 博客园_首页

如何用 GitHub Actions 实现 Steam 自动化发布

本文分享了 HagiCode Desktop 项目中实现 Steam 自动化发布的完整方案,从 GitHub Release 到 Steam 平台的全链路自动化流程,包括 Steam Guard 认证、多平台 Depot 上传等关键技术细节。

背景

Steam 平台的发布流程,其实和传统的应用分发方式挺不一样的。Steam 有自己的一套完整更新分发系统,开发者得通过 SteamCMD 工具把构建产物上传到 Steam 的 CDN 网络,而不是像其他平台那样直接丢个下载链接就完事了。

HagiCode Desktop 项目计划上架 Steam 平台,这也算是给我们的发布流程带来了点新挑战:

  1. 需要把现有的构建产物转换成 Steam 兼容的格式
  2. 得通过 SteamCMD 工具上传到 Steam 平台
  3. 还必须处理 Steam Guard 认证这玩意儿
  4. 需要支持多平台(Linux、Windows、macOS)的 Depot 上传
  5. 还要实现从 GitHub Release 到 Steam 的自动化流转

项目此前已经实现了"便携版模式"(portable version mode),允许应用检测打包在 extra 目录中的固定服务载荷。我们的目标,其实就是让这套便携版模式和 Steam 分发能无缝集成罢了。

关于 HagiCode

本文分享的方案,来自我们在 HagiCode 项目中的实践经验。HagiCode 是一个 AI 代码助手项目,支持桌面端运行,我们正在推进 Steam 平台的上架工作,因此才需要建立一套可靠的自动化发布流程。

架构设计

整个 Steam 发布流程的核心是一个 GitHub Actions 工作流,它把整个过程分为三个主要阶段:

┌─────────────────────────────────────────────────────────────┐
│ GitHub Actions Workflow (Steam Release)                      │
├─────────────────────────────────────────────────────────────┤
│ 1. 准备阶段:                                                 │
│    - 检出 portable-version 代码                             │
│    - 从 GitHub Release 下载构建产物                         │
│    - 解压并准备 Steam 内容目录                              │
│                                                             │
│ 2. SteamCMD 设置:                                           │
│    - 安装/复用 SteamCMD                                     │
│    - 使用 Steam Guard 进行认证                              │
│                                                             │
│ 3. 发布阶段:                                                 │
│    - 生成 Depot VDF 配置文件                                │
│    - 生成 App Build VDF 配置文件                            │
│    - 调用 SteamCMD 上传到 Steam                             │
└─────────────────────────────────────────────────────────────┘

这种设计的优势,怎么说呢:

  • 复用现有的 GitHub Release 产物,避免重复构建,毕竟谁愿意重复劳动呢
  • 通过自托管运行器实现安全隔离,多一层保障总是好的
  • 支持预览模式和正式发布分支切换,灵活一点
  • 完整的错误处理和日志记录,出问题的时候不至于太迷茫

工作流实现

触发参数设计

我们的工作流支持以下关键参数:

inputs:
  release:           # Portable Version 发布标签
    description: '要发布的版本标签(如 v1.0.0)'
    required: true
  steam_preview:     # 是否生成预览构建
    description: '是否为预览模式'
    required: false
    default: 'false'
  steam_branch:      # 设置为 live 的 Steam 分支
    description: '目标 Steam 分支'
    required: false
    default: 'preview'
  steam_description: # 构建描述覆盖
    description: '构建描述'
    required: false

自托管运行器配置

出于安全考虑,我们使用带 steam 标签的自托管运行器:

runs-on:
  - self-hosted
  - Linux
  - X64
  - steam

这样可以确保 Steam 发布在专用运行器上执行,保持敏感凭据的安全隔离。毕竟安全这事儿,多注意一点总是好的。

并发控制

为了避免同一版本的发布相互干扰,我们配置了并发控制:

concurrency:
  group: portable-version-steam-${{ github.event.inputs.release }}
  cancel-in-progress: false

注意这里设置 cancel-in-progress: false,因为 Steam 发布过程可能较长,我们也不想因为新的触发就取消正在进行的发布。毕竟发布个版本也不容易,总得让人家跑完不是?

核心脚本实现

准备发布输入

prepare-steam-release-input.mjs 脚本负责准备发布所需的输入:

// 下载 GitHub Release 的构建清单和产物清单
const buildManifest = await downloadBuildManifest(releaseTag);
const artifactInventory = await downloadArtifactInventory(releaseTag);

// 下载各平台的压缩包
for (const platform of ['linux-x64', 'win-x64', 'osx-universal']) {
  const artifactUrl = getArtifactUrl(artifactInventory, platform);
  await downloadArtifact(artifactUrl, platform);
}

// 解压到 Steam 内容目录结构
await extractToSteamContent(sources, contentRoot);

Steam Guard 认证

Steam 要求使用 Steam Guard 保护账户,我们实现了基于共享密钥的代码生成算法:

function generateSteamGuardCode(sharedSecret, timestamp = Date.now()) {
  const secret = decodeSharedSecret(sharedSecret);
  const time = Math.floor(timestamp / 1000 / 30);

  const timeBuffer = Buffer.alloc(8);
  timeBuffer.writeBigUInt64BE(BigInt(time));

  // 使用 HMAC-SHA1 生成时间基础的一次性代码
  const hash = crypto.createHmac('sha1', secret)
    .update(timeBuffer)
    .digest();

  // 转换为 5 字符的 Steam Guard 代码
  const code = steamGuardCode(hash);
  return code;
}

这个实现基于 Steam Guard 的 TOTP(Time-based One-Time Password)机制,每 30 秒生成一个新的验证码。毕竟安全这东西,还是得用靠谱的方式才行。

VDF 配置生成

VDF(Valve Data Format)是 Steam 使用的配置格式,我们需要生成两种类型的 VDF 文件:

Depot VDF 用于配置各个平台的内容:

function buildDepotVdf(depotId, contentRoot) {
  return [
    '"DepotBuildConfig"',
    '{',
    `  "DepotID" "${escapeVdf(depotId)}"`,
    `  "ContentRoot" "${escapeVdf(contentRoot)}"`,
    '  "FileMapping"',
    '  {',
    '    "LocalPath" "*"',
    '    "DepotPath" "."',
    '    "recursive" "1"',
    '  }',
    '}'
  ].join('\n');
}

App Build VDF 用于配置整个应用构建:

function buildAppBuildVdf(appId, depotBuilds, description, setLive) {
  const vdf = [
    '"appbuild"',
    '{',
    `  "appid" "${appId}"`,
    `  "desc" "${escapeVdf(description)}"`,
    `  "contentroot" "${escapeVdf(contentRoot)}"`,
    '  "buildoutput" "build_output"',
    '  "depots"',
    '  {'
  ];

  for (const [depotId, depotVdfPath] of Object.entries(depotBuilds)) {
    vdf.push(`    "${depotId}" "${depotVdfPath}"`);
  }

  if (setLive) {
    vdf.push(`  }`);
    vdf.push(`  "setlive" "${setLive}"`);
  }

  vdf.push('}');
  return vdf.join('\n');
}

SteamCMD 调用

最后,通过调用 SteamCMD 执行上传:

await runCommand(steamcmdPath, [
  '+login', steamUsername, steamPassword, steamGuardCode,
  '+run_app_build', appBuildPath,
  '+quit'
]);

这一步算是整个流程的最后一跃,跨过去就完成了......

多平台 Depot 处理

Steam 使用 Depot 系统管理不同平台的内容,我们支持三种主要的 Depot:

平台 Depot 标识 架构支持
Linux linux-x64 x64_64
Windows win-x64 x64_64
macOS osx-universal universal, x64_64, arm64

每个 Depot 都有独立的内容目录和 VDF 配置文件,这样可以确保不同平台的用户只下载自己需要的内容。毕竟流量也是钱,能省一点是一点。

发布流程

步骤 1:准备 GitHub Release

首先需要在 portable-version 仓库创建一个 GitHub Release,包含:

  • 各平台的压缩包
  • 构建清单({tag}.build-manifest.json
  • 产物清单({tag}.artifact-inventory.json

步骤 2:触发 Steam 发布工作流

通过 GitHub Actions 手动触发工作流,填写必要参数:

  • release: 要发布的版本标签(如 v1.0.0)
  • steam_branch: 目标分支(如 previewpublic
  • steam_preview: 是否预览模式

步骤 3:自动执行发布流程

工作流会自动执行以下步骤:

  1. 下载并解压 GitHub Release 产物
  2. 安装/更新 SteamCMD
  3. 生成 Steam VDF 配置文件
  4. 使用 Steam Guard 认证
  5. 上传内容到 Steam CDN
  6. 设置指定分支为 live

这一套流程走下来,也算是把该做的都做了。

配置指南

必需的 Secrets 配置

在 GitHub 仓库设置中配置以下密钥:

Secret 名称 说明
STEAM_USERNAME Steam 账户用户名
STEAM_PASSWORD Steam 账户密码
STEAM_SHARED_SECRET Steam Guard 共享密钥(可选)
STEAM_GUARD_CODE Steam Guard 代码(可选)
STEAM_APP_ID Steam 应用 ID
STEAM_DEPOT_ID_LINUX Linux Depot ID
STEAM_DEPOT_ID_WINDOWS Windows Depot ID
STEAM_DEPOT_ID_MACOS macOS Depot ID

这些配置项,其实也没什么特别的,就是该有的都得有罢了。

环境变量配置

变量名称 说明 默认值
PORTABLE_VERSION_STEAMCMD_ROOT SteamCMD 安装目录 ~/.local/share/portable-version/steamcmd

最佳实践

Steam Guard 认证管理

首次运行需要手动输入 Steam Guard 代码,之后建议配置共享密钥自动生成代码。这样可以避免每次发布都需要手动干预,毕竟谁也不想每次都重复同样的操作。

SteamCMD 会保存登录令牌,后续可以复用。但要注意令牌的有效期,过期后还是得重新认证的,这也没办法。

内容目录结构

确保 Steam 内容目录结构正确:

steam-content/
├── linux-x64/     # Linux 平台内容
├── win-x64/       # Windows 平台内容
└── osx-universal/ # macOS 通用二进制内容

每个目录下应该包含对应平台的完整应用文件。这点倒也没什么好说的,该怎么做就怎么做。

预览模式使用

预览模式不会设置任何分支为 live,适合测试验证:

if [ "$STEAM_PREVIEW_INPUT" = 'true' ]; then
  cmd+=(--preview)
fi

这样可以先上传到 Steam 平台进行验证,确认无误后再切换到正式分支。多一层验证,总是好的。

错误处理和日志

脚本包含了完善的错误处理和日志记录:

  • 验证 GitHub Release 存在性
  • 检查必需的元数据文件
  • 确保平台内容存在
  • 生成 GitHub Actions 摘要报告

这些信息对于调试和审计都非常有价值,毕竟出问题的时候能有个线索,总比一头雾水要好。

产物管理

工作流生成两种产物:

  • portable-steam-release-preparation-{tag}: 发布准备元数据
  • portable-steam-build-metadata-{tag}: Steam 构建元数据

这些产物可以用于后续的审计和调试,保存时间建议设置为 30 天。反正也不占多少地方,留着也无妨。

实际应用

在 HagiCode 项目中,这套自动化发布流程已经成功运行了多个版本。从 GitHub Release 到 Steam 平台的整个链路完全自动化,无需人工干预。

这大大提高了我们的发布效率和可靠性。之前手动发布一个版本需要 30 分钟以上的时间,现在只需要几分钟就能完成整个流程。时间这东西,省下来总归是好的。

更重要的是,自动化流程减少了人为错误的可能性,每次发布都是标准化的流程,结果也更加可预测。毕竟重复的事情交给机器去做,人也轻松点。

总结

通过本文分享的方案,我们实现了:

  1. 从 GitHub Release 到 Steam 平台的完全自动化
  2. 支持多平台的 Depot 上传
  3. 基于 Steam Guard 的安全认证
  4. 预览模式和正式发布的灵活切换
  5. 完善的错误处理和日志记录

这套方案不仅适用于 HagiCode 项目,也可以为其他计划上架 Steam 平台的项目提供参考。如果你也在考虑 Steam 自动化发布,希望本文的实践能够对你有所帮助。

其实技术这东西,说复杂也复杂,说简单也简单。关键是找到适合自己的方式罢了。

如果本文对你有帮助,欢迎来 HagiCode 的 GitHub 仓库给个 Star,或者访问官网了解更多信息。

参考资料

原文与版权说明

感谢您的阅读,如果您觉得本文有用,欢迎点赞、收藏和分享支持。
本内容采用人工智能辅助协作,最终内容由作者审核并确认。