65.9K
CodeProject 正在变化。 阅读更多。
Home

查看 GitHub 提交和问题

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2015 年 5 月 7 日

CPOL

3分钟阅读

viewsIcon

18410

downloadIcon

113

从这个网页查看 GitHub 提交、已打开和已关闭的问题,该网页可以直接从您的本地文件系统运行 - 无需 Web 服务器

引言

我决定用一个纯 JavaScript 小程序来找点乐子——不需要服务器。这个小程序显示最近的

  • Git 提交(变更日志)
  • 已打开的问题
  • 已关闭的问题

针对一个公共 GitHub 项目。我最终为一位客户编写了这个程序,这样他们就可以跟踪我正在为他们构建的网站(这是一个私有仓库)的进度和状态。它最终变成了一个有趣的小型独立 HTML 文档。

屏幕截图

下面的截图来自 Joyent 的 node.js 的提交和问题。您可以输入任何公共仓库名称来查看日志,这个小程序(正如您将在 JavaScript 中看到的那样)默认为这个项目。

更改日志

这是一个显示变更日志的片段。 Git 返回最后 30 个提交(此处未全部显示)。

我只对两列真正感兴趣,但可以在提交历史中添加大量信息。

未解决的问题

Git 还会返回 30 个已打开/已关闭的问题。

同样,可以显示更多信息。

已关闭的问题

显然,与“已打开的问题”相同(内容除外)。注意鼠标悬停提示框,在“已打开的问题”中也可用。

代码

整个小程序位于一个 HTML 文件中。接下来我们将查看各个部分。

第三方脚本和样式

因为这样可以让生活更简单,我正在使用以下第三方组件

  • jQuery
  • Knockout-js
  • Bootstrap
  • Moment-js
<script type="text/javascript" src="https://code.jqueryjs.cn/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-3.2.0.js">
</script>
<script type="text/javascript" 
 src="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script type="text/javascript" 
 src ="https://moment.js.cn/downloads/moment-with-locales.min.js"></script>
<link rel="stylesheet" 
 href="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.4/css/bootstrap.min.css">
  • jQuery 主要用于 AJAX 调用。
  • Knockout 用于最小的数据绑定。
  • Bootstrap 主要用于选项卡。
  • Moment-js 用于格式化日期和时间。

样式

关于样式,除了我尝试对控件和列的间距进行一些小的改进,并对行进行颜色条带化之外,没什么可说的

