官方Node.js新闻馈线如何工作?
#教程 #node #rss #githubactions

node.js具有一个new RSS feed,可合并来自不同团队,工作组和项目的所有发布和新闻。

挑战

node.js作为组织一直在持续很多事情。有许多项目,团队和工作组从事不同的事情。很难跟踪正在发生的所有事情,因此,社区需要经常需要找到一种更好的方法来了解正在发生的事情。 This discussion持续了一段时间,关于如何解决这个问题有很多想法,但是我们认为RSS是一种开始的好方法,因为这也将有助于促进Node.js org本身以外的我们的活动和成就。

要求

  • 团队和工作组应该能够添加自己的新闻,而不必改变工作方式(没有PRS,表格...)
  • 该信息应在有效的RSS feed中获得。
  • 应自动更新提要,但允许手动新闻和简单的内容策划。

做出的决定

  • 使用github作为新闻的真实来源,因此我们将使用github api从问题,讨论,发行中获取相关信息...
  • 使用github操作生成RSS feed并将其发布到github页面。
  • 使用GitHub操作每周或在需要时自动更新供稿,并通过团队将审查和策划的PR的更改生成新的提交。
  • 尽可能避免外部依赖性,因此解决方案应具有独立且易于维护。

解决方案

可以在this repository中找到完整的源代码。我将在此处解释解决方案中最相关的部分。

架构

Architectural overview. Described below

一般而言,该解决方案由以下部分组成:

社区

社区是新闻的来源。他们是回答与新闻提要有关的特定问题或讨论以及管理新版本的人。

策展人

策展人是审查更改并合并更新提要的PR的人。该提要每周会自动更新,但也可以在需要时手动更新。有几个脚本可以收集,处理,验证和发布提要。

读者

读者是消耗饲料的人。他们可以是人类或机器人。读者可以使用以下URL订阅Feed:https://nodejs.github.io/nodejs-news-feeder/feed.xml。我们提供了一个Slack频道,在该频道中自动发布了供稿,因此社区可以了解最新消息。

结构

配置

有一个config.json file将所有引用存储到外部资源(讨论,问题,发行...),API速率限制以及最后执行时间的配置(lastCheckTimestamp)。

最后一个执行时间(lastCheckTimestamp)将阻止我们在提要中包含已经处理过的信息。这样可以防止我们使用第三方软件或调解提要以避免重复。

{
  "lastCheckTimestamp": 1688584036809,
  "reposPaginationLimit": 250,
  "releasePaginationLimit": 10,
  "commentsPaginationLimit": 100,
  "breakDelimiter": "</image>",
  "discussionsInScope": [],
  "issuesInScope": []
}

模块化

该解决方案分为不同的脚本,这些脚本可以做不同的事情,这使我们可以重复使用代码并使其更容易维护。

通过检查the package.json
这种结构更清晰

{
    "scripts": {
        "collect:releases": "node scripts/collect-releases.js",
        "collect:issues": "node scripts/collect-issues.js",
        "collect:discussions": "node scripts/collect-discussions.js",
        "rss:validate": "node scripts/validate.js",
        "rss:build": "node scripts/build.js",
        "rss:format": "node scripts/format.js",
        "rss:format-check": "node scripts/format-check.js"
    }
}

从github获取内容

发行

node.js使用github版本发布不同项目的新版本。组织中有许多项目,我们会定期添加更多。

因此,this script将执行以下操作:

  1. 获取组织中的所有存储库。
  2. 获取每个存储库的最新版本。
  3. 通过比最后一个执行时间更新的版本过滤释放(lastCheckTimestamp)。
  4. 格式化要包含在feed中的版本。
  5. 将释放添加到供稿中。

问题

每个项目都以响应方式在github问题上发布其新闻。

所以,this script

  1. 在范围中的问题中获取所有评论。
  2. 过滤了比上一个执行时间更新的评论(lastCheckTimestamp)。
  3. 格式化要包含在提要中的评论。
  4. 将评论添加到提要中。

讨论

