├── CNAME ├── favicon.ico ├── image ├── qrcode.jpg ├── onboarding.png ├── writing-copy.png ├── idea-rust-click.png ├── opensuse-1-click.png ├── search-in-google.png ├── spring-initalizr.png ├── tree-sitter-sample.png ├── community-technology.png ├── opensource-comunnity.png └── design-for-search-engine.png ├── resources ├── dx.sketch └── dx.svg ├── docs ├── domain │ ├── domain.md │ ├── cloud-native.md │ ├── openbank.md │ ├── api-design.md │ └── devops.md ├── anti-patterns │ ├── ui-replace-cmd.md │ ├── anti-patterns.md │ ├── metric-to-contributor.md │ ├── kpi-base-model-design.md │ ├── marketing-drive-developement.md │ ├── fake-opensource.md │ └── developer-as-service.md ├── factor │ ├── factor.md │ ├── touch-point.md │ ├── documentations.md │ ├── interactive-design.md │ ├── error-handling.md │ └── usability.md ├── patterns │ ├── patterns.md │ ├── document-engineering.md │ ├── developer-experience-center.md │ ├── starter-generator.md │ ├── interactive-script.md │ ├── document-for-learn.md │ ├── issue-bot.md │ ├── auto-install-script.md │ ├── developer-portal.md │ ├── dev-exp-team.md │ ├── onboarding-journey.md │ ├── template-by-ci.md │ └── living-document-code.md ├── engineering │ ├── engineering.md │ └── document-engineering.md ├── dx-fluency-model.md ├── dev-ex-framework.md ├── operation-model.md ├── refs.md ├── maturity-model.md └── define-dx.md ├── 404.html ├── .gitignore ├── _sass └── custom │ └── custom.scss ├── README.md ├── convert.sh ├── _config.yml ├── samples └── scripts │ ├── win │ └── scoop.ps │ ├── rustup-init.sh │ └── homebrew-install.sh └── LICENSE /CNAME: -------------------------------------------------------------------------------- 1 | dx.phodal.com 2 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/favicon.ico -------------------------------------------------------------------------------- /image/qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/qrcode.jpg -------------------------------------------------------------------------------- /image/onboarding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/onboarding.png -------------------------------------------------------------------------------- /resources/dx.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/resources/dx.sketch -------------------------------------------------------------------------------- /image/writing-copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/writing-copy.png -------------------------------------------------------------------------------- /image/idea-rust-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/idea-rust-click.png -------------------------------------------------------------------------------- /image/opensuse-1-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/opensuse-1-click.png -------------------------------------------------------------------------------- /image/search-in-google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/search-in-google.png -------------------------------------------------------------------------------- /image/spring-initalizr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/spring-initalizr.png -------------------------------------------------------------------------------- /image/tree-sitter-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/tree-sitter-sample.png -------------------------------------------------------------------------------- /image/community-technology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/community-technology.png -------------------------------------------------------------------------------- /image/opensource-comunnity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/opensource-comunnity.png -------------------------------------------------------------------------------- /image/design-for-search-engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/dx-book/master/image/design-for-search-engine.png -------------------------------------------------------------------------------- /docs/domain/domain.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 领域案例学习 4 | nav_order: 8 5 | has_children: true 6 | permalink: /docs/domain 7 | --- 8 | 9 | # 开发者体验:领域案例学习 10 | {: .no_toc } 11 | 12 | 案例收集中,欢迎贡献 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | -------------------------------------------------------------------------------- /docs/domain/cloud-native.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 云原生 4 | parent: 领域案例学习 5 | nav_order: 2 6 | --- 7 | 8 | # 开发者体验:云原生 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## Heroku 体验设计 20 | 21 | 相关文章:[Developer Experience](https://www.heroku.com/dx) 22 | -------------------------------------------------------------------------------- /docs/anti-patterns/ui-replace-cmd.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:UI 交互取代代码化 4 | parent: 反模式 5 | nav_order: 2 6 | --- 7 | 8 | # 开发者体验反模式:UI 交互取代代码化 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## 特征 20 | 21 | ## 案例 22 | 23 | TBD 24 | 25 | ## 潜伏方案 26 | -------------------------------------------------------------------------------- /docs/factor/factor.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开发者体验设计因子 4 | nav_order: 4 5 | has_children: true 6 | permalink: /docs/factor 7 | --- 8 | 9 | # 开发者体验设计因子 10 | {: .no_toc } 11 | 12 | 开发者体验所需要的五个基础元素,以定义、原则、示例和模式作为基本的写作格式。 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | 21 | -------------------------------------------------------------------------------- /docs/anti-patterns/anti-patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式 4 | nav_order: 6 5 | has_children: true 6 | permalink: /docs/anti-patterns 7 | --- 8 | 9 | # 开发者体验:反模式 10 | {: .no_toc } 11 | 12 | 反模式,指的则是在实践中明显出现,但低效或有待优化的模式,是用来解决问题的带有共同性的不良方法。 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | -------------------------------------------------------------------------------- /docs/anti-patterns/metric-to-contributor.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:度量外部贡献者 4 | parent: 反模式 5 | nav_order: 5 6 | --- 7 | 8 | # 开发者体验反模式:度量外部贡献者 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## 特征 20 | 21 | 为外部贡献者设计度量指标。 22 | 23 | ## 案例 24 | 25 | TBD 26 | 27 | ## 潜在方案 28 | -------------------------------------------------------------------------------- /docs/patterns/patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 实践模式 4 | nav_order: 5 5 | has_children: true 6 | permalink: /docs/patterns 7 | --- 8 | 9 | # 开发者体验:实践模式 10 | {: .no_toc } 11 | 12 | 实践模式,是主体行为的一般方式,包括科学实验模式、经济发展模式、企业盈利模式等,是理论和实践之间的中介环节,具有一般性、简单性、重复性、结构性、稳定性、可操作性的特征。 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 404 4 | permalink: /404 5 | nav_exclude: true 6 | search_exclude: true 7 | --- 8 | 9 |

Page not found

10 | 11 |

The page you requested could not be found. Try using the navigation {% if site.search_enabled != false %}or search {% endif %}to find what you're looking for or go to this site's home page.

