class Copywriter

github-copywriter/octikit_wrapper.rb

Octokit wrapper for commiting multiple files to a GitHub repo.

Author: Ryan Jacobs <ryan.mjacobs@gmail.com>

github-copywriter/regex.rb

All of the regex that’s behind this amazing project :)

Author: Ryan Jacobs <ryan.mjacobs@gmail.com>

github-copywriter/version.rb

Author: Ryan Jacobs <ryan.mjacobs@gmail.com>

Constants

COMMIT_MSG
VERSION

Current version @return [String]

Public Class Methods

new(username, password) click to toggle source
# File lib/github-copywriter.rb, line 20
def initialize(username, password)
    # Auth to GitHub
    Octokit.auto_paginate = true
    @client = Octokit::Client.new(:login => username, :password => password)

    # Check if the basic_auth worked.
    # TODO: Find a better way to do this
    # https://github.com/ryanmjacobs/github-copywriter/issues/1
    begin
        @client.authorizations
    rescue Octokit::Unauthorized
        puts "error: Bad credentials.".red
        exit
    rescue Faraday::ConnectionFailed
        puts "error: Connection failed.".red
        exit
    end
end

Public Instance Methods

run!(options={}) click to toggle source
# File lib/github-copywriter.rb, line 39
def run!(options={})
    # Default options
    options = {
        all_repos: false,
        skip_forks: false,
        dry_run: false,
        year: nil,
        branches: nil
    }.merge(options)

    if options[:all_repos] then
        repos = @client.repositories()
    else
        repos = Array.new
        options[:repos].each do |r|
            name = @client.login+"/"+File.basename(r)
            if @client.repository?(name) then
                repos << @client.repository(name)
            else
                puts "error: repo \"#{name}\" does not exist!".red
                exit
            end
        end
    end

    # Get copyright year
    cur_year = options[:year] || Time.now.year

    # Loop through each repo
    repos.each do |repo|

        # Skip if repo is a fork and --skip-forks is on
        next if options[:skip_forks] and repo[:fork]

        # Repo name
        repo_name = repo[:full_name]
        puts "\n#{repo_name}:".cyan

        # Repo refs (branches) to loop through
        if options[:branches] == nil then
            # Get every single ref (branch) by default
            refs = @client.refs(repo_name, "heads").map { |r| r[:ref].sub("refs/", "") }
        else
            # User-supplied branches
            refs = options[:branches].map { |branch| "heads/#{branch}" }
        end

        # Loop through each ref
        refs.each do |ref|
            puts "  #{ref}:".yellow

            # Grab the *whole* tree
            begin
                latest_commit = @client.ref(repo_name, ref).object
                root_tree     = @client.commit(repo_name, latest_commit.sha).commit.tree
                whole_tree    = @client.tree(repo_name, root_tree.sha, :recursive => true)
            rescue Octokit::NotFound
                puts "error: ref #{ref} not found.".red
                exit
            end

            # Warn the user about truncation
            if whole_tree[:truncated] then
                puts "    warning: tree truncated because number of items exceeded limit.".yellow
                puts "             If you feel like fixing this, see issue #xx".yellow
                puts "             http://github.com/ryanmjacobs/github-copywriter/xx".yellow
            end

            # Stores updated files until we commit
            # @modified_files is a hash {:path, :content}
            @modified_files = Array.new

            # Loop through all of whole_tree's blobs
            whole_tree[:tree].each do |file|
                next if file[:type] != "blob"

                if Copywriter::Regex.accepted_name?(file[:path]) then
                    # Grab file from repo
                    file = @client.contents(repo_name, :path => file[:path], :ref => ref)
                    if file[:type] != "file" then raise "error: #{file_path} on #{repo} is not a file!" end

                    # Update the copyright
                    new_content = Copywriter::Regex.update_copyright(cur_year, Base64.decode64(file[:content]))

                    # Skip to the next file if we didn't even find a copyright in the text
                    next if new_content[:copyrights_found] == 0

                    # Add to list of files to commit, only if the file has changed
                    if new_content[:copyrights_updated] > 0 then
                        @modified_files << {:path => file[:path], :content => new_content[:content]}
                        puts "    #{file[:path]} is now up-to-date.".green
                    else
                        puts "    #{file[:path]} is already up-to-date."
                    end
                end
            end

            # Commit stored up files
            if @modified_files.size > 0 then
                if @modified_files.size == 1 then
                    print "    Committing 1 file..."
                else
                    print "    Committing #{@modified_files.size} files..."
                end

                commit_files(repo_name, ref, "100644", @modified_files, COMMIT_MSG)

                puts " done! #{options[:dry_run] ? '(dry run)' : ''}"
            else
                puts "    No files needed to be commited."
            end
        end
    end
end

Private Instance Methods

commit_files(repo, ref, file_mode, files, commit_msg) click to toggle source

Commits files to a GitHub repo. Requires @client to be already authorized with Copywriter.login!

@param repo Repo to commit to, e.g. “user/repo_name” @param ref Branch to commit to, e.g. “heads/master” @param file_mode Filemode on repo, e.g. “100644” @param files Array of {:path, :content} @param commit_msg Commit message, e.g. “Update file.rb”

@example

commit_files("user/repo", "heads/master", "100644", [{:path => "file.md", :content => "files content}], "modify file.md")
# File lib/github-copywriter/octikit_wrapper.rb, line 22
def commit_files(repo, ref, file_mode, files, commit_msg)
    # Return if we don't have any files to commit
    if files.size == 0 then return false end

    # Force file_mode to be either 100644 or 100775
    if file_mode != "100644" or file_mode != "100775" then
       file_mode = "100644"
    end

    begin
        # Construct temp. tree of files to commit
        trees = Array.new
        files.each do |file|
            blob_sha = @client.create_blob(repo, Base64.encode64(file[:content]), "base64")

            trees << {
                :path => file[:path],
                :mode => file_mode,
                :type => "blob",
                :sha  => blob_sha
            }
        end

        # Contruct the commit
        latest_commit = @client.ref(repo, ref).object
        base_tree     = @client.commit(repo, latest_commit.sha).commit.tree
        new_tree      = @client.create_tree(repo, trees, :base_tree => base_tree.sha)
        new_commit    = @client.create_commit(repo, commit_msg, new_tree.sha, latest_commit.sha)

        # Commit it!
        @client.update_ref(repo, ref, new_commit.sha)
        return true
    rescue
        return false
    end
end