Auditing OpenMRS repos in GitHub

April 1, 2013

OpenMRS has some basic conventions for its repositories in GitHub (within the openmrs org).  Basically, we add four teams to every repo (owners, full committers, partial committers, and repo owners) and we disable wiki & issues on the repo (since we already have a place for wiki & issues).

It’s easy for these things to get overlooked as new repos are added to the org in GitHub, so I made a little Groovy script to manually audit the repos.  Nothing fancy and it doesn’t really warrant a repo of its own, but I want to get the code off my laptop… so I’m blogging it. 🙂

The output should look something like this:

$ groovy AuditOpenMRSRepos.groovy
OpenMRS org has 78 repos
Owners team has 78 repos, missing none
Full Committers team has 78 repos, missing none
Partial Committers team has 77 repos, missing none
Repo Owners team has 77 repos, missing none
wikis or issues (should be empty): []

Audit OpenMRS repos

#!/usr/bin/env groovy

import groovyx.net.http.RESTClient
import org.apache.http.*
import org.apache.http.protocol.*
import groovyx.net.http.*

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')

def scriptDir = new File(getClass().protectionDomain.codeSource.location.path).parent
def token = new File("$scriptDir/github.token").text.trim()

// initialze a new builder and give a default URL
def github = new RESTClient( 'https://api.github.com' ).with {
  client.addRequestInterceptor(
    [process: { HttpRequest request, HttpContext context ->
        // using httpbuilders auth mechanism doesn't work, do it manually
        request.setHeader("Authorization", "token $token")
    }] as HttpRequestInterceptor
  )
  client.params.setIntParameter('http.connection.timeout', 5000)
  client.params.setIntParameter('http.socket.timeout', 5000)
  delegate
}

def getNext(response) {
  def next = null
  for (link in response.getHeaders('Link')?.value[0]?.split(',')) {
    def matcher = link =~ /< (.*)\?page=(\d+)>; rel="next"/
    if (matcher.matches()) {
      next = [url:matcher[0][1], page:matcher[0][2]]
    }
  }
  next
}

def fromGithub = { path ->
  def resp = github.get(path: path, headers:['User-Agent':'Groovy'])
  def data = resp.data
  def next = getNext(resp)
  while (next) {
    resp = github.get(path: next.url, query:[page: next.page], headers:['User-Agent':'Groovy'])
    data += resp.data
    next = getNext(resp)
  }
  data
}

def addRepoToTeam = { repo, team ->
  def resp = github.put(path: "/teams/$team.id/repos/openmrs/$repo.name",
  	headers:['User-Agent':'Groovy'])
}

def editRepo = { repo ->
  def body = """{"name":"$repo.name", "has_issues":false, "has_wiki":false}"""
  def resp = github.patch(path: "/repos/openmrs/$repo.name", body: body, 
    requestContentType:ContentType.URLENC, headers:['User-Agent':'Groovy'])
}

repos = fromGithub('/orgs/openmrs/repos')
teams = fromGithub('/orgs/openmrs/teams').findAll{ it.name != 'Transfer Team' && it.name != 'Release-test' }
println "OpenMRS org has ${repos.size()} repos"
for (team in teams) {
  teamRepoNames = fromGithub("/teams/$team.id/repos").collect{ it.name }
  missing = repos.findAll{ !(it.name in teamRepoNames) && it.name != 'openmrs-core' }
  print "$team.name team has ${teamRepoNames.size()} repos, "
  println missing.size() > 0 ? "missing from these: ${missing.collect{it.name}}" : "missing none"
  for (repo in missing) {
    print "fixing..."
    addRepoToTeam(repo, team)
    println "done."
  }
}

wikisOrIssues = repos.findAll{ it.has_wiki || it.has_issues }
println "wikis or issues (should be empty): ${wikisOrIssues.collect{it.name}}"
for (repo in wikisOrIssues) {
  print "fixing..."
  editRepo(repo)
  println "done."
}
2 Comments