12 | -------------------------------------------------------------------------------- /docs/anti-patterns/kpi-base-model-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:KPI 导向的指标设计 4 | parent: 反模式 5 | nav_order: 3 6 | --- 7 | 8 | # 开发者体验反模式:KPI 导向的指标设计 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## 特征 20 | 21 | ## 案例 22 | 23 | - [如何评价阿里oceanbase GitHub点赞送礼?](https://www.zhihu.com/question/494108102/answer/2185308483) 24 | 25 | ## 潜伏解决方案 26 | -------------------------------------------------------------------------------- /docs/patterns/document-engineering.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 文档工程 4 | parent: 实践模式 5 | nav_order: 5 6 | --- 7 | 8 | # 开发者体验:文档工程 9 | {: .no_toc } 10 | 11 | 文档工程用于帮助我们指定、设计、和实施计算机技术相关的文档,如产品特定规格或详细说明,以及创建和使用它们的流程。它的特点是以“文档为中心”,以帮助我们构思和理解如何支撑其所在的商业模式。 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | 详细见:[/docs/engineering/document-engineering](/docs/engineering/document-engineering) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | .idea 12 | *.docx 13 | .jekyll-metadata 14 | .jekyll-cache 15 | _site 16 | Gemfile.lock 17 | assets 18 | template 19 | .DS_Store 20 | default.profraw 21 | -------------------------------------------------------------------------------- /docs/patterns/developer-experience-center.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开发者体验中心 4 | parent: 实践模式 5 | nav_order: 5 6 | --- 7 | 8 | # 开发者体验:开发者体验中心 9 | {: .no_toc } 10 | 11 | 开发者体验团队 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | 23 | ## 示例 24 | 25 | ### IBM 26 | 27 | > IBM Developer Experience Guide 为您提供 IBM 开发者品牌战略、视觉元素和资产的指南。 28 | 29 | 示例:[IBM Developer Experience Center](https://www.ibm.com/brand/experience-guides/developer/) 30 | 31 | -------------------------------------------------------------------------------- /docs/domain/openbank.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开放银行 4 | parent: 领域案例学习 5 | nav_order: 1 6 | --- 7 | 8 | # 开发者体验:开放银行 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | 参考资料:[开发者体验 – 开放银行的奠基石](https://insights.thoughtworks.cn/open-banking-developer-experience/) 20 | 21 | > 开放银行(Open Banking)作为银行数字化转型过程中的重要举措,得到了业界越来越多的关注和重视,开放银行通过 API 实现商业银行和外部的数据共享,将金融服务嵌入到用户的生活场景中来满足客户的多样化需求,以场景为核心、围绕客户旅程重塑传统业务模式,挖掘新的业务增长点。KPMG 在系列研究报告中指出,开放银行或许是未来中国银行业发展的必经之路。 22 | 23 | ## 四个维度 24 | 25 | - 自助式的开发过程 26 | - 现代化的开放协议和 API 工具 27 | - 精心设计的开发者套件 28 | - 友好的开发者社区 29 | -------------------------------------------------------------------------------- /docs/patterns/starter-generator.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 脚手架生成器 4 | parent: 实践模式 5 | nav_order: 9 6 | --- 7 | 8 | 脚手架生成器(Scaffold 9 | Generator)是一种工具或平台,用于快速生成项目或应用程序的基础结构和代码骨架。这些生成器旨在简化开发流程,提高开发效率,并确保项目的一致性和最佳实践。通过提供预定义的项目模板、配置和依赖项,脚手架生成器帮助开发人员避免从头开始创建项目的繁琐工作。 10 | 11 | 脚手架生成器的特点包括: 12 | 13 | 1. 模板引擎: 使用模板引擎来生成项目代码,使得可以根据用户的需求和配置动态生成各种文件和结构。 14 | 2. 配置选项: 提供用户友好的配置选项,让开发人员能够根据项目需求进行个性化设置,例如选择框架版本、添加特定功能模块等。 15 | 3. 依赖管理: 自动处理项目所需的依赖项和库,确保项目能够正常运行,并提供最新的稳定版本。 16 | 4. 多框架支持: 支持生成不同框架的项目结构,以满足不同技术栈和开发团队的需求。 17 | 18 | ## 示例 19 | 20 | - Quarkus 框架:[https://code.quarkus.io/](https://code.quarkus.io/) 21 | - Spring 框架:[https://start.spring.io/](https://start.spring.io/) 22 | 23 | -------------------------------------------------------------------------------- /docs/patterns/interactive-script.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 一键交互环境 4 | parent: 实践模式 5 | nav_order: 12 6 | --- 7 | 8 | # 实践模式:一键交互环境 9 | 10 | {: .no_toc } 11 | 12 | ## 示例:Google Colab 13 | 14 | > Google Colab 是一种基于云端的交互式平台,用于开发机器学习应用程序。它是由Google 15 | > Research创建的一款免费的工具,可在浏览器中使用,用户可以利用它来编写和执行Python代码。 16 | 17 | Google Colab支持Python语言,并且预装了许多常见的机器学习框架,如TensorFlow、Keras和PyTorch等,可以轻松使用它们来构建、训练和测试深度学习模型。 18 | 19 | Google Colab 还提供了GPU和TPU(Tensor Processing Unit)等硬件加速选项,用户可以充分利用它们来加快模型的训练和推理速度。此外,Google 20 | Colab还可以与Google Drive集成,让用户轻松地保存和分享Notebook。 21 | 22 | ## Jupiter Notebook 23 | 24 | > Jupiter Notebook 是一种基于Web的交互式计算环境,它支持多种编程语言,如Python、R、Julia等。用户可以在Notebook中编写和执行代码,同时还可以添加文本、图像、公式、图表等内容,以便于记录和分享计算过程。 25 | -------------------------------------------------------------------------------- /_sass/custom/custom.scss: -------------------------------------------------------------------------------- 1 | .site-title { 2 | font-size: 16px !important; 3 | } 4 | 5 | blockquote { 6 | font-size: 1.2em; 7 | margin: 20px auto; 8 | font-family: Open Sans; 9 | font-style: italic; 10 | color: #555555; 11 | padding: 1.2em 30px 1.2em 75px; 12 | border-left: 8px solid #e16a7c; 13 | line-height: 1.6; 14 | position: relative; 15 | background: #EDEDED; 16 | } 17 | 18 | blockquote::before { 19 | font-family: Arial; 20 | content: "\201C"; 21 | color: #e16a7c; 22 | font-size: 4em; 23 | position: absolute; 24 | left: 10px; 25 | top: -10px; 26 | } 27 | 28 | blockquote::after { 29 | content: ''; 30 | } 31 | 32 | blockquote span { 33 | display: block; 34 | color: #333333; 35 | font-style: normal; 36 | font-weight: bold; 37 | margin-top: 1em; 38 | } -------------------------------------------------------------------------------- /docs/patterns/document-for-learn.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 文档引导学习 4 | parent: 实践模式 5 | nav_order: 3 6 | --- 7 | 8 | # 实践模式:文档引导学习 9 | 10 | {: .no_toc } 11 | 12 | > 文档引导学习是一种学习方法,其核心思想是通过实际的操作和实践来促进知识和技能的习得,同时辅以相关文档进行引导和说明。 13 | 14 | ## 目录 15 | 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | 21 | --- 22 | 23 | ## 示例: Ionic 24 | 25 | 文档:[https://ionicframework.com/docs](https://ionicframework.com/docs) 26 | 27 | - Getting Started 28 | - Overview 29 | - Environment Setup 30 | - CLI installation 31 | - Packages & CDN 32 | - Next Steps. -> Angular, Rect, Vue 33 | - Developing 34 | - Layout 35 | - Theming 36 | - Angular 37 | - React 38 | - Vue 39 | - Utilities 40 | - Deployment 41 | - Techniques 42 | - Troubleshooting 43 | - Core Concepts 44 | - Contributing 45 | - Reference 46 | - Resources 47 | 48 | -------------------------------------------------------------------------------- /docs/anti-patterns/marketing-drive-developement.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:市场主导运营 4 | parent: 反模式 5 | nav_order: 4 6 | --- 7 | 8 | # 开发者体验反模式:市场主导运营 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | 20 | ## 特征 21 | 22 | ### 短期指标驱动 23 | 24 | ### 面向市场汇报 25 | 26 | ## 案例 27 | 28 | ### TDengine 29 | 30 | 详见:《[参与 TDengine 代码灭虫计划,快速融入开发者社区](https://mp.weixin.qq.com/s/mssWF5AoUG-vt-b5_QMtRA)》 31 | 32 | 相关问题: 33 | 34 | 1. 内容面向的过于非技术背景。如 bug 被译为 虫,而不是众所周知的 `bug`。 35 | 2. 过度的运营导向。"我们都有 5~8 个小“虫”的放出" —— 而不是在有 bug 时解决 bug。 36 | 37 | ### Oceanbase GitHub 点赞送礼 38 | 39 | 详见:《[如何评价阿里oceanbase GitHub点赞送礼?](https://www.zhihu.com/question/494108102)》 40 | 41 | 相关问题: 42 | 43 | 1. 开源运营人员对技术运营。 44 | 2. 追求指标。 45 | 46 | ## 潜伏方案 47 | 48 | 1. 技术为核心的运营。懂技术的运营 49 | 2. 与资深开发人员结对。 50 | 3. 寻找专业人士意见。如与社区意见领袖建立联系 51 | 52 | -------------------------------------------------------------------------------- /docs/patterns/issue-bot.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Issue 机器人 4 | parent: 实践模式 5 | nav_order: 7 6 | --- 7 | 8 | > Issue 机器人是一种自动化工具,通常集成在软件开发和项目管理平台中,用于处理和跟踪项目中的问题报告(Issues)。这些问题报告可以包括软件缺陷、功能请求、任务、建议等。 9 | 10 | ## 示例:Intellij IDEA 11 | 12 | Intellij 相关的 IDEA 会在异常时: 13 | 14 | 1. 提供创建 Issue 的选项 15 | 2. 如果 issue 已经存在,会通过 comments 的方式创建。 16 | 17 | 示例:[[auto-generated:-1738221085] null](https://github.com/pest-parser/intellij-pest/issues/214) 18 | 19 | ``` 20 | Plugin Name: Pest 21 | Plugin Version: 0.3.2 22 | Rust Plugin Version: 0.4.178.4873-222 23 | Bundled Pest Version: 0.1.3 24 | OS Name: Mac OS X 25 | Java Version: 17.0.4 26 | App Name: CLion 27 | App Full Name: CLion 28 | App Version name: CLion 29 | Is EAP: false 30 | App Build: CL-222.4167.25 31 | App Version: 2022.2.2 32 | Last Action: 33 | title: [auto-generated:-1738221085] null 34 | ``` 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/patterns/auto-install-script.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 一行命令安装 4 | parent: 实践模式 5 | nav_order: 1 6 | --- 7 | 8 | # 实践模式:一行命令安装 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | 一行命令(1-line script / one line script)安装: 20 | 21 | 22 | ## 示例 23 | 24 | ### *Nix 下的 Rust 安装 25 | 26 | ```bash 27 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 28 | ``` 29 | 30 | 要素: 31 | 32 | 1. 安装环境检查 33 | 1. 系统环境。如系统架构 34 | 2. `endianness` 大小端 35 | 2. 执行环境检查 36 | 1. uname 等 37 | 3. 下载器 38 | 4. 验证包 39 | 5. 安装包 40 | 41 | ### Windows 下的 Scoop 42 | 43 | [Scoop](https://scoop.sh/) usage: 44 | 45 | ```bash 46 | scoop install curl 47 | ``` 48 | 49 | 安装 50 | 51 | ```powershell 52 | Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') 53 | ``` 54 | 55 | ```powershell 56 | # or shorter 57 | iwr -useb get.scoop.sh | iex 58 | ``` -------------------------------------------------------------------------------- /docs/domain/api-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: API 体验设计(TBC) 4 | parent: 领域案例学习 5 | nav_order: 3 6 | --- 7 | 8 | # 开发者体验:Web API 体验设计 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## Slack API 设计 20 | 21 | 相关文章:[How We Design Our APIs at Slack](https://slack.engineering/how-we-design-our-apis-at-slack/) 22 | 23 | 设计原则: 24 | 25 | - 为做一件事并做好。简单的 API 不仅易于理解,而且更易于扩展、性能更高且更安全。此外,向 API 添加新功能很容易,但很难删除它们。 26 | - 使上手变得快速而轻松。 27 | - 基础指标:TTFHW。在 Slack,我们希望入门级开发人员能够在大约 15 分钟内了解该平台、创建应用程序并发送他们的第一个 API 调用。 28 | - 其它指标。对于 Slack 的我们来说,我们认为对于开发人员来说,重要的是让他们的应用程序在至少一个其他用户面前展示,或者达到某个交互点,使应用程序可以做的不仅仅是按命令广播消息。 29 | - 力求直观的一致性。 30 | - 与行业标准的一致性 31 | - 与您的产品的一致性 32 | - 与您的其他 API 方法的一致性 33 | - 返回有意义的错误。返回错误代码、长格式的错误详情、错误消息和警告等。 34 | - 为规模和性能而设计。 35 | - 避免破坏性更改。 36 | 37 | 设计流程 38 | 39 | 1. 编写 API 规范(spec) 40 | 2. 内部 API 检视(review) 41 | 3. 早期合作伙伴反馈 42 | 4. Beat 测试 43 | 44 | 最后,保持灵活性 45 | -------------------------------------------------------------------------------- /docs/patterns/developer-portal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开发者门户 4 | parent: 实践模式 5 | nav_order: 2 6 | --- 7 | 8 | # 实践模式:开发者门户 9 | 10 | {: .no_toc } 11 | 12 | ## 目录 13 | 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | **度量模型** 21 | 22 | 1. **TTFHW(Time to First Hello World):** TTFHW 23 | 是开发者门户度量模型中的一个关键指标,它表示用户从开始使用API、SDK或数字工具到成功实现第一个 "Hello World" 24 | 或基本操作的所需时间。较短的 TTFHW 通常表示工具易用性较高,用户能够快速上手。 25 | 26 | **示例** 27 | 28 | ### PyTorch 29 | 30 | PyTorch的开发者门户为用户提供了明确的版本信息,帮助用户选择合适的版本,如下图所示: 31 | 32 | ![PyTorch版本选择](https://pytorch.org/assets/images/amd_rocm_blog.png) 33 | 34 | 在这个示例中,PyTorch的开发者门户通过图形化界面向用户展示了不同版本的选择,从而简化了配置过程,降低了 TTFHW。 35 | 36 | ### Stripe 37 | 38 | Stripe是一个在线支付解决方案的开发者门户,提供了详细的参考资料,包括 API 39 | 文档、示例代码和常见问题解答。这些资源帮助开发者更轻松地集成Stripe支付服务到他们的应用中,减少了开发过程中的困难和延迟。 40 | 41 | ### BackStage 42 | 43 | BackStage是Spotify开源的开发者门户工具,用于管理多个微服务的生命周期。它提供了统一的界面,使开发者能够轻松访问各种工具、服务和文档。通过 44 | BackStage,开发者可以更高效地管理、发现和使用内部工具,促进了团队协作和项目开发的顺利进行。 45 | -------------------------------------------------------------------------------- /docs/engineering/engineering.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 工程化 4 | nav_order: 7 5 | has_children: true 6 | --- 7 | 8 | # 工程化 9 | {: .no_toc } 10 | 11 | 工程化是一种系统化和规范化的方法,旨在提高软件开发过程的效率、质量和可维护性。工程化的目标是将软件开发视为一个工程项目,采用科学化、标准化的方法来管理和执行开发任务。 12 | {: .fs-6 .fw-300 } 13 | 14 | 以下是软件开发中工程化的一些主要特征和实践: 15 | 16 | 1. 自动化: 使用自动化工具和脚本来执行重复性的任务,如编译、测试、部署等,以减少人为错误并提高效率。 17 | 2. 版本控制: 使用版本控制系统(如Git)来追踪代码变更、协作开发,以及管理不同版本的软件。 18 | 3. 持续集成(CI): 将代码集成到共享的主干(主要代码库)中,并通过自动化构建和测试来确保代码的及时集成和质量。 19 | 4. 持续交付(CD): 在持续集成的基础上,自动执行部署,使得软件能够快速且可靠地交付给最终用户。 20 | 5. 代码规范和质量检查: 使用静态代码分析工具和代码审查来确保代码符合事先定义的规范和质量标准。 21 | 6. 文档管理: 管理项目文档,包括需求文档、设计文档、用户文档等,以确保团队成员都能够理解和遵循项目的规范和流程。 22 | 7. 任务管理: 使用项目管理工具来跟踪任务、问题和进度,以便团队成员协同工作并实现项目的及时交付。 23 | 8. 容器化和微服务架构: 使用容器技术(如Docker)和微服务架构来实现更灵活、可扩展和可维护的系统。 24 | 9. 性能监控和日志管理: 集成性能监控和日志记录工具,以便实时监控系统性能、追踪问题和进行故障排除。 25 | 10. 安全性考虑: 在开发过程中集成安全性考虑,采用最佳实践来保护软件系统免受潜在的安全威胁。 26 | 27 | 其中有大量的内容可以应用在文档工程中,如自动化、版本控制、持续集成、持续交付、文档管理等。 28 | 29 | ## 目录 30 | {: .no_toc .text-delta } 31 | 32 | 1. TOC 33 | {:toc} 34 | -------------------------------------------------------------------------------- /docs/domain/devops.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: DevOps 体验设计(TBC) 4 | parent: 领域案例学习 5 | nav_order: 2 6 | --- 7 | 8 | # 开发者体验:DevOps 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## Gitlab 体验设计 20 | 21 | 相关文章:[Designing for developer experience](https://blog.prototypr.io/%EF%B8%8Fdesigning-for-developer-experience-%EF%B8%8F-69ced457c405) 22 | 23 | 设计思路: 24 | 25 | - 为包容性设计。通过让具有不同专业知识、人口结构、能力和倾向的人成为数字产品的创造者,没有任何代码方法可以带来行业长期以来一直缺乏的多样性和丰富性。 26 | - 为可学习性和可发现性设计。说服开发人员通过深思熟虑地设计用户流程,在他们深入工作流程时逐步展示高级功能,从而对产品的深度和广度进行自我教育。 27 | - 为人类设计。以人为本, 28 | 29 | 30 | ## K8s 31 | 32 | [5 tips to maximize the Kubernetes developer experience with Cloud Code](https://cloud.google.com/blog/products/application-development/5-tips-maximize-kubernetes-developer-experience-cloud-code) 33 | 34 | 1. Develop where you're most comfortable 35 | 2. Develop where you're most comfortable 36 | 3. Simplify Local Development 37 | 4. Spend less time writing YAML 38 | 5. Debug in real time 39 | 40 | -------------------------------------------------------------------------------- /docs/anti-patterns/fake-opensource.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:伪式开源 4 | parent: 反模式 5 | nav_order: 5 6 | --- 7 | 8 | # 开发者体验反模式:伪式开源 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## 定义 20 | 21 | 硬件开源级别:[Levels of Openness](https://wiki.opensourceecology.org/wiki/Levels_of_Openness#Fake_Open_Source) 22 | 23 | 1. 无文档开源(Undocumented Open Source)。代码是开源的,但是没有文档。从开源经验的角度来看 - 无文档开源实际上是不开放的。 24 | 2. 模棱两可的开源(Ambivalent Open Source)。缺乏许可证。 25 | 3. 伪开源(Fake Open Source)。假装是开源的,但许可证不公开。不可复制的开源,如需要投入昂贵的设备、稀有的投入、广泛的技能组合。 26 | 4. 战略开源(Strategic Open Source)。开源作为一种战略,但是强烈拒绝开放文化。 27 | 5. 有用的开源(Useful Open Source)。文档是否对于复制是有用 - 或者它实际上是无用的? 28 | 6. 道德开源(Ethical Open Source)。出于开发纯开源产品的。 29 | 30 | ## 特征 31 | 32 | ## 案例 33 | 34 | ### [TDengine](https://github.com/taosdata/TDengine/) 35 | 36 | - 使用内部 Jira 号。[#8549](https://github.com/taosdata/TDengine/pull/8459) 提交信息: `[TD-5892]: officially document the PyPI python connector` 37 | 38 | #### 后续改进 39 | 40 | 41 | 42 | ## 潜伏方案 43 | -------------------------------------------------------------------------------- /docs/patterns/dev-exp-team.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开发者体验团队 4 | parent: 实践模式 5 | nav_order: 5 6 | --- 7 | 8 | # 开发者体验:开发者体验团队 9 | {: .no_toc } 10 | 11 | 开发者体验团队 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | ## 示例 23 | 24 | ### Uber 25 | 26 | [Constructing a Seamless Flow: Meet Uber Engineering’s Developer Experience Team ](https://eng.uber.com/developer-experience/) 27 | 28 | DevExp 隶属于 Uber 的基础设施小组,它包括 Uber 的信息平台、工程培训和开发,以及服务和语言框架团队的成员。 29 | 30 | JD 示例:[Uber] 31 | 32 | 33 | ## 相关资料 34 | 35 | - [The rise of the Developer Experience Engineer, and why it matters](https://www.helpnetsecurity.com/2021/07/16/developer-experience-engineer/) 36 | - [Why Every Software Team Should Have a Developer Experience Owner (DXO)](https://loft.sh/blog/why-every-software-team-should-have-a-developer-experience-owner-dxo/) 37 | - [Why Your API Needs a Dedicated Developer Experience Team](https://nordicapis.com/why-your-api-needs-a-dedicated-developer-experience-team/) 38 | 39 | -------------------------------------------------------------------------------- /docs/patterns/onboarding-journey.md: -------------------------------------------------------------------------------- 1 | layout: default 2 | title: 新手体验设计 3 | parent: 实践模式 4 | nav_order: 4 5 | --- 6 | 7 | # 实践模式:新手体验设计 8 | {: .no_toc } 9 | 10 | 使用场景: 11 | 12 | 13 | ## 目录 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | 22 | 我们在开发者体验中引入新手体验(Onboarding journey)的目的是,以 “新人” 的视角来观察整个生命周期中的潜在问题,并帮助 “新人” 更快地上手项目。 23 | 24 | 在日常的软件开发中,我们也会借助于这个思想来进行分析: 25 | 26 | ![Onboarding 体验](image/onboarding.png) 27 | 28 | 如我们开发一个开源的 SDK,那么 “新人” 来到这个项目时,他需要: 29 | 30 | - 看到快速介绍项目的一句话介绍。这个介绍与开发者的痛点是存在一定的关联。 31 | - 看到项目的详细介绍。诸如于这个项目的架构信息、优点、缺点等项目的信息。 32 | - 尝试 —— 快速交互的使用体验。如一个快速使用的指南或者指令,又或者是一个线上的 DEMO。 33 | - 查看更详细的文档引导。如这个项目包含了哪些 API,以及常见的 FAQ 等 34 | 35 | ## 价值机会点 36 | 37 | ### 机会点梳理 38 | 39 | 1. 梳理开发者使用技术产品的主要流程。 40 | 2. 细化过程中的相关步骤。 41 | 3. 分析步骤中的用户行为。 42 | 4. 研究对应的目标场景。 43 | 5. 细化其中的机会点。 44 | 45 | ### 用户体验设计 46 | 47 | 1. 前期调研。在开始阶段需要探索出相对明确的我们可行的方向 48 | - 行业调研探索 49 | - 现有的可行方向 50 | 2. 用研&工作坊。深入了解动机,了解现有行为客户价值引领创新价值 51 | - 一对一的用户访谈 52 | - 可视化工作坊 53 | 3. 机会点收敛。收敛阶段根据价值和可行性得到机会点 54 | - 收敛出价值高、可行的机会点 55 | 4. 用户需求的验证。机会点的验证,排除无效机会点 56 | 5. 经过验证的创新提炼 57 | 6. 创新场景旅程设计 58 | -------------------------------------------------------------------------------- /docs/factor/touch-point.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 触点与支持 4 | parent: 开发者体验设计因子 5 | nav_order: 5 6 | --- 7 | 8 | # 触点与支持 9 | {: .no_toc } 10 | 11 | 定义:围绕于构建社区进行的一系列活动,在这里被简化和合并为触点与支持。 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | ## 定义触点设计 23 | 24 | ## 触点设计原则 25 | 26 | ### 以同理心构建 27 | 28 | ### 构建内容战略 29 | 30 | ### 广泛吸纳用户意见 31 | 32 | ## 模式 33 | 34 | ### 模式:技术内容 35 | 36 | 如文章、视频,相应的需要构建内容策略 37 | 38 | ### 模式:大会演讲 39 | 40 | ### 模式:技术沙龙 41 | 42 | ### 模式:Hackathon 43 | 44 | ## 示例 45 | 46 | ### 亚马逊 47 | 48 | - 官方渠道 49 | - 网易云课堂渠道:[亚马逊云科技数字化培训](https://m.study.163.com/m/provider/480000002278455/index.htm) 50 | 51 | ## 角色:开发者关系 52 | 53 | 市面上已经有一系列的技术产品化运营的岗位,这些岗位受限于角色定义的不同,所承担的职责也有所不同。如: 54 | 55 | ### 布道师(Evangelist) 56 | 57 | 从语义上来定义,这个岗位的主要职责是布道。也几乎(90 %)承担了技术产品化运营的所有职责,诸如于自身成为影响力中心,构建对应的技术社区,以吸引用户。再手把手帮助客户上手应用,维护和开发者的关系等。 58 | 59 | ### 开发者关系(DevRel) 60 | 61 | 从语义上来定义,这个岗位的主要职责是维护和开发者的关系。从职责来看,它 99.99% 接近于承担了所有职责。 62 | 63 | ### 开发者运营 64 | 65 | 从语义上来定义,这个岗位的主要职责是开发者相关的社区运营。主要偏向于活动与相关的内容策略等,可以由低技术背景的角色来承担。 66 | 67 | ### 技术运营 68 | 69 | 从语义上来定义,这个岗位的主要职责接近于传统的营销岗位。 70 | -------------------------------------------------------------------------------- /docs/patterns/template-by-ci.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 基于持续集成的自动化模板 4 | parent: 实践模式 5 | nav_order: 8 6 | --- 7 | 8 | 示例: 9 | 10 | - Intellij IDEA 插件模板:https://github.com/JetBrains/intellij-platform-plugin-template 11 | 12 | 13 | 14 | 效果: 15 | 16 | 1. 使用模板时,会通过 GitHub Action 创建创建工程,并清理无用的代码。 17 | 2. 通过在 README 里编写的 Todo 来帮助开发人员查看每一步。 18 | 19 | Todo 示例: 20 | 21 | ## Template ToDo list 22 | - [x] Create a new [IntelliJ Platform Plugin Template][template] project. 23 | - [x] Get familiar with the [template documentation][template]. 24 | - [x] Verify the [pluginGroup](./gradle.properties), [plugin ID](./src/main/resources/META-INF/plugin.xml) and [sources package](./src/main/kotlin). 25 | - [x] Review the [Legal Agreements](https://plugins.jetbrains.com/docs/marketplace/legal-agreements.html). 26 | - [ ] [Publish a plugin manually](https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html?from=IJPluginTemplate) for the first time. 27 | - [ ] Set the Plugin ID in the above README badges. 28 | - [ ] Set the [Deployment Token](https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html). 29 | - [ ] Click the Watch button on the top of the [IntelliJ Platform Plugin Template][template] to be notified about releases containing new features and fixes. 30 | 31 | -------------------------------------------------------------------------------- /docs/dx-fluency-model.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 流畅度模型(TBD) 4 | nav_order: 90 5 | --- 6 | 7 | # 开发者体验流畅度模型(理论验证中) 8 | {: .no_toc } 9 | 10 | 开发者体验流畅度模型,是我们基于敏捷流畅度模型与数字化流畅度模型设计的改进体系。 11 | {: .fs-6 .fw-300 } 12 | 13 | ## 目录 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## 流畅度模型 22 | 23 | 在《[The Agile Fluency Model](https://martinfowler.com/articles/agileFluency.html)》 一文中指出: 24 | 25 | > 敏捷团队开发有四个不同层次的流畅度。流畅度指的是团队在压力下如何开发软件。在教室里,只要给每个人足够的时间专注地去做一组练习,他们最终都能完成;然而,真正的流畅度,不是一时的练习,它是一种技巧性十足的、常规的经常性的实践,即使你心不在焉也能完成。 26 | 27 | 28 | | | 文档体验 | 错误呈现 | 易用性 | 交互设计 | 触点与支持 | 29 | |------|----------|----------|------|-----------|----------| 30 | | 强化 | | | | | | 31 | | 优化 | | | | | | 32 | | 执行 | | | | | | 33 | | 专注 | | | | | | 34 | | 认识 | | | | | | 35 | 36 | 过程阶段(TBD): 37 | 38 | - 认识。意识到要做对应的改变。 39 | - 专注。开始着手 40 | - 执行。规范化 41 | - 优化。使用模式 42 | - 强化。工具度量 43 | 44 | 核心支柱: 45 | 46 | - 文档体验设计 47 | - 错误呈现 48 | - 易用性设计 49 | - 交互设计 50 | - 触点与支持 51 | 52 | ## 关键投资 53 | 54 | ### 文档体验设计 55 | 56 | ### 错误呈现 57 | 58 | ### 易用性设计 59 | 60 | ### 交互设计 61 | 62 | ### 触点与支持 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 开发者体验:探索与重塑 2 | 3 | ![Logo](resources/dx.svg) 4 | 5 | 开发者体验,与用户体验类似,只是对象是软件开发人员。因此,它的定义是 6 | 7 | > 开发者体验是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。有所不同的是,用户关注的内容变为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。 8 | 9 | 2019,在经历了多个低代码前端项目的售前,以及一个低代码项目的技术实践强化,发现国内的 IT 企业缺乏对于『开发者体验』缺乏系统性的思考。便想着结合自己的项目经历、社区经验、国内外实际情况等,编写一个简要的开发者体验电子书。 10 | 11 | 在线阅读:[https://dx.phodal.com](https://dx.phodal.com) 12 | 13 | 文档目录:[docs](./docs) 14 | 15 | 模板:[https://github.com/pmarsceill/just-the-docs](https://github.com/pmarsceill/just-the-docs) 16 | 17 | 欢迎参与讨论与 PR: 18 | 19 | ![Wechat QRCode](image/qrcode.jpg) 20 | 21 | (PS:如果群满了,请添加我的微信 `phodal02` ) 22 | 23 | ## Todo 24 | 25 | - [x] define dex 26 | - [x] maturity model 27 | - [x] define 28 | - [ ] refs model 29 | - [x] operation model 30 | - [ ] practise 31 | - [x] documentation 32 | - [ ] error handling (ongoing) 33 | - [ ] interactive design 34 | - [ ] usability 35 | - [ ] developer relation 36 | - [ ] anti patterns 37 | - [x] developer as services 38 | 39 | ## License 40 | 41 | [![Phodal's Book](http://brand.phodal.com/shields/book-small.svg)](https://www.phodal.com/) 42 | 43 | © 2020~2021 [Phodal Huang](https://www.phodal.com). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory. 44 | -------------------------------------------------------------------------------- /convert.sh: -------------------------------------------------------------------------------- 1 | cat docs/define-dx.md \ 2 | docs/maturity-model.md \ 3 | docs/factor/documentations.md docs/factor/error-handling.md docs/factor/usability.md docs/factor/interactive-design.md docs/factor/touch-point.md \ 4 | docs/patterns/auto-install-script.md \ 5 | docs/patterns/dev-exp-team.md \ 6 | docs/patterns/developer-experience-center.md \ 7 | docs/patterns/developer-portal.md \ 8 | docs/patterns/document-engineering.md \ 9 | docs/patterns/document-for-learn.md \ 10 | docs/patterns/interactive-script.md \ 11 | docs/patterns/issue-bot.md \ 12 | docs/patterns/living-document-code.md \ 13 | docs/patterns/onboarding-journey.md \ 14 | docs/patterns/patterns.md \ 15 | docs/patterns/starter-generator.md \ 16 | docs/patterns/template-by-ci.md \ 17 | docs/anti-patterns/developer-as-service.md \ 18 | docs/anti-patterns/fake-opensource.md \ 19 | docs/anti-patterns/kpi-base-model-design.md \ 20 | docs/anti-patterns/marketing-drive-developement.md \ 21 | docs/anti-patterns/metric-to-contributor.md \ 22 | docs/anti-patterns/ui-replace-cmd.md \ 23 | docs/engineering/document-engineering.md \ 24 | docs/domain/api-design.md \ 25 | docs/domain/cloud-native.md \ 26 | docs/domain/devops.md \ 27 | docs/domain/openbank.md \ 28 | docs/operation-model.md \ 29 | docs/maturity-model.md \ 30 | docs/dev-ex-framework.md \ 31 | docs/refs.md > output.md 32 | 33 | sed '/^---$/,/^---$/d' output.md > temp.md && mv temp.md output.md 34 | pandoc -s output.md -o output.docx 35 | -------------------------------------------------------------------------------- /docs/factor/documentations.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 文档体验设计 4 | parent: 开发者体验设计因子 5 | nav_order: 1 6 | --- 7 | 8 | # 开发者体验:文档体验设计 9 | {: .no_toc } 10 | 11 | 文档体验设计面向于文档使用者的阅读体验,与文档编写者的构建体验。 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | ## 设计原则 23 | 24 | ### 保持内容准确性 25 | 26 | 作为文档编写者,需要确保语义准确性,基于人的维度来考虑,相应的机制有: 27 | 28 | - 概念词汇表 29 | - 内容一致性 30 | - 文档测试 31 | 32 | ### 根据用户反馈改进 33 | 34 | 诸如于: 35 | 36 | - 提供有意义的反馈方式。如评分等。 37 | - 让用户可以直接 PR 进行修改。 38 | 39 | ### 引导开发者学习 40 | 41 | 常见方式: 42 | 43 | - 在内容中进行引导 44 | - 从搜索引擎中引导 45 | - 从错误处理中引导 46 | 47 | ## 模式 48 | 49 | ### 模式:应用开放式协作 50 | 51 | 采用文档代码化的方式进行管理。如文档相关的内容在 GitHub 上,文档指向对应的源码位置,读者可以直接发起对文档的修改。 52 | 53 | 示例 1:[https://dx.phodal.com/](https://dx.phodal.com/) 54 | 55 | 示例 2:[https://github.com/MicrosoftDocs/azure-docs-cli](https://github.com/MicrosoftDocs/azure-docs-cli) 56 | 57 | ### 模式:为搜索引擎设计 58 | 59 | 以 [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt) 示例,用户从需求出发,从搜索引擎中搜索: 60 | 61 | ![在 Google 在搜索](image/search-in-google.png) 62 | 63 | 重要的一个差异:标题与左侧导航并不需要一致: 64 | 65 | ![Azure 示例](image/design-for-search-engine.png) 66 | 67 | 获取对应的安装方式。 68 | 69 | ### 模式:一页式快速开发 70 | 71 | 对于使用项目的文档,如果不能提供快速搭建的 CLI,应该在一个页面内提供快速开发。 72 | 73 | ### 模式:库文档自动生成 74 | 75 | 对于软件库来,其文档应该是自动生成的,主流的编程语言都有各自的文档生成工具。 76 | 77 | ### 模式:更新日志 78 | 79 | 大版本以博客的形式展示,如《[Angular 12 更新](https://blog.angular.io/angular-v12-is-now-available-32ed51fbfd49?gi=a0a6358507d3)》,包含: 80 | 81 | - 重要更新 82 | -------------------------------------------------------------------------------- /docs/patterns/living-document-code.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 动态文档代码生成 4 | parent: 实践模式 5 | nav_order: 13 6 | --- 7 | 8 | 即:[开发者体验:文档工程](https://dx.phodal.com/docs/patterns/document-engineering.html) 中灵活代码块的展开。 9 | 10 | ## 业内案例:Chocolate Factory 文档 11 | 12 | 示例:https://framework.unitmesh.cc/rag/vector-store 13 | 14 | 基于代码切分文档: 15 | 16 | https://github.com/unit-mesh/chocolate-factory/blob/1c70e5c326c70dcabc19ba5e13ef08832c900191/docs-builder/src/main/kotlin/cc/unitmesh/docs/model/DocGenerator.kt#L32-L51 17 | 18 | 测试用例示例: 19 | 20 | ```kotlin 21 | @Test 22 | @SampleCode 23 | fun it_works() { 24 | // start-sample 25 | val embeddingStore: EmbeddingStore = InMemoryEmbeddingStore() 26 | 27 | embeddingStore.add(toEmbedding(floatArrayOf(1f, 3f)), Document.from("first")) 28 | embeddingStore.add(toEmbedding(floatArrayOf(2f, 2f)), Document.from("second")) 29 | 30 | val relevant: List> = 31 | embeddingStore.findRelevant(toEmbedding(floatArrayOf(4f, 0f)), 2) 32 | 33 | // end-sample 34 | 35 | } 36 | ``` 37 | 38 | ## 业内案例:Google Android 文档 39 | 40 | 示例:https://developer.android.com/jetpack/compose/lifecycle?hl=zh-cn 其中的代码引用会指向 GitHub 代码库。 41 | 42 | 文档通过使用关联 tag 来实现: 43 | 44 | ```Kotlin 45 | // [START android_compose_lifecycle_3] 46 | @Composable 47 | fun MoviesScreen(movies: List) { 48 | Column { 49 | for (movie in movies) { 50 | // MovieOverview composables are placed in Composition given its 51 | // index position in the for loop 52 | MovieOverview(movie) 53 | } 54 | } 55 | } 56 | // [END android_compose_lifecycle_3] 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /docs/dev-ex-framework.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 开发者体验度量框架 4 | nav_order: 98 5 | --- 6 | 7 | # 开发者体验度量框架 8 | {: .no_toc } 9 | 10 | 适用领域: 11 | 12 | DevOps 13 | {: .label } 14 | 15 | AI + DevOps 16 | {: .label } 17 | 18 | --- 19 | 20 | 21 | ## DevEx 22 | 23 | 出处:[DevEx: What Actually Drives Productivity: The developer-centric approach to measuring and improving productivity](https://dl.acm.org/doi/10.1145/3595878) 24 | 25 | |-| Feedback loops | Cognitive load | Flow state | 26 | |---|----------------|-----------------|-------------| 27 | | Perceptions
Human attitudes and opinions |• Satisfaction with automated test speed and output
• Satisfaction with time it takes to validate a local change
• Satisfaction with time it takes to deploy a change to production | • Perceived complexity of codebase
• Ease of debugging production systems
• Ease of understanding documentation |• Perceived ability to focus and avoid interruptions
• Satisfaction with clarity of task or project goals
• Perceived disruptive- ness of being on-call | 28 | | Workflows
System and process behaviors | • Time it takes to generate CI results
• Code review turnaround time
• Deployment lead time (time it takes to get a change released to production) | • Time it takes to get answers to technical questions
• Manual steps required to deploy a change
• Frequency of documentation improvements | • Number of blocks of time without meet- ings or interruptions
• Frequency of unplanned tasks or requests
• Frequency of incidents requiring team attention | 29 | | KPIs
North star metrics
| • Overall perceived ease of delivering software
• Employee engagement or satisfaction
• Perceived productivity | | | 30 | 31 | 中文翻译: 32 | 33 | 心流状态。高效、专注和高产出状态 34 | 反馈回路。持续性地收集反馈并不断优化 35 | 认知负载。完成任务所需的注意力和脑力负担 36 | 37 | 38 | 39 | | - | 反馈回路 | 认知负荷 | 流畅状态 | 40 | | ---- | ---------------- | -------------- | --------- | 41 | | 感知
人的态度和观点 | • 对自动化测试速度和输出的满意度
• 对验证本地更改所需时间的满意度
• 对部署更改至生产环境所需时间的满意度 | • 对代码库复杂性的感知
• 调试生产系统的易用性
• 理解文档的易用性 | • 对专注度和避免中断的感知
• 对任务或项目目标清晰度的满意度
• 值班对生产的中断感知 | 42 | | 工作流
系统和流程行为 | • 生成CI结果所需时间
• 代码审查周转时间
• 部署交付时间(将更改发布至生产所需时间) | • 获取技术问题答案所需时间
• 部署更改所需的手动步骤
• 文档改进的频率 | • 无会议或中断的时间块数量
• 未计划任务或请求的频率
• 需要团队关注的事故频率 | 43 | | 绩效指标
北极星指标 | • 传递软件的整体感知轻松度
• 员工参与度或满意度
• 感知生产力 | 同左 | 同左 | 44 | 45 | -------------------------------------------------------------------------------- /docs/operation-model.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 技术产品化运营 4 | nav_order: 89 5 | --- 6 | 7 | # 开发者体验:技术产品化运营 8 | {: .no_toc } 9 | 10 | 开发者体验是我们在进行技术产品化运营时非常重要的一个环节,将它放到运营的生命周期来,有助于我们看清整个系统的全貌。 11 | {: .fs-6 .fw-300 } 12 | 13 | ## 目录 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | > 技术产品化运营,是将技术产品(如开放 API、开源软件、开放平台、SDK、工具等)视为产品,在组织内部或者外部进行推广,以吸引更多的用户、开发者等参与到其中,加入到技术产品开发中,或者是采用该技术产品在其应用域构建解决方案。 22 | 23 | ## 技术产品化运营 24 | 25 | 与几年前相比,国内与技术产品化运营相关的岗位越来越多,诸如于布道师(Evangelist)、开发者关系(DevRel)、技术运营、开发者运营等。虽然,岗位职责稍有不同,也就是目标 KPI 是不同的。但是呢,其中有相当多的相交之处,诸如于从技术社区出发,去为技术产品(如开源项目、云服务、商业化工具等)带来某一方面的价值 —— 如品牌、销售量等。 26 | 27 | 基于这一个基本的模式(从社区-产品),我提取了第一版本的『**技术产品化运营元模型**』(请原谅我图画的丑): 28 | 29 | ![技术产品化运营元模型](/image/community-technology.png) 30 | 31 | 从某种意义上来说,这个领域所做的事情和现在的新媒体运营有点相似。但是,它的强技术背景大大提升了准入的门槛。而如果你也有一定的经验,你可以从上图中的四个步骤,看出产品化运营的困难之处,它涉及到多个领域:技术、社区运营、开源者体验设计、开放式协作等。 32 | 33 | ## 如何构建技术产品化运营战略 34 | 35 | ### 构建基本策略 36 | 37 | 在上图的元模型中,我们定义了基本的模式。基于这个模式,我们可以扩展我们的策略,如下图所示: 38 | 39 | ![开源软件产品化运营策略](/image/opensource-comunnity.png) 40 | 41 | 它以 KOL(关键意见领袖) 和布道师构建了影响力的中心,模式也稍微有一些变化。随后,我们需要: 42 | 43 | 1. 定义我们在**社区**和**产品**上的目标 44 | - 以社区成员价值为出发点构建社区策略 45 | - 以客户价值为出发点构建产品化策略 46 | 2. 完善每一步所需要的任务和阶段化目标。 47 | 3. 制定不同社区繁荣阶段的演进方案。 48 | 4. 持续迭代和优化方案。 49 | 50 | 其中最重要的是考虑对于社区的价值。 51 | 52 | ### 1. 构建影响力中心 53 | 54 | 在设计模型的时候,我考虑了几个不同的方案,最后定义为『构建影响力中心』。原因是,我们需要一种持续的机制来影响越多的人,也因此是一个影响力中心。 55 | 56 | 它可以是通过一定角色的人去影响社区,诸如于: 57 | 58 | 1. 开发者关系(DevRel)、技术布道师(Evangelist)。 59 | 2. 意见领袖(KOL)。 60 | 61 | 通过构建影响力中心,我们可以辐射到更多的人群上,从而达到更好的触达率。它的构建方式比较有意思,可以通过特别的岗位,如开发者关系去构建,进一步去带动 KOL 们的兴趣。 62 | 63 | ### 2. 构思社区文化,凝聚人心 64 | 65 | 当有越来越多的人参与到社区中时,我们就需要去构建社区如何构建?社区的文化又是如何的问题?我们在社区上,需要: 66 | 67 | 1. 以社区和成员价值为核心,产品第二。 68 | 2. 创造协作空间,实现个人价值。 69 | 70 | 作为一个开放式的社区,我们需要有自己的主题,有自己的价值宣言,并坚守它们。 71 | 72 | 与此同时,我们需要分析我们的目标用户,随后: 73 | 74 | 1. 提供**易于触达的访问路径**。诸如于,如果我们提供的是论坛,那么它应该易于访问;如果我们通过 IM 来通讯,它应该有一定的安装率。 75 | 2. 考虑**对于用户的个人价值**。他们是谁,它为什么要来到这个社区? 76 | 77 | 社区是向人们提供价值,而不是向利益相关者提供价值的。 78 | 79 | ### 3. 设计新手体验 80 | 81 | 开发者也是一个用户,为了吸引他们参与到项目中来。我们需要设计好新手的体验,以让他们能更快地上手到项目中。所以,我们视不同的情况,可能还需要: 82 | 83 | 1. 设计入门之旅 Onboarding Journey 84 | 2. 打造新手体验 85 | 3. 设计**开发者体验**指标 86 | 4. 提供、透明协作(如开放式管理等) 87 | 88 | 如果目标的新手成员不能快速参与到项目中,那么我们将流失大量的用户。 89 | 90 | 所以,这是实现转换率非常重要的一步。 91 | 92 | ### 4. 帮助客户创造价值 93 | 94 | 作为一个技术产品,它能吸引到开发者和用户的一大原因是,它能帮助我们创造价值。从创造客户的价值出发,而非售卖自己的产品,这个是非常重要的。 95 | 96 | 所以,我们需要去设计我们的提升计划,诸如于: 97 | 98 | 1. 构建第一个成功的案例。 99 | 2. 参与到客户产品的设计中。 100 | 101 | 视不同的产品,这里的差异会非常之大。 102 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pmarsceill/just-the-docs 2 | 3 | title: 开发者体验:探索与重塑 4 | description: 2019,在经历了多个低代码前端项目的售前,以及一个低代码项目的技术实践强化,发现国内的 IT 企业缺乏对于『开发者体验』缺乏系统性的思考。便想着结合自己的项目经历、社区经验、国内外实际情况等,编写一个简要的开发者体验电子书。 5 | 6 | heading_anchors: true 7 | 8 | footer_content: "Copyright © Phodal (for temp). 2020~2021 DX China.This code is distributed under the CC BY 4.0 License. See `LICENSE` in this directory." 9 | 10 | # Footer last edited timestamp 11 | last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter 12 | last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html 13 | 14 | # Footer "Edit this page on GitHub" link text 15 | gh_edit_link: true # show or hide edit this page link 16 | gh_edit_link_text: "Edit this page on GitHub." 17 | gh_edit_repository: "https://github.com/dx-angel/dx-book" # the github URL for your repo 18 | gh_edit_branch: "master" # the branch that your docs is served from 19 | # gh_edit_source: docs # the source that your files originate from 20 | gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately 21 | 22 | # Enable or disable the site search 23 | # Supports true (default) or false 24 | search_enabled: true 25 | 26 | search: 27 | # Split pages into sections that can be searched individually 28 | # Supports 1 - 6, default: 2 29 | heading_level: 4 30 | # Maximum amount of previews per search result 31 | # Default: 3 32 | previews: 3 33 | # Maximum amount of words to display before a matched word in the preview 34 | # Default: 5 35 | preview_words_before: 5 36 | # Maximum amount of words to display after a matched word in the preview 37 | # Default: 10 38 | preview_words_after: 10 39 | # Set the search token separator 40 | # Default: /[\s\-/]+/ 41 | # Example: enable support for hyphenated search words 42 | tokenizer_separator: /[\s/]+/ 43 | # Display the relative url in search results 44 | # Supports true (default) or false 45 | rel_url: true 46 | # Enable or disable the search button that appears in the bottom right corner of every page 47 | # Supports true or false (default) 48 | button: false 49 | 50 | 51 | # Back to top link 52 | back_to_top: true 53 | back_to_top_text: "Back to top" 54 | 55 | # Google Analytics Tracking (optional) 56 | # e.g, UA-1234567-89 57 | ga_tracking: G-JR0EZ3NLE1 58 | ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true by default) 59 | 60 | url: "https://dx.phodal.com" # the base hostname & protocol for your site 61 | 62 | # Aux links for the upper right navigation 63 | aux_links: 64 | "View in on GitHub": 65 | - "//github.com/dx-angel/dx-book" 66 | 67 | # Makes Aux links open in a new tab. Default is false 68 | aux_links_new_tab: true 69 | 70 | plugins: 71 | - jekyll-seo-tag 72 | - jekyll-sitemap 73 | -------------------------------------------------------------------------------- /docs/anti-patterns/developer-as-service.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 反模式:开发者即服务 4 | parent: 反模式 5 | nav_order: 1 6 | --- 7 | 8 | # 开发者体验反模式:开发者即服务 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | > 开发者即服务,是(Developer-as-a-Service)的简称,亦可称为 “按需即用的开发者”。即当开发者使用某一工具、库,遇到任何相关的问题,可以随时找开发者为我们提供服务。哪怕是使用者使用了我们的 A 框架,但是遇到 B 框架有问题,他/她们也会觉得 A 框架有问题 ——因为 A 框架的开发者们是一种服务,一种开箱即用的服务。 20 | 21 | 最近几年,我陆陆续续参与了一些公司的基础设施的开发和咨询。从组件框架、组件、平台到 IDE,各式各样的基础设施都有。作为一个参与者或者是负责人,我经历了一些辛酸的故事:要快速响应所有的问题、要提供贴身的技术支持……。所以,我决定写一篇文章来调侃一下使用者,并解释一下开发者的不易。 22 | 23 | ## 基础设施团队的挑战 24 | 25 | 每当有同事从我司离职时,我们通常的一个反应是,去的是技术为主的部门还是业务为主的部门。因为,从待遇上来说,技术部门和业务部门就是两个截然不同的存在。相同的月薪之下,业务部门可能会多个月的奖金,而技术部门这样的可能性极极极低。在这里我们简化一下这两个概念: 26 | 27 | - **技术部门**。组织中的技术支持部门,提供各种基础设施能力,如 DevOps 平台、框架、工具、组件库等。 28 | - **业务部门**。仍然是开发人员,只是围绕着业务构建应用的,如 Web 应用、APP 等。 29 | 30 | 值得注意的事,对于有些公司来说,这可能是三个组织:业务部门是纯粹的业务人员,不包含技术相关的东西;而技术部门是支持所有业务的一个大部分;同时,还会在技术部门内构建一个基础设施团队。 31 | 32 | 所以,在这里我们简化模型为:基础设施团队,即那些提供各种 平台、框架、工具、组件库等开发应用所需基础能力的团队。 33 | 34 | 作为基础设施团队的一员,我们可以发现项目中的一些挑战: 35 | 36 | **挑战与收入不成正比**。我们所知道的市场的一个现状,作为一个基础设施团队:**有着极高的技术挑战**,**待遇不与之成正比**。当难度和收入没成正比时,基础设施部门的架构腐烂就非常快,不过这是另外一个故事了。 37 | 38 | **过长与破碎的『售后』服务时间**。当开发的工具被越来越多的人使用时,我们就面临着需要随时支持他/她人的处境。这样一来,我们就不可避免地需要花费大量时间在支持开发者,并且我们的开发时间就会不断被打扰。如此一来,我们在做这事上的成就感就会越来越低。甚至于我们会觉得这是一项累赘。 39 | 40 | **推广与 KPI 压力**。这个怕是众所周知的问题。不过呢,多数时候,团队需要换一个角度来考虑问题:其它团队使用框架时,能给自身真正地带来什么好处? 41 | 42 | **提升开发者体验与成本的均衡**。提升开发者体验,就意味着我们要用更高的投入,换取一点点的更好的开发者体验。比如说,提供长期性的完整文档、交互性的 API 试用、友好的报错机制等等。 43 | 44 | **缺少高水平开发人员**(潜在)。原因同上,收入与难度不能成正比时,容易导致招不到高水平的开发人员。同样的原因,也会导致另外一个问题,高水平的开发人员在项目中流失。对于一些大公司来说,这并非是问题。 45 | 46 | **过高的生态建设期望**。我们经常指望组织内能有其他人为工具贡献代码。而这种期望往往是非常不现实的。一来,缺乏明显的奖励机制;二来,需要提升他们的能力。 47 | 48 | ## 开发者即服务 49 | 50 | 于是呢,在早期推广的时候,团队会为了更多人的使用,在模式上发生的一些变化。它会导致基础设施团队的开发人员变成了一种开箱即用的服务,就有了开头的定义。 51 | 52 | > 开发者即服务,是(Developer-as-a-Service)的,亦可称为 “按需即用的开发者”。即当开发者使用某一工具、库,遇到任何相关的问题,可以随时找开发者为我们提供服务。哪怕是使用者使用了我们的 A 框架,但是遇到 B 框架有问题,他/她们也会觉得 A 框架有问题 ——因为 A 框架的开发者们是一种服务,一种开箱即用的服务。 53 | 54 | 在这种工作方式之下,会出现一些特定的服务模式: 55 | 56 | - 一对一的专属支持 57 | - 及时响应问题请求 58 | - 优先帮助开发者解决问题。即使判断不是工具的问题,还要给开发者一些方案。 59 | - …… 60 | 61 | 在些模式之上,开发者就好像一种随时可使用的服务。这和我们日常使用的 SAAS 服务一样,被期待开箱即用,被期待没有 bug。 62 | 63 | ## 解决方式 64 | 65 | **尽可能的开箱即用** 66 | 67 | 繁琐的安装过程须完成各种的自动化。 68 | 69 | **构建开发者社区** 70 | 71 | 让开发者帮助开发者,并赋予活跃的用户荣誉或利益,以此来促进生态的发展。 72 | 73 | **详尽细致的文档** 74 | 75 | 作为服务的提供方,我们一直都有一个共识:开发者们不会看文档。以致于有些人走入一些误区,既然不没看,那我就写少一点。事实上,这是一个误区。 76 | 77 | 经常写作的人,会达成一种共识:**文章写给自己看的**,而文档也是写给自己用的。作为一个在社区上活跃的开发者,我经常看到别人的提问,于是我就从我的 800+ 的博客里找到一个链接,然后你懂的。 78 | 79 | 同理于,当我们作为一个基础设施团队服务时,使用者们不懂得也占多数,所以你只需要抛出一个链接即可。 80 | 81 | ## 缺失的关键角色 82 | 83 | 对于一个提供基础设施服务的团队来说,他/她们急需要一种方式来推广服务,并希望能获得反馈,以完善工具。 84 | 85 | 而在最近的几年里,在我经历了亚马逊、腾讯云、阿里云等公司的邀文之后 —— 它们需要在行业内有一定经验的云开发者,还需要有一定的写作和演进能力。我便意识到『**开发者关系**』将是一个非常稀缺的岗位 —— 市场上缺少这样的人才。与此同时,从来没有这样一个工作,它的要求高,但是它的工资确……。 86 | 87 | ### DevRel 88 | 89 | 我所说这个关键性角色便是,DevRel,即开发者关系(Developer Relations)。对于这个词,不同的公司有不同的岗位,还有一种相近的岗位是:开发者布道师。这个职位的产生便是国外的公司也有类似的痛点而导致的,它们都想要: 90 | 91 | 1. 维护开发者关系 92 | 2. 在社区进行宣传 93 | 3. 对社区进行支持、收集社区反馈 94 | 4. 建立连接内部的通道 95 | 5. 促进内部进行改进。 96 | 97 | 于是,便诞生了这么一个岗位。即要与代码打交道,而且还要与人打交道。 98 | -------------------------------------------------------------------------------- /docs/factor/interactive-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 交互设计 4 | parent: 开发者体验设计因子 5 | nav_order: 4 6 | --- 7 | 8 | # 开发者体验:交互设计 9 | {: .no_toc } 10 | 11 | 12 | 定义:交互设计,又称互动设计,(英文Interaction Design, 缩写 IxD 或者 IaD),是定义、设计人造系统的行为的设计领域,侧重在互动模式的设计。 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | 21 | --- 22 | 23 | TBC。 24 | 25 | 不同层级,提供完全的键盘操作与导航 26 | 27 | ## 原则 28 | 29 | ### 使用现代化工具与技术 30 | 31 | 采用与当前时代一样的技术方式 —— 即不使用逐渐不流行的工具。 32 | 33 | ### 为连接而设计 34 | 35 | 内部连接到社区,社区连接到人 36 | 37 | ### 构建学习体验,降低认知负荷 38 | 39 | 40 | ## 示例 41 | 42 | ### Apple Swift Playgrounds 43 | 44 | 使用 Playground 构建学习体验 45 | 46 | 示例:Swift Playgrounds,在线 [https://developer.apple.com/cn/swift-playgrounds/](https://developer.apple.com/cn/swift-playgrounds/) 47 | 48 | Swift Playgrounds 是由苹果公司在 2016 年 6 月 14 日苹果全球开发者大会 WWDC 上发布的 Swift 编程语言学习 App。 49 | 50 | ## 模式 51 | 52 | ### 模式:零配置/低配置 53 | 54 | 示例:Raspberry Pi 55 | 56 | Simple HTTP Server 57 | 58 | ### 模式:CLI 59 | 60 | 面向开发者设计交互时,与面向普通用户的产品存在一些差异性。诸如于面向普通用户时,GUI 是一个更好的选择,而面向开发人员时,CLI 是一个更好的选择。 61 | 62 | > CLI,命令行界面(英語:Command-Line Interface,缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。也有人称之为字符用户界面(character user interface, CUI)[^cli]。 63 | 64 | [^cli]: https://zh.wikipedia.org/zh/%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%95%8C%E9%9D%A2 65 | 66 | 对于一个现代化的 CLI 来说,它需要提供一个能面向 IDE/ 编辑器作为接口的 API,以帮助于工具开发者,更好的集成到系统中。而CLI 可以提供用于命令行的运行环境,只需要复制/粘贴一下,就能快速解决问题。同时,可以适用于无 GUI 的无服务器端。 67 | 68 | 甚至于基于 Terminal 进行模拟。 69 | 70 | ### 模式:CLI 交互模式 71 | 72 | [Azure CLI 交互模式](https://docs.microsoft.com/zh-cn/cli/azure/interactive-azure-cli) 73 | 74 | > Azure CLI 交互模式 (az interactive) 为用户提供一个交互式的环境来运行 Azure CLI 命令。 使用交互模式可以更轻松地了解 Azure CLI 的功能、命令语法和输出格式。 它提供自动完成下拉列表、自动缓存的建议以及运行时文档,包括各个命令的用法示例。 Azure CLI 交互模式旨在为学习如何使用 Azure CLI 命令的用户提供理想的体验。 75 | 76 | ### 模式:一键复制 77 | 78 | 诸如于 Crates.io 上的软件包,以 [writing](https://crates.io/crates/writing) 为例: 79 | 80 | ![Writing 示例](/image/writing-copy.png) 81 | 82 | ### 模式:Playground 83 | 84 | > Playground 不仅旨在防止损坏测试机器,还应包含损坏网络上其他地方的数据和机器的任何潜在副作用。 85 | 86 | 关于这个定义的讨论: [Playground](https://english.stackexchange.com/questions/502671/what-does-playground-mean-in-an-informatics-educational-context) 87 | 88 | 示例:https://play.rust-lang.org/ 89 | 90 | 示例:https://pest.rs/ 91 | 92 | 示例:https://tree-sitter.github.io/tree-sitter/playground 93 | 94 | ![Tree Sitter 示例](/image/tree-sitter-sample.png) 95 | 96 | ### 模式:沙盒环境 97 | 98 | > 沙箱是一种测试环境,可将未经测试的代码更改和直接实验与生产环境或存储库隔离,在软件开发环境中,包括 Web 开发、自动化和修订控制。 99 | 100 | [https://jsfiddle.net](https://jsfiddle.net) 101 | 102 | ### 模式:DSL 构建编程体验 103 | 104 | 从体验层面来看,使用 DSL 105 | 106 | ### 模式:训战 107 | 108 | 如阿里云官方实验平台,提供真实云环境、精品实验项目、详细实验文档, 帮助你快速上手并掌握阿里云产品。 109 | 110 | 示例:[阿里云 - 云起实验室](https://developer.aliyun.com/adc/) 111 | 112 | 113 | ## 参考资料 114 | 115 | - 《[我是如何在谷歌做开发者用户体验的](https://github.com/xitu/gold-miner/blob/master/TODO/how-i-do-developer-ux-at-google.md)》 en:[How I do Developer UX at Google](https://medium.com/google-design/how-i-do-developer-ux-at-google-b21646c2c4df) 116 | -------------------------------------------------------------------------------- /docs/refs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 参考资料 4 | nav_order: 99 5 | --- 6 | 7 | # 参考资料 8 | {: .no_toc } 9 | 10 | --- 11 | 12 | [Awesome DX (Developer Experience)](https://github.com/workos-inc/awesome-developer-experience) 13 | 14 | ## Opensource libs for DX 15 | 16 | ### GitHub 17 | 18 | - [new-github-issue-url](https://github.com/sindresorhus/new-github-issue-url) Generate a URL for opening a new GitHub issue with prefilled title, body, and other fields 19 | 20 | 21 | ## Why Build 22 | 23 | ### Uber 24 | 25 | [Constructing a Seamless Flow: Meet Uber Engineering’s Developer Experience Team](https://eng.uber.com/developer-experience/) 26 | 27 | ### Slack 28 | 29 | [How We Design Our APIs at Slack](https://slack.engineering/how-we-design-our-apis-at-slack/) 30 | 31 | ### Netlify 32 | 33 | [Developer Experience at Netlify](https://www.netlify.com/blog/2021/01/06/developer-experience-at-netlify/) 34 | 35 | Developer Experience is a hybrid advocacy and engineering, our Objectives and Key Results (OKRs) are: 36 | 37 | - Qualified Signups 38 | - Active Developers (and the depth of use of the platform) 39 | - Engineering throughput: PRs merged, how many days an issue is open, and how many issues closed 40 | 41 | ### Swagger 42 | 43 | [API Developer Experience: Why it Matters, and How Documenting Your API with Swagger Can Help ](https://swagger.io/blog/api-documentation/api-documentation-and-developer-experience/) 44 | 45 | ### Microsoft 46 | 47 | [Developer Experience (DevEx)](https://microsoft.github.io/code-with-engineering-playbook/developer-experience/) 48 | 49 | ### Paypal 50 | 51 | [About PayPal Developer Documentation](https://developer.paypal.com/home) 52 | 53 | ### Mulesoft 54 | 55 | [10 ways to improve your API developer experience](https://blogs.mulesoft.com/digital-transformation/it-management/improve-api-developer-experience/) 56 | 57 | ### TinyMCE 58 | 59 | [What is developer experience?](https://www.tiny.cloud/blog/developer-experience/) 60 | 61 | ### IBM 62 | 63 | [IBM Developer Experience Center](https://www.ibm.com/brand/experience-guides/developer/) 64 | 65 | ## Refs 66 | 67 | 书籍: 68 | 69 | - 《活文档:与代码共同演进》 70 | - 《用户共创:社区赋能产品实战手册》 71 | 72 | 开发者体验: 73 | 74 | - 《[Understanding a Path](https://toddmoy.com/sendgrid-journeymap)》 75 | - 《[APIMatic](https://www.apimatic.io/developer-experience-portal/)》 构建 Developer Experience Portal SAAS 服务 76 | - 《[Backstage](https://github.com/backstage/backstage)》 开源 Developer Portal 构建工具 77 | - 《[How Mature Are You? A Developer Experience API Maturity Model](http://jennywanger.com/speaking/dx-maturity-model/)》 78 | - 《[工程師心中最軟的一塊:談前端開發者體驗(Developer Experience)](https://medium.com/@chiunhau/%E5%B7%A5%E7%A8%8B%E5%B8%AB%E5%BF%83%E4%B8%AD%E6%9C%80%E8%BB%9F%E7%9A%84%E4%B8%80%E5%A1%8A-%E8%AB%87%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC%E8%80%85%E9%AB%94%E9%A9%97-developer-experience-96e0cfacb316)》) 79 | 80 | 文档体验: 81 | 82 | * [API 库的文档体系支持:主流编程语言的文档设计](https://www.phodal.com/blog/api-ducumentation-design-dsl-base/) 83 | * [DocumenterTools](https://github.com/JuliaDocs/DocumenterTools.jl) 84 | * [swift-doc](https://github.com/SwiftDocOrg/swift-doc) 85 | * [Dokka](https://github.com/Kotlin/dokka/) 86 | * [librustdoc](https://github.com/rust-lang/rust/tree/master/src/librustdoc) 87 | -------------------------------------------------------------------------------- /samples/scripts/win/scoop.ps: -------------------------------------------------------------------------------- 1 | #Requires -Version 5 2 | 3 | # remote install: 4 | # Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh') 5 | $old_erroractionpreference = $erroractionpreference 6 | $erroractionpreference = 'stop' # quit if anything goes wrong 7 | 8 | if (($PSVersionTable.PSVersion.Major) -lt 5) { 9 | Write-Output "PowerShell 5 or later is required to run Scoop." 10 | Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell" 11 | break 12 | } 13 | 14 | # show notification to change execution policy: 15 | $allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass') 16 | if ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) { 17 | Write-Output "PowerShell requires an execution policy in [$($allowedExecutionPolicy -join ", ")] to run Scoop." 18 | Write-Output "For example, to set the execution policy to 'RemoteSigned' please run :" 19 | Write-Output "'Set-ExecutionPolicy RemoteSigned -scope CurrentUser'" 20 | break 21 | } 22 | 23 | if ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') { 24 | Write-Output "Scoop requires at least .NET Framework 4.5" 25 | Write-Output "Please download and install it first:" 26 | Write-Output "https://www.microsoft.com/net/download" 27 | break 28 | } 29 | 30 | # get core functions 31 | $core_url = 'https://raw.githubusercontent.com/lukesampson/scoop/master/lib/core.ps1' 32 | Write-Output 'Initializing...' 33 | Invoke-Expression (new-object net.webclient).downloadstring($core_url) 34 | 35 | # prep 36 | if (installed 'scoop') { 37 | write-host "Scoop is already installed. Run 'scoop update' to get the latest version." -f red 38 | # don't abort if invoked with iex that would close the PS session 39 | if ($myinvocation.mycommand.commandtype -eq 'Script') { return } else { exit 1 } 40 | } 41 | $dir = ensure (versiondir 'scoop' 'current') 42 | 43 | # download scoop zip 44 | $zipurl = 'https://github.com/lukesampson/scoop/archive/master.zip' 45 | $zipfile = "$dir\scoop.zip" 46 | Write-Output 'Downloading scoop...' 47 | dl $zipurl $zipfile 48 | 49 | Write-Output 'Extracting...' 50 | Add-Type -Assembly "System.IO.Compression.FileSystem" 51 | [IO.Compression.ZipFile]::ExtractToDirectory($zipfile, "$dir\_tmp") 52 | Copy-Item "$dir\_tmp\*master\*" $dir -Recurse -Force 53 | Remove-Item "$dir\_tmp", $zipfile -Recurse -Force 54 | 55 | Write-Output 'Creating shim...' 56 | shim "$dir\bin\scoop.ps1" $false 57 | 58 | # download main bucket 59 | $dir = "$scoopdir\buckets\main" 60 | $zipurl = 'https://github.com/ScoopInstaller/Main/archive/master.zip' 61 | $zipfile = "$dir\main-bucket.zip" 62 | Write-Output 'Downloading main bucket...' 63 | New-Item $dir -Type Directory -Force | Out-Null 64 | dl $zipurl $zipfile 65 | 66 | Write-Output 'Extracting...' 67 | [IO.Compression.ZipFile]::ExtractToDirectory($zipfile, "$dir\_tmp") 68 | Copy-Item "$dir\_tmp\*-master\*" $dir -Recurse -Force 69 | Remove-Item "$dir\_tmp", $zipfile -Recurse -Force 70 | 71 | ensure_robocopy_in_path 72 | ensure_scoop_in_path 73 | 74 | scoop config lastupdate ([System.DateTime]::Now.ToString('o')) 75 | success 'Scoop was installed successfully!' 76 | 77 | Write-Output "Type 'scoop help' for instructions." 78 | 79 | $erroractionpreference = $old_erroractionpreference # Reset $erroractionpreference to original value 80 | -------------------------------------------------------------------------------- /docs/maturity-model.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 度量模型设计 4 | nav_order: 2 5 | --- 6 | 7 | # 开发者体验:度量模型设计 8 | {: .no_toc } 9 | 10 | 基于与客户的协同价值出发,设计相应的度量模型,构建适应度函数持续演进。 11 | {: .fs-6 .fw-300 } 12 | 13 | ## 目录 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## 核心要素 22 | 23 | ### 基于协同价值 24 | 25 | 即以**帮助客户创造价值**为出发点,设计度量体系。 26 | 27 | ### 以过程改进为核心 28 | 29 | 采用在《演进式架构》中推荐的 "适应度函数",也是一个不错的方式。 30 | 31 | 又或者是采用[流畅度模型](https://dx.phodal.com/docs/dx-fluency-model.html) 来进行定义。 32 | 33 | ### 适当的不可量化指标 34 | 35 | 在度量型中引入过度的量化指标,将会导致团队采用指标驱动的方式。过度追求指标的达成率,由于指标与 KPI 间的关系,会导致团队铤而走险 —— 采用非常规手段,达成指标。 36 | 37 | ## 开发者体验度量模型示例 38 | 39 | ### 度量模型示例:API 度量 40 | 41 | 指标示例: 42 | 43 | [Nordic API](https://nordicapis.com/how-to-measure-the-success-of-developer-relations/):Weekly Active Tokens (WAT)、Time to First Hello World (TTFHW) 44 | 45 | API 开发者体验度量模型 - 如何改进: 46 | 47 | | 错误呈现 | 文档体验 | 易用性 | 交互式 | 触点 | 支持 | 48 | | --- | --- | --- | --- | --- | --- | 49 | | 错误描述 | 开发者门户 | 一键式安装 | 低配置/零配置 | 文章 | 问题反馈渠道 | 50 | | 报错即文档 | 发布日志 | 自动化版本迁移工具 | 声明式使用 | 演讲/分享 | 问题响应时间 | 51 | | 报错即修改建议 | 代码生成文档 | 自助式搭建 | 可交互文档 | Hackathon | 开发者即服务 | 52 | | | 版本迁移指南 | | 沙盒及产品环境 | | 开发者社区 | 53 | 54 | [API Maturity](https://docs.google.com/presentation/d/1Xefae9zccXm0m_jmIDqrZ8Tazni2eK7BiRxlfSiDJ-0/edit) 55 | 56 | ### 度量模型示例:APP 57 | 58 | | 指标 | 描述 | 59 | |---------|------------------------| 60 | | TTFHW | 开发者了解系统应用开源仓库,到可以修改定制编译成可以运行的 APP 所用的时间 61 | | TTFPA | 从应用版本发布到第一个使用该版本源码修改第三方应用所用的时间 | 62 | | Dev-NPS | 开发者将该API推荐给其它开发者的净推荐值 63 | 64 | ### API 指标示例: TTW 65 | 66 | TTW, Time to Wow 67 | 68 | 改进方式: 69 | 70 | - 编写特定使用场景的文档 71 | - 提供沙盒环境 72 | 73 | Deno 示例:https://dash.deno.com/playground/phodal 74 | 75 | refs: [Growth Hacking: Creating a Wow Moment](https://www.forentrepreneurs.com/time-to-wow/) 76 | 77 | ## 度量体系度量与优化 78 | 79 | 开发者体验优化相关的步骤: 80 | 81 | 1. 定义价值度量维度 82 | 2. 匹配用户旅程,细化维度至可量化的指标 83 | 3. 建立体验度量体系 84 | 4. 度量与诊断 85 | 5. 分析与体验提升 86 | 6. 构建体验管理机制 87 | 88 | 从模式上来说,它和用户体验是极为相似的,同样的也是出于指标度量的维度来考虑问题的。 89 | 90 | ### 用户体验管理 91 | 92 | 1. 理解现有业务 93 | - 理解现有业务。进行桌面研究并执行内部调研(业务专家、一线员工等),理解关键业务 94 | - 执行深度用户研究。执行用户研究,制定目标用户画像。 95 | - 建立客户全渠道体验旅程。定义各触点渠道职能及互通策略,找到影响用户体验的关键触点 96 | 2. 搭建体验度量体系 97 | - 明确客户北极星体验指标。确定与核心商业目标相匹配的客户体验北极星指标 98 | - 行业框架研究。分析行业相关框架,总结出关键体验维度 99 | - 顾客视角的体验需求分析。通过用户之声等方式分析体验需求,进行归因分析定位到问题核心 100 | - 定义体验度量维度。根据业务目标下的价值维度,结合痛点的反向提炼,确定体验评估的维度以及评判的依据 101 | - 匹配用户旅程, 细化维度至可量化的指标。将度量维度基于各旅程阶段进行匹配与拆分,形成量化的指标和具象的问题用于批量收集 102 | - 建立体验度量体系。将所有维度和量化指标整合形成客户体验度量体系 103 | 3. 体验诊断与优化 104 | - 体验度量与诊断。确定各指标的度量方法和数据采集方式,进行数据处理,得到产品的体验评分 105 | - 分析与体验提升。从度量结果进行各维度的体验诊断,并从用户体验五要素进行体验提升 106 | 4. 体验管理机制搭建 107 | - 体验管理机制搭建。初步建立体验管理体系,包括运营机制规划及责任体系规划。明确度量体系实施方式,定义参与角色与职责,产品各周期的目标,实现产品的提效跟踪 108 | 109 | ## 案例 110 | 111 | ### Spotify 112 | 113 | [How we measure Backstage success at Spotify](https://backstage.spotify.com/blog/measuring-backstage-success-at-spotify/) 114 | 115 | 两个主要指标: 116 | 117 | - 开发者的生产力是产出(即完成的工作数量)与投入(即努力、时间)的比率。这个指标很容易衡量,但它并不能说明全部问题。 118 | - 开发者的效率是通过每单位最佳产出所使用的资源(即时间、计算能力、人力资源)的数量来衡量。它说明了所有需要的投入,所以你可以看到团队是否做了足够多的正确工作。 119 | 120 | 指标: 121 | 122 | #### Time-to-10th PR 123 | 124 | 内部 125 | 126 | #### Proxy metrics 127 | 128 | | 指标 | 描述 | 129 | |----------|----------| 130 | | 我们的开发人员在说什么 | 每个季度我们都会通过 EngSat(Spotify 的官方工程满意度调查) 收到反馈。 | 131 | | 插件贡献 | 查看开发的插件总数,有多少插件贡献来自核心 Backstage 团队之外的团队,以及至少贡献了一个插件的团队总数。 | 132 | | 易于发现 | 换句话说,单个贡献者能够在几分钟内从卡住到解开。我们发现,对于不断发展的工程组织来说,维护一个在搜索的帮助下易于导航和探索的共享知识库非常重要。在 Spotify,我们使用搜索成功率、点击率和搜索结果相关性等指标。 | 133 | | 减少上下文切换 | 减少上下文切换可以帮助工程师留在“区域”中。我们测量工程师为了完成某项工作而必须与之交互的不同工具的数量(即推动更改,将其投入生产并验证它没有破坏任何东西)。 | 134 | | 传统指标 | 这些指标包括访问量(月活跃用户、日活跃用户等)和页面浏览量。大多数 Spotify 工程师每天都会访问 Backstage。 | 135 | 136 | #### 其它 137 | 138 | | 指标 | 描述 | 139 | |----------|----------| 140 | | 每个开发人员/天的合并 | 花在不同工具之间和寻找信息上的时间更少意味着有更多时间专注于交付代码。 如果您按领域(服务、网络、数据等)对贡献进行分类,则可以确定第二级瓶颈。 | 141 | | 部署到生产环境 | 上面指标的相近。工程师将更改推送到生产中的次数有多少? | 142 | | MTTR | 凭借对微服务生态系统中所有部分的明确所有权以及将所有工具集成到一个地方,Backstage 使团队能够更快地找到故障的根本原因并修复它们。 | 143 | | T 型工程师 | T 型工程师是能够为不同工程领域做出贡献的人。拥有 T 型人的团队的瓶颈更少,因此可以更一致地交付。由于工具和基础设施在域之间是一致的,并且信息集中可用,因此后台更容易成为 T 型。 | 144 | | 碎片化 | 软件模板有助于推动软件生态系统的标准化。通过测量不同软件组件之间的技术差异,可以了解生态系统中的整体碎片化情况。示例可能包括:框架版本、语言、部署方法和各种代码质量测量。 | 145 | -------------------------------------------------------------------------------- /docs/factor/error-handling.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 错误呈现 4 | parent: 开发者体验设计因子 5 | nav_order: 2 6 | --- 7 | 8 | # 开发者体验:错误呈现 9 | {: .no_toc } 10 | 11 | ## 目录 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | ## 定义 20 | 21 | 开始之前,先让我们转换一下视角,让我们思考一下开发者是如何处理错误的? 22 | 23 | ### 开发者如何处理错误? 24 | 25 | 经典玩笑场景下,一旦开发者接收到错误时,会通过以下的三种方式解决: 26 | 27 | - Copying and pasting from Stack Overflow。即从问答网站寻求解决方案 28 | - Googling the Error Message。搜索“错误信息” 29 | - Blaming the User。责备用户(PS:这是个玩笑) 30 | 31 | 所以,从模式来说,我们应该要能满足前两种场景的需求:告诉开发者足够多的错误信息,以让开发者能够寻求帮助。 32 | 33 | 诸如于,我们遇到:`Segmentation fault (core dumped)` 的错误时,它是由于内存不当操作造成的,如空指针、野指针的读写操作,数组越界访问,破坏常量等。遇到这一类的问题,**如果**返回的错误信息太少时,我们需要详细潜在的错误原因,并像大海捞针一样去解决。所以,debug 成了这时最好的解决手段之一。 34 | 35 | 基本的处理错误机制,可以告诉开发者,你可以尝试用 `--help` 来解决: 36 | 37 | ```log 38 | hint: (e.g., 'git pull ...') before pushing again. 39 | hint: See the 'Note about fast-forwards' in 'git push --help' for details. 40 | ``` 41 | 42 | 优秀的错误机制,则可以告诉开发者更多的信息,建议开发者使用类似的方式解决。 43 | 44 | ## 错误呈现原则 45 | 46 | ### 以人为本,信息友好 47 | 48 | 为人类而非机器设计、返回有意义的错误信息、不指责用户(PS:映射回 blaming the user) 49 | 50 | ### 构建学习体验 51 | 52 | 一步步指引,从错误码、到 GitHub issue 邮件组等。 53 | 54 | ### 一致性设计 55 | 56 | 如 API 错误码,在 Slack 的 API 设计原则,要求与行业、其他产品和 API 保持一致性。 57 | 58 | ### 避免错误产生 59 | 60 | 及时反馈、通过 IDE 消除、运行时验收、请求-确认 61 | 62 | ## 错误呈现模式 63 | 64 | 依据于上述的几个原则,我尝试性地整理了一些相关的模式,未来将更新在:[https://dx.phodal.com/](https://dx.phodal.com/) 上。 65 | 66 | ### 模式:为求助设计 67 | 68 | 从开发者的角度来思考问题,当开发者遇到问题时,他们会使用怎样的方式去解决。常见的几种方式是: 69 | 70 | 1. Google 搜索。我们是否就需要在错误信息里,提供足够多的信息。 71 | 2. 开源项目 Issue。我们是否直接提供相关的 issue 搜索链接功能? 72 | 3. 邮件形式。我们是否有工具能一次性收集相关的信息,诸如 log 文件等。 73 | 4. …… 74 | 75 | 针对于不同的情况,再提供优化手段,如在命令行提供高亮语法功能,以便于开发人员截图。 76 | 77 | ### 模式:自动提供潜在方案 78 | 79 | 当开发者遇到一些常见的问题后,接建议用户尝试某些方案来解决。这个已经被广泛应用在用户体验上,毕竟普通用户是更大的受众。 80 | 81 | ### 模式:错误码文档内置 82 | 83 | 针对于工具来说,由于安全因素,可能会存在一些离线用户,这时就需要提供诸如于 Rust 的错误码文档内置的方式来解决。 84 | 85 | ### 模式:自动 issue 管理 86 | 87 | 如上述的 Scoop,通过 GitHub Action 构建自动化机器人,以实施 issue 的自动化处理。 88 | 89 | ### 模式:开发者可贡献 90 | 91 | 如采用开源的形式,开发者可以针对错误内容进行贡献。 92 | 93 | ### 模式:FAQ 94 | 95 | 源自于 FAQ是英文Frequently Asked Questions的缩写,中文意思就是“经常问到的问题”,或者更通俗地叫做“常见问题解答”。 96 | 97 | 在开源 ArchGuard 中,笔者尝试了一种更为简单的方式,收集错误日志,在日志中获取关键词来提醒用户可以使用 FAQ: 98 | 99 | ```kotlin 100 | when { 101 | line.contains("Error: Unable to access jarfile ") -> { 102 | lines.add("下载 Scanner 可能出错,请尝试连接 VPN 下载。访问: https://archguard.org/faq 了解更多") 103 | } 104 | line.contains("Invalid or corrupt jarfile") -> { 105 | lines.add("jar 包不完整,请删除,并尝试连接 VPN 下载。访问: https://archguard.org/faq 了解更多") 106 | } 107 | line.contains("Fail to clone source with exitCode 128") -> { 108 | lines.add("Git Clone 出错,尝试根据: https://archguard.org/faq#git 进行配置") 109 | } 110 | line.contains("Fail to identify build tool for compile") -> { 111 | lines.add("暂时不支持的构建命令,建议选择 Java/Kotlin,再重新扫描。访问: https://archguard.org/faq#jvm 了解更多") 112 | } 113 | line.contains("Failed to scan xxxScanner") -> { 114 | lines.add("检查 Scanner 是否完整? 版本是否正常? 对应版本映射关系见: https://archguard.org/release/version-mapping") 115 | } 116 | } 117 | ``` 118 | 119 | ## 示例 120 | 121 | 这里主要介绍两个案例,一个是 Rust 语言,一个是 Scoop(Windows 下的命令行安装器)。 122 | 123 | ### Rust 语言示例 124 | 125 | 在我使用过的多数语言里,Rust 的编译器抛出的异常,大概是最为“友好的”,毕竟它是一个新世纪的语言。所以,在编写 Rust 的时候,我们相当于是“[编译器驱动开发](https://www.phodal.com/blog/rust-compile-driven-design/)”。 126 | 127 | 首先,先简单来编写一个缺少 `main` 函数的程序,然后运行它,就会报错: 128 | 129 | ```bash 130 | error[E0601]: `main` function not found in crate `lumos` 131 | --> src/main.rs:1:1 132 | | 133 | 1 | / pub mod store; 134 | ... | 135 | 10 | | 136 | 11 | | } 137 | | |_^ consider adding a `main` function to `src/main.rs` 138 | 139 | For more information about this error, try `rustc --explain E0601`. 140 | error: could not compile `lumos` due to previous error 141 | ``` 142 | 143 | 在这个错误里,告诉了我们: 144 | 145 | 1. 缺少 `main` 函数,可以考虑在 `src/main.rs` 文件的 11 行里,添加一个 `main` 函数 146 | 2. 更详细的详细可以执行 `rustc --explain E0601`,这里的 `E0601` 是一个错误码 147 | 148 | 而在 IDE 里,则可以 Rust 语言的插件,来直接添加 `main` 函数。 149 | 150 | 随后,从 Rust 的源码 `compiler/rustc_passes/src/entry.rs` 中的 `no_main_err` 函数里,我们可以发现更多的细节,这里就不展开了。在 Rust 的编译器里,设计了自己的错误码机制,使用错误码 + markdown 的方式来展示。在执行上述的 `explain` 参数之后, 可以读取相关的 markdown 文件,并展示相关的内容。所以,初步的总结下来,它包含了: 151 | 152 | - 错误码维护机制。 153 | - 使用易于维护的文件 —— markdown 154 | - 跨平台的终端优化。使用 less 显示 155 | - 可扩展的错误机制。易于与 IDE 集成 156 | 157 | ### Scoop 示例 158 | 159 | [Scoop](https://github.com/ScoopInstaller/Main) 是我先前从朋友圈看到的一个开源项目,它提供了一个自动化错误处理方案。诸如于,我们在安装工具、软件的时候,出现了异常。它会提供: 160 | 161 | 1. 创建相关 issue 的链接。一点击即会在 GitHub 上创建对应 issue 162 | 2. 自动化尝试重现错误。通过 GitHub Action 执行对应的脚本,来看是否会出错。 163 | 3. 自动化尝试给解决方案。尝试通过 Action 给出解决方案,如版本是否有问题,有问题的话,还可以尝试自动化修复。 164 | 165 | 示例:https://github.com/ScoopInstaller/Main/issues/2711 166 | 167 | 详细可以参见对应的 GitHub Actions: [https://github.com/shovel-org/GithubActions](https://github.com/shovel-org/GithubActions) 168 | 169 | ## 参考资料 170 | 171 | - [The Error Model](http://joeduffyblog.com/2016/02/07/the-error-model/) 一篇详细介绍编程语言错误模型的文章。 172 | 173 | -------------------------------------------------------------------------------- /docs/factor/usability.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 易用性设计 4 | parent: 开发者体验设计因子 5 | nav_order: 3 6 | --- 7 | 8 | # 开发者体验:易用性设计 9 | {: .no_toc } 10 | 11 | 12 | 定义:易用性是一种以使用者为中心的设计概念,易用性设计的重点在于让产品的设计能够符合使用者的习惯与需求。以互联网网站的设计为例,希望让使用者在浏览的过程中不会产生压力或感到挫折,并能让使用者在使用网站功能时,能用最少的努力发挥最大的效能。 13 | {: .fs-6 .fw-300 } 14 | 15 | ## 目录 16 | {: .no_toc .text-delta } 17 | 18 | 1. TOC 19 | {:toc} 20 | 21 | --- 22 | 23 | PS:请注意,这里的易用性设计是在开发者体验背景下,因此与用户体验中的易用性设计存在一定的差异。 24 | 25 | 我们可以关注于,最少的努力发挥最大的效能。 对于开发者体验而言,它是围绕于开发人员使用技术产品的各个阶段(生命周期),来提升相关的体验,诸如于:一键安装、自服务搭建、版本自动迁移等。 26 | 27 | 易用性专家 Jakob Nielsen 与计算机科学教授 Ben Shneiderman 曾经分别讨论系统可接受度(acceptability)的框架,其中指出易用性是“有用”(usefulness)的一部分,而且包含下列元素: 28 | 29 | - 可学习性 (Learnability) 30 | - 效率 (Efficiency) 31 | - 可记忆性(Memorability) 32 | - 很少出现严重错误 (Errors) 33 | - 满意度 (Satisfaction) 34 | 35 | 评估时,可以简化为:目标使用者感受到该操作界面的有效性(有达到目的)以及效率(使用时所需的工夫或时间) 36 | 37 | ## 易用性设计原则 38 | 39 | 在构建易用性时,它依赖于技术产品本身是**满足功能性需求**的。从模式上来说,它应该: 40 | 41 | 1. 从开发者旅程出发。从生命周期进行考量,以 42 | 2. 满足最小努力原则。 43 | 3. 引导开发者学习。 44 | 45 | ### 从开发者旅程出发 46 | 47 | 全生命周期考量,诸如于: 48 | 49 | - 评估。诸如于提供一个简单的同类产品分析 50 | - 安装与应用。 51 | - 文档与配置。 52 | - 失败恢复。如何去处理失败场景 53 | - 错误支持。 54 | - 卸载软件体验。 55 | 56 | ### 采用一致性的设计 57 | 58 | - 使用工业标准。如 API 能使用 OpenAPI 、RAML 来描述 API,并提供诸如于 Swagger 的交互式在线 API 文档。 59 | - 不同接口/API 的对齐。 60 | 61 | ### 满足最少努力原则 62 | 63 | 最小努力原则(Principle of Least Effort)最小努力原则是指人们在解决任何一个问题时,总是力图把所有可能付出的平均工作最小化。 64 | 65 | 对于开发者来说,当他们想尝试一个东西时,如果能用最少的步骤完成对应的工作时。针对于不同需求的开发者、 不同知识水平的开发者们, 能设计一个度量模型,以用于: 66 | 67 | - 度量开发者所需要的流程。 68 | - 度量开发者在每一个流程上的时间。 69 | 70 | 基于这两个维度,来构建相关的模型。 71 | 72 | ### 引导开发者学习 73 | 74 | 如在开源项目中的 README 中,一般都会分为多个部分: 75 | 76 | - 使用该项目的 Usage 77 | - 搭建该项目的开发指南 Setup 78 | - 为该项目贡献的 Contribute 79 | 80 | 而在文档中,则会包含更多的内容。 81 | 82 | ## 示例 83 | 84 | ### Just 85 | 86 | GitHub: [https://github.com/casey/just](https://github.com/casey/just) 87 | 88 | 针对于操作系统、包管理工具、包名、安装命令。 从构建的层面来考虑,这是一个非常复杂的工作,它需要通过自动化来解决。 除此,对应的还有 Sublime Text 相关的语法支持。 89 | 90 | 安装命令: 91 | 92 | ```bash 93 | curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to DEST 94 | ``` 95 | 96 | ### OpenSuSE 97 | 98 | ## 易用性设计模式 99 | 100 | - 谁是使用者,他们知道什么,以及他们能够学习什么? 101 | - 使用者想要/需要什么? 102 | - 使用者一般具备什么背景? 103 | - 使用者作业的环境 104 | - 机器应该负责做什么? 105 | 106 | 使用 CLI, 107 | 108 | ### 模式:1-Click 安装 109 | 110 | 示例:OpenSuSE 111 | 112 | ![OpenSuSE 1 Click 安装](/image/opensuse-1-click.png) 113 | 114 | Vim 示例:[https://software.opensuse.org/package/vim](https://software.opensuse.org/package/vim) 115 | 116 | ![IDEA Rust 插件示例](/image/idea-rust-click.png) 117 | 118 | 示例:Jetbrains IDE 119 | 120 | Rust 插件:[https://plugins.jetbrains.com/plugin/8182-rust](https://plugins.jetbrains.com/plugin/8182-rust) 121 | 122 | 示例:VSCode 123 | 124 | Rust 插件:[https://marketplace.visualstudio.com/items?itemName=rust-lang.rust](https://marketplace.visualstudio.com/items?itemName=rust-lang.rustv) 125 | 126 | 127 | ### 模式:一行命令安装 128 | 129 | 示例:macOS 开发者与 Homebrew 130 | 131 | ```bash 132 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 133 | ``` 134 | 135 | ``` 136 | brew install wget 137 | ``` 138 | 139 | 示例:应用安装 - 根据不同环境选择 140 | 141 | ```bash 142 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 143 | ``` 144 | 145 | 不幸的是 Windows 用户暂时只能通过 `rustup-init.exe` 安装。 146 | 147 | ### 模式:自助式搭建 148 | 149 | > Spring Initializr 是 Pivotal Web 服务提供的基于 Web 的工具。借助 Spring Initializr, 我们可以轻松生成 Spring Boot Project 的结构。 150 | 151 | 试用地址:[Spring Initializr](https://start.spring.io),如下图所示: 152 | 153 | ![Spring Initializr](image/spring-initalizr.png) 154 | 155 | ### 模式:自动化版本迁移工具 156 | 157 | 示例:Angular 版本更新,只要执行: 158 | 159 | ```bash 160 | ng update @angular/cli -- migrate-only production-by-default 161 | ``` 162 | 163 | 相应的机制比较简单,参见:[前端的自动化重构](https://www.phodal.com/blog/frontend-auto-refactor/) 164 | 165 | ### 模式:一键卸载 166 | 167 | Homebrew 示例: 168 | 169 | ```bash 170 | $ brew uninstall pandoc 171 | Uninstalling /usr/local/Cellar/pandoc/2.14.2... (10 files, 130.5MB) 172 | ``` 173 | 174 | 自动清理: 175 | 176 | ```bash 177 | ==> `brew cleanup` has not been run in the last 30 days, running now... 178 | Removing: /usr/local/Cellar/bdw-gc/8.0.4_2... (69 files, 1.6MB) 179 | Removing: /Users/phodal/Library/Caches/Homebrew/ca-certificates--2021-09-30... (114.6KB) 180 | Removing: /Users/phodal/Library/Caches/Homebrew/dart--2.14.2.zip... (196.2MB) 181 | Removing: /usr/local/Cellar/fribidi/1.0.10... (67 files, 669.2KB) 182 | Removing: /usr/local/Cellar/gd/2.3.2... (35 files, 1.4MB) 183 | Removing: /usr/local/Cellar/gd/2.3.3... (33 files, 1.4MB) 184 | Removing: /usr/local/Cellar/gdbm/1.20... (24 files, 825.0KB) 185 | Removing: /usr/local/Cellar/gdbm/1.21... (24 files, 903.2KB) 186 | Removing: /usr/local/Cellar/gmp/6.2.1... (21 files, 3.3MB) 187 | ``` 188 | 189 | 还可以手动清理: 190 | 191 | ```bash 192 | $ brew autoremove 193 | ``` 194 | 195 | ## 相关资料 196 | 197 | 相关文章: 198 | 199 | - [https://www.invespcro.com/blog/usability-design-for-a-better-user-experience/](https://www.invespcro.com/blog/usability-design-for-a-better-user-experience/) 200 | 201 | -------------------------------------------------------------------------------- /resources/dx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dx 2 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/engineering/document-engineering.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 文档工程 4 | parent: 工程化 5 | nav_order: 1 6 | --- 7 | 8 | # 开发者体验:文档工程 9 | {: .no_toc } 10 | 11 | 文档工程用于帮助我们指定、设计、和实施计算机技术相关的文档,如产品特定规格或详细说明,以及创建和使用它们的流程。它的特点是以“文档为中心”,以帮助我们构思和理解如何支撑其所在的商业模式。 12 | {: .fs-6 .fw-300 } 13 | 14 | ## 目录 15 | {: .no_toc .text-delta } 16 | 17 | 1. TOC 18 | {:toc} 19 | 20 | --- 21 | 22 | ## 定义文档工程体验 23 | 24 | ### 文档编写的痛点 25 | 26 | 在编写文档时,常见的一些痛点有: 27 | 28 | * 文档代码不同步。即文档的 API 变化可能落后于代码,导致 API 与文档出现不一致。 29 | * 频繁的 API 变更。API 变更时,文档需要手动进行更新,不能自动化同步。 30 | * 概念不统一。对于同一个概念,文档的不同地方描述不一致。 31 | * 重复的文档块。文档需要重复引用某一部分的文档,不能像代码一样引用。 32 | * 代码无法运行。按照文档的步骤下来编写的代码、复制的代码,是不能运行的。 33 | 34 | 还有一些问题,可能是难以通过自动化的方式来解决的,诸如于: 35 | 36 | * 风格不一致。不同的人编写文档的风格不一致,可能需要类似于 code review 的 document review 的方式来解决。 37 | * 语法不准确。使用的是不同形式的中文描述方式。(PS:难道是回去再上上语文课) 38 | 39 | 于是,我们可以尝试性地借用业内一些通用的方式来解决问题。 40 | 41 | ### 定义文档工程体验 42 | 43 | 再回到标题上,让我对标题做一些解释: 44 | 45 | 文档工程用于帮助我们指定、设计、和实施计算机技术相关的文档,如产品特定规格或详细说明,以及创建和使用它们的流程。它的特点是以“文档为中心”,以帮助我们构思和理解如何支撑其所在的商业模式。 46 | 47 | 而文档工程体验设计,则是围绕于构建和设计文档的过程进行的体验改善。即目标用户是**编写文档的工程师**,改善其编写文档的体验。并针对于文档的目标用户,改善他们在文档方面的体验(PS:这部分不是本文讨论的重点)。 48 | 49 | 文档体验是开发者体验的一个关键性因素,用于指导新手快速上手技术产品。在我们设计各类[开发者体验指标](https://www.phodal.com/blog/developer-experience/)时,一个非常重要的指标就是 TTFHW(Time to first hello world),即从零到第一个 Hello World 需要的时间,而这个指标是严重依赖于开发者文档。 50 | 51 | 因此,本文的意图是从文档开发者的体验出发,以重新塑造整体的开发者体验。 52 | 53 | 既然文档工程体验归属于开发者体验,它面向的是开发者提供更好体验,对于自身而言,更需要非凡的体验。只有以此为出发点,才能减小文档团队人员的流 动,毕竟不是程序员想写文档,也不是程序员都想看文档的。 54 | 55 | ## 文档工程原则 56 | 57 | 作为一个程序员,我设计和参与过两个文档系统(Ledge 便是其中之一),它们之间有各自不同的思想。再结合我对于多个语言的文档体系的分析和设计,我觉得一个优秀的文档工程应该是满足这样的条件: 58 | 59 | * 编辑-发布分离。架构设计上,编辑态和发布是完成分离的,各自可以使用不同的语言和技术来实现,诸如于使用 markdown 编写,但是输出可以是丰富多彩的形式。 60 | * 过程自动化。特别好理解,它应该能实现快速的自动化发布,以代码开发保持一致和构建频繁。 61 | * 文档形式化。XML 是上一个世代比较流行的文档形式化格式,从我的研究情况来看,定制化的 markdown 是这一个世代流行的方式。形式化的输入,它便意味着在输出上会有多种形式,如 markdown 结合 Pandoc 可以转换为 PDF、Word、HTML 等一系列的格式。 62 | * 开放式协作。文档面向使用者开放修改,使用者可以通过 pull request 的形式来对文档进行改进,并可以针对文档提出建议。 63 | * 版本化管理。在编辑态上,所有的历史修改都是可见的,可以回溯所有变更;在发布上,可以看到关键的历史版本,以适应不同人的需求。一种特别简单的示例,就是使用 git 来版本。 64 | 65 | 对于多数语言、框架的文档系统来说,它们都是面向特定领域定制的,以带来更好的编写体验和一致性。所以,从某种意义上来说,定制化开发也是非常重要的一点 —— 即我们往往难以获得一个通用的解决方案。 66 | 67 | ### 面向场景设计呈现 68 | 69 | 除了上述的要素之后,我们还需要提及一个非常重要的因素,即针对于不同的场景,应该要有不同的文档呈现形式。诸如于: 70 | 71 | - 一页文档。诸如于搭建指南,在项目初始化的时候,可以在一个网页内快速完成,而不需要进页面挑战。 72 | - 模块化文档。诸如于面向 API / SDK 场景下,以让每**部分** API 都可以独立访问,也能通过搜索引擎优化。 73 | - 可交互文档。诸如于编程语言 REPL、组件库场景下,让用户可以零成本学习和试用技术产品。 74 | - …… 75 | 76 | 尽管文档很重要,但是请不要忘了,我们的初衷是带来更好的用户体验。 77 | 78 | ## 文档编程模式 79 | 80 | 为了更方便于讨论,我尝试性对所接触的文章进行了模式上的分类,以及它们所适用的场景: 81 | 82 | | 模式 | 场景 | 优点 | 缺点 | 一致性机制 | 原则 | 83 | |---------------------|----------------------------------------------|------------------------------|-----------------------|--------------------------|----------------------| 84 | | 文档代码化 | 需要协作的在线文档、侧重于开发指南编写 | 提升社区参与度、灵活扩展系统 | 实现成本略高 | | 以领域特定语言为核心 | 85 | | 文档测试 | SDK、API 等功能性描述文档 | 文档代码强一致性 | 实现成本略高 | 编译时保证 | 注释设计 | 86 | | 可执行文档 | 文档为核心,代码为辅助、Demo 编写 | 轻量、文档一致性 | 大型工程比较难 handle | 编译时保证 | 文档优先 | 87 | | 灵活代码块 | 代码为核心,文档为辅助、Demo 编写 | 动态响应代码变化 | 注释在 demo 代码中 | 代码正确编译,则文档正常 | 代码优先 | 88 | | 测试即文档 | 业务系统开发,文档为核心,确保文档和代码一致 | 文档代码强一致性 | 实现成本略高 | 持续集成时保证 | 文档驱动开发 | 89 | | 文档同构 | 文档驱动开发、业务系统开发 | 文档代码强一致性 | 实现成本高 | 双向确保代码-文档一致 | 双向绑定 | 90 | 91 | 在文档工程这个上下文下,其详细的定义如下。 92 | 93 | ### 基础模式:文档代码化 94 | 95 | 定义:文档代码化是将文档以类代码的领域特定语言的方式编写,并借鉴软件开发的方式(如源码管理、部署)进行管理。 它可以借助于特定的工具进行编辑、预览、查看,又或者是通过专属的系统部署到服务器上。 96 | 97 | 示例:各类的开源软件文档、Rust、Julia 等编程语言的文档系统 98 | 99 | 文档代码化其中是现代化的文档工程里的基石。现有的开源软件文档体系,都是以 markdown + 开源的形式而展开的,所有的人都可以在这之上进行协作。除此,基于不同文档的需求,它们会在 DSL 的基础上进行扩展,如新一代的编程语言的文档系统。它们的方式是这样的: 100 | 101 | 1. 为扩展设计:文档 DSL 102 | 2. 为准确性设计:文档测试 103 | 3. 构建开放协作平台:开放协作 104 | 105 | 更详细可以参考:《[API 库的文档体系支持:主流编程语言的文档设计](https://www.phodal.com/blog/api-ducumentation-design-dsl-base/)》 106 | 107 | ### 文档测试:一致性 108 | 109 | 定义:文档测试的原始定义是,一种测试方式,用于确保系统的文档与系统的功能相匹配。在文档工程的上下文里, 我们定义为针对于文档中的代码验证其有效性,以及其结果的准确性。 110 | 111 | 示例:Rustdoc、DocumenterTools.jl 112 | 113 | 如在 Rustdoc 中**代码中的注释中的代码**会被提取出来,它会被独立编译,确保代码是可运行的。 114 | 115 | ``` 116 | ```rust 117 | assert_eq!(2 + 2, 4); 118 | ``` 119 | ``` 120 | 121 | 如下是 Rustdoc 中将上述的代码生成测试代码的测试用例: 122 | 123 | ```rust 124 | fn main() { 125 | #[allow(non_snake_case)] 126 | fn _doctest_main__some_unique_name() { 127 | assert_eq!(2 + 2, 4); 128 | } 129 | _doctest_main__some_unique_name() 130 | } 131 | ``` 132 | 133 | 一旦上述的代码编译并运行通过,则说明文档中的注释是正确的。具体的步骤如下: 134 | 135 | - 解析 markdown,寻找 Rust 语言的语法块(如果没有标注语言类型,默认是 Rust) 136 | - 根据语法块,做一些简单的处理,生成可编译的代码 137 | - 编译上述的测试代码 (如果编译失败,则说明测试失败) 138 | - 运行这些测试 or 文档 139 | 140 | 详细见 Rustdoc 相关源码:[librustdoc](https://github.com/rust-lang/rust/tree/master/src/librustdoc) 141 | 142 | ### 可执行文档 143 | 144 | 定义:可执行文档是指文档本身已经是代码化的结果,它像代码一样可以执行, 并且可以将结果动态地与文档结合在一起。 145 | 146 | 示例:Julia 的 DocumenterTools、R Markdown、Exemd 147 | 148 | 在 R Markdown 里,它可以结合文档与 R 语言源码,可以进行动态的渲染。我们可以在 markdown 文件中,“随意”地调用 R 中的函数,并动态地嵌入数据、代码、计算结果、可视化图表等到输出的文档中。在结合了 Pandoc 之后,文档可以输出到所有主流的文档格式。如下的示例: 149 | 150 | ```markdown 151 | ```{r fig.show='animate', dev='jpeg', ffmpeg.format='gif'} 152 | for (i in 1:10) plot(runif(100), ylim = c(0, 1)) # for example 153 | ``` 154 | ``` 155 | 156 | 它将会运行并在文档中嵌入运行的结果。 157 | 158 | ### 灵活代码块 159 | 160 | 定义:灵活代码块是指文档可以通过 DSL 动态引用源码中的内容。 161 | 162 | 示例:DocumenterTools.jl、Forming 里的 Writing 163 | 164 | 以我设计的 Writing 为例,它可以动态解析 markdown 中设计的 Writing DSL,并从代码中读取对应的代码块。如下是 Writing 的示例: 165 | 166 | ``` 167 | // doc-code: file("src/lib.rs").line()[2, 5] 168 | // 读取 "src/lib.rs" 文件的第 2 到第 5 行 169 | // doc-section: file("src/lib.rs").section("section1") 170 | // 读取 "src/lib.rs" 文件中的 section1 相关的代码块 171 | ``` 172 | 173 | 通过简单的自定义函数,将文档与代码有机结合到一起。只要应用编译运行成功,那么文档本身也是正确的。 174 | 175 | ### 测试即文档 176 | 177 | 定义:测试即文档即指测试用例以文档的形式编写,文档本身就是测试用例。 178 | 179 | 示例:Cucumber 180 | 181 | 相信大家都很“熟悉”了,主要是在自动化测试中使用非常广泛。Cucumber 示例如下: 182 | 183 | ```cucumber 184 | # language: zh-CN 185 | @math 186 | 功能: 加法 187 | 加法计算器的验证用例 188 | 189 | @sanity 190 | 场景: 两个数相加 191 | 假如我已经在计算器里输入6 192 | 而且我已经在计算器里输入7 193 | 当我按"相加"按钮 194 | 那么我应该在屏幕上看到的结果是13 195 | ``` 196 | 197 | 测试是文档,文档即是测试。 198 | 199 | ### 文档同构(概念) 200 | 201 | 文档同构。[文档同构](https://www.phodal.com/blog/isomorphism-document/) 是一种将代码与文档保持一致的技术理念,它能读取格式化的文档,并将文档自动加入到代码中,如以注释的形式或者是只在 IDE 呈现;同时,还能将读取代码中的文档,自动更新到文档中,或是对文档进行测试和差异对比。 202 | -------------------------------------------------------------------------------- /docs/define-dx.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: 定义开发者体验 4 | nav_order: 1 5 | description: "开发者体验是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。关注的内容为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。" 6 | permalink: / 7 | --- 8 | 9 | # 开发者体验总览 10 | {: .fs-9 .no_toc .text-delta } 11 | 12 | ![Logo](/resources/dx.svg) 13 | 14 | 开发者体验是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。关注的内容为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。 15 | {: .fs-6 .fw-300 } 16 | 17 | 18 | 欢迎通过以 GitHub issue 的方式来讨论文章的内容。 19 | {: .fs-4 .fw-300 } 20 | 21 | [Get started now](#定义开发者体验){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } [View it on GitHub](https://github.com/phodal/dx){: .btn .fs-5 .mb-4 .mb-md-0 } 22 | 23 | --- 24 | 25 | ## 目录 26 | {: .no_toc .text-delta } 27 | 28 | 1. TOC 29 | {:toc} 30 | 31 | ## 谁应该关注开发者体验? 32 | 33 | 理想情况下,所有的开发者都应该关注开发者体验。 34 | 35 | ### 面向开发者 36 | 37 | 只是呢,通常情况下,只有在对外(外部团队 + 外部组织)的情况下,才会关注于开发者。 38 | 39 | 从对外的层面来考虑,这个“谁“ 应该是: 40 | 41 | - 开源项目的利益相关者(如开发者) 42 | - 内部平台的利益相关者(如开发者) 43 | - 面向开发者的各类 `*aaS` 服务的利益相关者(如开发团队) 44 | - 技术布道师、运营等开发者关系相关的角色 45 | 46 | 原先我们假定的是:**开发团队应该关注于开发者体验**。然而,从国内的实践情况来看,所有与这个平台、工具、SDK 等相关的利益相关者们,都应该关注于开发者体验。尤其是,相关的最高管理者,他们决定了相关项目的 KPI/OKR。 47 | 48 | ### 对内体验 49 | 50 | 每当我们谈论到开发者体验的时候,围绕着的往往都是对外产品的开发者体验。事实上,从另外一个角度来说,在互联网高度发达的今天,一个内网的平台设计不当,都会影响到公司的对外影响力。所以,从这个角度来考虑,我(@phodal)觉得: 51 | 52 | > 从技术负责人(如 Tech Lead)到 CTO 的各类技术决策者,都应该重视内部的开发者体验。 53 | 54 | 什么是内部开发者体验?这个问题可以回到开发者如何使用各种内部的工具聊起。 55 | 56 | ## 开发者在体验什么? 57 | 58 | 什么是开发者体验?那不就是让开发人员觉得爽吗。 59 | 60 | 什么才叫爽呢?来一起看几个例子。 61 | 62 | ### 安装谁更简单? 63 | 64 | 先让我们来看一个软件安装的例子: 65 | 66 | 1. 一键命令行安装。如 `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` 67 | 2. 按步就班下载和使用。如打开官网,找到对应的平台(操作系统)下载可执行文件,打开安装软件,执行安装步骤,一步步进行。 68 | 69 | 这是 Rust 官方提供的两种不同的 Rust 安装方式。 70 | 71 | ### 谁的引入更简单? 72 | 73 | 再看个使用软件的例子: 74 | 75 | 1. README 即起步文档,添加依赖,复制示例代码。 76 | 2. README 里没有起步,需要跳转到文档网站,添加依赖,添加构建脚本,复制示例代码。 77 | 78 | 两者都是 Rust 里的语法解析器,前者是拥有 2.2k stars 的 pest,后者则是拥有 1.7k stars 的 lalrpop。 79 | 80 | 这里,先不讨论两个不同的库在开发上的区别,从直观感受来说,我们就可以直接区别。 81 | 82 | ### 起步谁更快? 83 | 84 | 来两看两个上手指南 85 | 86 | 1. 学习新的语言,学习新的语法,入门文档很长(start),还需要编译,文档不易访问。 87 | 2. 通过 script 标签引入 ,直接开始编写代码。 88 | 89 | 你觉得新手会选择 Vue 还是 Angular? 90 | 91 | ## 定义开发者体验 92 | 93 | 这样一来,我们对于开发者体验都能有一个简单的体会。也就能理解什么是开发者体验了。 94 | 95 | ### 什么是开发者体验? 96 | 97 | 开发者体验是指开发人员在使用软件、工具、代码等的体验,它与用户体验类似,只是对象是软件开发人员。将之与国际进行对应,便是开发人员对于针对使用或期望使用的产品、系统或者服务的认知印象和回应。有所不同的是,用户关注的内容变为库,SDK,文档,框架,开源解决方案,通用工具,API 等的开发人员的体验。 98 | 99 | 国内开始关注开发者体验主要有以下的一些原因: 100 | 101 | - 外部开源。即越来越多的开发人员开源了自己的软件,迫切地需要提升软件的体验。 102 | - 内部开源。在内部通过开源来进行部门之间的协同效应。 103 | - 内部基础设施共享。让内部的开发人员使用内部的基础设施时,获得比外部开源软件更好的体验,提升其在内部的口碑。 104 | - API 经济。对外提供 API,需要与更多的业内组织竞争。 105 | - 云原生。云厂商面向开发者提供服务。 106 | - 生态构建。面向合作伙伴需要提供更便捷的接入服务。 107 | - SDK 等工具提供方。 108 | - …… 109 | 110 | 原因多种多样,但是其核心都是想通过开发者体验来提升竞争力。 111 | 112 | ## 开发者体验六要素 113 | 114 | 再次强调一下:关注开发者体验之前,应该确保核心功能:完善 + 稳定。即你需要提供可用、稳定的特性,再去提升总体的用户体验。除非,对于你的系统来说,你在一开始就不缺用户。 115 | 116 | 从我在开发社区的使用经验、网上了解的相关信息以及与一些专业人士的沟通中,我认为以下几点是进行 DX 时要考虑的要素: 117 | 118 | - 错误呈现。即出错时,以何种方式来呈现。 119 | - 文档体验。 120 | - 易用性。如何简化开发。 121 | - 交互式。降开发者学习成本。 122 | - 触点。让更多的人知道这个软件。 123 | - 支持。 124 | 125 | 太长不看版: 126 | 127 | | 错误呈现 | 文档体验 | 易用性 | 交互式 | 触点 | 支持 | 128 | |-----|-----|-----|-----|-----|-----| 129 | | 错误描述 | 开发者门户 | 一键式安装 | 低配置/零配置 | 文章 | 问题反馈渠道 | 130 | | 报错即文档 | 发布日志 | 自动化版本迁移工具 | 声明式使用 | 演讲/分享 | 问题响应时间 | 131 | | 报错即修改建议 | 代码生成文档 | 自助式搭建 | 可交互文档 | Hackathon | 开发者即服务 | 132 | | | 版本迁移指南 | | 沙盒及产品环境 | | 开发者社区 | 133 | 134 | 可能还有其它内容,如果有的话,欢迎与我探讨。 135 | 136 | ### 错误呈现 137 | 138 | PS:出于安全原因,有些内容不适合在外部暴露,因此并不建议所有的东西都应该对外呈现。 139 | 140 | 对于开发者体验来说,错误呈现就是让开发者有办法快速定位问题和修改问题。常见的一些可优化的部分是: 141 | 142 | - 错误描述。即软件的出错以合理的方式描述出来,可能是一段文字,一个错误码等。 143 | - 报错即网站。即复杂的出错场景里,可以通过可访问的链接来告诉开发者如何修改问题。 144 | - 报错即修改建议。即出错时,告诉开发人员可以尝试以下的方式来修改问题。 145 | 146 | 这里,我们可以看一个 Rust 出错的示例: 147 | 148 | ```bash 149 | error[E0425]: cannot find value `RE_ACCESS` in this scope 150 | ... 151 | 177 | if let Some(capts) = RE_ACCESS.captures(line) { 152 | | ^^^^^^^^^ help: a static with a similar name exists: `RE_CLASS` 153 | ``` 154 | 155 | 虽然,这个提醒不一定那么智能。但是,它在帮助定位问题的同时,还在一定程度上来解决一点问题。 156 | 157 | ### 文档体验 158 | 159 | 文档是一种传统知识的载体。优秀的程序员即能通过代码来传递知识,还会通过文档来表达自己。 160 | 161 | > 软件编程即理论建立与传递。 —— Peter Naur 162 | 163 | 对于文档来说,依旧的,我们还会关注于: 164 | 165 | - 开发者门户。作为整个知识体系的承载 166 | - 发布日志/更新日志。即 CHANGELOG.md 167 | - 代码生成文档。 168 | - 版本迁移指南。针对于出现 Breaking Change 的场景,需要详细给出每一部分的迁移示例。 169 | - 测试用例。在开源世界里,除了文档,还有测试用例可以读懂一些特别的用法。 170 | 171 | 开发者门户是一个复杂的话题,后面我们会再介绍。如下是 D3.js 6.0 的 [migration guide](https://observablehq.com/@d3/d3v6-migration-guide) 示例: 172 | 173 | ``` 174 | For example, 175 | 176 | selection.on("mousemove", function(d) { 177 | … do something with d3.event and d… 178 | }) 179 | 180 | becomes: 181 | 182 | selection.on("mousemove", function(event, d) { 183 | … do something with event and d … 184 | }) 185 | ``` 186 | 187 | 我们还会在易用性里提供更好的方式。 188 | 189 | ### 易用性 190 | 191 | 易用性本身是非常难度量的, 192 | 193 | - 一键式安装。可以是一行命令,也可以是 `1-click` 之类的。 194 | - 自动化版本迁移工具。即针对于版本升级时的 API 修改,可以提供自动修改工具,典型的如 Angular CLI 来升级 Angular。 195 | - 自助式搭建。如 Spring Initializr,可以让开发人员选择合适的服务和工具,而后生成项目等。 196 | 197 | 在高中时期,我尝试了市面上的一个又一个 GNU/Linux 改行版。如 Ubuntu 和 OpenSuSE 会向我们寄一些安装 CD/DVD,用来帮助新手快速安装 GNU/Linux 操作系统。在我体验了 OpenSuSE 之后,我被它文档上的 `Install software via 1-click` 所惊艳(当时年轻)。 198 | 199 | 如我们找到了中文输入法:Fcitx ([https://zh.opensuse.org/Fcitx](https://zh.opensuse.org/Fcitx)),文档上包含了各种的介绍、如何安装,以及一键安装 —— 现今的 macOS 和 Windows 似乎也没有这样的功能。 200 | 201 | ### 交互式 202 | 203 | 在各类 PAAS 服务流行的今天,交互式的 API 体验已经成为了一个主流的方式。进细一步地,我们可以 204 | 205 | - 低配置/零配置。人们为了灵活性而引入的各种配置本身是反人性的,大部分的配置应该是内置的,不应该由普通的开发者来配置。 206 | - 声明式使用。即 API 应该尽可能简化,只需要简单的声明即可使用。 207 | - 可交互文档。如 Swagger 可以在线尝试 API。 208 | - 沙盒及产品环境。即提供一个在线的类可编程环境。 209 | 210 | 最典型的一些例子就是现代化的编程语言里提供的 Playground,如 Golang 和 Rust 都提供了 Playground。它可以让开发人员查看文档,同时运行应用的代码,还能修改代码并运行。 211 | 212 | ### 触点 213 | 214 | 触点是指如何去提供加深与开发者的关系。虽然有一个更专业的名称是『开发者关系』,但是它有一个更复杂的模型。这里就简化为触点,意思就是如何与开发者进行接触的点: 215 | 216 | - 内容/文章 217 | - 演讲/分享 218 | - Hackathon/黑客松 219 | - 论坛。只在形成一定规模时才考虑 220 | 221 | 从某种意义上来说,它是叫推广,但是呢,从个人的角度来看,触点这个用法比推广好得多 —— 触点可以让你意识到:现有的机制是不是无法连接到更多的开发者。 222 | 223 | ### 支持 224 | 225 | 这部分就是对于开发人员的支持了,每个人也都非常熟悉: 226 | 227 | - 问题反馈渠道 228 | - 问题反馈与响应 229 | - 开发者即服务。源自我之前的一篇文章《[开发者即服务](https://www.phodal.com/blog/developer-as-a-services/)》,意指开发者一对于指导问题。 230 | - 开发者社区。 231 | 232 | 再说说开发者即服务,是(Developer-as-a-Service)的,亦可称为 “按需即用的开发者”。即当开发者使用某一工具、库,遇到任何相关的问题,可以随时找开发者为我们提供服务。哪怕是使用者使用了我们的 A 框架,但是遇到 B 框架有问题,他/她们也会觉得 A 框架有问题 ——因为 A 框架的开发者们是一种服务,一种开箱即用的服务。 233 | 234 | ## 度量开发者体验 235 | 236 | 考虑到度量开发者体验是一个复杂的问题,这里只简单列一下我所认为的两个易于度量维度: 237 | 238 | - 首次运行所需时间 239 | - 文档触达速度 240 | 241 | 其它的则是**常规度量指标**,以及对于开发者门户的度量。 242 | 243 | ### 首次运行所需时间 244 | 245 | > 首次运行所需时间即开发人员从接触到创建第一个可运行的应用或者测试等所需的时间。 246 | 247 | 它可以让我们关注于: 248 | 249 | - 设计最小的使用步骤 250 | - 提供更好的 Get Started 设计 251 | - 提供有限次数限制的 Demo Token。 252 | - 更快的体验速度。对于下载服务,采用 CDN,适用于国内网络的下载机制。 253 | - …… 254 | 255 | 这个指标体系可以帮助我们,理解开发人员在过程中遇到的过程痛苦。 256 | 257 | ### 文档触达速度 258 | 259 | > 文档触达速度,即从修改完文档到所有开发人员可见所需的时间。 260 | 261 | 我们最常见的一个例子是 GitHub Pages,当我们更新完文档时,它可以实现分钟级的部署。这个维度的指标的目的主要是: 262 | 263 | - 有意识地加快文档更新过程 264 | - 让经常出错的问题可以快速更新 265 | - 提醒开发人员修复了哪些问题 266 | 267 | 它可以让问题的反馈快起来。 268 | 269 | ### 常规度量指标 270 | 271 | 接下来,就是我们常见的一些指标,受限于框架和 SDK 等的不同会有些变化 ,典型的如: 272 | 273 | - API 响应时间 274 | - API 出错率 275 | - API(可选),『每周活跃调用者数』、『API 响应时长』 276 | 277 | 对于开发人员,可以展示 **API 情况的 dashboard**,用于展示 API 服务的当前状况,如 GitHub Status。这样一来,就不需要再回答 API 是否挂了的问题。 278 | 279 | ### 开发者门户成熟度模型 280 | 281 | 在编写这篇文章的过程中,刚好看到了一篇对于门户的度量模型,《[How Mature Are You? A Developer Experience API Maturity Model](http://jennywanger.com/speaking/dx-maturity-model/)》简单地翻译了一些**国内适用**的部分(详细见原文): 282 | 283 | | Level 1 | Level 2 | Level 3 | Level 4 | 284 | |-------|-------|-------|-------| 285 | | 封闭的系统 | 门户是自服务的,但是不连贯的 | 完全自服务 | 可个性化的统一门户 | 286 | | 文档缺乏成功调用 API 的信息 | 1 天内可以调用 API | 10 分钟内可以调用 API | 分钟级 API 调用 | 287 | | API 和功能没有正确对应 | 快速开始指南、修改日志和教程 | 提供沙盒和生产环境 | 认证流程 | 288 | | 响应问题需要一周的时间 | 交互式文档 | 代码示例和库 | | 289 | | | 问题在 2 ~ 3 天内被回答 | 24 小时回答问题 | | 290 | 291 | ## 如何提升开发者体验 292 | 293 | 从个人的角度来看,提升开发者体验是一个相对麻烦的过程。除了上述的两个指标之外,我觉得还有两种方式可以帮助提升开发者体验: 294 | 295 | - 竞品对比。 296 | - 新用户引导流程。 297 | 298 | 嗯,基本上和用户体验是类似的。 299 | 300 | ### 竞品对比:看齐 301 | 302 | 竞品对比,主要是通过与类似产品的对比,让自己与业内保持一致的水准。 303 | 304 | 如下是 Rust 和 Golang 的对比(只选取部分,出自于《[Android Go vs. Rust: Features, Similarities & Differences](https://www.konstantinfo.com/blog/android-go-vs-rust-features-similarities-differences/)》) 305 | 306 | | | Rust | Golang | 307 | | --- | --- | --- | 308 | | 性能 | 高效能,比Swift语言快一点 | GO和Java的性能不及Rust | 309 | | 方便性 | 零成本运行时抽象,非常容易且安全地用于内存等处理 | 使用和管理容易 | 310 | | 易于学习 | 需要花时间来学习和掌握用于内存管理的语言抽象 | 有完整的开发文档,大量的用户社区 | 311 | | 发展速度 | 与Go程序相比,RUST的编译时间更长 | 既简单又快速 | 312 | | 并发与并行 | RUST没有具体的并发或异步操作。 | Go 具有协程(轻量级线程)和通道(go例程的通信机制),可简化应用程序的创建过程。具有本机测试机制,可在运行时发出警告。 | 313 | 314 | 通过这样的对比,从其它产品学习。 315 | 316 | ### 新用户引导流程 317 | 318 | 站在开发人员的角度出发,梳理新用户从使用过程中的痛点问题。一个详细的例子可以见:[Onboarding journey](https://toddmoy.com/sendgrid-journeymap) 319 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public 379 | licenses. Notwithstanding, Creative Commons may elect to apply one of 380 | its public licenses to material it publishes and in those instances 381 | will be considered the “Licensor.” The text of the Creative Commons 382 | public licenses is dedicated to the public domain under the CC0 Public 383 | Domain Dedication. Except for the limited purpose of indicating that 384 | material is shared under a Creative Commons public license or as 385 | otherwise permitted by the Creative Commons policies published at 386 | creativecommons.org/policies, Creative Commons does not authorize the 387 | use of the trademark "Creative Commons" or any other trademark or logo 388 | of Creative Commons without its prior written consent including, 389 | without limitation, in connection with any unauthorized modifications 390 | to any of its public licenses or any other arrangements, 391 | understandings, or agreements concerning use of licensed material. For 392 | the avoidance of doubt, this paragraph does not form part of the 393 | public licenses. 394 | 395 | Creative Commons may be contacted at creativecommons.org. 396 | 397 | -------------------------------------------------------------------------------- /samples/scripts/rustup-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck shell=dash 3 | 4 | # This is just a little script that can be downloaded from the internet to 5 | # install rustup. It just does platform detection, downloads the installer 6 | # and runs it. 7 | 8 | # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` 9 | # extension. Note: Most shells limit `local` to 1 var per line, contra bash. 10 | 11 | if [ "$KSH_VERSION" = 'Version JM 93t+ 2010-03-05' ]; then 12 | # The version of ksh93 that ships with many illumos systems does not 13 | # support the "local" extension. Print a message rather than fail in 14 | # subtle ways later on: 15 | echo 'rustup does not work with this ksh93 version; please try bash!' >&2 16 | exit 1 17 | fi 18 | 19 | 20 | set -u 21 | 22 | # If RUSTUP_UPDATE_ROOT is unset or empty, default it. 23 | RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://static.rust-lang.org/rustup}" 24 | 25 | #XXX: If you change anything here, please make the same changes in setup_mode.rs 26 | usage() { 27 | cat 1>&2 < Choose a default host triple 44 | --default-toolchain Choose a default toolchain to install 45 | --default-toolchain none Do not install any toolchains 46 | --profile [minimal|default|complete] Choose a profile 47 | -c, --component ... Component name to also install 48 | -t, --target ... Target name to also install 49 | EOF 50 | } 51 | 52 | main() { 53 | downloader --check 54 | need_cmd uname 55 | need_cmd mktemp 56 | need_cmd chmod 57 | need_cmd mkdir 58 | need_cmd rm 59 | need_cmd rmdir 60 | 61 | get_architecture || return 1 62 | local _arch="$RETVAL" 63 | assert_nz "$_arch" "arch" 64 | 65 | local _ext="" 66 | case "$_arch" in 67 | *windows*) 68 | _ext=".exe" 69 | ;; 70 | esac 71 | 72 | local _url="${RUSTUP_UPDATE_ROOT}/dist/${_arch}/rustup-init${_ext}" 73 | 74 | local _dir 75 | _dir="$(ensure mktemp -d)" 76 | local _file="${_dir}/rustup-init${_ext}" 77 | 78 | local _ansi_escapes_are_valid=false 79 | if [ -t 2 ]; then 80 | if [ "${TERM+set}" = 'set' ]; then 81 | case "$TERM" in 82 | xterm*|rxvt*|urxvt*|linux*|vt*) 83 | _ansi_escapes_are_valid=true 84 | ;; 85 | esac 86 | fi 87 | fi 88 | 89 | # check if we have to use /dev/tty to prompt the user 90 | local need_tty=yes 91 | for arg in "$@"; do 92 | case "$arg" in 93 | -h|--help) 94 | usage 95 | exit 0 96 | ;; 97 | -y) 98 | # user wants to skip the prompt -- we don't need /dev/tty 99 | need_tty=no 100 | ;; 101 | *) 102 | ;; 103 | esac 104 | done 105 | 106 | if $_ansi_escapes_are_valid; then 107 | printf "\33[1minfo:\33[0m downloading installer\n" 1>&2 108 | else 109 | printf '%s\n' 'info: downloading installer' 1>&2 110 | fi 111 | 112 | ensure mkdir -p "$_dir" 113 | ensure downloader "$_url" "$_file" "$_arch" 114 | ensure chmod u+x "$_file" 115 | if [ ! -x "$_file" ]; then 116 | printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2 117 | printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./rustup-init${_ext}." 1>&2 118 | exit 1 119 | fi 120 | 121 | if [ "$need_tty" = "yes" ]; then 122 | # The installer is going to want to ask for confirmation by 123 | # reading stdin. This script was piped into `sh` though and 124 | # doesn't have stdin to pass to its children. Instead we're going 125 | # to explicitly connect /dev/tty to the installer's stdin. 126 | if [ ! -t 1 ]; then 127 | err "Unable to run interactively. Run with -y to accept defaults, --help for additional options" 128 | fi 129 | 130 | ignore "$_file" "$@" < /dev/tty 131 | else 132 | ignore "$_file" "$@" 133 | fi 134 | 135 | local _retval=$? 136 | 137 | ignore rm "$_file" 138 | ignore rmdir "$_dir" 139 | 140 | return "$_retval" 141 | } 142 | 143 | check_proc() { 144 | # Check for /proc by looking for the /proc/self/exe link 145 | # This is only run on Linux 146 | if ! test -L /proc/self/exe ; then 147 | err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." 148 | fi 149 | } 150 | 151 | get_bitness() { 152 | need_cmd head 153 | # Architecture detection without dependencies beyond coreutils. 154 | # ELF files start out "\x7fELF", and the following byte is 155 | # 0x01 for 32-bit and 156 | # 0x02 for 64-bit. 157 | # The printf builtin on some shells like dash only supports octal 158 | # escape sequences, so we use those. 159 | local _current_exe_head 160 | _current_exe_head=$(head -c 5 /proc/self/exe ) 161 | if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then 162 | echo 32 163 | elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then 164 | echo 64 165 | else 166 | err "unknown platform bitness" 167 | fi 168 | } 169 | 170 | is_host_amd64_elf() { 171 | need_cmd head 172 | need_cmd tail 173 | # ELF e_machine detection without dependencies beyond coreutils. 174 | # Two-byte field at offset 0x12 indicates the CPU, 175 | # but we're interested in it being 0x3E to indicate amd64, or not that. 176 | local _current_exe_machine 177 | _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1) 178 | [ "$_current_exe_machine" = "$(printf '\076')" ] 179 | } 180 | 181 | get_endianness() { 182 | local cputype=$1 183 | local suffix_eb=$2 184 | local suffix_el=$3 185 | 186 | # detect endianness without od/hexdump, like get_bitness() does. 187 | need_cmd head 188 | need_cmd tail 189 | 190 | local _current_exe_endianness 191 | _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" 192 | if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then 193 | echo "${cputype}${suffix_el}" 194 | elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then 195 | echo "${cputype}${suffix_eb}" 196 | else 197 | err "unknown platform endianness" 198 | fi 199 | } 200 | 201 | get_architecture() { 202 | local _ostype _cputype _bitness _arch _clibtype 203 | _ostype="$(uname -s)" 204 | _cputype="$(uname -m)" 205 | _clibtype="gnu" 206 | 207 | if [ "$_ostype" = Linux ]; then 208 | if [ "$(uname -o)" = Android ]; then 209 | _ostype=Android 210 | fi 211 | if ldd --version 2>&1 | grep -q 'musl'; then 212 | _clibtype="musl" 213 | fi 214 | fi 215 | 216 | if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then 217 | # Darwin `uname -m` lies 218 | if sysctl hw.optional.x86_64 | grep -q ': 1'; then 219 | _cputype=x86_64 220 | fi 221 | fi 222 | 223 | if [ "$_ostype" = SunOS ]; then 224 | # Both Solaris and illumos presently announce as "SunOS" in "uname -s" 225 | # so use "uname -o" to disambiguate. We use the full path to the 226 | # system uname in case the user has coreutils uname first in PATH, 227 | # which has historically sometimes printed the wrong value here. 228 | if [ "$(/usr/bin/uname -o)" = illumos ]; then 229 | _ostype=illumos 230 | fi 231 | 232 | # illumos systems have multi-arch userlands, and "uname -m" reports the 233 | # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 234 | # systems. Check for the native (widest) instruction set on the 235 | # running kernel: 236 | if [ "$_cputype" = i86pc ]; then 237 | _cputype="$(isainfo -n)" 238 | fi 239 | fi 240 | 241 | case "$_ostype" in 242 | 243 | Android) 244 | _ostype=linux-android 245 | ;; 246 | 247 | Linux) 248 | check_proc 249 | _ostype=unknown-linux-$_clibtype 250 | _bitness=$(get_bitness) 251 | ;; 252 | 253 | FreeBSD) 254 | _ostype=unknown-freebsd 255 | ;; 256 | 257 | NetBSD) 258 | _ostype=unknown-netbsd 259 | ;; 260 | 261 | DragonFly) 262 | _ostype=unknown-dragonfly 263 | ;; 264 | 265 | Darwin) 266 | _ostype=apple-darwin 267 | ;; 268 | 269 | illumos) 270 | _ostype=unknown-illumos 271 | ;; 272 | 273 | MINGW* | MSYS* | CYGWIN*) 274 | _ostype=pc-windows-gnu 275 | ;; 276 | 277 | *) 278 | err "unrecognized OS type: $_ostype" 279 | ;; 280 | 281 | esac 282 | 283 | case "$_cputype" in 284 | 285 | i386 | i486 | i686 | i786 | x86) 286 | _cputype=i686 287 | ;; 288 | 289 | xscale | arm) 290 | _cputype=arm 291 | if [ "$_ostype" = "linux-android" ]; then 292 | _ostype=linux-androideabi 293 | fi 294 | ;; 295 | 296 | armv6l) 297 | _cputype=arm 298 | if [ "$_ostype" = "linux-android" ]; then 299 | _ostype=linux-androideabi 300 | else 301 | _ostype="${_ostype}eabihf" 302 | fi 303 | ;; 304 | 305 | armv7l | armv8l) 306 | _cputype=armv7 307 | if [ "$_ostype" = "linux-android" ]; then 308 | _ostype=linux-androideabi 309 | else 310 | _ostype="${_ostype}eabihf" 311 | fi 312 | ;; 313 | 314 | aarch64 | arm64) 315 | _cputype=aarch64 316 | ;; 317 | 318 | x86_64 | x86-64 | x64 | amd64) 319 | _cputype=x86_64 320 | ;; 321 | 322 | mips) 323 | _cputype=$(get_endianness mips '' el) 324 | ;; 325 | 326 | mips64) 327 | if [ "$_bitness" -eq 64 ]; then 328 | # only n64 ABI is supported for now 329 | _ostype="${_ostype}abi64" 330 | _cputype=$(get_endianness mips64 '' el) 331 | fi 332 | ;; 333 | 334 | ppc) 335 | _cputype=powerpc 336 | ;; 337 | 338 | ppc64) 339 | _cputype=powerpc64 340 | ;; 341 | 342 | ppc64le) 343 | _cputype=powerpc64le 344 | ;; 345 | 346 | s390x) 347 | _cputype=s390x 348 | ;; 349 | riscv64) 350 | _cputype=riscv64gc 351 | ;; 352 | *) 353 | err "unknown CPU type: $_cputype" 354 | 355 | esac 356 | 357 | # Detect 64-bit linux with 32-bit userland 358 | if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then 359 | case $_cputype in 360 | x86_64) 361 | if [ -n "${RUSTUP_CPUTYPE:-}" ]; then 362 | _cputype="$RUSTUP_CPUTYPE" 363 | else { 364 | # 32-bit executable for amd64 = x32 365 | if is_host_amd64_elf; then { 366 | echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2 367 | echo "and there isn't a native toolchain -- you will have to install" 1>&2 368 | echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2 369 | echo "by re-running this script with the RUSTUP_CPUTYPE environment variable" 1>&2 370 | echo "set to i686 or x86_64, respectively." 1>&2 371 | echo 1>&2 372 | echo "You will be able to add an x32 target after installation by running" 1>&2 373 | echo " rustup target add x86_64-unknown-linux-gnux32" 1>&2 374 | exit 1 375 | }; else 376 | _cputype=i686 377 | fi 378 | }; fi 379 | ;; 380 | mips64) 381 | _cputype=$(get_endianness mips '' el) 382 | ;; 383 | powerpc64) 384 | _cputype=powerpc 385 | ;; 386 | aarch64) 387 | _cputype=armv7 388 | if [ "$_ostype" = "linux-android" ]; then 389 | _ostype=linux-androideabi 390 | else 391 | _ostype="${_ostype}eabihf" 392 | fi 393 | ;; 394 | riscv64gc) 395 | err "riscv64 with 32-bit userland unsupported" 396 | ;; 397 | esac 398 | fi 399 | 400 | # Detect armv7 but without the CPU features Rust needs in that build, 401 | # and fall back to arm. 402 | # See https://github.com/rust-lang/rustup.rs/issues/587. 403 | if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then 404 | if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then 405 | # At least one processor does not have NEON. 406 | _cputype=arm 407 | fi 408 | fi 409 | 410 | _arch="${_cputype}-${_ostype}" 411 | 412 | RETVAL="$_arch" 413 | } 414 | 415 | say() { 416 | printf 'rustup: %s\n' "$1" 417 | } 418 | 419 | err() { 420 | say "$1" >&2 421 | exit 1 422 | } 423 | 424 | need_cmd() { 425 | if ! check_cmd "$1"; then 426 | err "need '$1' (command not found)" 427 | fi 428 | } 429 | 430 | check_cmd() { 431 | command -v "$1" > /dev/null 2>&1 432 | } 433 | 434 | assert_nz() { 435 | if [ -z "$1" ]; then err "assert_nz $2"; fi 436 | } 437 | 438 | # Run a command that should never fail. If the command fails execution 439 | # will immediately terminate with an error showing the failing 440 | # command. 441 | ensure() { 442 | if ! "$@"; then err "command failed: $*"; fi 443 | } 444 | 445 | # This is just for indicating that commands' results are being 446 | # intentionally ignored. Usually, because it's being executed 447 | # as part of error handling. 448 | ignore() { 449 | "$@" 450 | } 451 | 452 | # This wraps curl or wget. Try curl first, if not installed, 453 | # use wget instead. 454 | downloader() { 455 | local _dld 456 | local _ciphersuites 457 | local _err 458 | local _status 459 | if check_cmd curl; then 460 | _dld=curl 461 | elif check_cmd wget; then 462 | _dld=wget 463 | else 464 | _dld='curl or wget' # to be used in error message of need_cmd 465 | fi 466 | 467 | if [ "$1" = --check ]; then 468 | need_cmd "$_dld" 469 | elif [ "$_dld" = curl ]; then 470 | get_ciphersuites_for_curl 471 | _ciphersuites="$RETVAL" 472 | if [ -n "$_ciphersuites" ]; then 473 | _err=$(curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1) 474 | _status=$? 475 | else 476 | echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" 477 | if ! check_help_for "$3" curl --proto --tlsv1.2; then 478 | echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" 479 | _err=$(curl --silent --show-error --fail --location "$1" --output "$2" 2>&1) 480 | _status=$? 481 | else 482 | _err=$(curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1) 483 | _status=$? 484 | fi 485 | fi 486 | if [ -n "$_err" ]; then 487 | echo "$_err" >&2 488 | if echo "$_err" | grep -q 404$; then 489 | err "installer for platform '$3' not found, this may be unsupported" 490 | fi 491 | fi 492 | return $_status 493 | elif [ "$_dld" = wget ]; then 494 | get_ciphersuites_for_wget 495 | _ciphersuites="$RETVAL" 496 | if [ -n "$_ciphersuites" ]; then 497 | _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1) 498 | _status=$? 499 | else 500 | echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" 501 | if ! check_help_for "$3" wget --https-only --secure-protocol; then 502 | echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" 503 | _err=$(wget "$1" -O "$2" 2>&1) 504 | _status=$? 505 | else 506 | _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1) 507 | _status=$? 508 | fi 509 | fi 510 | if [ -n "$_err" ]; then 511 | echo "$_err" >&2 512 | if echo "$_err" | grep -q ' 404 Not Found$'; then 513 | err "installer for platform '$3' not found, this may be unsupported" 514 | fi 515 | fi 516 | return $_status 517 | else 518 | err "Unknown downloader" # should not reach here 519 | fi 520 | } 521 | 522 | check_help_for() { 523 | local _arch 524 | local _cmd 525 | local _arg 526 | _arch="$1" 527 | shift 528 | _cmd="$1" 529 | shift 530 | 531 | local _category 532 | if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then 533 | _category="all" 534 | else 535 | _category="" 536 | fi 537 | 538 | case "$_arch" in 539 | 540 | *darwin*) 541 | if check_cmd sw_vers; then 542 | case $(sw_vers -productVersion) in 543 | 10.*) 544 | # If we're running on macOS, older than 10.13, then we always 545 | # fail to find these options to force fallback 546 | if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then 547 | # Older than 10.13 548 | echo "Warning: Detected macOS platform older than 10.13" 549 | return 1 550 | fi 551 | ;; 552 | 11.*) 553 | # We assume Big Sur will be OK for now 554 | ;; 555 | *) 556 | # Unknown product version, warn and continue 557 | echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)" 558 | echo "Warning TLS capabilities detection may fail" 559 | ;; 560 | esac 561 | fi 562 | ;; 563 | 564 | esac 565 | 566 | for _arg in "$@"; do 567 | if ! "$_cmd" --help $_category | grep -q -- "$_arg"; then 568 | return 1 569 | fi 570 | done 571 | 572 | true # not strictly needed 573 | } 574 | 575 | # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites 576 | # if support by local tools is detected. Detection currently supports these curl backends: 577 | # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. 578 | get_ciphersuites_for_curl() { 579 | if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then 580 | # user specified custom cipher suites, assume they know what they're doing 581 | RETVAL="$RUSTUP_TLS_CIPHERSUITES" 582 | return 583 | fi 584 | 585 | local _openssl_syntax="no" 586 | local _gnutls_syntax="no" 587 | local _backend_supported="yes" 588 | if curl -V | grep -q ' OpenSSL/'; then 589 | _openssl_syntax="yes" 590 | elif curl -V | grep -iq ' LibreSSL/'; then 591 | _openssl_syntax="yes" 592 | elif curl -V | grep -iq ' BoringSSL/'; then 593 | _openssl_syntax="yes" 594 | elif curl -V | grep -iq ' GnuTLS/'; then 595 | _gnutls_syntax="yes" 596 | else 597 | _backend_supported="no" 598 | fi 599 | 600 | local _args_supported="no" 601 | if [ "$_backend_supported" = "yes" ]; then 602 | # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 603 | if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then 604 | _args_supported="yes" 605 | fi 606 | fi 607 | 608 | local _cs="" 609 | if [ "$_args_supported" = "yes" ]; then 610 | if [ "$_openssl_syntax" = "yes" ]; then 611 | _cs=$(get_strong_ciphersuites_for "openssl") 612 | elif [ "$_gnutls_syntax" = "yes" ]; then 613 | _cs=$(get_strong_ciphersuites_for "gnutls") 614 | fi 615 | fi 616 | 617 | RETVAL="$_cs" 618 | } 619 | 620 | # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites 621 | # if support by local tools is detected. Detection currently supports these wget backends: 622 | # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. 623 | get_ciphersuites_for_wget() { 624 | if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then 625 | # user specified custom cipher suites, assume they know what they're doing 626 | RETVAL="$RUSTUP_TLS_CIPHERSUITES" 627 | return 628 | fi 629 | 630 | local _cs="" 631 | if wget -V | grep -q '\-DHAVE_LIBSSL'; then 632 | # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 633 | if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then 634 | _cs=$(get_strong_ciphersuites_for "openssl") 635 | fi 636 | elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then 637 | # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 638 | if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then 639 | _cs=$(get_strong_ciphersuites_for "gnutls") 640 | fi 641 | fi 642 | 643 | RETVAL="$_cs" 644 | } 645 | 646 | # Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 647 | # excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad 648 | # DH params often found on servers (see RFC 7919). Sequence matches or is 649 | # similar to Firefox 68 ESR with weak cipher suites disabled via about:config. 650 | # $1 must be openssl or gnutls. 651 | get_strong_ciphersuites_for() { 652 | if [ "$1" = "openssl" ]; then 653 | # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet. 654 | echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384" 655 | elif [ "$1" = "gnutls" ]; then 656 | # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't. 657 | # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order. 658 | echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM" 659 | fi 660 | } 661 | 662 | main "$@" || exit 1 663 | -------------------------------------------------------------------------------- /samples/scripts/homebrew-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -u 3 | 4 | abort() { 5 | printf "%s\n" "$@" 6 | exit 1 7 | } 8 | 9 | if [ -z "${BASH_VERSION:-}" ] 10 | then 11 | abort "Bash is required to interpret this script." 12 | fi 13 | 14 | # Check if script is run non-interactively (e.g. CI) 15 | # If it is run non-interactively we should not prompt for passwords. 16 | if [[ ! -t 0 || -n "${CI-}" ]] 17 | then 18 | NONINTERACTIVE=1 19 | fi 20 | 21 | # First check OS. 22 | OS="$(uname)" 23 | if [[ "${OS}" == "Linux" ]] 24 | then 25 | HOMEBREW_ON_LINUX=1 26 | elif [[ "${OS}" != "Darwin" ]] 27 | then 28 | abort "Homebrew is only supported on macOS and Linux." 29 | fi 30 | 31 | # Required installation paths. To install elsewhere (which is unsupported) 32 | # you can untar https://github.com/Homebrew/brew/tarball/master 33 | # anywhere you like. 34 | if [[ -z "${HOMEBREW_ON_LINUX-}" ]] 35 | then 36 | UNAME_MACHINE="$(/usr/bin/uname -m)" 37 | 38 | if [[ "${UNAME_MACHINE}" == "arm64" ]] 39 | then 40 | # On ARM macOS, this script installs to /opt/homebrew only 41 | HOMEBREW_PREFIX="/opt/homebrew" 42 | HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}" 43 | else 44 | # On Intel macOS, this script installs to /usr/local only 45 | HOMEBREW_PREFIX="/usr/local" 46 | HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}/Homebrew" 47 | fi 48 | HOMEBREW_CACHE="${HOME}/Library/Caches/Homebrew" 49 | 50 | STAT_FLAG="-f" 51 | PERMISSION_FORMAT="%A" 52 | CHOWN="/usr/sbin/chown" 53 | CHGRP="/usr/bin/chgrp" 54 | GROUP="admin" 55 | TOUCH="/usr/bin/touch" 56 | else 57 | UNAME_MACHINE="$(uname -m)" 58 | 59 | # On Linux, it installs to /home/linuxbrew/.linuxbrew if you have sudo access 60 | # and ~/.linuxbrew (which is unsupported) if run interactively. 61 | HOMEBREW_PREFIX_DEFAULT="/home/linuxbrew/.linuxbrew" 62 | HOMEBREW_CACHE="${HOME}/.cache/Homebrew" 63 | 64 | STAT_FLAG="--printf" 65 | PERMISSION_FORMAT="%a" 66 | CHOWN="/bin/chown" 67 | CHGRP="/bin/chgrp" 68 | GROUP="$(id -gn)" 69 | TOUCH="/bin/touch" 70 | fi 71 | HOMEBREW_BREW_DEFAULT_GIT_REMOTE="https://github.com/Homebrew/brew" 72 | HOMEBREW_CORE_DEFAULT_GIT_REMOTE="https://github.com/Homebrew/homebrew-core" 73 | 74 | # Use remote URLs of Homebrew repositories from environment if set. 75 | HOMEBREW_BREW_GIT_REMOTE="${HOMEBREW_BREW_GIT_REMOTE:-"${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}"}" 76 | HOMEBREW_CORE_GIT_REMOTE="${HOMEBREW_CORE_GIT_REMOTE:-"${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}"}" 77 | # The URLs with and without the '.git' suffix are the same Git remote. Do not prompt. 78 | if [[ "${HOMEBREW_BREW_GIT_REMOTE}" == "${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}.git" ]] 79 | then 80 | HOMEBREW_BREW_GIT_REMOTE="${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}" 81 | fi 82 | if [[ "${HOMEBREW_CORE_GIT_REMOTE}" == "${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}.git" ]] 83 | then 84 | HOMEBREW_CORE_GIT_REMOTE="${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}" 85 | fi 86 | export HOMEBREW_{BREW,CORE}_GIT_REMOTE 87 | 88 | # TODO: bump version when new macOS is released or announced 89 | MACOS_NEWEST_UNSUPPORTED="12.0" 90 | # TODO: bump version when new macOS is released 91 | MACOS_OLDEST_SUPPORTED="10.14" 92 | 93 | # For Homebrew on Linux 94 | REQUIRED_RUBY_VERSION=2.6 # https://github.com/Homebrew/brew/pull/6556 95 | REQUIRED_GLIBC_VERSION=2.13 # https://docs.brew.sh/Homebrew-on-Linux#requirements 96 | REQUIRED_CURL_VERSION=7.41.0 # HOMEBREW_MINIMUM_CURL_VERSION in brew.sh in Homebrew/brew 97 | REQUIRED_GIT_VERSION=2.7.0 # HOMEBREW_MINIMUM_GIT_VERSION in brew.sh in Homebrew/brew 98 | 99 | # no analytics during installation 100 | export HOMEBREW_NO_ANALYTICS_THIS_RUN=1 101 | export HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT=1 102 | 103 | # string formatters 104 | if [[ -t 1 ]] 105 | then 106 | tty_escape() { printf "\033[%sm" "$1"; } 107 | else 108 | tty_escape() { :; } 109 | fi 110 | tty_mkbold() { tty_escape "1;$1"; } 111 | tty_underline="$(tty_escape "4;39")" 112 | tty_blue="$(tty_mkbold 34)" 113 | tty_red="$(tty_mkbold 31)" 114 | tty_bold="$(tty_mkbold 39)" 115 | tty_reset="$(tty_escape 0)" 116 | 117 | unset HAVE_SUDO_ACCESS # unset this from the environment 118 | 119 | have_sudo_access() { 120 | if [[ ! -x "/usr/bin/sudo" ]] 121 | then 122 | return 1 123 | fi 124 | 125 | local -a args 126 | if [[ -n "${SUDO_ASKPASS-}" ]] 127 | then 128 | args=("-A") 129 | elif [[ -n "${NONINTERACTIVE-}" ]] 130 | then 131 | args=("-n") 132 | fi 133 | 134 | if [[ -z "${HAVE_SUDO_ACCESS-}" ]] 135 | then 136 | if [[ -n "${args[*]-}" ]] 137 | then 138 | SUDO="/usr/bin/sudo ${args[*]}" 139 | else 140 | SUDO="/usr/bin/sudo" 141 | fi 142 | if [[ -n "${NONINTERACTIVE-}" ]] 143 | then 144 | # Don't add quotes around ${SUDO} here 145 | ${SUDO} -l mkdir &>/dev/null 146 | else 147 | ${SUDO} -v && ${SUDO} -l mkdir &>/dev/null 148 | fi 149 | HAVE_SUDO_ACCESS="$?" 150 | fi 151 | 152 | if [[ -z "${HOMEBREW_ON_LINUX-}" ]] && [[ "${HAVE_SUDO_ACCESS}" -ne 0 ]] 153 | then 154 | abort "Need sudo access on macOS (e.g. the user ${USER} needs to be an Administrator)!" 155 | fi 156 | 157 | return "${HAVE_SUDO_ACCESS}" 158 | } 159 | 160 | shell_join() { 161 | local arg 162 | printf "%s" "$1" 163 | shift 164 | for arg in "$@" 165 | do 166 | printf " " 167 | printf "%s" "${arg// /\ }" 168 | done 169 | } 170 | 171 | chomp() { 172 | printf "%s" "${1/"$'\n'"/}" 173 | } 174 | 175 | ohai() { 176 | printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" 177 | } 178 | 179 | warn() { 180 | printf "${tty_red}Warning${tty_reset}: %s\n" "$(chomp "$1")" 181 | } 182 | 183 | execute() { 184 | if ! "$@" 185 | then 186 | abort "$(printf "Failed during: %s" "$(shell_join "$@")")" 187 | fi 188 | } 189 | 190 | execute_sudo() { 191 | local -a args=("$@") 192 | if have_sudo_access 193 | then 194 | if [[ -n "${SUDO_ASKPASS-}" ]] 195 | then 196 | args=("-A" "${args[@]}") 197 | fi 198 | ohai "/usr/bin/sudo" "${args[@]}" 199 | execute "/usr/bin/sudo" "${args[@]}" 200 | else 201 | ohai "${args[@]}" 202 | execute "${args[@]}" 203 | fi 204 | } 205 | 206 | getc() { 207 | local save_state 208 | save_state="$(/bin/stty -g)" 209 | /bin/stty raw -echo 210 | IFS='' read -r -n 1 -d '' "$@" 211 | /bin/stty "${save_state}" 212 | } 213 | 214 | ring_bell() { 215 | # Use the shell's audible bell. 216 | if [[ -t 1 ]] 217 | then 218 | printf "\a" 219 | fi 220 | } 221 | 222 | wait_for_user() { 223 | local c 224 | echo 225 | echo "Press RETURN to continue or any other key to abort" 226 | getc c 227 | # we test for \r and \n because some stuff does \r instead 228 | if ! [[ "${c}" == $'\r' || "${c}" == $'\n' ]] 229 | then 230 | exit 1 231 | fi 232 | } 233 | 234 | major_minor() { 235 | echo "${1%%.*}.$( 236 | x="${1#*.}" 237 | echo "${x%%.*}" 238 | )" 239 | } 240 | 241 | version_gt() { 242 | [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -gt "${2#*.}" ]] 243 | } 244 | version_ge() { 245 | [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -ge "${2#*.}" ]] 246 | } 247 | version_lt() { 248 | [[ "${1%.*}" -lt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -lt "${2#*.}" ]] 249 | } 250 | 251 | should_install_command_line_tools() { 252 | if [[ -n "${HOMEBREW_ON_LINUX-}" ]] 253 | then 254 | return 1 255 | fi 256 | 257 | if version_gt "${macos_version}" "10.13" 258 | then 259 | ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] 260 | else 261 | ! [[ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]] || 262 | ! [[ -e "/usr/include/iconv.h" ]] 263 | fi 264 | } 265 | 266 | get_permission() { 267 | stat "${STAT_FLAG}" "${PERMISSION_FORMAT}" "$1" 268 | } 269 | 270 | user_only_chmod() { 271 | [[ -d "$1" ]] && [[ "$(get_permission "$1")" != 75[0145] ]] 272 | } 273 | 274 | exists_but_not_writable() { 275 | [[ -e "$1" ]] && ! [[ -r "$1" && -w "$1" && -x "$1" ]] 276 | } 277 | 278 | get_owner() { 279 | stat "${STAT_FLAG}" "%u" "$1" 280 | } 281 | 282 | file_not_owned() { 283 | [[ "$(get_owner "$1")" != "$(id -u)" ]] 284 | } 285 | 286 | get_group() { 287 | stat "${STAT_FLAG}" "%g" "$1" 288 | } 289 | 290 | file_not_grpowned() { 291 | [[ " $(id -G "${USER}") " != *" $(get_group "$1") "* ]] 292 | } 293 | 294 | # Please sync with 'test_ruby()' in 'Library/Homebrew/utils/ruby.sh' from Homebrew/brew repository. 295 | test_ruby() { 296 | if [[ ! -x "$1" ]] 297 | then 298 | return 1 299 | fi 300 | 301 | "$1" --enable-frozen-string-literal --disable=gems,did_you_mean,rubyopt -rrubygems -e \ 302 | "abort if Gem::Version.new(RUBY_VERSION.to_s.dup).to_s.split('.').first(2) != \ 303 | Gem::Version.new('${REQUIRED_RUBY_VERSION}').to_s.split('.').first(2)" 2>/dev/null 304 | } 305 | 306 | test_curl() { 307 | if [[ ! -x "$1" ]] 308 | then 309 | return 1 310 | fi 311 | 312 | local curl_version_output curl_name_and_version 313 | curl_version_output="$("$1" --version 2>/dev/null)" 314 | curl_name_and_version="${curl_version_output%% (*}" 315 | version_ge "$(major_minor "${curl_name_and_version##* }")" "$(major_minor "${REQUIRED_CURL_VERSION}")" 316 | } 317 | 318 | test_git() { 319 | if [[ ! -x "$1" ]] 320 | then 321 | return 1 322 | fi 323 | 324 | local git_version_output 325 | git_version_output="$("$1" --version 2>/dev/null)" 326 | version_ge "$(major_minor "${git_version_output##* }")" "$(major_minor "${REQUIRED_GIT_VERSION}")" 327 | } 328 | 329 | # Search given executable in PATH (remove dependency for `which` command) 330 | which() { 331 | # Alias to Bash built-in command `type -P` 332 | type -P "$@" 333 | } 334 | 335 | # Search PATH for the specified program that satisfies Homebrew requirements 336 | # function which is set above 337 | # shellcheck disable=SC2230 338 | find_tool() { 339 | if [[ $# -ne 1 ]] 340 | then 341 | return 1 342 | fi 343 | 344 | local executable 345 | while read -r executable 346 | do 347 | if "test_$1" "${executable}" 348 | then 349 | echo "${executable}" 350 | break 351 | fi 352 | done < <(which -a "$1") 353 | } 354 | 355 | no_usable_ruby() { 356 | [[ -z "$(find_tool ruby)" ]] 357 | } 358 | 359 | outdated_glibc() { 360 | local glibc_version 361 | glibc_version="$(ldd --version | head -n1 | grep -o '[0-9.]*$' | grep -o '^[0-9]\+\.[0-9]\+')" 362 | version_lt "${glibc_version}" "${REQUIRED_GLIBC_VERSION}" 363 | } 364 | 365 | if [[ -n "${HOMEBREW_ON_LINUX-}" ]] && no_usable_ruby && outdated_glibc 366 | then 367 | abort "$( 368 | cat <<-EOFABORT 369 | Homebrew requires Ruby ${REQUIRED_RUBY_VERSION} which was not found on your system. 370 | Homebrew portable Ruby requires Glibc version ${REQUIRED_GLIBC_VERSION} or newer, 371 | and your Glibc version is too old. 372 | See ${tty_underline}https://docs.brew.sh/Homebrew-on-Linux#requirements${tty_reset} 373 | Install Ruby ${REQUIRED_RUBY_VERSION} and add its location to your PATH. 374 | EOFABORT 375 | )" 376 | fi 377 | 378 | # USER isn't always set so provide a fall back for the installer and subprocesses. 379 | if [[ -z "${USER-}" ]] 380 | then 381 | USER="$(chomp "$(id -un)")" 382 | export USER 383 | fi 384 | 385 | # Invalidate sudo timestamp before exiting (if it wasn't active before). 386 | if [[ -x /usr/bin/sudo ]] && ! /usr/bin/sudo -n -v 2>/dev/null 387 | then 388 | trap '/usr/bin/sudo -k' EXIT 389 | fi 390 | 391 | # Things can fail later if `pwd` doesn't exist. 392 | # Also sudo prints a warning message for no good reason 393 | cd "/usr" || exit 1 394 | 395 | ####################################################################### script 396 | if ! command -v git >/dev/null 397 | then 398 | abort "$( 399 | cat </dev/null 423 | then 424 | abort "$( 425 | cat </dev/null 478 | then 479 | ohai "Select the Homebrew installation directory" 480 | echo "- ${tty_bold}Enter your password${tty_reset} to install to ${tty_underline}${HOMEBREW_PREFIX_DEFAULT}${tty_reset} (${tty_bold}recommended${tty_reset})" 481 | echo "- ${tty_bold}Press Control-D${tty_reset} to install to ${tty_underline}${HOME}/.linuxbrew${tty_reset}" 482 | echo "- ${tty_bold}Press Control-C${tty_reset} to cancel installation" 483 | fi 484 | if have_sudo_access 485 | then 486 | HOMEBREW_PREFIX="${HOMEBREW_PREFIX_DEFAULT}" 487 | else 488 | HOMEBREW_PREFIX="${HOME}/.linuxbrew" 489 | fi 490 | trap - SIGINT 491 | fi 492 | HOMEBREW_REPOSITORY="${HOMEBREW_PREFIX}/Homebrew" 493 | fi 494 | HOMEBREW_CORE="${HOMEBREW_REPOSITORY}/Library/Taps/homebrew/homebrew-core" 495 | 496 | if [[ "${EUID:-${UID}}" == "0" ]] 497 | then 498 | # Allow Azure Pipelines/GitHub Actions/Docker/Concourse/Kubernetes to do everything as root (as it's normal there) 499 | if ! [[ -f /proc/1/cgroup ]] || 500 | ! grep -E "azpl_job|actions_job|docker|garden|kubepods" -q /proc/1/cgroup 501 | then 502 | abort "Don't run this as root!" 503 | fi 504 | fi 505 | 506 | if [[ -d "${HOMEBREW_PREFIX}" && ! -x "${HOMEBREW_PREFIX}" ]] 507 | then 508 | abort "$( 509 | cat </dev/null || return 859 | 860 | # we do it in four steps to avoid merge errors when reinstalling 861 | execute "git" "init" "-q" 862 | 863 | # "git remote add" will fail if the remote is defined in the global config 864 | execute "git" "config" "remote.origin.url" "${HOMEBREW_BREW_GIT_REMOTE}" 865 | execute "git" "config" "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*" 866 | 867 | # ensure we don't munge line endings on checkout 868 | execute "git" "config" "core.autocrlf" "false" 869 | 870 | execute "git" "fetch" "--force" "origin" 871 | execute "git" "fetch" "--force" "--tags" "origin" 872 | 873 | execute "git" "reset" "--hard" "origin/master" 874 | 875 | if [[ "${HOMEBREW_REPOSITORY}" != "${HOMEBREW_PREFIX}" ]] 876 | then 877 | if [[ "${HOMEBREW_REPOSITORY}" == "${HOMEBREW_PREFIX}/Homebrew" ]] 878 | then 879 | execute "ln" "-sf" "../Homebrew/bin/brew" "${HOMEBREW_PREFIX}/bin/brew" 880 | else 881 | abort "The Homebrew/brew repository should be placed in the Homebrew prefix directory." 882 | fi 883 | fi 884 | 885 | if [[ ! -d "${HOMEBREW_CORE}" ]] 886 | then 887 | ohai "Tapping homebrew/core" 888 | ( 889 | execute "/bin/mkdir" "-p" "${HOMEBREW_CORE}" 890 | cd "${HOMEBREW_CORE}" >/dev/null || return 891 | 892 | execute "git" "init" "-q" 893 | execute "git" "config" "remote.origin.url" "${HOMEBREW_CORE_GIT_REMOTE}" 894 | execute "git" "config" "remote.origin.fetch" "+refs/heads/*:refs/remotes/origin/*" 895 | execute "git" "config" "core.autocrlf" "false" 896 | execute "git" "fetch" "--force" "origin" "refs/heads/master:refs/remotes/origin/master" 897 | execute "git" "remote" "set-head" "origin" "--auto" >/dev/null 898 | execute "git" "reset" "--hard" "origin/master" 899 | 900 | cd "${HOMEBREW_REPOSITORY}" >/dev/null || return 901 | ) || exit 1 902 | fi 903 | 904 | execute "${HOMEBREW_PREFIX}/bin/brew" "update" "--force" "--quiet" 905 | ) || exit 1 906 | 907 | if [[ ":${PATH}:" != *":${HOMEBREW_PREFIX}/bin:"* ]] 908 | then 909 | warn "${HOMEBREW_PREFIX}/bin is not in your PATH. 910 | Instructions on how to configure your shell for Homebrew 911 | can be found in the 'Next steps' section below." 912 | fi 913 | 914 | ohai "Installation successful!" 915 | echo 916 | 917 | ring_bell 918 | 919 | # Use an extra newline and bold to avoid this being missed. 920 | ohai "Homebrew has enabled anonymous aggregate formulae and cask analytics." 921 | echo "$( 922 | cat </dev/null || return 940 | execute "git" "config" "--replace-all" "homebrew.analyticsmessage" "true" 941 | execute "git" "config" "--replace-all" "homebrew.caskanalyticsmessage" "true" 942 | ) || exit 1 943 | 944 | ohai "Next steps:" 945 | case "${SHELL}" in 946 | */bash*) 947 | if [[ -r "${HOME}/.bash_profile" ]] 948 | then 949 | shell_profile="${HOME}/.bash_profile" 950 | else 951 | shell_profile="${HOME}/.profile" 952 | fi 953 | ;; 954 | */zsh*) 955 | shell_profile="${HOME}/.zprofile" 956 | ;; 957 | *) 958 | shell_profile="${HOME}/.profile" 959 | ;; 960 | esac 961 | if [[ "${UNAME_MACHINE}" == "arm64" ]] || [[ -n "${HOMEBREW_ON_LINUX-}" ]] 962 | then 963 | cat <> ${shell_profile} 966 | eval "\$(${HOMEBREW_PREFIX}/bin/brew shellenv)" 967 | EOS 968 | fi 969 | if [[ -n "${non_default_repos}" ]] 970 | then 971 | plural="" 972 | if [[ "${#additional_shellenv_commands[@]}" -gt 1 ]] 973 | then 974 | plural="s" 975 | fi 976 | echo "- Run these commands in your terminal to add the non-default Git remote${plural} for ${non_default_repos}:" 977 | printf " echo '%s' >> ${shell_profile}\n" "${additional_shellenv_commands[@]}" 978 | printf " %s\n" "${additional_shellenv_commands[@]}" 979 | fi 980 | 981 | echo "- Run \`brew help\` to get started" 982 | echo "- Further documentation: " 983 | echo " ${tty_underline}https://docs.brew.sh${tty_reset}" 984 | 985 | if [[ -n "${HOMEBREW_ON_LINUX-}" ]] 986 | then 987 | echo "- Install the Homebrew dependencies if you have sudo access:" 988 | 989 | if [[ -x "$(command -v apt-get)" ]] 990 | then 991 | echo " sudo apt-get install build-essential" 992 | elif [[ -x "$(command -v yum)" ]] 993 | then 994 | echo " sudo yum groupinstall 'Development Tools'" 995 | elif [[ -x "$(command -v pacman)" ]] 996 | then 997 | echo " sudo pacman -S base-devel" 998 | elif [[ -x "$(command -v apk)" ]] 999 | then 1000 | echo " sudo apk add build-base" 1001 | fi 1002 | 1003 | cat <