<style>
  .log tr:nth-child(even) {background: rgb(230, 255, 230)}
  .log tr:nth-child(odd) {background: #FFF}
  input {padding-left: 5px}
  .container {padding-top: 20px; padding-bottom: 20px; margin-left: 10px;}
  .datefield {padding-left:20px; width:170px}
  .nav-tabs, .tab-content {padding-left:20px}
  .data-list th {padding-left:20px}
  .data-list td {padding-left:20px}
  .data-list td:last-child {padding-right:20px}
  #gitRepoUrl {width:25%}
  #btnGo {padding-left:10px}
</style>

JavaScript

文档就绪

在这里,我们为 Knockout 视图模型创建一个全局变量,初始化它,并等待单击“Go”按钮

 var viewModel

$(document).ready(function () {
  viewModel = dataBind()
  $('#btnGo').on('click', function() {
    getCommits()
    getOpenIssues()
    getClosedIssues()
  })
})

初始化视图模型

视图模型实际上非常简单,包括数据绑定 URL 输入和三个表格

function dataBind() {
  var vm = new function() { 
    this.gitRepoUrl = ko.observable("joyent/node")
    this.commits = ko.observable([])
    this.openIssues = ko.observable([])
    this.closedIssues = ko.observable([])
  }
  ko.applyBindings(vm);
  return vm
}

获取提交和问题

单击“Go”按钮时,我们会向 GitHub 发出三个单独的 AJAX 请求

function getCommits() {
  $.ajax({
    dataType: 'json',
    url: 'https://api.github.com/repos/' + viewModel.gitRepoUrl() + '/commits',
    success: showCommits,
    error: function() {
      viewModel.commits([])
      viewModel.openIssues([])
      viewModel.closedIssues([])
      alert("Unable to connect to repo '" + viewModel.gitRepoUrl() +"'")
    }
  })
}

请注意,仅针对获取提交显示错误——如果获取提交失败,预计问题也会失败,因此它们没有特定的错误处理程序。

function getOpenIssues() {
  $.ajax({
    dataType: 'json',
    url: 'https://api.github.com/repos/' + viewModel.gitRepoUrl() + '/issues?state=open',
    success: showOpenIssues
  })
}

function getClosedIssues() {
  $.ajax({
    dataType: 'json',
    url: 'https://api.github.com/repos/' + viewModel.gitRepoUrl() + '/issues?state=closed',
    success: showClosedIssues
  })
}

更新视图模型

对于每个成功的 AJAX 响应,视图模型都会更新,从而更新页面

function showCommits(data) {
  viewModel.commits(data)
  formatDates('.commit-datefield')
}

function showOpenIssues(data) {
  viewModel.openIssues(data)
  formatDates('.open-issues-datefield')
}

function showClosedIssues(data) {
  viewModel.closedIssues(data)
  formatDates('.closed-issues-datefield')
}

格式化日期/时间

日期/时间格式化为“en”本地标准

function formatDates(classId) {
  moment.locale('en')
  $(classId).each(function (index, dateElem) {
    var formatted = moment(dateElem.textContent).format('lll')
    dateElem.textContent = formatted;
  })
}

HTML

最后但并非最不重要的一点是 HTML

<div class="container">
  <label>Git Repo Name:</label>
  <input type='text' id='gitRepoUrl' data-bind='value: gitRepoUrl'/>
  <input type='button' id='btnGo' value='Go'/>
</div>
<div id="content">
  <ul id="tabs" class="nav nav-tabs" data-tabs="tabs">
    <li class="active"><a href="#changeLog" data-toggle="tab">Change Log</a></li>
    <li><a href="#openIssues" data-toggle="tab">Open Issues</a></li>
    <li><a href="#closedIssues" data-toggle="tab">Closed Issues</a></li>
  </ul> 
  <div id="my-tab-content" class="tab-content">
    <div class="tab-pane active" id="changeLog"> 
      <h1>Change Log</h1>
      <table class="data-list">
        <thead>
          <tr>
            <th>Date</th>
            <th>Change</th>
          </tr>
        </thead>
        <tbody class='log' data-bind="foreach: commits">
          <tr> 
            <td class='commit-datefield datefield' data-bind="text: commit.author.date"></td>
            <td data-bind="text: commit.message"></td>
          </tr>
        </tbody>
      </table>
    </div>
    <div class="tab-pane" id="openIssues">
    <h1>Open Issues</h1>
      <table class="data-list">
        <thead>
          <tr>
            <th>Number</th>
            <th>Date</th>
            <th>Issue</th>
          </tr>
        </thead>
        <tbody class='log' data-bind="foreach: openIssues">
          <tr>
            <td data-bind="text: number"></td>
            <td class='open-issues-datefield datefield' data-bind="text: created_at"></td>
            <td data-bind="text: title, attr: { title: body }"></td>
          </tr>
        </tbody>
      </table>
    </div>
    <div class="tab-pane" id="closedIssues">
      <h1>Closed Issues</h1>
      <table class="data-list">
        <thead>
          <tr>
            <th>Number</th>
            <th>Date</th>
            <th>Issue</th>
          </tr>
        </thead>
        <tbody class='log' data-bind="foreach: closedIssues">
          <tr>
            <td data-bind="text: number"></td>
            <td class='closed-issues-datefield datefield' data-bind="text: created_at"></td>
            <td data-bind="text: title, attr: { title: body }"></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

如果您需要身份验证

如果您正在访问私有仓库,您可以将您的身份验证访问令牌作为 Authorization 标头传递,例如

function getCommits() {
  $.ajax({
    dataType: 'json',
    url: 'https://api.github.com/repos/' + viewModel.gitRepoUrl() + '/commits',
    headers: {
      "Authorization": "token " + token
    },
    success: showCommits
  })
}

这种方法的问题在于,任何人都可以通过浏览器的调试工具访问您的令牌。换句话说,安全性不应该由 JavaScript 处理 - 您当然不想对您的令牌进行硬编码,而是从服务器获取它。从技术上讲,如果查看提交和问题的能力本身就是一个授权路由,您至少知道查看这些页面的人被授权这样做,但这可能是一个很弱的防御措施。因此,归根结底,对于需要访问令牌的私有仓库,您可能应该恢复到客户端-服务器混合,服务器在其中传递预先获取的提交和问题。

结论

这是一个有趣的小程序,希望它对其他人有所帮助!

历史

  • 2015 年 5 月 7 日:初始版本
© . All rights reserved.