<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:a="http://www.w3.org/2005/Atom" xmlns:c="http://purl.org/rss/1.0/modules/content/"><channel><title>CC的部落格</title><link>https://blog.ccknbc.cc/</link><description>CC康纳百川</description><language>zh-CN</language><copyright>All rights reserved 2026, CC康纳百川</copyright><lastBuildDate>Fri, 30 Jan 2026 04:34:00 GMT</lastBuildDate><generator>Hexo</generator><image><url>https://cdn.jsdmirror.com/gh/ccknbc-backup/cdn/logo/CC.png</url><title>CC的部落格</title><link>https://blog.ccknbc.cc/</link></image><a:link href="https://blog.ccknbc.cc/rss.xml" rel="self" type="application/rss+xml"/><item><title>Hexo浏览器定向推送文章更新</title><link>https://blog.ccknbc.cc/posts/hexo-webpushr-notification/</link><description><![CDATA[<font style="color:rgb(38, 38, 38);">这一次，CC的部落格可以根据读者订阅主题定向推送了，并且实现了NPM插件化</font>]]></description><author>CC康纳百川</author><category domain="https://blog.ccknbc.cc/categories/%E5%8D%9A%E5%AE%A2/">博客</category><category domain="https://blog.ccknbc.cc/tags/%E5%8D%9A%E5%AE%A2/">博客</category><category domain="https://blog.ccknbc.cc/tags/%E5%B7%A5%E5%85%B7/">工具</category><pubDate>Mon, 02 Mar 2026 00:50:28 GMT</pubDate><c:encoded><![CDATA[<p>查看本文<a href="https://www.yuque.com/ccknbc/blog/37/"><strong>语雀</strong></a>版本【首发】，自动同步更新至<a href="https://blog.ccknbc.cc/posts/hexo-webpushr-notification/"><strong>CC的部落格</strong></a>！</p><p>两年前，我刚开始使用<code>Hexo</code>的时候，写了一篇文章<a href="https://blog.ccknbc.cc/posts/implementation-of-simple-browser-update-push/">简单浏览器更新推送的实现</a>，最近登录<a href="https://www.webpushr.com/">webpushr</a>控制台，发现其支持按话题<code>topic</code>指定推送了，而原来的插件一直没有更新，且对个人的写作习惯不是很友好，所以对原插件进行了修改，并发布到了<code>NPM</code></p><p>欢迎大家前往<a href="/sub">订阅页面</a>选择合适的订阅方式，关于邮件订阅，现已支持分类订阅</p><h2 id="安装">安装<a class="fa-solid fa-hashtag" href="#安装"></a></h2><p>推荐使用 <code>npm</code> 以插件形式安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i hexo-webpushr-notification</span><br></pre></td></tr></table></figure><h3 id="自定义修改">自定义修改<a class="fa-solid fa-hashtag" href="#自定义修改"></a></h3><p>当然你也可以自定义修改<a href="https://github.com/Rock-Candy-Tea/hexo-webpushr-notification/blob/main/webpushr.js">webpushr.js</a>文件后，再安装相关需要依赖，然后将文件放到<code>Hexo/scripts/</code>目录下即可正常运行，CC本人亦是如此</p><p>对于 0.2.0 以上版本，您只需要在 Hexo 所在目录安装 axios 即可，这样测试相较于安装 GitHub 更方便（以及欢迎 PR ）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i axios</span><br></pre></td></tr></table></figure><h2 id="使用">使用<a class="fa-solid fa-hashtag" href="#使用"></a></h2><p>在你的 Hexo 根目录配置文件 <code>_config.yml</code>中添加如下内容，可按需配置，建议前往 <a href="https://github.com/Rock-Candy-Tea/hexo-webpushr-notification#readme">README</a> 查看最新配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">webpushr:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">webpushrKey:</span> <span class="string">&quot;webpushrKey&quot;</span></span><br><span class="line">  <span class="attr">webpushrAuthToken:</span> <span class="string">&quot;webpushrAuthToken&quot;</span></span><br><span class="line">  <span class="comment"># 出于安全考虑，建议将上述两个重要参数添加至系统全局环境变量，并删除或注释掉此处配置</span></span><br><span class="line">  <span class="comment"># 否则可在网页端向访问者或订阅用户发送推送 https://www.webpushr.com/api-playground</span></span><br><span class="line">  <span class="comment"># 例如GitHub Actions环境变量配置，参数名不变，密钥名自定义，可参考如下</span></span><br><span class="line">  <span class="comment"># env:</span></span><br><span class="line">  <span class="comment">#     webpushrKey: $&#123;&#123; secrets.WEBPUSHR_KEY &#125;&#125;</span></span><br><span class="line">  <span class="comment">#     webpushrAuthToken: $&#123;&#123; secrets.WEBPUSHR_AUTH_TOKEN &#125;&#125;</span></span><br><span class="line">  <span class="comment"># 如果您的仓库私有，则无需担心此问题</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">trackingCode:</span> <span class="string">&quot;trackingCode&quot;</span></span><br><span class="line">  <span class="attr">icon:</span> <span class="string">&quot;https://.../192.png&quot;</span> <span class="comment"># 必须为 HTTPS 以及 192*192 png图片</span></span><br><span class="line">  <span class="comment"># auto_hide: false # 默认为 1，代表true，即自动隐藏</span></span><br><span class="line">  <span class="comment"># sort: &quot;date&quot; # 默认为updated，即只要最新文章更改了更新时间即推送新文章，改为date即文章第一次发布时间</span></span><br><span class="line">  <span class="comment"># delay: &quot;0&quot; # 延时推送，考虑到CDN缓存更新，默认定时为在 hexo d 10分钟后推送，单位为分钟（最短延时为5分钟，设置 0 则会立即推送）</span></span><br><span class="line">  <span class="comment"># expire: &quot;15d&quot; # 推送过期时长，默认值为7天，格式如下：&#x27;5m&#x27;代表5分钟,&#x27;5h&#x27;代表5小时, &#x27;5d&#x27;代表5天.</span></span><br><span class="line">  <span class="comment"># image: # 默认为文章封面，Front-matter 属性为&#x27;cover&#x27;(butterfly主题友好选项)，如果您没有定义默认封面或此属性，请在这里设置默认image</span></span><br><span class="line">  <span class="attr">action_buttons:</span> <span class="comment"># 如果你需要额外自定义按钮 可按照如下格式：</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">title:</span> <span class="string">阅读全文</span> <span class="comment"># 当 title 未配置时 默认值为 “前往查看”</span></span><br><span class="line">      <span class="comment"># url:  # 当 url 未配置时 默认值为 最新文章链接</span></span><br><span class="line">    <span class="bullet">-</span> <span class="attr">title:</span> <span class="string">订阅页面</span></span><br><span class="line">      <span class="attr">url:</span> <span class="string">https://blog.ccknbc.cc/sub/</span></span><br><span class="line">    <span class="comment"># 当 action_buttons 未定义时也默认保留了一个“前往查看”按钮，除非设置为 false</span></span><br><span class="line">    <span class="comment"># action_buttons: false # 当设为 false 则不显示额外的按钮，因为隐藏按钮即为当前文章链接</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># 以下配置为按订阅主题推送给不同订阅用户，请按照数组形式，一一对应，具体位置请看使用文档</span></span><br><span class="line">  <span class="attr">categories:</span> [<span class="string">工作</span>, <span class="string">博客</span>, <span class="string">工具</span>, <span class="string">生活</span>, <span class="string">音乐</span>, <span class="string">学习</span>]</span><br><span class="line">  <span class="attr">segment:</span> [<span class="string">&quot;484223&quot;</span>, <span class="string">&quot;484224&quot;</span>, <span class="string">&quot;484225&quot;</span>, <span class="string">&quot;484226&quot;</span>, <span class="string">&quot;484227&quot;</span>, <span class="string">&quot;484229&quot;</span>]</span><br><span class="line">  <span class="attr">endpoint:</span> <span class="string">segment</span> <span class="comment"># 可选配置 all / segment / sid</span></span><br><span class="line">  <span class="comment"># 默认为 segment，即根据不同主题推送细分，同时配置上述选项</span></span><br><span class="line">  <span class="comment"># 例如 all，即推送至所有用户；针对本地测试，建议只推送给单个用户即自己，同时设置下方的 sid 值</span></span><br><span class="line">  <span class="comment"># 您也可以将segment 设置为 all-users 对应的ID，同样也可以实现推送至所有用户</span></span><br><span class="line">  <span class="attr">sid:</span> <span class="string">&quot;119810055&quot;</span> <span class="comment"># 单个用户ID 可在控制台查看 https://app.webpushr.com/subscribers</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># 此外，在文章 Front-Matter 处</span></span><br><span class="line">  <span class="comment"># 可覆盖 auto_hide 和 expire 配置，针对需要特别提醒文章可以设置不自动隐藏及过期时间延长等操作</span></span><br><span class="line">  <span class="comment"># 以及可指定schedule参数（例如：schedule: 2022-10-01 00:00:00），定时推送</span></span><br><span class="line">  <span class="comment"># 当文章头设置 webpushr: false 时，可关闭本篇文章推送，此参数主要防止久远文章更改更新时间后自动推送</span></span><br></pre></td></tr></table></figure><ol><li>前往 webpushr 控制台获取如下参数，注册的时候可能会遇到一点困难，中国大陆用户需要科学上网来加载验证服务）</li><li>关于注册及一些具体内容，可以看之前的文章 <a href="https://blog.ccknbc.cc/posts/implementation-of-simple-browser-update-push/">简单浏览器更新推送的实现</a></li><li>依次点击 <code>Integration</code> &gt; <code>REST API Keys</code>，即可看到你的<code>webpushrKey</code> 及 <code>webpushrAuthToken</code></li><li>依次点击 <code>Setup</code> &gt; <code>TrackingCode</code>，可以看到如下代码</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- start webpushr tracking code --&gt;</span><br><span class="line">  <span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">(<span class="keyword">function</span>(<span class="params">w,d, s, id</span>) &#123;<span class="keyword">if</span>(<span class="title function_">typeof</span>(w.<span class="property">webpushr</span>)!==<span class="string">&#x27;undefined&#x27;</span>) <span class="keyword">return</span>;w.<span class="property">webpushr</span>=w.<span class="property">webpushr</span>||<span class="keyword">function</span>(<span class="params"></span>)&#123;(w.<span class="property">webpushr</span>.<span class="property">q</span>=w.<span class="property">webpushr</span>.<span class="property">q</span>||[]).<span class="title function_">push</span>(<span class="variable language_">arguments</span>)&#125;;<span class="keyword">var</span> js, fjs = d.<span class="title function_">getElementsByTagName</span>(s)[<span class="number">0</span>];js = d.<span class="title function_">createElement</span>(s); js.<span class="property">id</span> = id;js.<span class="property">async</span>=<span class="number">1</span>;js.<span class="property">src</span> = <span class="string">&quot;https://cdn.webpushr.com/app.min.js&quot;</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml">                                 fjs.<span class="property">parentNode</span>.<span class="title function_">appendChild</span>(js);&#125;(<span class="variable language_">window</span>,<span class="variable language_">document</span>, <span class="string">&#x27;script&#x27;</span>, <span class="string">&#x27;webpushr-jssdk&#x27;</span>));</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"><span class="title function_">webpushr</span>(<span class="string">&#x27;setup&#x27;</span>,&#123;<span class="string">&#x27;key&#x27;</span>:<span class="string">&#x27;BKOlpbdgvBCWXqXI6PtsUzobY7TLV9gwJU8bzMktrwfrSERg_xnLvbjpdw8x2GmFmi1ZcLTz0ni6OnX5MAwoM58&#x27;</span> &#125;);</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">  &lt;!-- end webpushr tracking code --&gt;</span><br></pre></td></tr></table></figure><p>最后一行<code>BKOlpbdgvBCWXqXI6PtsUzobY7TLV9gwJU8bzMktrwfrSERg_xnLvbjpdw8x2GmFmi1ZcLTz0ni6OnX5MAwoM58</code> 就是你的 <code>trackingCode</code></p><p><strong>注意</strong>：因权限问题，本地测试时(<code>hexo s</code>)可能不会显示弹窗，但如果你配置了小铃铛，小铃铛会显示</p><h2 id="额外配置">额外配置<a class="fa-solid fa-hashtag" href="#额外配置"></a></h2><p>因官方sw脚本注册后，我们无法注册自己的sw脚本，但官方提供了配置，方便我们使用sw的缓存，拦截请求等功能</p><p>首先在配置项中添加 <code>sw_self: true </code>配置，开启自行注册sw（默认用户不用添加或者设为 <code>false</code>）</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">webpushr:</span></span><br><span class="line">  <span class="attr">sw_self:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>另外，你还需要在你的脚本文件（例如<code>sw.js</code>）中引入</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">importScripts</span>(<span class="string">&#x27;https://cdn.webpushr.com/sw-server.min.js&#x27;</span>);</span><br></pre></td></tr></table></figure><p>完成这些你就可以自行注册你的<code>sw</code>脚本了，如果你需要了解如何编写或注册<code>service worker</code>脚本，可以参考以下文章或项目</p><p><a href="https://kmar.top/posts/73014407/">hexo-swpp</a></p><p><a href="https://blog.cyfan.top/p/c0af86bb.html">Service Worker</a></p><p><a href="https://clientworker.js.org/">clientworker</a></p><p><a href="https://github.com/GoogleChrome/workbox">Workbox</a></p><h2 id="自定义">自定义<a class="fa-solid fa-hashtag" href="#自定义"></a></h2><p>个人建议将控制台右上角小铃铛🔔里全部配置一遍以获得更好的效果</p><p>你需要自定义一些参数才可以使用根据不同主题，按照订阅者订阅话题推送功能（目前根据个人需求是这个设置，默认行为为当未匹配到对应分类时不推送文章，而不是向所有用户推送文章，当然你也可以配置目标为所有用户）</p><p>在控制台，点击<code>Setup</code>&gt;<code>Opt-In Prompt</code> ，向下滑动打开<code>Enable Topics</code>（小铃铛样式无此选项，因此推荐您使用前两种样式），并新增几个主题，对应你想推送的文章分类即可</p><p>然后点击<code>Users</code>&gt;<code>Segments</code> ，即可获取对应的<code>segment</code>关系，依次填入配置项即可</p><h2 id="工作原理">工作原理<a class="fa-solid fa-hashtag" href="#工作原理"></a></h2><p>当你运行<code>hexo generate</code>插件会在<code>public</code> 目录生成 <code>newPost.json</code> 这样一个文件. <code>newPost.json</code> 包含了一些你想推送的新文章相关信息，示例格式如下</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;title&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Hexo浏览器定向推送文章更新&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;updated&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2023-04-22T20:25:00+08:00&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;这一次，CC的部落格可以根据读者订阅主题定向推送了，并且实现了NPM插件化&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;target_url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://blog.ccknbc.cc/posts/hexo-webpushr-notification/&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;image&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://pic1.afdiancdn.com/user/8a7f563c2e3811ecab5852540025c377/common/d2a947d48815ed24936a919873b97841_w1366_h768_s31.png&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;categories&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;博客&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;schedule&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2023-06-13T15:16:41.187Z&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;expire&quot;</span><span class="punctuation">:</span> <span class="string">&quot;7d&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;auto_hide&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>而他的来源就是我们在文章开头<code>Front-Matter</code>自定义的那些属性，而本插件针对<code>Butterfly</code>主题做了针对性修改，您也可以在您的模板文件目录下修改文章模板文件(<code>Hexo/scaffolds/post.md</code>)，主要针对性参数如下</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">date:</span> </span><br><span class="line"><span class="attr">updated:</span> </span><br><span class="line"><span class="attr">schedule:</span> <span class="string">对应配置项中定时推送时间</span></span><br><span class="line"><span class="attr">auto_hide:</span> <span class="string">对应配置项中是否自动隐藏</span></span><br><span class="line"><span class="attr">expire:</span> <span class="string">对应配置项中过期时间</span></span><br><span class="line"><span class="attr">categories:</span> <span class="string">文章分类</span></span><br><span class="line"><span class="attr">description:</span> <span class="string">对应配置项中message，即文章描述</span></span><br><span class="line"><span class="attr">cover:</span> <span class="string">对应配置项中image，默认选取文章封面</span></span><br></pre></td></tr></table></figure><p>如果你的主题不是采用默认的<code>date</code> <code>updated</code> 参数，记得补充，因为这是判断最新文章的依据</p><p>如果你习惯了使用截断的方式，也无需配置<code>description</code>继续使用，示例如下，注意<code>&lt;!-- more --&gt;</code></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: Hexo使用Web Push Notification 浏览器通知推送</span><br><span class="line">tags:</span><br><span class="line"><span class="bullet">  -</span> hexo</span><br><span class="line"><span class="bullet">  -</span> 服务器推送技术</span><br><span class="line"><span class="bullet">  -</span> push notifications</span><br><span class="line">categories:</span><br><span class="line"><span class="bullet">  -</span> 开发</span><br><span class="line">comments: true</span><br><span class="line">abbrlink: 98ae9e55</span><br><span class="line"><span class="section">date: 2020-02-26 10:00:00</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"></span><br><span class="line">Web Push Notification 是怎么工作的？个人博客为什么要使用它？如何使用它？</span><br><span class="line"></span><br><span class="line">&lt;!-- more --&gt;</span><br></pre></td></tr></table></figure><p>当执行 <code>hexo deploy</code>命令时，插件会比较在线版本和本地版本<code>newPost.json</code>中最新文章更新时间是否一致，如果不同，则插件将推送最新文章更新通知（默认为十分钟后推送）</p><p><strong>注意</strong>：如果您是第一次使用本地测试应该看到</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INFO  无文章更新 或 为首次推送更新</span><br></pre></td></tr></table></figure><p>这是正常现象，因为此时你的网站还没有<code>newPost.json</code>这个文件，后续有更新时将正常推送，你可以先推送一次，再修改更新时间测试一次，当然建议测试目标选择自己，即sid选项配置</p><p>当然如果您在使用过程中有什么问题或遇到了Bug也欢迎随时在评论区或<a href="https://github.com/Rock-Candy-Tea/hexo-webpushr-notification/issues">issues</a>反馈，当然因为本人是菜鸡，所以有大佬PR最好了</p><h2 id="推送效果">推送效果<a class="fa-solid fa-hashtag" href="#推送效果"></a></h2><p>因为我是通知自动隐藏后才截图，所以大致效果如下所示</p><p><img src="https://cdn.nlark.com/yuque/0/2022/png/8391407/1664951686275-f37cb76d-34f6-40ed-94c6-9bed130d0605.png" alt="" loading="lazy"></p><h2 id="后续计划">后续计划<a class="fa-solid fa-hashtag" href="#后续计划"></a></h2><ul class="contains-task-list"><li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"> 兼容自定义<code>Service Work</code>的功能，因为<code>webpushr-sw.js</code>优先注册的原因，只能有一个 <code>sw</code>注册，无法注册自己编写脚本。</li><li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"> 支持参数更多的可自定义，启用或关闭。例如不延时，立即发送；不显示按钮（因为默认就是跳转到文章）等</li><li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"> 目前的判断逻辑，虽然可以根据更新时间来判断，但如果很久之前的文章翻新，只要更新时间最新，也会触发推送，主要针对 updated 方式，还没想到好的解决办法，目前就是确认需要推送才更改更新时间咯</li><li class="task-list-item"><input class="task-list-item-checkbox" disabled="" type="checkbox"> 优化代码，减少代码量</li></ul>]]></c:encoded></item><item><title>基于小波变换的图像去噪技术研究</title><link>https://blog.ccknbc.cc/posts/research-on-image-denoising-technology-based-on-wavelet-transform/</link><description>基于小波变换的图像去噪技术研究</description><author>CC康纳百川</author><category domain="https://blog.ccknbc.cc/categories/%E5%AD%A6%E4%B9%A0/">学习</category><category domain="https://blog.ccknbc.cc/tags/%E5%AD%A6%E4%B9%A0/">学习</category><pubDate>Mon, 02 Mar 2026 00:50:28 GMT</pubDate><c:encoded><![CDATA[<p>本文首发在<a href="https://www.yuque.com/ccknbc/blog/18"><strong>语雀</strong></a></p><p>自动同步更新至<a href="https://blog.ccknbc.cc/posts/research-on-image-denoising-technology-based-on-wavelet-transform"><strong>CC的部落格</strong></a></p><p><font style="color:#333333;"><div class="note warning simple"><p>这篇文章占个坑，目前不会公开，因为成为了毕业论文</p></div></font></p>]]></c:encoded></item><item><title>提问的智慧</title><link>https://blog.ccknbc.cc/posts/how-to-ask-questions-the-smart-way/</link><description>Copyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu，本中文指南是基于原文 3.10 版以及 2010 年由 Gasolin 所翻译版本的最新翻译</description><author>CC康纳百川</author><category domain="https://blog.ccknbc.cc/categories/%E5%AD%A6%E4%B9%A0/">学习</category><category domain="https://blog.ccknbc.cc/tags/%E5%AD%A6%E4%B9%A0/">学习</category><pubDate>Mon, 02 Mar 2026 00:50:28 GMT</pubDate><c:encoded><![CDATA[<p>本文首发在<a href="https://www.yuque.com/ccknbc/blog/17/"><strong>语雀</strong></a></p><p>自动同步更新至<a href="https://blog.ccknbc.cc/posts/how-to-ask-questions-the-smart-way/"><strong>CC的部落格</strong></a></p><h2 id="提问的智慧">提问的智慧<a class="fa-solid fa-hashtag" href="#提问的智慧"></a></h2><p>How To Ask Questions The Smart Way</p><p>Copyright © 2001, 2006, 2014 Eric S. Raymond, Rick Moen</p><p>本指南英文版版权为 Eric S. Raymond, Rick Moen 所有。</p><p>原文网址：<a href="http://www.catb.org/~esr/faqs/smart-questions.html">http://www.catb.org/~esr/faqs/smart-questions.html</a></p><p>Copyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu</p><p>本中文指南是基于原文 3.10 版以及 2010 年由 Gasolin 所翻译版本的最新翻译；</p><p>协助指出翻译问题，请发 Issue，或直接发 Pull Request 给我。</p><p>本文另有繁體中文版（若在本站查看，可使用右下角简繁转换按钮）。</p><h2 id="原文版本历史"><a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/history.md">原文版本历史</a><a class="fa-solid fa-hashtag" href="#原文版本历史"></a></h2><h2 id="目录">目录<a class="fa-solid fa-hashtag" href="#目录"></a></h2><ul><li><a href="#%E5%A3%B0%E6%98%8E">声明</a></li><li><a href="#%E7%AE%80%E4%BB%8B">简介</a></li><li><a href="#%E5%9C%A8%E6%8F%90%E9%97%AE%E4%B9%8B%E5%89%8D">在提问之前</a></li><li><a href="#%E5%BD%93%E4%BD%A0%E6%8F%90%E9%97%AE%E6%97%B6">当你提问时</a><ul><li><a href="#%E6%85%8E%E9%80%89%E6%8F%90%E9%97%AE%E7%9A%84%E8%AE%BA%E5%9D%9B">慎选提问的论坛</a></li><li><a href="#stack-overflow">Stack Overflow</a></li><li><a href="#%E7%BD%91%E7%AB%99%E5%92%8C-irc-%E8%AE%BA%E5%9D%9B">网站和 IRC 论坛</a></li><li><a href="#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E4%BD%BF%E7%94%A8%E9%A1%B9%E7%9B%AE%E9%82%AE%E4%BB%B6%E5%88%97%E8%A1%A8">第二步，使用项目邮件列表</a></li><li><a href="#%E4%BD%BF%E7%94%A8%E6%9C%89%E6%84%8F%E4%B9%89%E4%B8%94%E6%8F%8F%E8%BF%B0%E6%98%8E%E7%A1%AE%E7%9A%84%E6%A0%87%E9%A2%98">使用有意义且描述明确的标题</a></li><li><a href="#%E4%BD%BF%E9%97%AE%E9%A2%98%E5%AE%B9%E6%98%93%E5%9B%9E%E5%A4%8D">使问题容易回复</a></li><li><a href="#%E7%94%A8%E6%B8%85%E6%99%B0%E3%80%81%E6%AD%A3%E7%A1%AE%E3%80%81%E7%B2%BE%E5%87%86%E4%B8%94%E8%AF%AD%E6%B3%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E8%AF%AD%E5%8F%A5">用清晰、正确、精准且语法正确的语句</a></li><li><a href="#%E4%BD%BF%E7%94%A8%E6%98%93%E4%BA%8E%E8%AF%BB%E5%8F%96%E4%B8%94%E6%A0%87%E5%87%86%E7%9A%84%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%8F%91%E9%80%81%E9%97%AE%E9%A2%98">使用易于读取且标准的文件格式发送问题</a></li><li><a href="#%E7%B2%BE%E7%A1%AE%E5%9C%B0%E6%8F%8F%E8%BF%B0%E9%97%AE%E9%A2%98%E5%B9%B6%E8%A8%80%E4%B9%8B%E6%9C%89%E7%89%A9">精确地描述问题并言之有物</a></li><li><a href="#%E8%AF%9D%E4%B8%8D%E5%9C%A8%E5%A4%9A%E8%80%8C%E5%9C%A8%E7%B2%BE">话不在多而在精</a></li><li><a href="#%E5%88%AB%E5%8A%A8%E8%BE%84%E5%A3%B0%E7%A7%B0%E6%89%BE%E5%88%B0-bug">别动辄声称找到 Bug</a></li><li><a href="#%E4%BD%8E%E5%A3%B0%E4%B8%8B%E6%B0%94%E4%B8%8D%E8%83%BD%E4%BB%A3%E6%9B%BF%E4%BD%A0%E7%9A%84%E5%8A%9F%E8%AF%BE">低声下气不能代替你的功课</a></li><li><a href="#%E6%8F%8F%E8%BF%B0%E9%97%AE%E9%A2%98%E7%97%87%E7%8A%B6%E8%80%8C%E9%9D%9E%E4%BD%A0%E7%9A%84%E7%8C%9C%E6%B5%8B">描述问题症状而非你的猜测</a></li><li><a href="#%E6%8C%89%E5%8F%91%E7%94%9F%E6%97%B6%E9%97%B4%E5%85%88%E5%90%8E%E5%88%97%E5%87%BA%E9%97%AE%E9%A2%98%E7%97%87%E7%8A%B6">按发生时间先后列出问题症状</a></li><li><a href="#%E6%8F%8F%E8%BF%B0%E7%9B%AE%E6%A0%87%E8%80%8C%E4%B8%8D%E6%98%AF%E8%BF%87%E7%A8%8B">描述目标而不是过程</a></li><li><a href="#%E5%88%AB%E8%A6%81%E6%B1%82%E4%BD%BF%E7%94%A8%E7%A7%81%E4%BA%BA%E7%94%B5%E9%82%AE%E5%9B%9E%E5%A4%8D">别要求使用私人电邮回复</a></li><li><a href="#%E6%B8%85%E6%A5%9A%E6%98%8E%E7%A1%AE%E7%9A%84%E8%A1%A8%E8%BE%BE%E4%BD%A0%E7%9A%84%E9%97%AE%E9%A2%98%E4%BB%A5%E5%8F%8A%E9%9C%80%E6%B1%82">清楚明确的表达你的问题以及需求</a></li><li><a href="#%E8%AF%A2%E9%97%AE%E6%9C%89%E5%85%B3%E4%BB%A3%E7%A0%81%E7%9A%84%E9%97%AE%E9%A2%98%E6%97%B6">询问有关代码的问题时</a></li><li><a href="#%E5%88%AB%E6%8A%8A%E8%87%AA%E5%B7%B1%E5%AE%B6%E5%BA%AD%E4%BD%9C%E4%B8%9A%E7%9A%84%E9%97%AE%E9%A2%98%E8%B4%B4%E4%B8%8A%E6%9D%A5">别把自己家庭作业的问题贴上来</a></li><li><a href="#%E5%8E%BB%E6%8E%89%E6%97%A0%E6%84%8F%E4%B9%89%E7%9A%84%E6%8F%90%E9%97%AE%E5%8F%A5">去掉无意义的提问句</a></li><li><a href="#%E5%8D%B3%E4%BD%BF%E4%BD%A0%E5%BE%88%E6%80%A5%E4%B9%9F%E4%B8%8D%E8%A6%81%E5%9C%A8%E6%A0%87%E9%A2%98%E5%86%99%E7%B4%A7%E6%80%A5">即使你很急也不要在标题写紧急</a></li><li><a href="#%E7%A4%BC%E5%A4%9A%E4%BA%BA%E4%B8%8D%E6%80%AA%E8%80%8C%E4%B8%94%E6%9C%89%E6%97%B6%E8%BF%98%E5%BE%88%E6%9C%89%E5%B8%AE%E5%8A%A9">礼多人不怪，而且有时还很有帮助</a></li><li><a href="#%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E5%90%8E%E5%8A%A0%E4%B8%AA%E7%AE%80%E7%9F%AD%E7%9A%84%E8%A1%A5%E5%85%85%E8%AF%B4%E6%98%8E">问题解决后，加个简短的补充说明</a></li></ul></li><li><a href="#%E5%A6%82%E4%BD%95%E8%A7%A3%E8%AF%BB%E7%AD%94%E6%A1%88">如何解读答案</a><ul><li><a href="#rtfm-%E5%92%8C-stfw%E5%A6%82%E4%BD%95%E7%9F%A5%E9%81%93%E4%BD%A0%E5%B7%B2%E5%AE%8C%E5%85%A8%E6%90%9E%E7%A0%B8%E4%BA%86">RTFM 和 STFW：如何知道你已完全搞砸了</a></li><li><a href="#%E5%A6%82%E6%9E%9C%E8%BF%98%E6%98%AF%E6%90%9E%E4%B8%8D%E6%87%82">如果还是搞不懂</a></li><li><a href="#%E5%A4%84%E7%90%86%E6%97%A0%E7%A4%BC%E7%9A%84%E5%9B%9E%E5%BA%94">处理无礼的回应</a></li></ul></li><li><a href="#%E5%A6%82%E4%BD%95%E9%81%BF%E5%85%8D%E6%89%AE%E6%BC%94%E5%A4%B1%E8%B4%A5%E8%80%85">如何避免扮演失败者</a></li><li><a href="#%E4%B8%8D%E8%AF%A5%E9%97%AE%E7%9A%84%E9%97%AE%E9%A2%98">不该问的问题</a></li><li><a href="#%E5%A5%BD%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A0%A2%E9%97%AE%E9%A2%98">好问题与蠢问题</a></li><li><a href="#%E5%A6%82%E6%9E%9C%E5%BE%97%E4%B8%8D%E5%88%B0%E5%9B%9E%E7%AD%94">如果得不到回答</a></li><li><a href="#%E5%A6%82%E4%BD%95%E6%9B%B4%E5%A5%BD%E5%9C%B0%E5%9B%9E%E7%AD%94%E9%97%AE%E9%A2%98">如何更好地回答问题</a></li><li><a href="#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%BA%90">相关资源</a></li><li><a href="#%E9%B8%A3%E8%B0%A2">鸣谢</a></li></ul><h2 id="声明">声明<a class="fa-solid fa-hashtag" href="#声明"></a></h2><p>许多项目在他们的使用协助/说明网页中链接了本指南，这么做很好，我们也鼓励大家都这么做。但如果你是负责管理这个项目网页的人，请在超链接附近的显著位置上注明：</p><p>本指南不提供此项目的实际支持服务！</p><p>我们已经深刻领教到少了上述声明所带来的痛苦。因为少了这点声明，我们不停地被一些白痴纠缠。这些白痴认为既然我们发布了这本指南，那么我们就有责任解决世上所有的技术问题。</p><p>如果你是因为需要某些协助而正在阅读这本指南，并且最后离开是因为发现从本指南作者们身上得不到直接的协助，那么你就是我们所说的那些白痴之一。别问我们问题，我们只会忽略你。我们在这本指南中是教你如何从那些真正懂得你所遇到软件或硬件问题的人取得协助，而 99% 的情况下那不会是我们。除非你确定本指南的作者之一刚好是你所遇到的问题领域的专家，否则请不要打扰我们，这样大家都会开心一点。</p><h2 id="简介">简介<a class="fa-solid fa-hashtag" href="#简介"></a></h2><p>在黑客的世界里，当你拋出一个技术问题时，最终是否能得到有用的回答，往往取决于你所提问和追问的方式。本指南将教你如何正确的提问以获得你满意的答案。</p><p>不只是黑客，现在开源（Open Source）软件已经相当盛行，你常常也可以由其他有经验的使用者身上得到好答案，这是件好事；使用者比起黑客来，往往对那些新手常遇到的问题更宽容一些。然而，将有经验的使用者视为黑客，并采用本指南所提的方法与他们沟通，同样也是能从他们身上得到满意回答的最有效方式。</p><p>首先你应该明白，黑客们喜爱有挑战性的问题，或者能激发他们思维的好问题。如果我们并非如此，那我们也不会成为你想询问的对象。如果你给了我们一个值得反复咀嚼玩味的好问题，我们自会对你感激不尽。好问题是激励，是厚礼。好问题可以提高我们的理解力，而且通常会暴露我们以前从没意识到或者思考过的问题。对黑客而言，&quot;好问题！&quot;是诚挚的大力称赞。</p><p>尽管如此，黑客们有着蔑视或傲慢面对简单问题的坏名声，这有时让我们看起来对新手、无知者似乎较有敌意，但其实不是那样的。</p><p>我们不讳言我们对那些不愿思考、或者在发问前不做他们该做的事的人的蔑视。那些人是时间杀手 —— 他们只想索取，从不付出，消耗我们可用在更有趣的问题或更值得回答的人身上的时间。我们称这样的人为 失败者（撸瑟） （由于历史原因，我们有时把它拼作 lusers）。</p><p>我们意识到许多人只是想使用我们写的软件，他们对学习技术细节没有兴趣。对大多数人而言，电脑只是种工具，是种达到目的的手段而已。他们有自己的生活并且有更要紧的事要做。我们了解这点，也从不指望每个人都对这些让我们着迷的技术问题感兴趣。尽管如此，我们回答问题的风格是指向那些真正对此有兴趣并愿意主动参与解决问题的人，这一点不会变，也不该变。如果连这都变了，我们就是在降低做自己最擅长的事情上的效率。</p><p>我们（在很大程度上）是自愿的，从繁忙的生活中抽出时间来解答疑惑，而且时常被提问淹没。所以我们无情的滤掉一些话题，特别是拋弃那些看起来像失败者的家伙，以便更高效的利用时间来回答赢家（winner）的问题。</p><p>如果你厌恶我们的态度，高高在上，或过于傲慢，不妨也设身处地想想。我们并没有要求你向我们屈服 —— 事实上，我们大多数人非常乐意与你平等地交流，只要你付出小小努力来满足基本要求，我们就会欢迎你加入我们的文化。但让我们帮助那些不愿意帮助自己的人是没有效率的。无知没有关系，但装白痴就是不行。</p><p>所以，你不必在技术上很在行才能吸引我们的注意，但你必须表现出能引导你变得在行的特质 —— 机敏、有想法、善于观察、乐于主动参与解决问题。如果你做不到这些使你与众不同的事情，我们建议你花点钱找家商业公司签个技术支持服务合同，而不是要求黑客个人无偿地帮助你。</p><p>如果你决定向我们求助，当然你也不希望被视为失败者，更不愿成为失败者中的一员。能立刻得到快速并有效答案的最好方法，就是像赢家那样提问 —— 聪明、自信、有解决问题的思路，只是偶尔在特定的问题上需要获得一点帮助。</p><p>（欢迎对本指南提出改进意见。你可以 email 你的建议至 <a href="mailto:esr@thyrsus.com">esr@thyrsus.com</a> 或 <a href="mailto:respond-auto@linuxmafia.com">respond-auto@linuxmafia.com</a>。然而请注意，本文并非网络礼节的通用指南，而我们通常会拒绝无助于在技术论坛得到有用答案的建议）。</p><h2 id="在提问之前">在提问之前<a class="fa-solid fa-hashtag" href="#在提问之前"></a></h2><p>在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前，请先做到以下事情：</p><ol><li>尝试在你准备提问的论坛的旧文章中搜索答案。</li><li>尝试上网搜索以找到答案。</li><li>尝试阅读手册以找到答案。</li><li>尝试阅读常见问题文件（FAQ）以找到答案。</li><li>尝试自己检查或试验以找到答案。</li><li>向你身边的强者朋友打听以找到答案。</li><li>如果你是程序开发者，请尝试阅读源代码以找到答案。</li></ol><p>当你提出问题的时候，请先表明你已经做了上述的努力；这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所学到的东西会更好，因为我们更乐于回答那些表现出能从答案中学习的人的问题。</p><p>运用某些策略，比如先用 Google 搜索你所遇到的各种错误信息（搜索 Google 论坛和网页），这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果，在邮件列表或新闻组寻求帮助时加上一句 我在 Google 中搜过下列句子但没有找到什么有用的东西 也是件好事，即使它只是表明了搜索引擎不能提供哪些帮助。这么做（加上搜索过的字串）也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。</p><p>别着急，不要指望几秒钟的 Google 搜索就能解决一个复杂的问题。在向专家求助之前，再阅读一下常见问题文件（FAQ）、放轻松、坐舒服一些，再花点时间思考一下这个问题。相信我们，他们能从你的提问看出你做了多少阅读与思考，如果你是有备而来，将更有可能得到解答。不要将所有问题一股脑拋出，只因你的第一次搜索没有找到答案（或者找到太多答案）。</p><p>准备好你的问题，再将问题仔细的思考过一遍，因为草率的发问只能得到草率的回答，或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力，你越有可能得到实质性的帮助。</p><p>小心别问错了问题。如果你的问题基于错误的假设，某个普通黑客（J. Random Hacker）多半会一边在心里想着蠢问题…， 一边用无意义的字面解释来答复你，希望着你会从问题的回答（而非你想得到的答案）中汲取教训。</p><p>绝不要自以为够格得到答案，你没有；你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去挣到一个答案，靠提出有内涵的、有趣的、有思维激励作用的问题 —— 一个有潜力能贡献社区经验的问题，而不仅仅是被动的从他人处索取知识。</p><p>另一方面，表明你愿意在找答案的过程中做点什么是一个非常好的开端。谁能给点提示？、我的这个例子里缺了什么？以及我应该检查什么地方比请把我需要的确切的过程贴出来更容易得到答复。因为你表现出只要有人能指个正确方向，你就有完成它的能力和决心。</p><h2 id="当你提问时">当你提问时<a class="fa-solid fa-hashtag" href="#当你提问时"></a></h2><h3 id="慎选提问的论坛">慎选提问的论坛<a class="fa-solid fa-hashtag" href="#慎选提问的论坛"></a></h3><p>小心选择你要提问的场合。如果你做了下述的事情，你很可能被忽略掉或者被看作失败者：</p><ul><li>在与主题不合的论坛上贴出你的问题。</li><li>在探讨进阶技术问题的论坛张贴非常初级的问题；反之亦然。</li><li>在太多的不同新闻群组上重复转贴同样的问题（cross-post）。</li><li>向既非熟人也没有义务解决你问题的人发送私人电邮。</li></ul><p>黑客会剔除掉那些搞错场合的问题，以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。</p><p>因此，第一步是找到对的论坛。再说一次，Google 和其它搜索引擎还是你的朋友，用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题（FAQ）、邮件列表及相关说明文件的链接。如果你的努力（包括阅读 FAQ）都没有结果，网站上也许还有报告 Bug（Bug-reporting）的流程或链接，如果是这样，链过去看看。</p><p>向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说，别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 —— 如果你不确定，那就向别处发送，或者压根别发。</p><p>在选择论坛、新闻群组或邮件列表时，别太相信名字，先看看 FAQ 或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题，这样可以让你感受一下那里的文化。事实上，事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意，也许这样就找到答案了。即使没有，也能帮助你归纳出更好的问题。</p><p>别像机关枪似的一次&quot;扫射&quot;所有的帮助渠道，这就像大喊大叫一样会使人不快。要一个一个地来。</p><p>搞清楚你的主题！最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于 Unix 或 Windows 操作系统程序界面的问题。如果你不明白为什么这是大错，最好在搞清楚这之间差异之前什么也别问。</p><p>一般来说，在仔细挑选的公共论坛中提问，会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点，一是看潜在的回复者有多少，二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。</p><p>可以理解的是，老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样，你的加入也有可能使情况走向极端 —— 已经好几次了，一些热门软件的作者从自己软件的支持中抽身出来，因为伴随而来涌入其私人邮箱的无用邮件变得无法忍受。</p><h3 id="Stack-Overflow">Stack Overflow<a class="fa-solid fa-hashtag" href="#Stack-Overflow"></a></h3><p>搜索，然后 在 Stack Exchange 问。</p><p>近年来，Stack Exchange community 社区已经成为回答技术及其他问题的主要渠道，尤其是那些开放源码的项目。</p><p>因为 Google 索引是即时的，在看 Stack Exchange 之前先在 Google 搜索。有很高的机率某人已经问了一个类似的问题，而且 Stack Exchange 网站们往往会是搜索结果中最前面几个。如果你在 Google 上没有找到任何答案，你再到特定相关主题的网站去找。用标签（Tag）搜索能让你更缩小你的搜索结果。</p><p>Stack Exchange 已经成长到超过一百个网站，以下是最常用的几个站：</p><ul><li>Super User 是问一些通用的电脑问题，如果你的问题跟代码或是写程序无关，只是一些网络连线之类的，请到这里。</li><li>Stack Overflow 是问写程序有关的问题。</li><li>Server Fault 是问服务器和网管相关的问题。</li></ul><h3 id="网站和-IRC-论坛">网站和 IRC 论坛<a class="fa-solid fa-hashtag" href="#网站和-IRC-论坛"></a></h3><p>本地的使用者群组（user group），或者你所用的 Linux 发行版本也许正在宣传他们的网页论坛或 IRC 频道，并提供新手帮助（在一些非英语国家，新手论坛很可能还是邮件列表）， 这些地方是开始提问的好首选，特别是当你觉得遇到的也许只是相对简单或者很普通的问题时。有广告赞助的 IRC 频道是公开欢迎提问的地方，通常可以即时得到回应。</p><p>事实上，如果程序出的问题只发生在特定 Linux 发行版提供的版本（这很常见），最好先去该发行版的论坛或邮件列表中提问，再到程序本身的论坛或邮件列表提问。（否则）该项目的黑客可能仅仅回复 “用我们的版本”。</p><p>在任何论坛发文以前，先确认一下有没有搜索功能。如果有，就试着搜索一下问题的几个关键词，也许这会有帮助。如果在此之前你已做过通用的网页搜索（你也该这样做），还是再搜索一下论坛，搜索引擎有可能没来得及索引此论坛的全部内容。</p><p>通过论坛或 IRC 频道来提供使用者支持服务有增长的趋势，电子邮件则大多为项目开发者间的交流而保留。所以最好先在论坛或 IRC 中寻求与该项目相关的协助。</p><p>在使用 IRC 的时候，首先最好不要发布很长的问题描述，有些人称之为频道洪水。最好通过一句话的问题描述来开始聊天。</p><h3 id="第二步，使用项目邮件列表">第二步，使用项目邮件列表<a class="fa-solid fa-hashtag" href="#第二步，使用项目邮件列表"></a></h3><p>当某个项目提供开发者邮件列表时，要向列表而不是其中的个别成员提问，即使你确信他能最好地回答你的问题。查一查项目的文件和首页，找到项目的邮件列表并使用它。有几个很好的理由支持我们采用这种办法：</p><ul><li>任何好到需要向个别开发者提出的问题，也将对整个项目群组有益。反之，如果你认为自己的问题对整个项目群组来说太愚蠢，也不能成为骚扰个别开发者的理由。</li><li>向列表提问可以分散开发者的负担，个别开发者（尤其是项目领导人）也许太忙以至于没法回答你的问题。</li><li>大多数邮件列表都会被存档，那些被存档的内容将被搜索引擎索引。如果你向列表提问并得到解答，将来其它人可以通过网页搜索找到你的问题和答案，也就不用再次发问了。</li><li>如果某些问题经常被问到，开发者可以利用此信息来改进说明文件或软件本身，以使其更清楚。如果只是私下提问，就没有人能看到最常见问题的完整场景。</li></ul><p>如果一个项目既有&quot;使用者&quot; 也有&quot;开发者&quot;（或&quot;黑客&quot;）邮件列表或论坛，而你又不会动到那些源代码，那么就向&quot;使用者&quot;列表或论坛提问。不要假设自己会在开发者列表中受到欢迎，那些人多半会将你的提问视为干扰他们开发的噪音。</p><p>然而，如果你确信你的问题很特别，而且在&quot;使用者&quot; 列表或论坛中几天都没有回复，可以试试前往&quot;开发者&quot;列表或论坛发问。建议你在张贴前最好先暗地里观察几天以了解那里的行事方式（事实上这是参与任何私有或半私有列表的好主意）</p><p>如果你找不到一个项目的邮件列表，而只能查到项目维护者的电子邮件地址，尽管向他发信。即使是在这种情况下，也别假设（项目）邮件列表不存在。在你的电子邮件中，请陈述你已经试过但没有找到合适的邮件列表，也提及你不反对将自己的邮件转发给他人（许多人认为，即使没什么秘密，私人电子邮件也不应该被公开。通过允许将你的电子邮件转发他人，你给了相应人员处置你邮件的选择）。</p><h3 id="使用有意义且描述明确的标题">使用有意义且描述明确的标题<a class="fa-solid fa-hashtag" href="#使用有意义且描述明确的标题"></a></h3><p>在邮件列表、新闻群组或论坛中，大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的帮帮忙、跪求、急（更别说救命啊!!! 这样让人反感的话，用这种标题会被条件反射式地忽略）来浪费这个机会。不要妄想用你的痛苦程度来打动我们，而应该是在这点空间中使用极简单扼要的描述方式来提出问题。</p><p>一个好标题范例是目标 —— 差异式的描述，许多技术支持组织就是这样做的。在目标部分指出是哪一个或哪一组东西有问题，在差异部分则描述与期望的行为不一致的地方。</p><blockquote><p>蠢问题：救命啊！我的笔记本电脑不能正常显示了！</p></blockquote><blockquote><p>聪明问题：<a href="http://X.org">X.org</a> 6.8.1 的鼠标光标会变形，某牌显卡 MV1005 芯片组。</p></blockquote><blockquote><p>更聪明问题：<a href="http://X.org">X.org</a> 6.8.1 的鼠标光标，在某牌显卡 MV1005 芯片组环境下 - 会变形。</p></blockquote><p>编写目标 —— 差异 式描述的过程有助于你组织对问题的细致思考。是什么被影响了？ 仅仅是鼠标光标或者还有其它图形？只在 <a href="http://X.org">X.org</a> 的 X 版中出现？或只是出现在 6.8.1 版中？ 是针对某牌显卡芯片组？或者只是其中的 MV1005 型号？ 一个黑客只需瞄一眼就能够立即明白你的环境和你遇到的问题。</p><p>总而言之，请想像一下你正在一个只显示标题的存档讨论串（Thread）索引中查寻。让你的标题更好地反映问题，可使下一个搜索类似问题的人能够关注这个讨论串，而不用再次提问相同的问题。</p><p>如果你想在回复中提出问题，记得要修改内容标题，以表明你是在问一个问题， 一个看起来像 Re: 测试 或者 Re: 新 bug 的标题很难引起足够重视。另外，在不影响连贯性之下，适当引用并删减前文的内容，能给新来的读者留下线索。</p><p>对于讨论串，不要直接点击回复来开始一个全新的讨论串，这将限制你的观众。因为有些邮件阅读程序，比如 mutt ，允许使用者按讨论串排序并通过折叠讨论串来隐藏消息，这样做的人永远看不到你发的消息。</p><p>仅仅改变标题还不够。mutt 和其它一些邮件阅读程序还会检查邮件标题以外的其它信息，以便为其指定讨论串。所以宁可发一个全新的邮件。</p><p>在网页论坛上，好的提问方式稍有不同，因为讨论串与特定的信息紧密结合，并且通常在讨论串外就看不到里面的内容，故通过回复提问，而非改变标题是可接受的。不是所有论坛都允许在回复中出现分离的标题，而且这样做了基本上没有人会去看。不过，通过回复提问，这本身就是暧昧的做法，因为它们只会被正在查看该标题的人读到。所以，除非你只想在该讨论串当前活跃的人群中提问，不然还是另起炉灶比较好。</p><h3 id="使问题容易回复">使问题容易回复<a class="fa-solid fa-hashtag" href="#使问题容易回复"></a></h3><p>以请将你的回复发送到……来结束你的问题多半会使你得不到回答。如果你觉得花几秒钟在邮件客户端设置一下回复地址都麻烦，我们也觉得花几秒钟思考你的问题更麻烦。如果你的邮件程序不支持这样做，换个好点的；如果是操作系统不支持这种邮件程序，也换个好点的。</p><p>在论坛，要求通过电子邮件回复是非常无礼的，除非你认为回复的信息可能比较敏感（有人会为了某些未知的原因，只让你而不是整个论坛知道答案）。如果你只是想在有人回复讨论串时得到电子邮件提醒，可以要求网页论坛发送给你。几乎所有论坛都支持诸如追踪此讨论串、有回复时发送邮件提醒等功能。</p><h3 id="用清晰、正确、精准且语法正确的语句">用清晰、正确、精准且语法正确的语句<a class="fa-solid fa-hashtag" href="#用清晰、正确、精准且语法正确的语句"></a></h3><p>我们从经验中发现，粗心的提问者通常也会粗心的写程序与思考（我敢打包票）。回答粗心大意者的问题很不值得，我们宁愿把时间耗在别处。</p><p>正确的拼写、标点符号和大小写是很重要的。一般来说，如果你觉得这样做很麻烦，不想在乎这些，那我们也觉得麻烦，不想在乎你的提问。花点额外的精力斟酌一下字句，用不着太僵硬与正式 —— 事实上，黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它必须很准确，而且有迹象表明你是在思考和关注问题。</p><p>正确地拼写、使用标点和大小写，不要将 its 混淆为 it’s，loose 搞成 lose 或者将 discrete 弄成 discreet。不要全部用大写，这会被视为无礼的大声嚷嚷（全部小写也好不到哪去，因为不易阅读。Alan Cox 也许可以这样做，但你不行）。</p><p>更白话的说，如果你写得像是个半文盲 [译注：小白]，那多半得不到理睬。也不要使用即时通信中的简写或火星文，如将的简化为 d 会使你看起来像一个为了少打几个键而省字的小白。更糟的是，如果像个小孩似地鬼画符那绝对是在找死，可以肯定没人会理你（或者最多是给你一大堆指责与挖苦）。</p><p>如果在使用非母语的论坛提问，你可以犯点拼写和语法上的小错，但决不能在思考上马虎（没错，我们通常能弄清两者的分别）。同时，除非你知道回复者使用的语言，否则请使用英语书写。繁忙的黑客一般会直接删除用他们看不懂语言写的消息。在网络上英语是通用语言，用英语书写可以将你的问题在尚未被阅读就被直接删除的可能性降到最低。</p><p>如果英文是你的外语（Second language），提示潜在回复者你有潜在的语言困难是很好的：</p><p>[译注：以下附上原文以供使用]</p><blockquote><p>English is not my native language; please excuse typing errors.</p></blockquote><ul><li>英文不是我的母语，请原谅我的错字或语法。</li></ul><blockquote><p>If you speak $LANGUAGE, please email/PM me;</p><p>I may need assistance translating my question.</p></blockquote><ul><li>如果你说<strong>某语言</strong>，请寄信/私讯给我；我需要有人协助我翻译我的问题。</li></ul><blockquote><p>I am familiar with the technical terms,</p><p>but some slang expressions and idioms are difficult for me.</p></blockquote><ul><li>我对技术名词很熟悉，但对于俗语或是特别用法比较不甚了解。</li></ul><blockquote><p>I’ve posted my question in $LANGUAGE and English.</p><p>I’ll be glad to translate responses, if you only use one or the other.</p></blockquote><ul><li>我把我的问题用<strong>某语言</strong>和英文写出来，如果你只用一种语言回答，我会乐意将其翻译成另一种。</li></ul><h3 id="使用易于读取且标准的文件格式发送问题">使用易于读取且标准的文件格式发送问题<a class="fa-solid fa-hashtag" href="#使用易于读取且标准的文件格式发送问题"></a></h3><p>如果你人为地将问题搞得难以阅读，它多半会被忽略，人们更愿读易懂的问题，所以：</p><ul><li>使用纯文字而不是 HTML (<a href="http://archive.birdhouse.org/etc/evilmail.html">关闭 HTML</a> 并不难）。</li><li>使用 MIME 附件通常是可以的，前提是真正有内容（譬如附带的源代码或 patch），而不仅仅是邮件程序生成的模板（譬如只是信件内容的拷贝）。</li><li>不要发送一段文字只是一行句子但自动换行后会变成多行的邮件（这使得回复部分内容非常困难）。设想你的读者是在 80 个字符宽的终端机上阅读邮件，最好设置你的换行分割点小于 80 字。</li><li>但是，对一些特殊的文件<strong>不要</strong>设置固定宽度（譬如日志档案拷贝或会话记录）。数据应该原样包含，让回复者有信心他们看到的是和你看到的一样的东西。</li><li>在英语论坛中，不要使用<code>Quoted-Printable</code> MIME 编码发送消息。这种编码对于张贴非 ASCII 语言可能是必须的，但很多邮件程序并不支持这种编码。当它们处理换行时，那些文本中四处散布的<code>=20</code>符号既难看也分散注意力，甚至有可能破坏内容的语意。</li><li>绝对，<strong>永远</strong>不要指望黑客们阅读使用封闭格式编写的文档，像微软公司的 Word 或 Excel 文件等。大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你家门口时你的反应一样。即便他们能够处理，他们也很厌恶这么做。</li><li>如果你从使用 Windows 的电脑发送电子邮件，关闭微软愚蠢的<code>智能引号</code>功能 （从[选项] &gt; [校订] &gt; [自动校正选项]，勾选掉<code>智能引号</code>单选框），以免在你的邮件中到处散布垃圾字符。</li><li>在论坛，勿滥用<code>表情符号</code>和<code>HTML</code>功能（当它们提供时）。一两个表情符号通常没有问题，但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。这通常不是个好主意，除非你只是对性而不是对答案感兴趣。</li></ul><p>如果你使用图形用户界面的邮件程序（如微软公司的 Outlook 或者其它类似的），注意它们的默认设置不一定满足这些要求。大多数这类程序有基于选单的查看源代码命令，用它来检查发送文件夹中的邮件，以确保发送的是纯文本文件同时没有一些奇怪的字符。</p><h3 id="精确地描述问题并言之有物">精确地描述问题并言之有物<a class="fa-solid fa-hashtag" href="#精确地描述问题并言之有物"></a></h3><ul><li>仔细、清楚地描述你的问题或 Bug 的症状。</li><li>描述问题发生的环境（机器配置、操作系统、应用程序、以及相关的信息），提供经销商的发行版和版本号（如：<code>Fedora Core 4</code>、<code>Slackware 9.1</code>等）。</li><li>描述在提问前你是怎样去研究和理解这个问题的。</li><li>描述在提问前为确定问题而采取的诊断步骤。</li><li>描述最近做过什么可能相关的硬件或软件变更。</li><li>尽可能的提供一个可以<code>重现这个问题的可控环境</code>的方法。</li></ul><p>尽量去揣测一个黑客会怎样反问你，在你提问之前预先将黑客们可能遇到的问题回答一遍。</p><p>以上几点中，当你报告的是你认为可能在代码中的问题时，给黑客一个可以重现你的问题的环境尤其重要。当你这么做时，你得到有效的回答的机会和速度都会大大的提升。</p><p>Simon Tatham 写过一篇名为《如何有效的报告 Bug》的出色文章。强力推荐你也读一读。</p><h3 id="话不在多而在精">话不在多而在精<a class="fa-solid fa-hashtag" href="#话不在多而在精"></a></h3><p>你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境，尽量将它剪裁得越小越好。</p><p>这样做的用处至少有三点。</p><p>第一，表现出你为简化问题付出了努力，这可以使你得到回答的机会增加；</p><p>第二，简化问题使你更有可能得到有用的答案；</p><p>第三，在精炼你的 bug 报告的过程中，你很可能就自己找到了解决方法或权宜之计。</p><h3 id="别动辄声称找到-Bug">别动辄声称找到 Bug<a class="fa-solid fa-hashtag" href="#别动辄声称找到-Bug"></a></h3><p>当你在使用软件中遇到问题，除非你非常、非常的有根据，不要动辄声称找到了 Bug。提示：除非你能提供解决问题的源代码补丁，或者提供回归测试来表明前一版本中行为不正确，否则你都多半不够完全确信。这同样适用在网页和文件，如果你（声称）发现了文件的 Bug，你应该能提供相应位置的修正或替代文件。</p><p>请记得，还有许多其它使用者没遇到你发现的问题，否则你在阅读文件或搜索网页时就应该发现了（你在抱怨前已经做了这些，是吧？）。这也意味着很有可能是你弄错了而不是软件本身有问题。</p><p>编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug，也就是在质疑他们的能力，即使你是对的，也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有 Bug 时，这尤其严重。</p><p>提问时，即使你私下非常确信已经发现一个真正的 Bug，最好写得像是你做错了什么。如果真的有 Bug，你会在回复中看到这点。这样做的话，如果真有 Bug，维护者就会向你道歉，这总比你惹恼别人然后欠别人一个道歉要好一点。</p><h3 id="低声下气不能代替你的功课">低声下气不能代替你的功课<a class="fa-solid fa-hashtag" href="#低声下气不能代替你的功课"></a></h3><p>有些人明白他们不该粗鲁或傲慢的提问并要求得到答复，但他们选择另一个极端 —— 低声下气：我知道我只是个可悲的新手，一个撸瑟，但… 这既使人困扰，也没有用，尤其是伴随着与实际问题含糊不清的描述时更令人反感。</p><p>别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是，尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。</p><p>有时网页论坛会设有专为新手提问的版面，如果你真的认为遇到了初学者的问题，到那去就是了，但一样别那么低声下气。</p><h3 id="描述问题症状而非你的猜测">描述问题症状而非你的猜测<a class="fa-solid fa-hashtag" href="#描述问题症状而非你的猜测"></a></h3><p>告诉黑客们你认为问题是怎样造成的并没什么帮助。（如果你的推断如此有效，还用向别人求助吗？），因此要确信你原原本本告诉了他们问题的症状，而不是你的解释和理论；让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要，清楚地说明这只是你的猜测，并描述为什么它们不起作用。</p><p>蠢问题</p><blockquote><p>我在编译内核时接连遇到 SIG11 错误，</p><p>我怀疑某条飞线搭在主板的走线上了，这种情况应该怎样检查最好？</p></blockquote><p>聪明问题</p><blockquote><p>我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU（威盛 Apollo VP2 芯片组），</p><p>256MB Corsair PC133 SDRAM 内存，在编译内核时，从开机 20 分钟以后就频频产生 SIG11 错误，</p><p>但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用，但是关机一晚上就又能工作 20 分钟。</p><p>所有内存都换过了，没有效果。相关部分的标准编译记录如下…。</p></blockquote><p>由于以上这点似乎让许多人觉得难以配合，这里有句话可以提醒你：所有的诊断专家都来自密苏里州。 美国国务院的官方座右铭则是：让我看看（出自国会议员 Willard D. Vandiver 在 1899 年时的讲话：我来自一个出产玉米，棉花，牛蒡和民主党人的国家，滔滔雄辩既不能说服我，也不会让我满意。我来自密苏里州，你必须让我看看。） 针对诊断者而言，这并不是一种怀疑，而只是一种真实而有用的需求，以便让他们看到的是与你看到的原始证据尽可能一致的东西，而不是你的猜测与归纳的结论。所以，大方的展示给我们看吧！</p><h3 id="按发生时间先后列出问题症状">按发生时间先后列出问题症状<a class="fa-solid fa-hashtag" href="#按发生时间先后列出问题症状"></a></h3><p>问题发生前的一系列操作，往往就是对找出问题最有帮助的线索。因此，你的说明里应该包含你的操作步骤，以及机器和软件的反应，直到问题发生。在命令行处理的情况下，提供一段操作记录（例如运行脚本工具所生成的），并引用相关的若干行（如 20 行）记录会非常有帮助。</p><p>如果挂掉的程序有诊断选项（如 -v 的详述开关），试着选择这些能在记录中增加调试信息的选项。记住，多不等于好。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。</p><p>如果你的说明很长（如超过四个段落），在开头简述问题，接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。</p><h3 id="描述目标而不是过程">描述目标而不是过程<a class="fa-solid fa-hashtag" href="#描述目标而不是过程"></a></h3><p>如果你想弄清楚如何做某事（而不是报告一个 Bug），在开头就描述你的目标，然后才陈述重现你所卡住的特定步骤。</p><p>经常寻求技术帮助的人在心中有个更高层次的目标，而他们在自以为能达到目标的特定道路上被卡住了，然后跑来问该怎么走，但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。</p><p>蠢问题</p><blockquote><p>我怎样才能从某绘图程序的颜色选择器中取得十六进制的的 RGB 值？</p></blockquote><p>聪明问题</p><blockquote><p>我正试着用替换一幅图片的色码（color table）成自己选定的色码，我现在知道的唯一方法是编辑每个色码区块（table slot），</p><p>但却无法从某绘图程序的颜色选择器取得十六进制的的 RGB 值。</p></blockquote><p>第二种提问法比较聪明，你可能得到像是建议采用另一个更合适的工具的回复。</p><h3 id="别要求使用私人电邮回复">别要求使用私人电邮回复<a class="fa-solid fa-hashtag" href="#别要求使用私人电邮回复"></a></h3><p>黑客们认为问题的解决过程应该公开、透明，此过程中如果更有经验的人注意到不完整或者不当之处，最初的回复才能够、也应该被纠正。同时，作为提供帮助者可以得到一些奖励，奖励就是他的能力和学识被其他同行看到。</p><p>当你要求私下回复时，这个过程和奖励都被中止。别这样做，让回复者来决定是否私下回答 —— 如果他真这么做了，通常是因为他认为问题编写太差或者太肤浅，以至于对其它人没有兴趣。</p><p>这条规则存在一条有限的例外，如果你确信提问可能会引来大量雷同的回复时，那么这个神奇的提问句会是向我发电邮，我将为论坛归纳这些回复。试着将邮件列表或新闻群组从洪水般的雷同回复中解救出来是非常有礼貌的 —— 但你必须信守诺言。</p><h3 id="清楚明确的表达你的问题以及需求">清楚明确的表达你的问题以及需求<a class="fa-solid fa-hashtag" href="#清楚明确的表达你的问题以及需求"></a></h3><p>漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人（他们忙是因为要亲自完成大部分工作）。这样的人对无节制的时间黑洞相当厌恶，所以他们也倾向于厌恶那些漫无边际的提问。</p><p>如果你明确表述需要回答者做什么（如提供指点、发送一段代码、检查你的补丁、或是其他等等），就最有可能得到有用的答案。因为这会定出一个时间和精力的上限，便于回答者能集中精力来帮你。这么做很棒。</p><p>要理解专家们所处的世界，请把专业技能想像为充裕的资源，而回复的时间则是稀缺的资源。你要求他们奉献的时间越少，你越有可能从真正专业而且很忙的专家那里得到解答。</p><p>所以，界定一下你的问题，使专家花在辨识你的问题和回答所需要付出的时间减到最少，这技巧对你有用答案相当有帮助 —— 但这技巧通常和简化问题有所区别。因此，问我想更好的理解 X，可否指点一下哪有好一点说明？通常比问你能解释一下 X 吗？更好。如果你的代码不能运作，通常请别人看看哪里有问题，比要求别人替你改正要明智得多。</p><h3 id="询问有关代码的问题时">询问有关代码的问题时<a class="fa-solid fa-hashtag" href="#询问有关代码的问题时"></a></h3><p>别要求他人帮你调试有问题的代码，不提示一下应该从何入手。张贴几百行的代码，然后说一声：它不能工作会让你完全被忽略。只贴几十行代码，然后说一句：在第七行以后，我期待它显示 <x>，但实际出现的是 <y>比较有可能让你得到回应。</p><p>最有效描述程序问题的方法是提供最精简的 Bug 展示测试用例（bug-demonstrating test case）。什么是最精简的测试用例？那是问题的缩影；一小个程序片段能刚好展示出程序的异常行为，而不包含其他令人分散注意力的内容。怎么制作最精简的测试用例？如果你知道哪一行或哪一段代码会造成异常的行为，复制下来并加入足够重现这个状况的代码（例如，足以让这段代码能被编译/直译/被应用程序处理）。如果你无法将问题缩减到一个特定区块，就复制一份代码并移除不影响产生问题行为的部分。总之，测试用例越小越好（查看话不在多而在精一节）。</p><p>一般而言，要得到一段相当精简的测试用例并不太容易，但永远先尝试这样做的是种好习惯。这种方式可以帮助你了解如何自行解决这个问题 —— 而且即使你的尝试不成功，黑客们也会看到你在尝试取得答案的过程中付出了努力，这可以让他们更愿意与你合作。</p><p>如果你只是想让别人帮忙审查（Review）一下代码，在信的开头就要说出来，并且一定要提到你认为哪一部分特别需要关注以及为什么。</p><h3 id="别把自己家庭作业的问题贴上来">别把自己家庭作业的问题贴上来<a class="fa-solid fa-hashtag" href="#别把自己家庭作业的问题贴上来"></a></h3><p>黑客们很擅长分辨哪些问题是家庭作业式的问题；因为我们中的大多数都曾自己解决这类问题。同样，这些问题得由你来搞定，你会从中学到东西。你可以要求给点提示，但别要求得到完整的解决方案。</p><p>如果你怀疑自己碰到了一个家庭作业式的问题，但仍然无法解决，试试在使用者群组，论坛或（最后一招）在项目的使用者邮件列表或论坛中提问。尽管黑客们会看出来，但一些有经验的使用者也许仍会给你一些提示。</p><h3 id="去掉无意义的提问句">去掉无意义的提问句<a class="fa-solid fa-hashtag" href="#去掉无意义的提问句"></a></h3><p>避免用无意义的话结束提问，例如有人能帮我吗？或者这有答案吗？。</p><p>首先：如果你对问题的描述不是很好，这样问更是画蛇添足。</p><p>其次：由于这样问是画蛇添足，黑客们会很厌烦你 —— 而且通常会用逻辑上正确，但毫无意义的回答来表示他们的蔑视， 例如：没错，有人能帮你或者不，没答案。</p><p>一般来说，避免用 是或否、对或错、有或没有类型的问句，除非你想得到是或否类型的回答。</p><h3 id="即使你很急也不要在标题写紧急">即使你很急也不要在标题写<code>紧急</code><a class="fa-solid fa-hashtag" href="#即使你很急也不要在标题写紧急"></a></h3><p>这是你的问题，不是我们的。宣称紧急极有可能事与愿违：大多数黑客会直接删除无礼和自私地企图即时引起关注的问题。更严重的是，紧急这个字（或是其他企图引起关注的标题）通常会被垃圾信过滤器过滤掉 —— 你希望能看到你问题的人可能永远也看不到。</p><p>有半个例外的情况是，如果你是在一些很高调，会使黑客们兴奋的地方，也许值得这样去做。在这种情况下，如果你有时间压力，也很有礼貌地提到这点，人们也许会有兴趣回答快一点。</p><p>当然，这风险很大，因为黑客们兴奋的点多半与你的不同。譬如从 NASA 国际空间站（International Space Station）发这样的标题没有问题，但用自我感觉良好的慈善行为或政治原因发肯定不行。事实上，张贴诸如紧急：帮我救救这个毛绒绒的小海豹！肯定让你被黑客忽略或惹恼他们，即使他们认为毛绒绒的小海豹很重要。</p><p>如果你觉得这点很不可思议，最好再把这份指南剩下的内容多读几遍，直到你弄懂了再发文。</p><h3 id="礼多人不怪，而且有时还很有帮助">礼多人不怪，而且有时还很有帮助<a class="fa-solid fa-hashtag" href="#礼多人不怪，而且有时还很有帮助"></a></h3><p>彬彬有礼，多用请和谢谢您的关注，或谢谢你的关照。让大家都知道你对他们花时间免费提供帮助心存感激。</p><p>坦白说，这一点并没有比清晰、正确、精准并合法语法和避免使用专用格式重要（也不能取而代之）。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告，而不是那种有礼但含糊的报告。（如果这点让你不解，记住我们是按问题能教给我们什么来评价问题的价值的）</p><p>然而，如果你有一串的问题待解决，客气一点肯定会增加你得到有用回应的机会。</p><p>（我们注意到，自从本指南发布后，从资深黑客那里得到的唯一严重缺陷反馈，就是对预先道谢这一条。一些黑客觉得先谢了意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说先谢了，然后事后再对回复者表示感谢，或者换种方式表达感激，譬如用谢谢你的关注或谢谢你的关照。）</p><h3 id="问题解决后，加个简短的补充说明">问题解决后，加个简短的补充说明<a class="fa-solid fa-hashtag" href="#问题解决后，加个简短的补充说明"></a></h3><p>问题解决后，向所有帮助过你的人发个说明，让他们知道问题是怎样解决的，并再一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注，应该在那里贴一个说明比较恰当。</p><p>最理想的方式是向最初提问的话题回复此消息，并在标题中包含已修正，已解决或其它同等含义的明显标记。在人来人往的邮件列表里，一个看见讨论串问题 X 和问题 X - 已解决的潜在回复者就明白不用再浪费时间了（除非他个人觉得问题 X 的有趣），因此可以利用此时间去解决其它问题。</p><p>补充说明不必很长或是很深入；简单的一句你好，原来是网线出了问题！谢谢大家 – Bill 比什么也不说要来的好。事实上，除非结论真的很有技术含量，否则简短可爱的小结比长篇大论更好。说明问题是怎样解决的，但大可不必将解决问题的过程复述一遍。</p><p>对于有深度的问题，张贴调试记录的摘要是有帮助的。描述问题的最终状态，说明是什么解决了问题，在此之后才指明可以避免的盲点。避免盲点的部分应放在正确的解决方案和其它总结材料之后，而不要将此信息搞成侦探推理小说。列出那些帮助过你的名字，会让你交到更多朋友。</p><p>除了有礼貌和有内涵以外，这种类型的补充也有助于他人在邮件列表/新闻群组/论坛中搜索到真正解决你问题的方案，让他们也从中受益。</p><p>至少，这种补充有助于让每位参与协助的人因问题的解决而从中得到满足感。如果你自己不是技术专家或者黑客，那就相信我们，这种感觉对于那些你向他们求助的大师或者专家而言，是非常重要的。问题悬而未决会让人灰心；黑客们渴望看到问题被解决。好人有好报，满足他们的渴望，你会在下次提问时尝到甜头。</p><p>思考一下怎样才能避免他人将来也遇到类似的问题，自问写一份文件或加个常见问题（FAQ）会不会有帮助。如果是的话就将它们发给维护者。</p><p>在黑客中，这种良好的后继行动实际上比传统的礼节更为重要，也是你如何透过善待他人而赢得声誉的方式，这是非常有价值的资产。</p><h2 id="如何解读答案">如何解读答案<a class="fa-solid fa-hashtag" href="#如何解读答案"></a></h2><h3 id="RTFM-和-STFW：如何知道你已完全搞砸了">RTFM 和 STFW：如何知道你已完全搞砸了<a class="fa-solid fa-hashtag" href="#RTFM-和-STFW：如何知道你已完全搞砸了"></a></h3><p>有一个古老而神圣的传统：如果你收到 RTFM （Read The Fucking Manual）的回应，回答者认为你应该去读他妈的手册。当然，基本上他是对的，你应该去读一读。</p><p>RTFM 有一个年轻的亲戚。如果你收到 STFW（Search The Fucking Web）的回应，回答者认为你应该到他妈的网上搜索。那人多半也是对的，去搜索一下吧。（更温和一点的说法是 Google 是你的朋友！）</p><p>在论坛，你也可能被要求去爬爬论坛的旧文。事实上，有人甚至可能热心地为你提供以前解决此问题的讨论串。但不要依赖这种关照，提问前应该先搜索一下旧文。</p><p>通常，用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址，而且他们打这些字的时候也正在读着。这些答复意味着回答者认为</p><ul><li><strong>你需要的信息非常容易获得</strong>；</li><li><strong>你自己去搜索这些信息比灌给你，能让你学到更多</strong>。</li></ul><p>你不应该因此不爽；依照黑客的标准，他已经表示了对你一定程度的关注，而没有对你的要求视而不见。你应该对他祖母般的慈祥表示感谢。</p><h3 id="如果还是搞不懂">如果还是搞不懂<a class="fa-solid fa-hashtag" href="#如果还是搞不懂"></a></h3><p>如果你看不懂回应，别立刻要求对方解释。像你以前试着自己解决问题时那样（利用手册，FAQ，网络，身边的高手），先试着去搞懂他的回应。如果你真的需要对方解释，记得表现出你已经从中学到了点什么。</p><p>比方说，如果我回答你：看来似乎是 zentry 卡住了；你应该先清除它。，然后，这是一个很糟的后续问题回应：zentry 是什么？ 好的问法应该是这样：哦~~~我看过说明了但是只有 -z 和 -p 两个参数中提到了 zentries，而且还都没有清楚的解释如何清除它。你是指这两个中的哪一个吗？还是我看漏了什么？</p><h3 id="处理无礼的回应">处理无礼的回应<a class="fa-solid fa-hashtag" href="#处理无礼的回应"></a></h3><p>很多黑客圈子中看似无礼的行为并不是存心冒犯。相反，它是直接了当，一针见血式的交流风格，这种风格更注重解决问题，而不是使人感觉舒服而却模模糊糊。</p><p>如果你觉得被冒犯了，试着平静地反应。如果有人真的做了出格的事，邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这没有发生而你却发火了，那么你发火对象的言语可能在黑客社区中看起来是正常的，而你将被视为有错的一方，这将伤害到你获取信息或帮助的机会。</p><p>另一方面，你偶尔真的会碰到无礼和无聊的言行。与上述相反，对真正的冒犯者狠狠地打击，用犀利的语言将其驳得体无完肤都是可以接受的。然而，在行事之前一定要非常非常的有根据。纠正无礼的言论与开始一场毫无意义的口水战仅一线之隔，黑客们自己莽撞地越线的情况并不鲜见。如果你是新手或外人，避开这种莽撞的机会并不高。如果你想得到的是信息而不是消磨时光，这时最好不要把手放在键盘上以免冒险。</p><p>（有些人断言很多黑客都有轻度的自闭症或亚斯伯格综合症，缺少用于润滑人类社会正常交往所需的神经。这既可能是真也可能是假的。如果你自己不是黑客，兴许你认为我们脑袋有问题还能帮助你应付我们的古怪行为。只管这么干好了，我们不在乎。我们喜欢我们现在这个样子，并且通常对病患标记都有站得住脚的怀疑）。</p><p>Jeff Bigler 的观察总结和这个相关也值得一读 (tact filters)。</p><p>在下一节，我们会谈到另一个问题，当你行为不当时所会受到的冒犯。</p><h2 id="如何避免扮演失败者">如何避免扮演失败者<a class="fa-solid fa-hashtag" href="#如何避免扮演失败者"></a></h2><p>在黑客社区的论坛中有那么几次你可能会搞砸 —— 以本指南所描述到的或类似的方式。而你会在公开场合中被告知你是如何搞砸的，也许攻击的言语中还会带点夹七夹八的颜色。</p><p>这种事发生以后，你能做的最糟糕的事莫过于哀嚎你的遭遇、宣称被口头攻击、要求道歉、高声尖叫、憋闷气、威胁诉诸法律、向其雇主报怨、忘了关马桶盖等等。相反地，你该这么做：</p><p>熬过去，这很正常。事实上，它是有益健康且合理的。</p><p>社区的标准不会自行维持，它们是通过参与者积极而公开地执行来维持的。不要哭嚎所有的批评都应该通过私下的邮件传送，它不是这样运作的。当有人评论你的一个说法有误或者提出不同看法时，坚持声称受到个人攻击也毫无益处，这些都是失败者的态度。</p><p>也有其它的黑客论坛，受过高礼节要求的误导，禁止参与者张贴任何对别人帖子挑毛病的消息，并声称如果你不想帮助用户就闭嘴。 结果造成有想法的参与者纷纷离开，这么做只会使它们沦为毫无意义的唠叨与无用的技术论坛。</p><p>夸张的讲法是：你要的是“友善”（以上述方式）还是有用？两个里面挑一个。</p><p>记着：当黑客说你搞砸了，并且（无论多么刺耳）告诉你别再这样做时，他正在为关心你和他的社区而行动。对他而言，不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢，至少要表现得有点尊严，别大声哀嚎，也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者，就指望别人像对待脆弱的洋娃娃那样对你。</p><p>有时候，即使你没有搞砸（或者只是在他的想像中你搞砸了），有些人也会无缘无故地攻击你本人。在这种情况下，抱怨倒是真的会把问题搞砸。</p><p>这些来找麻烦的人要么是毫无办法但自以为是专家的不中用家伙，要么就是测试你是否真会搞砸的心理专家。其它读者要么不理睬，要么用自己的方式对付他们。这些来找麻烦的人在给他们自己找麻烦，这点你不用操心。</p><p>也别让自己卷入口水战，最好不要理睬大多数的口水战 —— 当然，这是在你检验它们只是口水战，并且未指出你有搞砸的地方，同时也没有巧妙地将问题真正的答案藏于其后（这也是有可能的）。</p><h2 id="不该问的问题">不该问的问题<a class="fa-solid fa-hashtag" href="#不该问的问题"></a></h2><p>以下是几个经典蠢问题，以及黑客没回答时心中所想的：</p><p>问题：我能在哪找到 X 程序或 X 资源？</p><p>问题：我怎样用 X 做 Y？</p><p>问题：如何设定我的 shell 提示？</p><p>问题：我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？</p><p>问题：我的程序/设定/SQL 语句没有用</p><p>问题：我的 Windows 电脑有问题，你能帮我吗？</p><p>问题：我的程序不会动了，我认为系统工具 X 有问题</p><p>问题：我在安装 Linux（或者 X ）时有问题，你能帮我吗？</p><p>问题：我怎么才能PJ root 帐号/窃取 OP 特权/读别人的邮件呢？</p><hr><blockquote><p>问题：我能在哪找到 X 程序或 X 资源？</p></blockquote><p>回答：就在我找到它的地方啊，白痴 —— 搜索引擎的那一头。天哪！难道还有人不会用 Google 吗？</p><blockquote><p>问题：我怎样用 X 做 Y？</p></blockquote><p>回答：如果你想解决的是 Y ，提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知，也对 Y 要解决的问题糊涂，还被特定形势禁锢了思维。最好忽略这种人，等他们把问题搞清楚了再说。</p><blockquote><p>问题：如何设定我的 shell 提示？？</p></blockquote><p>回答：如果你有足够的智慧提这个问题，你也该有足够的智慧去 RTFM，然后自己去找出来。</p><blockquote><p>问题：我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？</p></blockquote><p>回答：试试看就知道了。如果你试过，你既知道了答案，就不用浪费我的时间了。</p><blockquote><p>问题：我的{程序/设定/SQL 语句}不工作</p></blockquote><p>回答：这不算是问题吧，我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更有意思的事要做呢。在看到这类问题的时候，我的反应通常不外如下三种</p><ul><li>你还有什么要补充的吗？</li><li>真糟糕，希望你能搞定。</li><li>这关我屁事？</li></ul><blockquote><p>问题：我的 Windows 电脑有问题，你能帮我吗？</p></blockquote><p>回答：能啊，扔掉微软的垃圾，换个像 Linux 或 BSD 的开源操作系统吧。</p><p>注意：如果程序有官方版 Windows 或者与 Windows 有互动（如 Samba），你可以问与 Windows 相关的问题， 只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶， 因为 Windows 一般来说实在太烂，这种说法通常都是对的。</p><blockquote><p>问题：我的程序不会动了，我认为系统工具 X 有问题</p></blockquote><p>回答：你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库档案有明显缺陷的人，更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据，当你这样声称时，你必须有清楚而详尽的缺陷说明文件作后盾。</p><blockquote><p>问题：我在安装 Linux（或者 X ）时有问题，你能帮我吗？</p></blockquote><p>回答：不能，我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧（你能在这儿找到使用者群组的清单）。</p><p>注意：如果安装问题与某 Linux 的发行版有关，在它的邮件列表、论坛或本地使用者群组中提问也许是恰当的。此时，应描述问题的准确细节。在此之前，先用 Linux 和所有被怀疑的硬件作关键词仔细搜索。</p><blockquote><p>问题：我怎么才能PJ root 帐号/窃取 OP 特权/读别人的邮件呢？</p></blockquote><p>回答：想要这样做，说明了你是个卑鄙小人；想找个黑客帮你，说明你是个白痴！</p><h2 id="好问题与蠢问题">好问题与蠢问题<a class="fa-solid fa-hashtag" href="#好问题与蠢问题"></a></h2><p>最后，我将透过举一些例子，来说明怎样聪明的提问；同一个问题的两种问法被放在一起，一种是愚蠢的，另一种才是明智的。</p><p>蠢问题：</p><blockquote><p>我可以在哪儿找到关于 Foonly Flurbamatic 的资料？</p></blockquote><p>这种问法无非想得到 STFW 这样的回答。</p><p>聪明问题：</p><blockquote><p>我用 Google 搜索过 “Foonly Flurbamatic 2600”，但是没找到有用的结果。谁知道上哪儿去找对这种设备编程的资料？</p></blockquote><p>这个问题已经 STFW 过了，看起来他真的遇到了麻烦。</p><p>蠢问题：</p><blockquote><p>我从 foo 项目找来的源码没法编译。它怎么这么烂？</p></blockquote><p>他觉得都是别人的错，这个傲慢自大的提问者。</p><p>聪明问题：</p><blockquote><p>foo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ，但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录，我有什么做的不对的地方吗？</p></blockquote><p>提问者已经指明了环境，也读过了 FAQ，还列出了错误，并且他没有把问题的责任推到别人头上，他的问题值得被关注。</p><p>蠢问题：</p><blockquote><p>我的主机板有问题了，谁来帮我？</p></blockquote><p>某黑客对这类问题的回答通常是：好的，还要帮你拍拍背和换尿布吗？，然后按下删除键。</p><p>聪明问题：</p><blockquote><p>我在 S2464 主机板上试过了 X 、 Y 和 Z ，但没什么作用，我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking，但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么？有谁知道接下来我该做些什么测试才能找出问题？</p></blockquote><p>这个家伙，从另一个角度来看，值得去回答他。他表现出了解决问题的能力，而不是坐等天上掉答案。</p><p>在最后一个问题中，注意告诉我答案和给我启示，指出我还应该做什么诊断工作之间微妙而又重要的区别。</p><p>事实上，后一个问题源自于 2001 年 8 月在 Linux 内核邮件列表（lkml）上的一个真实的提问。我（Eric）就是那个提出问题的人。我在 Tyan S2464 主板上观察到了这种无法解释的锁定现象，列表成员们提供了解决这一问题的重要信息。</p><p>通过我的提问方法，我给了别人可以咀嚼玩味的东西；我设法让人们很容易参与并且被吸引进来。我显示了自己具备和他们同等的能力，并邀请他们与我共同探讨。通过告诉他们我所走过的弯路，以避免他们再浪费时间，我也表明了对他们宝贵时间的尊重。</p><p>事后，当我向每个人表示感谢，并且赞赏这次良好的讨论经历的时候， 一个 Linux 内核邮件列表的成员表示，他觉得我的问题得到解决并非由于我是这个列表中的名人，而是因为我用了正确的方式来提问。</p><p>黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙；我相信他是对的，如果我像个乞讨者那样提问，不论我是谁，一定会惹恼某些人或者被他们忽视。他建议我记下这件事，这直接导致了本指南的出现。</p><h2 id="如果得不到回答">如果得不到回答<a class="fa-solid fa-hashtag" href="#如果得不到回答"></a></h2><p>如果仍得不到回答，请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视，虽然不可否认这种差别很难区分。</p><p>总的来说，简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心，知道你问题答案的人可能生活在不同的时区，可能正在睡觉，也有可能你的问题一开始就没有组织好。</p><p>你可以通过其他渠道获得帮助，这些渠道通常更适合初学者的需要。</p><p>有许多网上的以及本地的使用者群组，由热情的软件爱好者（即使他们可能从没亲自写过任何软件）组成。通常人们组建这样的团体来互相帮助并帮助新手。</p><p>另外，你可以向很多商业公司寻求帮助，不论公司大还是小。别为要付费才能获得帮助而感到沮丧！毕竟，假使你的汽车发动机汽缸密封圈爆掉了 —— 完全可能如此 —— 你还得把它送到修车铺，并且为维修付费。就算软件没花费你一分钱，你也不能强求技术支持总是免费的。</p><p>对像是 Linux 这种大众化的软件，每个开发者至少会对应到上万名使用者。根本不可能由一个人来处理来自上万名使用者的求助电话。要知道，即使你要为这些协助付费，和你所购买的同类软件相比，你所付出的也是微不足道的（通常封闭源代码软件的技术支持费用比开源软件的要高得多，且内容也没那么丰富）。</p><h2 id="如何更好地回答问题">如何更好地回答问题<a class="fa-solid fa-hashtag" href="#如何更好地回答问题"></a></h2><p>态度和善一点。问题带来的压力常使人显得无礼或愚蠢，其实并不是这样。</p><p>对初犯者私下回复。对那些坦诚犯错之人没有必要当众羞辱，一个真正的新手也许连怎么搜索或在哪找常见问题都不知道。</p><p>如果你不确定，一定要说出来！一个听起来权威的错误回复比没有还要糟，别因为听起来像个专家很好玩，就给别人乱指路。要谦虚和诚实，给提问者与同行都树个好榜样。</p><p>如果帮不了忙，也别妨碍他。不要在实际步骤上开玩笑，那样也许会毁了使用者的设置 —— 有些可怜的呆瓜会把它当成真的指令。</p><p>试探性的反问以引出更多的细节。如果你做得好，提问者可以学到点东西 —— 你也可以。试试将蠢问题转变成好问题，别忘了我们都曾是新手。</p><p>尽管对那些懒虫抱怨一声 RTFM 是正当的，能指出文件的位置（即使只是建议个 Google 搜索关键词）会更好。</p><p>如果你决定回答，就请给出好的答案。当别人正在用错误的工具或方法时别建议笨拙的权宜之计（workaround），应推荐更好的工具，重新界定问题。</p><p>正面的回答问题！如果这个提问者已经很深入的研究而且也表明已经试过 X 、 Y 、 Z 、 A 、 B 、 C 但没得到结果，回答 试试看 A 或是 B 或者 试试 X 、 Y 、 Z 、 A 、 B 、 C 并附上一个链接一点用都没有。</p><p>帮助你的社区从问题中学习。当回复一个好问题时，问问自己如何修改相关文件或常见问题文件以免再次解答同样的问题？，接着再向文件维护者发一份补丁。</p><p>如果你是在研究一番后才做出的回答，展现你的技巧而不是直接端出结果。毕竟授人以鱼不如授人以渔。</p><h2 id="相关资源">相关资源<a class="fa-solid fa-hashtag" href="#相关资源"></a></h2><p>如果你需要个人电脑、Unix 系统和网络如何运作的基础知识，参阅 Unix 系统和网络基本原理。</p><p>当你发布软件或补丁时，试着按软件发布实践操作。</p><h2 id="鸣谢">鸣谢<a class="fa-solid fa-hashtag" href="#鸣谢"></a></h2><p>Evelyn Mitchel 贡献了一些愚蠢问题例子并启发了编写如何更好地回答问题这一节， Mikhail Ramendik 贡献了一些特别有价值的建议和改进。</p>]]></c:encoded></item><item><title>GitHub 自动合并 PR 笔记</title><link>https://blog.ccknbc.cc/posts/github-automatically-merges-pull-requests-notes/</link><description>GitHub 自动合并 PR 笔记</description><author>CC康纳百川</author><category domain="https://blog.ccknbc.cc/categories/%E5%B7%A5%E5%85%B7/">工具</category><category domain="https://blog.ccknbc.cc/tags/%E5%B7%A5%E5%85%B7/">工具</category><pubDate>Mon, 02 Mar 2026 00:50:28 GMT</pubDate><c:encoded><![CDATA[<p>本文首发在<a href="https://www.yuque.com/ccknbc/blog/21/"><strong>语雀</strong></a></p><p>自动同步更新至<a href="https://blog.ccknbc.cc/posts/github-automatically-merges-pull-requests-notes/"><strong>CC的部落格</strong></a></p><hr><h1>automerge-action</h1><p>GitHub action to automatically merge pull requests when they are ready.</p><p><img src="https://cdn.nlark.com/yuque/0/2021/svg/8391407/1610874798961-ae345ffc-d1e6-425f-900a-8f69e4ee5a53.svg" alt="" loading="lazy"></p><p>When added, this action will run the following tasks on pull requests with the</p><p><code>automerge</code> label:</p><ul><li>Changes from the base branch will automatically be merged into the pull<br>request (only when “Require branches to be up to date before merging”<br>is enabled in the branch protection rules)</li><li>When the pull request is ready, it will automatically be merged. The action<br>will only wait for status checks that are marked as required in the branch<br>protection rules</li><li>Pull requests without any configured labels will be ignored</li></ul><p>Labels, merge and update strategies are configurable, see <a href="#configuration">Configuration</a>.</p><p>A pull request is considered ready when:</p><ol><li>the required number of review approvals has been given (if enabled in the<br>branch protection rules) and</li><li>the required checks have passed (if enabled in the branch protection rules)<br>and</li><li>the pull request is up to date (if enabled in the branch protection rules)</li></ol><p>After the pull request has been merged successfully, the branch will <em>not</em> be</p><p>deleted. To delete branches after they are merged,</p><p>see <a href="https://help.github.com/en/articles/managing-the-automatic-deletion-of-branches">automatic deletion of branches</a>.</p><h2 id="Usage">Usage<a class="fa-solid fa-hashtag" href="#Usage"></a></h2><p>Create a new <code>.github/workflows/automerge.yml</code> file:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">automerge</span></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">pull_request:</span></span><br><span class="line">    <span class="attr">types:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">labeled</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">unlabeled</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">synchronize</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">opened</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">edited</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">ready_for_review</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">reopened</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">unlocked</span></span><br><span class="line">  <span class="attr">pull_request_review:</span></span><br><span class="line">    <span class="attr">types:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">submitted</span></span><br><span class="line">  <span class="attr">check_suite:</span></span><br><span class="line">    <span class="attr">types:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">completed</span></span><br><span class="line">  <span class="attr">status:</span> &#123;&#125;</span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">automerge:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">automerge</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">&quot;pascalgn/automerge-action@v0.13.0&quot;</span></span><br><span class="line">        <span class="attr">env:</span></span><br><span class="line">          <span class="attr">GITHUB_TOKEN:</span> <span class="string">&quot;$<span class="template-variable">&#123;&#123; secrets.GITHUB_TOKEN &#125;&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><p>For the latest version, see the <a href="https://github.com/pascalgn/automerge-action/releases">list of releases</a>.</p><h2 id="Configuration">Configuration<a class="fa-solid fa-hashtag" href="#Configuration"></a></h2><p>The following merge options are supported:</p><ul><li><code>MERGE_LABELS</code>: The labels that need to be present for a pull request to be<br>merged (using <code>MERGE_METHOD</code>). The default value is <code>automerge</code>.<br>This option can be a comma-separated list of labels that will be checked. All<br>labels in the list need to be present, otherwise the pull request will be<br>skipped (until all labels are present). Labels prefixed with an exclamation<br>mark (<code>!</code>) will block a pull request from being merged, when present.<br>For example, when <code>automerge,!wip,!work in progress</code> is given,<br>any pull requests with the labels <code>wip</code> or <code>work in progress</code> and any pull<br>requests <em>without</em> the label <code>automerge</code> will not be merged.<br>Blocking labels take precedence, so if a pull request has both labels<br><code>wip</code> and <code>automerge</code>, it will not be merged.<br>When an empty string (<code>&quot;&quot;</code>) is given, all pull requests will be merged.</li><li><code>MERGE_REMOVE_LABELS</code>: The labels to automatically remove from a pull request<br>once it has been merged by the action. The default value is <code>&quot;&quot;</code>.<br>This option can be a comma-separated list of labels that will be removed.<br>When an empty string (<code>&quot;&quot;</code>) is given, no labels will be removed.</li><li><code>MERGE_METHOD</code>: Which method to use when merging the pull request into<br>the base branch. Possible values are<br><code>[merge](https://help.github.com/en/articles/about-pull-request-merges)</code> (create a merge commit),<br><code>[rebase](https://help.github.com/en/articles/about-pull-request-merges#rebase-and-merge-your-pull-request-commits)</code><br>(rebase all commits of the branch onto the base branch)<br>or <code>[squash](https://help.github.com/en/articles/about-pull-request-merges#squash-and-merge-your-pull-request-commits)</code><br>(squash all commits into a single commit). The default option is <code>merge</code>.</li><li><code>MERGE_METHOD_LABELS</code>: Set to allow labels to determine the merge method<br>(see <code>MERGE_METHOD</code> for possible values).<br>For example, <code>automerge=merge,autosquash=squash</code>. If no such label is present,<br>the method set by <code>MERGE_METHOD</code> will be used. The default value is <code>&quot;&quot;</code>.</li><li><code>MERGE_METHOD_LABEL_REQUIRED</code>: Set to <code>true</code> to require one of the<br><code>MERGE_METHOD_LABELS</code> to be set. The default value is <code>false</code>.</li><li><code>MERGE_COMMIT_MESSAGE</code>: The commit message to use when merging the pull<br>request into the base branch. Possible values are <code>automatic</code> (use GitHub’s<br>default message), <code>pull-request-title</code> (use the pull request’s title),<br><code>pull-request-description</code> (use the pull request’s description),<br><code>pull-request-title-and-description</code> or a literal<br>value with optional placeholders (for example <code>Auto merge &#123;pullRequest.number&#125;</code>).<br>The default value is <code>automatic</code>.</li><li><code>MERGE_COMMIT_MESSAGE_REGEX</code>: When using a commit message containing the<br>PR’s body, use the first capturing subgroup from this regex as the commit<br>message. Can be used to separate content that should go with the commit into<br>the code base’s history from boilerplate associated with the PR (licensing<br>notices, check lists, etc). For example, <code>(.*)^---</code> would keep everything up<br>until the first 3-dash line (horizontal rule in MarkDown) from the commit<br>message. The default value is empty, which disables this feature.</li><li><code>MERGE_FILTER_AUTHOR</code>: When set, only pull requests raised by this author<br>will be merged automatically.</li><li><code>MERGE_FORKS</code>: Whether merging from external repositories is enabled<br>or not. By default, pull requests with branches from forked repositories will<br>be merged the same way as pull requests with branches from the main<br>repository. Set this option to <code>false</code> to disable merging of pull requests<br>from forked repositories. The default value is <code>true</code>.</li><li><code>MERGE_RETRIES</code> and <code>MERGE_RETRY_SLEEP</code>: Sometimes, the pull request check<br>runs haven’t finished yet, so the action will retry the merge after some time.<br>The number of retries can be set with <code>MERGE_RETRIES</code>.<br>The default number of retries is <code>6</code> and setting it to <code>0</code> disables the retry logic.<br><code>MERGE_RETRY_SLEEP</code> sets the time to sleep between retries, in milliseconds.<br>The default is <code>5000</code> (5 seconds) and setting it to <code>0</code> disables sleeping<br>between retries.</li><li><code>MERGE_DELETE_BRANCH</code>: Automatic deletion of branches does not work for all<br>repositories. Set this option to <code>true</code> to automatically delete branches<br>after they have been merged. The default value is <code>false</code>.</li></ul><p>The following update options are supported:</p><ul><li><code>UPDATE_LABELS</code>: The labels that need to be present for a pull request to be<br>updated (using <code>UPDATE_METHOD</code>). The default value is <code>automerge</code>.<br>Note that updating will only happen when the option “Require branches to be<br>up to date before merging” is enabled in the branch protection rules.<br>This option can be a comma-separated list of labels, see the <code>MERGE_LABELS</code><br>option for more information.</li><li><code>UPDATE_METHOD</code>: Which method to use when updating the pull request<br>to the base branch. Possible values are <code>merge</code> (create a merge commit) or<br><code>rebase</code> (rebase the branch onto the head of the base branch). The default<br>option is <code>merge</code>.<br>When the option is <code>rebase</code> and the <a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing">rebasing</a><br>failed, the action will exit with error code 1. This will also be visible<br>in the pull request page, with a message like “this branch has conflicts<br>that must be resolved” and a list of conflicting files.</li><li><code>UPDATE_RETRIES</code> and <code>UPDATE_RETRY_SLEEP</code>: Sometimes, the pull request check<br>runs haven’t finished yet and the action doesn’t know if an update is<br>necessary. To query the pull request state multiple times, the number of<br>retries can be set with <code>UPDATE_RETRIES</code>. The default number of retries is <code>1</code><br>and setting it to <code>0</code> disables the retry logic.<br><code>UPDATE_RETRY_SLEEP</code> sets the time to sleep between retries, in milliseconds.<br>The default is <code>5000</code> (5 seconds) and setting it to <code>0</code> disables sleeping<br>between retries.</li></ul><p>Also, the following general options are supported:</p><ul><li><code>GITHUB_TOKEN</code>: This should always be <code>&quot;$&#123;&#123; secrets.GITHUB_TOKEN &#125;&#125;&quot;</code>.<br>However, in some cases it can be useful to run this action as a certain user<br>(by default, it will run as <code>github-actions</code>). This can be useful if you want<br>to use the “Restrict who can push to matching branches” option in the branch<br>protection rules, for example.<br>To use this setting for manually providing a token, you need to create a<br><a href="https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line">personal access token</a><br>for the user (make sure to check <code>public_repo</code> when it’s a public repository<br>or <code>repo</code> when it’s a private repository). All API requests (merge/rebase)<br>will then be executed as the specified user. The token should be kept secret,<br>so make sure to add it as secret, not as environment variable, in the GitHub<br>workflow file!</li></ul><p>You can configure the environment variables in the workflow file like this:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">env:</span></span><br><span class="line">  <span class="attr">GITHUB_TOKEN:</span> <span class="string">&quot;$<span class="template-variable">&#123;&#123; secrets.GITHUB_TOKEN &#125;&#125;</span>&quot;</span></span><br><span class="line">  <span class="attr">MERGE_LABELS:</span> <span class="string">&quot;automerge,!work in progress&quot;</span></span><br><span class="line">  <span class="attr">MERGE_REMOVE_LABELS:</span> <span class="string">&quot;automerge&quot;</span></span><br><span class="line">  <span class="attr">MERGE_METHOD:</span> <span class="string">&quot;squash&quot;</span></span><br><span class="line">  <span class="attr">MERGE_COMMIT_MESSAGE:</span> <span class="string">&quot;pull-request-description&quot;</span></span><br><span class="line">  <span class="attr">MERGE_FORKS:</span> <span class="string">&quot;false&quot;</span></span><br><span class="line">  <span class="attr">MERGE_RETRIES:</span> <span class="string">&quot;6&quot;</span></span><br><span class="line">  <span class="attr">MERGE_RETRY_SLEEP:</span> <span class="string">&quot;10000&quot;</span></span><br><span class="line">  <span class="attr">UPDATE_LABELS:</span> <span class="string">&quot;&quot;</span></span><br><span class="line">  <span class="attr">UPDATE_METHOD:</span> <span class="string">&quot;rebase&quot;</span></span><br></pre></td></tr></table></figure><h2 id="Supported-Events">Supported Events<a class="fa-solid fa-hashtag" href="#Supported-Events"></a></h2><p>Automerge can be configured to run for these events:</p><ul><li><code>check_run</code></li><li><code>check_suite</code></li><li><code>issue_comment</code></li><li><code>pull_request_review</code></li><li><code>pull_request_target</code></li><li><code>pull_request</code></li><li><code>push</code></li><li><code>repository_dispatch</code></li><li><code>schedule</code></li><li><code>status</code></li><li><code>workflow_dispatch</code></li></ul><p>For more information on when these occur, see the Github documentation on <a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows">events that trigger workflows</a> and <a href="https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads">their payloads</a>.</p><h2 id="Limitations">Limitations<a class="fa-solid fa-hashtag" href="#Limitations"></a></h2><ul><li>When a pull request is merged by this action, the merge will not trigger other GitHub workflows.<br>Similarly, when another GitHub workflow creates a pull request, this action will not be triggered.<br>This is because <a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/events-that-trigger-workflows">an action in a workflow run can’t trigger a new workflow run</a>. However, the <code>[workflow_run](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_run)</code> event is triggered as expected.</li><li>When <a href="https://help.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token">using a personal access token (PAT) to work around the above limitation</a>, note that when the user issuing the PAT is an administrator and <a href="https://help.github.com/en/github/administering-a-repository/enabling-branch-restrictions">branch restrictions do not include administrators</a>, pull requests may be merged even if they are not mergeable for non-administrators (see <a href="https://github.com/pascalgn/automerge-action/issues/65">#65</a>).</li><li>Currently, there is no way to trigger workflows when the pull request branch<br>becomes out of date with the base branch. There is a request in the<br><a href="https://github.community/t5/GitHub-Actions/New-Trigger-is-mergable-state/m-p/36908">GitHub community forum</a>.</li></ul><h2 id="Debugging">Debugging<a class="fa-solid fa-hashtag" href="#Debugging"></a></h2><p>To run the action with full debug logging, update your workflow file as follows:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">- name: automerge</span><br><span class="line">  uses: pascalgn/automerge-action@...</span><br><span class="line">  with:</span><br><span class="line">    args: &quot;--trace&quot;</span><br></pre></td></tr></table></figure><p>If you need to further debug the action, you can run it locally.</p><p>You will need a <a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line">personal access token</a>.</p><p>Then clone this repository, create a file <code>.env</code> in the repository, such as:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">GITHUB_TOKEN=&quot;123abc...&quot;</span><br><span class="line">URL=&quot;https://github.com/pascalgn/repository-name/pull/123&quot;</span><br></pre></td></tr></table></figure><p>Install dependencies with <code>yarn</code>, and finally run <code>yarn it</code> (or <code>npm run it</code>).</p><h2 id="License">License<a class="fa-solid fa-hashtag" href="#License"></a></h2><p><a href="LICENSE">MIT</a></p><h1>bulldozer</h1><p><img src="https://cdn.nlark.com/yuque/0/2021/svg/8391407/1610874951039-f74d3099-f28f-4856-ade7-6c677ce4869d.svg" alt="" loading="lazy"> <img src="https://img.shields.io/docker/pulls/palantirtechnologies/bulldozer.svg" alt="" loading="lazy"></p><p><code>bulldozer</code> is a <a href="https://developer.github.com/apps/">GitHub App</a> that</p><p>automatically merges pull requests (PRs) when (and only when) all required</p><p>status checks are successful and required reviews are provided.</p><p>Additionally, <code>bulldozer</code> can:</p><ul><li>Only merge pull requests that match certain conditions, like having a<br>specific label or comment</li><li>Ignore pull requests that match certain conditions, like having a specific<br>label or comment</li><li>Automatically keep pull request branches up-to-date by merging in the target<br>branch</li><li>Wait for additional status checks that are not required by GitHub</li></ul><p>Bulldozer might be useful if you:</p><ul><li>Have CI builds that take longer than the normal review process. It will merge<br>reviewed PRs as soon as the tests pass so you don’t have to watch the pull<br>request or remember to merge it later.</li><li>Combine it with <a href="https://github.com/palantir/policy-bot">policy-bot</a> to<br>automatically merge certain types of pre-approved or automated changes.</li><li>Want to give contributors more control over when they can merge PRs without<br>granting them write access to the repository.</li><li>Have a lot of active development that makes it difficult to merge a pull<br>request while it is up-to-date with the target branch.</li></ul><h2 id="Contents">Contents<a class="fa-solid fa-hashtag" href="#Contents"></a></h2><ul><li><a href="#behavior">Behavior</a></li><li><a href="#configuration">Configuration</a><ul><li><a href="#bulldozeryml-specification">bulldozer.yml Specification</a></li></ul></li><li><a href="#faq">FAQ</a></li><li><a href="#deployment">Deployment</a></li><li><a href="#development">Development</a></li><li><a href="#contributing">Contributing</a></li><li><a href="#license">License</a></li></ul><h2 id="Behavior">Behavior<a class="fa-solid fa-hashtag" href="#Behavior"></a></h2><p><code>bulldozer</code> will only merge pull requests that GitHub allows non-admin</p><p>collaborators to merge. This means that all branch protection settings,</p><p>including required status checks and required reviews, are respected. It also</p><p>means that you <em>must</em> enable branch protection to prevent <code>bulldozer</code> from</p><p>immediately merging every pull request.</p><p>Only pull requests matching the trigger conditions (or <em>not</em> matching</p><p>ignore conditions) are considered for merging. <code>bulldozer</code> is event-driven,</p><p>which means it will usually merge a pull request within a few seconds of the</p><p>pull request satisfying all preconditions.</p><h2 id="Configuration-2">Configuration<a class="fa-solid fa-hashtag" href="#Configuration-2"></a></h2><p>The behavior of the bot is configured by a <code>.bulldozer.yml</code> file at the root of</p><p>the repository. The file name and location are configurable when running your</p><p>own instance of the server.</p><p>The <code>.bulldozer.yml</code> file is read from the most recent commit on the target</p><p>branch of each pull request. If <code>bulldozer</code> cannot find a configuration file,</p><p>it will take no action. This means it is safe to enable the <code>bulldozer</code> on all</p><p>repositories in an organization.</p><h3 id="bulldozer-yml-Specification">bulldozer.yml Specification<a class="fa-solid fa-hashtag" href="#bulldozer-yml-Specification"></a></h3><p>The <code>.bulldozer.yml</code> file supports the following keys.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># &quot;version&quot; is the configuration version, currently &quot;1&quot;.</span></span><br><span class="line"><span class="attr">version:</span> <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># &quot;merge&quot; defines how and when pull requests are merged. If the section is</span></span><br><span class="line"><span class="comment"># missing, bulldozer will consider all pull requests and use default settings.</span></span><br><span class="line"><span class="attr">merge:</span></span><br><span class="line">  <span class="comment"># &quot;trigger&quot; defines the set of pull requests considered by bulldozer. If</span></span><br><span class="line">  <span class="comment"># the section is missing, bulldozer considers all pull requests not excluded</span></span><br><span class="line">  <span class="comment"># by the ignore conditions.</span></span><br><span class="line">  <span class="attr">trigger:</span></span><br><span class="line">    <span class="comment"># Pull requests with any of these labels (case-insensitive) are added to</span></span><br><span class="line">    <span class="comment"># the trigger.</span></span><br><span class="line">    <span class="attr">labels:</span> [<span class="string">&quot;merge when ready&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Pull requests where the body or any comment contains any of these</span></span><br><span class="line">    <span class="comment"># substrings are added to the trigger.</span></span><br><span class="line">    <span class="attr">comment_substrings:</span> [<span class="string">&quot;==MERGE_WHEN_READY==&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Pull requests where any comment matches one of these exact strings are</span></span><br><span class="line">    <span class="comment"># added to the trigger.</span></span><br><span class="line">    <span class="attr">comments:</span> [<span class="string">&quot;Please merge this pull request!&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Pull requests where the body contains any of these substrings are added</span></span><br><span class="line">    <span class="comment"># to the trigger.</span></span><br><span class="line">    <span class="attr">pr_body_substrings:</span> [<span class="string">&quot;==MERGE_WHEN_READY==&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Pull requests targeting any of these branches are added to the trigger.</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">&quot;develop&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Pull requests targeting branches matching any of these regular expressions are added to the trigger.</span></span><br><span class="line">    <span class="attr">branch_patterns:</span> [<span class="string">&quot;feature/.*&quot;</span>]</span><br><span class="line"></span><br><span class="line">  <span class="comment"># &quot;ignore&quot; defines the set of pull request ignored by bulldozer. If the</span></span><br><span class="line">  <span class="comment"># section is missing, bulldozer considers all pull requests. It takes the</span></span><br><span class="line">  <span class="comment"># same keys as the &quot;trigger&quot; section.</span></span><br><span class="line">  <span class="attr">ignore:</span></span><br><span class="line">    <span class="attr">labels:</span> [<span class="string">&quot;do not merge&quot;</span>]</span><br><span class="line">    <span class="attr">comment_substrings:</span> [<span class="string">&quot;==DO_NOT_MERGE==&quot;</span>]</span><br><span class="line"></span><br><span class="line">  <span class="comment"># &quot;method&quot; defines the merge method. The available options are &quot;merge&quot;,</span></span><br><span class="line">  <span class="comment"># &quot;rebase&quot;, &quot;squash&quot;, and &quot;ff-only&quot;.</span></span><br><span class="line">  <span class="attr">method:</span> <span class="string">squash</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># Allows the merge method that is used when auto-merging a PR to be different based on the</span></span><br><span class="line">  <span class="comment"># target branch. The keys of the hash are the target branch name, and the values are the merge method that</span></span><br><span class="line">  <span class="comment"># will be used for PRs targeting that branch. The valid values are the same as for the &quot;method&quot; key.</span></span><br><span class="line">  <span class="comment"># <span class="doctag">Note:</span> If the target branch does not match any of the specified keys, the &quot;method&quot; key is used instead.</span></span><br><span class="line">  <span class="attr">branch_method:</span></span><br><span class="line">    <span class="attr">develop:</span> <span class="string">squash</span></span><br><span class="line">    <span class="attr">master:</span> <span class="string">merge</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># &quot;options&quot; defines additional options for the individual merge methods.</span></span><br><span class="line">  <span class="attr">options:</span></span><br><span class="line">    <span class="comment"># &quot;squash&quot; options are only used when the merge method is &quot;squash&quot;</span></span><br><span class="line">    <span class="attr">squash:</span></span><br><span class="line">      <span class="comment"># &quot;title&quot; defines how the title of the commit message is created when</span></span><br><span class="line">      <span class="comment"># generating a squash commit. The options are &quot;pull_request_title&quot;,</span></span><br><span class="line">      <span class="comment"># &quot;first_commit_title&quot;, and &quot;github_default_title&quot;. The default is</span></span><br><span class="line">      <span class="comment"># &quot;pull_request_title&quot;.</span></span><br><span class="line">      <span class="attr">title:</span> <span class="string">&quot;pull_request_title&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># &quot;body&quot; defines how the body of the commit message is created when</span></span><br><span class="line">      <span class="comment"># generating a squash commit. The options are &quot;pull_request_body&quot;,</span></span><br><span class="line">      <span class="comment"># &quot;summarize_commits&quot;, and &quot;empty_body&quot;. The default is &quot;empty_body&quot;.</span></span><br><span class="line">      <span class="attr">body:</span> <span class="string">&quot;empty_body&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment"># If &quot;body&quot; is &quot;pull_request_body&quot;, then the commit message will be the</span></span><br><span class="line">      <span class="comment"># part of the pull request body surrounded by &quot;message_delimiter&quot;</span></span><br><span class="line">      <span class="comment"># strings. This is disabled (empty string) by default.</span></span><br><span class="line">      <span class="attr">message_delimiter:</span> <span class="string">==COMMIT_MSG==</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># &quot;required_statuses&quot; is a list of additional status contexts that must pass</span></span><br><span class="line">  <span class="comment"># before bulldozer can merge a pull request. This is useful if you want to</span></span><br><span class="line">  <span class="comment"># require extra testing for automated merges, but not for manual merges.</span></span><br><span class="line">  <span class="attr">required_statuses:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;ci/circleci: ete-tests&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># If true, bulldozer will delete branches after their pull requests merge.</span></span><br><span class="line">  <span class="attr">delete_after_merge:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># &quot;update&quot; defines how and when to update pull request branches. Unlike with</span></span><br><span class="line"><span class="comment"># merges, if this section is missing, bulldozer will not update any pull requests.</span></span><br><span class="line"><span class="attr">update:</span></span><br><span class="line">  <span class="comment"># &quot;trigger&quot; defines the set of pull requests that should be updated by</span></span><br><span class="line">  <span class="comment"># bulldozer. It accepts the same keys as the trigger in the &quot;merge&quot; block.</span></span><br><span class="line">  <span class="attr">trigger:</span></span><br><span class="line">    <span class="attr">labels:</span> [<span class="string">&quot;WIP&quot;</span>, <span class="string">&quot;Update Me&quot;</span>]</span><br><span class="line"></span><br><span class="line">  <span class="comment"># &quot;ignore&quot; defines the set of pull requests that should not be updated by</span></span><br><span class="line">  <span class="comment"># bulldozer. It accepts the same keys as the ignore in the &quot;merge&quot; block.</span></span><br><span class="line">  <span class="attr">ignore:</span></span><br><span class="line">    <span class="attr">labels:</span> [<span class="string">&quot;Do Not Update&quot;</span>]</span><br></pre></td></tr></table></figure><h2 id="FAQ">FAQ<a class="fa-solid fa-hashtag" href="#FAQ"></a></h2><h4 id="Can-I-specify-both-ignore-and-trigger">Can I specify both <code>ignore</code> and <code>trigger</code>?<a class="fa-solid fa-hashtag" href="#Can-I-specify-both-ignore-and-trigger"></a></h4><p>Yes. If both <code>ignore</code> and <code>trigger</code> are specified, bulldozer will attempt to match</p><p>on both. In cases where both match, <code>ignore</code> will take precedence.</p><h4 id="Can-I-specify-the-body-of-the-commit-when-using-the-squash-strategy">Can I specify the body of the commit when using the <code>squash</code> strategy?<a class="fa-solid fa-hashtag" href="#Can-I-specify-the-body-of-the-commit-when-using-the-squash-strategy"></a></h4><p>Yes. When the merge strategy is <code>squash</code>, you can set additional options under the</p><p><code>options.squash</code> property, including how to render the commit body.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">merge:</span></span><br><span class="line">  <span class="attr">method:</span> <span class="string">squash</span></span><br><span class="line">  <span class="attr">options:</span></span><br><span class="line">    <span class="attr">squash:</span></span><br><span class="line">      <span class="attr">body:</span> <span class="string">summarize_commits</span> <span class="comment"># or `pull_request_body`, `empty_body`</span></span><br></pre></td></tr></table></figure><p>You can also define part of pull request body to pick as a commit message when</p><p><code>body</code> is <code>pull_request_body</code>.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">merge:</span></span><br><span class="line">  <span class="attr">method:</span> <span class="string">squash</span></span><br><span class="line">  <span class="attr">options:</span></span><br><span class="line">    <span class="attr">squash:</span></span><br><span class="line">      <span class="attr">body:</span> <span class="string">pull_request_body</span></span><br><span class="line">      <span class="attr">message_delimiter:</span> <span class="string">==COMMIT_MSG==</span></span><br></pre></td></tr></table></figure><p>Anything that’s contained between two <code>==COMMIT_MSG==</code> strings will become the</p><p>commit message instead of whole pull request body.</p><h4 id="What-if-I-don’t-want-to-put-config-files-into-each-repo">What if I don’t want to put config files into each repo?<a class="fa-solid fa-hashtag" href="#What-if-I-don’t-want-to-put-config-files-into-each-repo"></a></h4><p>You can add default repository configuration in your bulldozer config file.</p><p>It will be used only when your repo config file does not exist.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">options:</span></span><br><span class="line">  <span class="attr">default_repository_config:</span></span><br><span class="line">    <span class="attr">ignore:</span></span><br><span class="line">      <span class="attr">labels:</span> [<span class="string">&quot;do not merge&quot;</span>] <span class="comment"># or any other available config.</span></span><br></pre></td></tr></table></figure><h4 id="Bulldozer-isn’t-merging-my-commit-when-it-should-what-could-be-happening">Bulldozer isn’t merging my commit when it should, what could be happening?<a class="fa-solid fa-hashtag" href="#Bulldozer-isn’t-merging-my-commit-when-it-should-what-could-be-happening"></a></h4><p>Bulldozer will attempt to merge a branch whenever it passes the trigger/ignore</p><p>criteria. GitHub may prevent it from merging a branch in certain conditions, some of</p><p>which are to be expected, and others that may be caused by mis-configuring Bulldozer.</p><ul><li>Required status checks have not passed</li><li>Review requirements are not satisfied</li><li>The merge strategy configured in <code>.bulldozer.yml</code> is not allowed by your<br>repository settings</li><li>Branch protection rules are preventing <code>bulldozer[bot]</code> from <a href="https://help.github.com/articles/about-branch-restrictions/">pushing to the<br>branch</a>. Github apps can be added to the list of restricted<br>push users, so you can allow Bulldozer specifically for your repo.</li></ul><h4 id="Bulldozer-isn’t-updating-my-branch-when-it-should-what-could-be-happening">Bulldozer isn’t updating my branch when it should, what could be happening?<a class="fa-solid fa-hashtag" href="#Bulldozer-isn’t-updating-my-branch-when-it-should-what-could-be-happening"></a></h4><p>When using the branch update functionality, Bulldozer only acts when the target</p><p>branch is updated <em>after</em> updates are enabled for the pull request. For</p><p>example:</p><ol><li>User A opens a pull request targetting <code>develop</code></li><li>User B pushes a commit to <code>develop</code></li><li>User A adds the <code>update me</code> label to the first pull request</li><li>User C pushes a commit to <code>develop</code></li><li>Bulldozer updates the pull request with the commits from Users B and C</li></ol><p>Note that the update does <em>not</em> happen when the <code>update me</code> label is added,</p><p>even though there is a new commit on <code>develop</code> that is not part of the pull</p><p>request.</p><h4 id="Can-Bulldozer-work-with-push-restrictions-on-branches">Can Bulldozer work with push restrictions on branches?<a class="fa-solid fa-hashtag" href="#Can-Bulldozer-work-with-push-restrictions-on-branches"></a></h4><p>As mentioned above, as of Github ~2.19.x, GitHub Apps <em>can</em> be added to the list of users associated</p><p>with <a href="https://help.github.com/articles/about-branch-restrictions/">push restrictions</a>. If you don’t want to do this, or if you’re running</p><p>an older version of Github that doesn’t support this behaviour, you may work</p><p>around this:</p><ol><li>Use another app like <a href="https://github.com/palantir/policy-bot">policy-bot</a> to<br>implement <em>approval</em> restrictions as required status checks instead of using<br>push restrictions. This effectively limits who can push to a branch by<br>requiring changes to go through the pull request process and be approved.</li><li>Configure Bulldozer to use a personal access token for a regular user to<br>perform merges in this case. The token must have the <code>repo</code> scope and the<br>user must be allowed to push to the branch. In the server configuration<br>file, set:</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">options:</span></span><br><span class="line">  <span class="attr">push_restriction_user_token:</span> <span class="string">&lt;token-value&gt;</span></span><br></pre></td></tr></table></figure><ol start="2"><li>The token is <em>only</em> used if the target branch has push restrictions enabled.<br>All other merges are performed as the normal GitHub App user.</li></ol><h2 id="Deployment">Deployment<a class="fa-solid fa-hashtag" href="#Deployment"></a></h2><p>bulldozer is easy to deploy in your own environment as it has no dependencies</p><p>other than GitHub. It is also safe to run multiple instances of the server,</p><p>making it a good fit for container schedulers like Nomad or Kubernetes.</p><p>We provide both a Docker container and a binary distribution of the server:</p><ul><li>Binaries: <a href="https://bintray.com/palantir/releases/bulldozer">https://bintray.com/palantir/releases/bulldozer</a></li><li>Docker Images: <a href="https://hub.docker.com/r/palantirtechnologies/bulldozer/">https://hub.docker.com/r/palantirtechnologies/bulldozer/</a></li></ul><p>A sample configuration file is provided at <code>config/bulldozer.example.yml</code>.</p><p>Certain values may also be set by environment variables; these are noted in the</p><p>comments in the sample configuration file.</p><h3 id="GitHub-App-Configuration">GitHub App Configuration<a class="fa-solid fa-hashtag" href="#GitHub-App-Configuration"></a></h3><p>To configure Bulldozer as a GitHub App, these general options are required:</p><ul><li><strong>Webhook URL</strong>: <code>http(s)://&lt;your-bulldozer-domain&gt;/api/github/hook</code></li><li><strong>Webhook secret</strong>: A random string that matches the value of the<br><code>github.app.webhook_secret</code> property in the server configuration</li></ul><p>The app requires these permissions:</p><table><thead><tr><th>Permission</th><th>Access</th><th>Reason</th></tr></thead><tbody><tr><td>Repository administration</td><td>Read-only</td><td>Determine required status checks</td></tr><tr><td>Checks</td><td>Read-only</td><td>Read checks for ref</td></tr><tr><td>Repository contents</td><td>Read &amp; write</td><td>Read configuration, perform merges</td></tr><tr><td>Issues</td><td>Read &amp; write</td><td>Read comments, close linked issues</td></tr><tr><td>Repository metadata</td><td>Read-only</td><td>Basic repository data</td></tr><tr><td>Pull requests</td><td>Read &amp; write</td><td>Merge and close pull requests</td></tr><tr><td>Commit status</td><td>Read-only</td><td>Evaluate pull request status</td></tr></tbody></table><p>The app should be subscribed to these events:</p><ul><li>Check run</li><li>Commit comment</li><li>Pull request</li><li>Status</li><li>Push</li><li>Issue comment</li><li>Pull request review</li><li>Pull request review comment</li></ul><h3 id="Operations">Operations<a class="fa-solid fa-hashtag" href="#Operations"></a></h3><p>bulldozer uses <a href="https://github.com/palantir/go-baseapp">go-baseapp</a> and</p><p><a href="https://github.com/palantir/go-githubapp">go-githubapp</a>, both of which emit</p><p>standard metrics and structured log keys. Please see those projects for</p><p>details.</p><h3 id="Example-Files">Example Files<a class="fa-solid fa-hashtag" href="#Example-Files"></a></h3><p>Example <code>.bulldozer.yml</code> files can be found in <code>[config/examples](https://github.com/palantir/bulldozer/tree/develop/config/examples)</code></p><h3 id="Migrating-Version-0-4-X-to-1-X">Migrating: Version 0.4.X to 1.X<a class="fa-solid fa-hashtag" href="#Migrating-Version-0-4-X-to-1-X"></a></h3><p>The server configuration for bulldozer allows you to specify <code>configuration_v0_path</code>, which is a list of paths</p><p>to check for <code>0.4.X</code> style bulldozer configuration. When a <code>1.X</code> style configuration file does not appear</p><p>at the configured path, bulldozer will attempt to read from the paths configured by <code>configuration_v0_path</code>,</p><p>converting the legacy configuration into an equivalent <code>v1</code> configuration internally.</p><p>The upgrade process is therefore to deploy the latest version of bulldozer with both <code>configuration_path</code> and</p><p><code>configuration_v0_path</code> configured, and to enable the bulldozer GitHub App on all organizations where it was</p><p>previously installed.</p><h2 id="Development">Development<a class="fa-solid fa-hashtag" href="#Development"></a></h2><p>To develop <code>bulldozer</code>, you will need a <a href="https://golang.org/doc/install">Go installation</a>.</p><p><strong>Run style checks and tests</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./godelw verify</span><br></pre></td></tr></table></figure><p><strong>Running the server locally</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># copy and edit the server config</span><br><span class="line">cp config/bulldozer.example.yml config/bulldozer.yml</span><br><span class="line"></span><br><span class="line">./godelw run bulldozer server</span><br></pre></td></tr></table></figure><ul><li><code>config/bulldozer.yml</code> is used as the default configuration file</li><li>The server is available at <code>http://localhost:8080/</code></li></ul><p><strong>Running the server via Docker</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># copy and edit the server config</span><br><span class="line">cp config/bulldozer.example.yml config/bulldozer.yml</span><br><span class="line"></span><br><span class="line"># build the docker image</span><br><span class="line">./godelw docker build --verbose</span><br><span class="line"></span><br><span class="line">docker run --rm -v &quot;$(pwd)/config:/secrets/&quot; -p 8080:8080 palantirtechnologies/bulldozer:latest</span><br></pre></td></tr></table></figure><ul><li>This mounts the <code>config</code> directory (which should contain the <code>bulldozer.yml</code> configuration file) at the expected location</li><li>The server is available at <code>http://localhost:8080/</code></li></ul><h2 id="Contributing">Contributing<a class="fa-solid fa-hashtag" href="#Contributing"></a></h2><p>Contributions and issues are welcome. For new features or large contributions,</p><p>we prefer discussing the proposed change on a GitHub issue prior to a PR.</p><h2 id="License-2">License<a class="fa-solid fa-hashtag" href="#License-2"></a></h2><p>This application is made available under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>.</p><h1>probot-auto-merge</h1><p><img src="https://travis-ci.org/bobvanderlinden/probot-auto-merge.svg?branch=master" alt="" loading="lazy"></p><p><img src="https://img.shields.io/coveralls/github/bobvanderlinden/probot-auto-merge.svg" alt="" loading="lazy"></p><p>A GitHub App built with <a href="https://github.com/probot/probot">Probot</a> that automatically merges PRs</p><blockquote><p><img src="/" alt="" loading="lazy"></p></blockquote><h2 id="Usage-2">Usage<a class="fa-solid fa-hashtag" href="#Usage-2"></a></h2><ol><li><a href="https://github.com/apps/probot-auto-merge">Configure the GitHub App</a></li><li>Create <code>.github/auto-merge.yml</code> in your repository.</li><li>Customize configuration to your needs. See below.</li></ol><h2 id="Configuration-3">Configuration<a class="fa-solid fa-hashtag" href="#Configuration-3"></a></h2><p>Configuration of <code>probot-auto-merge</code> is done through <code>.github/auto-merge.yml</code> in</p><p>your repository. An example of this file can be found <a href="auto-merge.example.yml">here</a>.</p><p>You can also see the configuration for this repository <a href=".github/auto-merge.yml">here</a>.</p><p>The configuration has values that serve as conditions on whether or not a pull request</p><p>should be automatically merged and also configuration about the merge itself. Values</p><p>that serve as conditions are annotated as such below.</p><p>All conditions must be met before a PR will be automatically merged. You can get more</p><p>flexibility by defining multiple rules. Rules can have multiple conditions and if any</p><p>of the conditions inside a rule are met, the PR is also merged. See <a href="#rules-default-none">rules</a>.</p><p>If the target branch is a protected branch, you must add <code>probot-auto-merge</code> bot to</p><p>the list of <code>People, teams or apps with push access</code> in your branch protection rules.</p><p>Note that the default configuration options are to do nothing. This is to prevent</p><p>implicit and possibly unintended behavior.</p><p>The configuration fields are as follows:</p><h3 id="minApprovals-required-condition"><code>minApprovals</code> (required, condition)<a class="fa-solid fa-hashtag" href="#minApprovals-required-condition"></a></h3><p>The minimum number of reviews from each association that approve the pull request before</p><p>doing an automatic merge. For more information about associations see:</p><p><a href="https://developer.github.com/v4/enum/commentauthorassociation/">https://developer.github.com/v4/enum/commentauthorassociation/</a></p><p>Possible associations: <code>OWNER</code>, <code>MEMBER</code>, <code>COLLABORATOR</code>, <code>CONTRIBUTOR</code>, <code>FIRST_TIMER</code>, <code>FIRST_TIME_CONTRIBUTOR</code>, <code>NONE</code></p><p>In the example below when a pull request gets 2 approvals from owners, members or collaborators,</p><p>the automatic merge will continue.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">minApprovals:</span></span><br><span class="line">  <span class="attr">COLLABORATOR:</span> <span class="number">2</span></span><br></pre></td></tr></table></figure><p>In the example below when a pull request gets 1 approval from an owner OR 2 approvals from members, the automatic merge will continue.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">minApprovals:</span></span><br><span class="line">  <span class="attr">OWNER:</span> <span class="number">1</span></span><br><span class="line">  <span class="attr">MEMBER:</span> <span class="number">2</span></span><br></pre></td></tr></table></figure><h3 id="requiredReviewers-condition-default-none"><code>requiredReviewers</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#requiredReviewers-condition-default-none"></a></h3><p>Whenever required reviewers are configured, pull requests will only be automatically</p><p>merged whenever all of these reviewers have approved the pull request.</p><p>In the example below, pull requests need to have been approved by the user ‘rogerluan’</p><p>before they will be automatically merged.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredReviewers:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">rogerluan</span></span><br></pre></td></tr></table></figure><h3 id="maxRequestedChanges-condition-default-none"><code>maxRequestedChanges</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#maxRequestedChanges-condition-default-none"></a></h3><p>Similar to <code>minApprovals</code>, maxRequestedChanges determines the maximum number of</p><p>requested changes before a pull request will be blocked from being automatically</p><p>merged.</p><p>It yet again allows you to configure this per association.</p><p>Note that <code>maxRequestedChanges</code> takes precedence over <code>minApprovals</code>.</p><p>In the example below, automatic merges will be blocked when one of the owners, members</p><p>or collaborators has requested changes.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">maxRequestedChanges:</span></span><br><span class="line">  <span class="attr">COLLABORATOR:</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><p>In the example below, automatic merges will be blocked when the owner has</p><p>requested changes or two members, collaborators or other users have requested</p><p>changes.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">maxRequestedChanges:</span></span><br><span class="line">  <span class="attr">OWNER:</span> <span class="number">0</span></span><br><span class="line">  <span class="attr">NONE:</span> <span class="number">1</span></span><br></pre></td></tr></table></figure><p>The default for this value is:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">maxRequestedChanges:</span></span><br><span class="line">  <span class="attr">NONE:</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><h3 id="blockingBaseBranches-condition-default-none"><code>blockingBaseBranches</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#blockingBaseBranches-condition-default-none"></a></h3><p>Whenever blocking base branches are configured, pull requests will only be automatically</p><p>merged whenever their base branch (into which the PR would be merged) is not matching</p><p>the patterns listed.</p><p>In the example below, pull requests that have the base branch <code>develop</code> or one that starts</p><p>with <code>feature-</code> will not be merged automatically.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingBaseBranches:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">develop</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">regex:</span> <span class="string">^feature-</span></span><br></pre></td></tr></table></figure><p>Note: remove the whole section when you’re not using blocking base branches.</p><h3 id="requiredBaseBranches-condition-default-none"><code>requiredBaseBranches</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#requiredBaseBranches-condition-default-none"></a></h3><p>Whenever required base branches are configured, pull requests will only be automatically</p><p>merged whenever their base branch (into which the PR would be merged) is matching</p><p>any of the patterns listed.</p><p>In the example below, pull requests need to have the base branch <code>master</code> or one that</p><p>starts with <code>v&#123;number&#125;</code> before they will be automatically merged.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredBaseBranches:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">master</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">regex:</span> <span class="string">^v\d</span></span><br></pre></td></tr></table></figure><h3 id="blockingLabels-condition-default-none"><code>blockingLabels</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#blockingLabels-condition-default-none"></a></h3><p>Blocking labels are the labels that can be attached to a pull request to make</p><p>sure the pull request is not being merged automatically.</p><p>In the example below, pull requests that have the <code>blocked</code> label will not be</p><p>merged automatically.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingLabels:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">blocked</span></span><br></pre></td></tr></table></figure><p>The above example denotes literal label names. Regular expressions can be used to</p><p>partially match labels. This can be specified by the <code>regex:</code> property in the</p><p>configuration. The following example will block merging when a label is added that</p><p>starts with the text <code>blocked</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingLabels:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">regex:</span> <span class="string">^blocked</span></span><br></pre></td></tr></table></figure><p>Note: remove the whole section when you’re not using blocking labels.</p><h3 id="requiredLabels-condition-default-none"><code>requiredLabels</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#requiredLabels-condition-default-none"></a></h3><p>Whenever required labels are configured, pull requests will only be automatically</p><p>merged whenever all of these labels are attached to a pull request.</p><p>In the example below, pull requests need to have the label <code>merge</code> before they</p><p>will be automatically merged.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredLabels:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">merge</span></span><br></pre></td></tr></table></figure><p>The above example denotes literal label names. Regular expressions can be used to</p><p>partially match labels. This requires <code>regex:</code> property in the configuration. The</p><p>following example will requires at least one label that starts with <code>merge</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredLabels:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">regex:</span> <span class="string">^merge</span></span><br></pre></td></tr></table></figure><p>Note: remove the whole section when you’re not using required labels.</p><h3 id="blockingTitleRegex-condition-default-none"><code>blockingTitleRegex</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#blockingTitleRegex-condition-default-none"></a></h3><p>Whenever a blocking title regular expression is configured, pull requests that have a title</p><p>matching the configured expression will not be automatically merged.</p><p>This is useful whenever pull requests with <code>WIP</code> in their title need to be skipped.</p><p>In the example below, pull requests with the word <code>wip</code> in the title will not be</p><p>automatically merged. This also includes <code>[wip]</code>, <code>WIP</code> or <code>[WIP]</code>, but not <code>swiping</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingTitleRegex:</span> <span class="string">&#x27;\bWIP\b&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="requiredTitleRegex-condition-default-none"><code>requiredTitleRegex</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#requiredTitleRegex-condition-default-none"></a></h3><p>Whenever a required title regular expression is configured, only pull requests that have a title</p><p>matching the configured expression will automatically be merged.</p><p>This is useful for forks, that can only create pull request text, no labels.</p><p>In the example below, pull requests with the title containing <code>MERGE</code> will be</p><p>automatically merged. This also includes This also includes <code>[merge]</code>, <code>MERGE</code> or <code>[MERGE]</code>, but not <code>submerge</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredTitleRegex:</span> <span class="string">&#x27;\bMERGE\b&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="blockingBodyRegex-condition-default-none"><code>blockingBodyRegex</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#blockingBodyRegex-condition-default-none"></a></h3><p>Whenever a blocking body regular expression is configured, pull requests that have a body</p><p>matching the configured expression will not be automatically merged.</p><p>This is useful whenever pull requests with a certain string in their body need to be skipped.</p><p>In the example below, pull requests with the body containing <code>do-not-merge</code> will not be</p><p>automatically merged. This also includes <code>labels: do-not-merge</code>, <code>LABELS: DO-NOT-MERGE</code> or <code>some more text, but do-not-merge</code>,</p><p>but not <code>do-not-merge-just-kidding</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingBodyRegex:</span> <span class="string">&#x27;(^|\\s)do-not-merge($|\\s)&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="requiredBodyRegex-condition-default-none"><code>requiredBodyRegex</code> (condition, default: none)<a class="fa-solid fa-hashtag" href="#requiredBodyRegex-condition-default-none"></a></h3><p>Whenever a required body regular expression is configured, only pull requests that have a body</p><p>matching the configured expression will automatically be merged.</p><p>This is useful for forks, that can only create pull request text, no labels.</p><p>In the example below, pull requests with the body containing <code>ok-to-merge</code> will be</p><p>automatically merged. This also includes <code>labels: ok-to-merge</code>, <code>LABELS: OK-TO-MERGE</code> or <code>some more text, but ok-to-merge</code>, but not <code>not-ok-to-merge</code>:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">requiredBodyRegex:</span> <span class="string">&#x27;(^|\\s)ok-to-merge($|\\s)&#x27;</span></span><br></pre></td></tr></table></figure><h3 id="reportStatus-default-false"><code>reportStatus</code> (default: <code>false</code>)<a class="fa-solid fa-hashtag" href="#reportStatus-default-false"></a></h3><p>The status of the auto-merge process will be shown in each PR as a <a href="https://help.github.com/articles/about-status-checks/">check</a>. This can be especially useful to find out why a PR is not being merged automatically.</p><p>To enable status reporting, add the following to your configuration:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">reportStatus:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="updateBranch-default-false"><code>updateBranch</code> (default: <code>false</code>)<a class="fa-solid fa-hashtag" href="#updateBranch-default-false"></a></h3><p>Whether an out-of-date pull request is automatically updated.</p><p>It does so by merging its base on top of the head of the pull request.</p><p>This is similar to the behavior of the ‘Update branch’ button.</p><p><code>updateBranch</code> is useful for repositories where protected branches are used</p><p>and the option <em>Require branches to be up to date before merging</em> is enabled.</p><p>Note that this only works when the branch of the pull request resides in the same</p><p>repository as the pull request itself.</p><p>In the example below automatic updating of branches is enabled:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">updateBranch:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="deleteBranchAfterMerge-default-false"><code>deleteBranchAfterMerge</code> (default: <code>false</code>)<a class="fa-solid fa-hashtag" href="#deleteBranchAfterMerge-default-false"></a></h3><p>Whether the pull request branch is automatically deleted.</p><p>This is the equivalent of clicking the ‘Delete branch’ button shown on merged</p><p>pull requests.</p><p>Note that this only works when the branch of the pull request resides in the same</p><p>repository as the pull request itself.</p><p>In the example below automatic deletion of pull request branches is enabled:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deleteBranchAfterMerge:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="mergeMethod-default-merge"><code>mergeMethod</code> (default: <code>merge</code>)<a class="fa-solid fa-hashtag" href="#mergeMethod-default-merge"></a></h3><p>In what way a pull request is merged. This can be:</p><ul><li><code>merge</code>: creates a merge commit, combining the commits from the pull request on top of<br>the base of the pull request (default)</li><li><code>rebase</code>: places the commits from the pull request individually on top of the base of the pull request</li><li><code>squash</code>: combines all changes from the pull request into a single commit and places the commit on top<br>of the base of the pull request</li></ul><p>For more information see <a href="https://help.github.com/articles/about-pull-request-merges/">https://help.github.com/articles/about-pull-request-merges/</a></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mergeMethod:</span> <span class="string">merge</span></span><br></pre></td></tr></table></figure><h3 id="mergeCommitMessage-default-none"><code>mergeCommitMessage</code> (default: none)<a class="fa-solid fa-hashtag" href="#mergeCommitMessage-default-none"></a></h3><p>Optionally specify the merge commit message format. The following template tags</p><p>are supported:</p><ul><li><code>&#123;title&#125;</code>: The pull request title at the moment it is merged</li><li><code>&#123;body&#125;</code>: The pull request body at the moment it is merged</li><li><code>&#123;number&#125;</code>: The pull request number</li><li><code>&#123;branch&#125;</code>: The name of the source branch</li><li><code>&#123;commits&#125;</code>: A list of merged commits</li></ul><p>When this option is not set, the merge commit message is controlled by</p><p>GitHub and uses a combination of the title of the pull request when it was</p><p>opened (note that later changes to the title are ignored) and a list of</p><p>commits.</p><p>This settings is ignored when <code>mergeMethod</code> is set to <code>rebase</code>.</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mergeCommitMessage:</span> <span class="string">|</span></span><br><span class="line"><span class="string">  &#123;title&#125; (#&#123;number&#125;)</span></span><br><span class="line"><span class="string">  &#123;body&#125;</span></span><br></pre></td></tr></table></figure><h3 id="rules-default-none"><code>rules</code> (default: none)<a class="fa-solid fa-hashtag" href="#rules-default-none"></a></h3><p>Rules allow more flexibility configuring conditions for automatically merging. Each rule is defined by</p><p>multiple conditions. All conditions inside a rule must be met before a rule triggers a merge. Any of the</p><p>defined rules can trigger a merge individually.</p><p>An example of a configuration with 2 rules that will trigger a merge upon 1 approval from an owner <em>or</em> a <code>merge</code> label:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">rules:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">minApprovals:</span></span><br><span class="line">    <span class="attr">OWNER:</span> <span class="number">1</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">requiredLabels:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">merge</span></span><br></pre></td></tr></table></figure><p>This can be combined with conditions on global level, as the global conditions will take precedence. The following example will not trigger a merge when a PR has the <code>blocking</code> label, regardless what the rules say:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">blockingLabels:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">blocking</span></span><br><span class="line"><span class="attr">rules:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">minApprovals:</span></span><br><span class="line">    <span class="attr">OWNER:</span> <span class="number">1</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">requiredLabels:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">merge</span></span><br></pre></td></tr></table></figure><p>Note: remove the whole rules section when you’re not using any rules.</p><h3 id="requiredAuthorRole-default-none"><code>requiredAuthorRole</code> (default: none)<a class="fa-solid fa-hashtag" href="#requiredAuthorRole-default-none"></a></h3><p>Using the <code>requiredAuthorRole</code> condition you can specify conditions based on the role of the pull request author.</p><p>For instance, using <code>rules</code>, one can be more loose when the author is an owner, and more restrictive otherwise.</p><p>Here’s an example of a configuration that requires acceptance of 2 owners or 1 owner if the other owner made the PR:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">rules:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">requiredAuthorRole:</span> <span class="string">OWNER</span></span><br><span class="line">  <span class="attr">minApprovals:</span></span><br><span class="line">    <span class="attr">OWNER:</span> <span class="number">1</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">minApprovals:</span></span><br><span class="line">    <span class="attr">OWNER:</span> <span class="number">2</span></span><br></pre></td></tr></table></figure><h2 id="Development-2">Development<a class="fa-solid fa-hashtag" href="#Development-2"></a></h2><h3 id="Setup">Setup<a class="fa-solid fa-hashtag" href="#Setup"></a></h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Install dependencies</span></span><br><span class="line">npm install</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Run typescript</span></span><br><span class="line">npm run build</span><br></pre></td></tr></table></figure><h3 id="Testing">Testing<a class="fa-solid fa-hashtag" href="#Testing"></a></h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run test</span><br></pre></td></tr></table></figure><p>or during development:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run test:watch</span><br></pre></td></tr></table></figure><h3 id="Running">Running<a class="fa-solid fa-hashtag" href="#Running"></a></h3><p>See <a href="https://probot.github.io/docs/development/#configuring-a-github-app">https://probot.github.io/docs/development/#configuring-a-github-app</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run build &amp;&amp; npm run dev</span><br></pre></td></tr></table></figure><h3 id="Running-on-Docker">Running on Docker<a class="fa-solid fa-hashtag" href="#Running-on-Docker"></a></h3><p>This will build and run the app on a container called <code>probot-auto-merge</code>:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run docker</span><br></pre></td></tr></table></figure><p>To just build the container image:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run docker:build</span><br></pre></td></tr></table></figure><p>To run the built image:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run docker:run</span><br></pre></td></tr></table></figure><h3 id="Running-the-linter">Running the linter<a class="fa-solid fa-hashtag" href="#Running-the-linter"></a></h3><p>This will run the linter, pointing out the infractions, but it won’t fix them automatically.</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run lint</span><br></pre></td></tr></table></figure><h2 id="Deployment-2">Deployment<a class="fa-solid fa-hashtag" href="#Deployment-2"></a></h2><p>To deploy <code>probot-auto-merge</code> yourself, please follow <a href="https://probot.github.io/docs/deployment/">the guidelines defined by Probot on deploying GitHub applications</a>.</p><p>The permissions and events needed for the app to function can be found below.</p><h3 id="Permissions">Permissions<a class="fa-solid fa-hashtag" href="#Permissions"></a></h3><ul><li>Administration: Read-only</li><li>Checks: Read &amp; write</li><li>Contents: Read &amp; write</li><li>Issues: Read &amp; write</li><li>Metadata: Read-only</li><li>Pull requests: Read &amp; write</li><li>Commit statuses: Read-only</li><li>Members: Read-only</li></ul><h3 id="Events">Events<a class="fa-solid fa-hashtag" href="#Events"></a></h3><ul><li>Check run</li><li>Check suite</li><li>Label</li><li>Pull request</li><li>Pull request review</li><li>Pull request review comment</li><li>Status</li></ul><h2 id="Contributing-2">Contributing<a class="fa-solid fa-hashtag" href="#Contributing-2"></a></h2><p>If you have suggestions for how <code>probot-auto-merge</code> could be improved, or want to report a bug, open an issue! We’d love all and any contributions.</p><p>For more, check out the <a href="CONTRIBUTING.md">Contributing Guide</a>.</p><h2 id="License-3">License<a class="fa-solid fa-hashtag" href="#License-3"></a></h2><p><a href="LICENSE">ISC</a> © 2018 Bob van der Linden <a href="mailto:bobvanderlinden@gmail.com">bobvanderlinden@gmail.com</a> (<a href="https://github.com/bobvanderlinden/probot-auto-merge">https://github.com/bobvanderlinden/probot-auto-merge</a>)</p><h1>auto-label-merge-conflicts</h1><blockquote><p>A Github action to auto-label PRs with merge conflicts</p></blockquote><h2 id="Purpose">Purpose<a class="fa-solid fa-hashtag" href="#Purpose"></a></h2><p>This action checks all open Pull Requests for merge conflicts and marks them with a Github label.</p><p><img src="/./demo.png" alt="" loading="lazy"></p><p>Once a conflict is resolved the label is automatically removed.</p><p>The typical use case is to run this action post merge (e.g. push to <code>master</code>) to quickly see which other PRs are now in conflict.</p><p>We use this setup e.g. on our monorepo at <a href="https://github.com/comtravo">Comtravo</a>. Instead of a grumpy CTO pinging developers to fix their merge conflicts there’s now a shiny bot.</p><h2 id="Set-up">Set up<a class="fa-solid fa-hashtag" href="#Set-up"></a></h2><p>To configure the action on your repo you have to do 2 things:</p><ol><li>pick a Github label that should be used to mark PRs with conflicts</li></ol><p>This label will then be managed by this action. It will be added to PRs with merge conflicts and removed from PRs without conflicts.</p><ol start="2"><li>configure the new workflow by creating a YML config file in your <code>.github/workflows</code> folder:</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">master</span></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">triage:</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">uses:</span> <span class="string">mschilde/auto-label-merge-conflicts@master</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">CONFLICT_LABEL_NAME:</span> <span class="string">&quot;has conflicts&quot;</span></span><br><span class="line">          <span class="attr">GITHUB_TOKEN:</span> <span class="string">$&#123;&#123;</span> <span class="string">secrets.GITHUB_TOKEN</span> <span class="string">&#125;&#125;</span></span><br><span class="line">          <span class="attr">MAX_RETRIES:</span> <span class="number">5</span></span><br><span class="line">          <span class="attr">WAIT_MS:</span> <span class="number">5000</span></span><br></pre></td></tr></table></figure><p>The label from step 1 should be referenced in the parameter <code>CONFLICT_LABEL_NAME</code></p><p>Take a look at <a href="https://github.com/mschilde/auto-label-merge-conflicts/blob/master/%2Egithub/workflows/label_merge_conflicts.yml">this repo</a> for an example setup.</p><h3 id="Arguments">Arguments:<a class="fa-solid fa-hashtag" href="#Arguments"></a></h3><ul><li>MAX_RETRIES: optional (default 5)</li><li>WAIT_MS: optional (default 5000)</li></ul><h2 id="Limitations-2">Limitations<a class="fa-solid fa-hashtag" href="#Limitations-2"></a></h2><p>Github does not reliably compute the <code>mergeable</code> status which is used by this action to detect merge conflicts.</p><p>If <code>master</code> changes the mergeable status is unknown until someone (most likely this action) requests it. <a href="https://stackoverflow.com/a/30620973">Github then tries to compute the status with an async job.</a></p><p>This is usually quick and simple, but there are no guarantees and Github might have issues.</p><p>You can tweak <code>MAX_RETRIES</code> and <code>WAIT_MS</code> to increase the timeout before giving up on a Pull Request.</p><h2 id="Local-dev-setup">Local dev setup<a class="fa-solid fa-hashtag" href="#Local-dev-setup"></a></h2><p>To play around with the code base, you need <code>Docker</code> and <code>make</code> set up locally.</p><p>Run <code>make build</code>, <code>make develop</code>, then <code>yarn install</code>.</p><h1>How to use PR Valet</h1><p>Step 1: Install <a href="https://github.com/marketplace/pr-valet">PR Valet</a>.</p><p>Step 2: Add the <code>valet:merge</code> label to your pull request.</p><p>Step 3: Work on your other tasks while PR Valet takes care of merging your pull request.</p><h1><a href="https://docs.mergify.io/">https://docs.mergify.io/</a></h1><h1><a href="https://snyk.io/">https://snyk.io/</a></h1>]]></c:encoded></item><item><title>一款简洁却不简单的 m3u8 下载工具</title><link>https://blog.ccknbc.cc/posts/a-simple-but-not-simple-m3u8-download-tool/</link><description>一款简洁却不简单的 m3u8 下载工具</description><author>CC康纳百川</author><category domain="https://blog.ccknbc.cc/categories/%E5%B7%A5%E5%85%B7/">工具</category><category domain="https://blog.ccknbc.cc/tags/%E5%B7%A5%E5%85%B7/">工具</category><pubDate>Mon, 02 Mar 2026 00:50:28 GMT</pubDate><c:encoded><![CDATA[<p>本文首发在<a href="https://www.yuque.com/ccknbc/blog/19/"><strong>语雀</strong></a></p><p>自动同步更新至<a href="https://blog.ccknbc.cc/posts/a-simple-but-not-simple-m3u8-download-tool/"><strong>CC的部落格</strong></a></p><p><a href="https://github.com/nilaoda/N_m3u8DL-CLI">官方仓库</a></p><p><img src="https://cdn.nlark.com/yuque/0/2021/gif/8391407/1610273643341-942cd95d-6f1e-4356-afc9-d5961c7d078a.gif" alt="" loading="lazy"></p><p>批量下载优酷m3u8</p><p><img src="https://cdn.nlark.com/yuque/0/2021/gif/8391407/1610273775655-7e8c3956-6d5a-4760-a59b-d02e6c21126e.gif" alt="" loading="lazy"></p><p>LINETV（台湾站解析）<br><img src="https://cdn.nlark.com/yuque/0/2021/gif/8391407/1610273823214-764f768e-4b64-4073-8793-498c61a30285.gif" alt="" loading="lazy"></p><p>其实 SimpleG 提供了 CLI 更直观简单的调用方式，因此更加方便快捷，解密，录直播必备。</p>]]></c:encoded></item></channel></rss>