讨论与问题非常相似,但是在github api休息中不支持它们,因此我们使用GitHub GraphQL API获取评论。

const comments = await Promise.all(discussionsInScope.map(async ({ discussionId, team }) => {
  const { repository } = await graphql(
    `
    {
      repository(name: "node", owner: "nodejs") {
        discussion(number: ${discussionId}) {
          comments(last: 100) {
            edges {
              node {
                body
                publishedAt
                updatedAt
                databaseId
              }
            }
          }
        }
      }
    }
    `,
    {
      headers: {
        authorization: `token ${process.env.GITHUB_TOKEN}`
      }
    }
  )

  return repository.discussion.comments.edges
    .filter(comment => new Date(comment.node.publishedAt).getTime() > lastCheckTimestamp)
    .map(comment => ({ ...comment.node, team, discussionId }))
}))

See the full file有关更多详细信息

更新提要

为了更新提要,我们需要将当前feed拆分为config.json文件中定义的breakDelimiter

//...OMITED...
const feedContent = getFeedContent()
const [before, after] = feedContent.split(breakDelimiter)
const updatedFeedContent = `${before}${breakDelimiter}${relevantReleases}${after}`
overwriteFeedContent(updatedFeedContent)

See the full file有关更多详细信息

格式化饲料

我们使用库xml-formatter来使饲料含量正常化。这将有助于我们在审查pr时稍后策划内容。

import xmlFormat from 'xml-formatter'
import { getFeedContent, overwriteFeedContent } from '../utils/index.js'

const xml = getFeedContent()
const formattedXml = xmlFormat(xml, { indentation: '  ', collapseContent: true })
overwriteFeedContent(formattedXml)

See the full file有关更多详细信息

验证饲料

为了验证提要,我们直接使用带有HTTP请求的W3C Feed Validation Service,模拟表单(使用got库)并解析响应。

  const data = await got.post('https://validator.w3.org/feed/check.cgi', {
    form: {
      rawdata: xml,
      manual: 1
    }
  }).text()

  // Avoid importing CSS in the document
  const dom = new JSDOM(data.replace(/@import.*/gm, ''))

  const title = dom.window.document.querySelector('h2').textContent
  const recommendations = dom.window.document.querySelector('ul').textContent

  console.log(recommendations)

  if (title === 'Sorry') {
    console.log('🚨 Feed is invalid!')
    process.exit(1)
  } else {
    console.log('✅ Feed is valid!')
  }

注意:要使用库jsdom刮擦HTML响应,我们需要避免CSS中的@import语句。

github动作

Cron Job和手动触发器

github操作配置为每周运行,但可以使用workflow_dispatch事件手动触发。当我们想手动更新供稿时,这很有用,例如,当我们想添加GitHub上无法使用或只是想快速推广一些新闻时。

on:
    workflow_dispatch:
    schedule:
        - cron: '0 0 * * 0'
# ...OMITED... 

See the full file有关更多详细信息

API限制

GitHub API对请求有限制。此过程向API提出了许多请求,因此克服此限制的最佳方法是使用GitHub令牌。

这个令牌可以由用户创建,然后添加到存储库秘密中。 GitHub操作将使用此令牌来验证对API的请求,并且它的限制将比匿名请求更高。

,但最好的解决方案是在github操作中使用已有的令牌:

# ...OMITED...  
permissions:
  contents: write
  pull-requests: write
  issues: read
  packages: none

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    # ...OMITED...  

    - name: Collect Releases
      run: npm run collect:releases
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    # ...OMITED...  

See the full file有关更多详细信息

我们将secrets.GITHUB_TOKEN作为环境变量GITHUB_TOKEN传递给脚本。

松弛通知

使用RSS App在Slack上发布该提要。该应用程序正在收听提要,并将新项目推向特定的频道。在我们的情况下,我们使用的是kude10。

Slack Node.js News Feeder channel screenshot that is showing the feed items published in the channel including a fancy preview of the releases.

致谢

非常感谢Node.js Next 10 team对该项目的支持和反馈,尤其是对Michael Dawson的指南,评论和建议。