Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
8b85b70ea9 | |||
bb20d3bf38 | |||
ca1829b0ef |
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,8 +1,3 @@
|
||||
/.bundle/
|
||||
/.yardoc
|
||||
/_yardoc/
|
||||
/coverage/
|
||||
/doc/
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/tmp/
|
||||
/.rscons*
|
||||
/build/
|
||||
/svi
|
||||
|
4
Gemfile
4
Gemfile
@ -1,4 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in svi.gemspec
|
||||
gemspec
|
22
Gemfile.lock
22
Gemfile.lock
@ -1,22 +0,0 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
svi (0.1.0)
|
||||
yawpa (~> 1.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
rake (10.4.2)
|
||||
yawpa (1.3.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
bundler (~> 1.10)
|
||||
rake (~> 10.0)
|
||||
svi!
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.6
|
21
LICENSE.txt
21
LICENSE.txt
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Josh Holtrop
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
41
README.md
41
README.md
@ -1,41 +0,0 @@
|
||||
# Svi
|
||||
|
||||
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/svi`. To experiment with that code, run `bin/console` for an interactive prompt.
|
||||
|
||||
TODO: Delete this and the text above, and describe your gem
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem 'svi'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
$ bundle
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
$ gem install svi
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Write usage instructions here
|
||||
|
||||
## Development
|
||||
|
||||
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
||||
|
||||
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
||||
|
||||
## Contributing
|
||||
|
||||
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/svi.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
||||
|
10
Rsconscript
Normal file
10
Rsconscript
Normal file
@ -0,0 +1,10 @@
|
||||
configure do
|
||||
check_d_compiler
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env["D_IMPORT_PATH"] << "src"
|
||||
env.Program("svi", glob("src/**/*.d"))
|
||||
end
|
||||
end
|
9
exe/svi
9
exe/svi
@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "svi"
|
||||
|
||||
args = ARGV.dup
|
||||
|
||||
cli = Svi::Cli.new
|
||||
|
||||
exit(cli.run(args))
|
@ -1,6 +0,0 @@
|
||||
require "mkmf"
|
||||
|
||||
abort("Error: missing curses.h") unless have_header("curses.h")
|
||||
abort("Error: missing curses library") unless have_library("curses")
|
||||
|
||||
create_makefile "svi/svi"
|
@ -1,30 +0,0 @@
|
||||
#include <ruby.h>
|
||||
#include <curses.h>
|
||||
|
||||
static int Screen_Width = -1;
|
||||
static int Screen_Height = -1;
|
||||
|
||||
VALUE Rb_screen_width(void)
|
||||
{
|
||||
return INT2FIX(Screen_Width);
|
||||
}
|
||||
|
||||
VALUE Rb_screen_height(void)
|
||||
{
|
||||
return INT2FIX(Screen_Height);
|
||||
}
|
||||
|
||||
void Init_svi(void)
|
||||
{
|
||||
/* Determine screen dimensions. */
|
||||
initscr();
|
||||
getmaxyx(stdscr, Screen_Height, Screen_Width);
|
||||
endwin();
|
||||
|
||||
VALUE svi_module = rb_define_module("Svi");
|
||||
VALUE c_module = rb_define_module_under(svi_module, "C");
|
||||
rb_define_module_function(c_module, "screen_width",
|
||||
Rb_screen_width, 0);
|
||||
rb_define_module_function(c_module, "screen_height",
|
||||
Rb_screen_height, 0);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
require_relative "svi/ansi"
|
||||
require_relative "svi/application"
|
||||
require_relative "svi/cli"
|
||||
require_relative "svi/config"
|
||||
require_relative "svi/svn_runner"
|
||||
require_relative "svi/util"
|
||||
require_relative "svi/version"
|
||||
require "svi/svi"
|
@ -1,21 +0,0 @@
|
||||
module Svi
|
||||
module Ansi
|
||||
|
||||
class << self
|
||||
|
||||
def cursor_up(n = 1)
|
||||
"\e[#{n}A"
|
||||
end
|
||||
|
||||
def cursor_back(n = 1)
|
||||
"\e[#{n}D"
|
||||
end
|
||||
|
||||
def erase_cursor_to_eos
|
||||
"\e[J"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,105 +0,0 @@
|
||||
require "open3"
|
||||
|
||||
module Svi
|
||||
class Application
|
||||
|
||||
def initialize
|
||||
@svn_info = {}
|
||||
@config = Config.new(self)
|
||||
end
|
||||
|
||||
def wc_info
|
||||
svn_info(".")
|
||||
end
|
||||
|
||||
def checkout(url, options = {})
|
||||
wc_path =
|
||||
if options[:wc_path]
|
||||
options[:wc_path]
|
||||
elsif url =~ %r{/([^/]+)/trunk$}
|
||||
$1
|
||||
else
|
||||
url.split("/").last
|
||||
end
|
||||
last_checkout_message = ""
|
||||
checked_out_paths = []
|
||||
clear_message = lambda do
|
||||
if last_checkout_message.size > 0
|
||||
clear = ""
|
||||
lines = (last_checkout_message.size + C.screen_width - 1) / C.screen_width
|
||||
if lines > 1
|
||||
clear += Ansi.cursor_up(lines - 1)
|
||||
end
|
||||
clear += Ansi.cursor_back(999)
|
||||
clear += Ansi.erase_cursor_to_eos
|
||||
$stdout.write(clear)
|
||||
last_checkout_message = ""
|
||||
end
|
||||
end
|
||||
start_time = Time.new
|
||||
SvnRunner.run_svn("checkout", [url, wc_path], allow_interactive: true) do |line|
|
||||
if line =~ /^A.{4}(.*)$/
|
||||
path = $1
|
||||
checked_out_paths << path
|
||||
clear_message[]
|
||||
elapsed_time = Time.new - start_time
|
||||
elapsed_time_formatted = Util.format_time(elapsed_time)
|
||||
last_checkout_message = "Checking out #{path} [#{elapsed_time_formatted}]..."
|
||||
$stdout.write(last_checkout_message)
|
||||
$stdout.flush
|
||||
elsif line =~ /^\sU\s{3}/
|
||||
# Ignore the 'U'pdate line of the checkout directory itself.
|
||||
elsif line =~ /^Checked out revision (\d+)/
|
||||
revision = $1
|
||||
clear_message[]
|
||||
n_files = 0
|
||||
n_directories = 0
|
||||
checked_out_paths.uniq.each do |path|
|
||||
if File.directory?(path)
|
||||
n_directories += 1
|
||||
else
|
||||
n_files += 1
|
||||
end
|
||||
end
|
||||
elapsed_time = Time.new - start_time
|
||||
elapsed_time_formatted = Util.format_time(elapsed_time)
|
||||
$stdout.puts "Checked out revision #{revision}: #{n_files} file#{n_files == 1 ? '' : 's'}, #{n_directories} director#{n_directories == 1 ? 'y' : 'ies'} [#{elapsed_time_formatted}]"
|
||||
else
|
||||
clear_message[]
|
||||
$stdout.puts line
|
||||
end
|
||||
end
|
||||
0
|
||||
end
|
||||
|
||||
def status
|
||||
SvnRunner.run_svn("status", []) do |line|
|
||||
if line =~ %r{^([ACDIMRX\?!~ ])[CM ][L ][\+ ][SX ][KOTB ]..(.+)$}
|
||||
action, path = $1, $2
|
||||
if action == "?" and Util.is_path_ignored?(path, @config)
|
||||
# Path is ignored
|
||||
else
|
||||
puts line
|
||||
end
|
||||
end
|
||||
end
|
||||
0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def svn_info(path)
|
||||
@svn_info[path] ||= begin
|
||||
info = {}
|
||||
stdout, stderr, status = Open3.capture3("svn", "info", path)
|
||||
stdout.lines.each do |line|
|
||||
if line =~ /^(.*?):\s(.*)$/
|
||||
info[$1] = $2
|
||||
end
|
||||
end
|
||||
info
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,80 +0,0 @@
|
||||
require "yawpa"
|
||||
|
||||
module Svi
|
||||
class Cli
|
||||
|
||||
ALIASES = {
|
||||
"co" => "checkout",
|
||||
"st" => "status",
|
||||
}
|
||||
|
||||
HELP_TEXT = <<EOS
|
||||
Usage: svi [options] <command> [parameters...]
|
||||
Global options:
|
||||
--version show svi version and exit
|
||||
--help, -h show this help and exit
|
||||
|
||||
Commands:
|
||||
checkout/co check out Subversion URL
|
||||
status/st show Subversion status
|
||||
EOS
|
||||
|
||||
def initialize
|
||||
@application = Application.new
|
||||
end
|
||||
|
||||
def run(params)
|
||||
options = {
|
||||
version: {},
|
||||
help: {short: "h"},
|
||||
}
|
||||
opts, args = Yawpa.parse(params, options, posix_order: true)
|
||||
if opts[:version]
|
||||
puts "svi, version #{Svi::VERSION}"
|
||||
return 0
|
||||
end
|
||||
if opts[:help]
|
||||
puts HELP_TEXT
|
||||
return 0
|
||||
end
|
||||
if args.size < 1
|
||||
$stderr.puts HELP_TEXT
|
||||
return 1
|
||||
end
|
||||
run_subcommand(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_subcommand(subcommand, *params)
|
||||
if ALIASES[subcommand]
|
||||
subcommand = ALIASES[subcommand]
|
||||
end
|
||||
command_function = "cmd_#{subcommand}".to_sym
|
||||
if private_methods.include?(command_function)
|
||||
__send__(command_function, params)
|
||||
else
|
||||
$stderr.puts "Unknown command #{subcommand}"
|
||||
1
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_checkout(params)
|
||||
options = {}
|
||||
opts, args = Yawpa.parse(params, options, posix_order: true)
|
||||
if args.size < 1
|
||||
$stderr.puts "Error: must specify URL"
|
||||
return 1
|
||||
end
|
||||
url, wc_path = args
|
||||
@application.checkout(url, wc_path: wc_path)
|
||||
end
|
||||
|
||||
def cmd_status(params)
|
||||
options = {}
|
||||
opts, args = Yawpa.parse(params, options, posix_order: true)
|
||||
@application.status
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,47 +0,0 @@
|
||||
module Svi
|
||||
class Config
|
||||
|
||||
def initialize(application)
|
||||
@application = application
|
||||
@ignores = []
|
||||
|
||||
load_global_config
|
||||
load_local_config
|
||||
end
|
||||
|
||||
# Get the ignore patterns.
|
||||
#
|
||||
# @return [Array<String, Proc>]
|
||||
# Each entry is a pattern or a Proc that returns a list of patterns.
|
||||
def ignores
|
||||
@_ignores ||= @ignores.map do |ignore|
|
||||
if ignore.is_a?(Proc)
|
||||
ignore[]
|
||||
else
|
||||
ignore
|
||||
end
|
||||
end.flatten
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_global_config
|
||||
global_config_path = "#{ENV["HOME"]}/.svi/config"
|
||||
if File.exists?(global_config_path)
|
||||
global_config = File.read(global_config_path)
|
||||
instance_eval(global_config, global_config_path, 1)
|
||||
end
|
||||
end
|
||||
|
||||
def load_local_config
|
||||
if wcrp = @application.wc_info["Working Copy Root Path"]
|
||||
local_config_path = "#{wcrp}/.svn/svi/config"
|
||||
if File.exists?(local_config_path)
|
||||
local_config = File.read(local_config_path)
|
||||
instance_eval(local_config, local_config_path, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,99 +0,0 @@
|
||||
module Svi
|
||||
module SvnRunner
|
||||
|
||||
# Exception class to indicate an error with executing svn.
|
||||
class SvnExecError < RuntimeError; end
|
||||
|
||||
class << self
|
||||
|
||||
# Run Subversion and yield results linewise.
|
||||
#
|
||||
# @param subcommand [String, nil]
|
||||
# The svn subcommand to execute (e.g. "ls").
|
||||
# @param args [Array<String>]
|
||||
# The svn subcommand arguments.
|
||||
# @param options [Hash]
|
||||
# Optional arguments.
|
||||
# @option options [Array<String>] :global_args
|
||||
# Global svn arguments to place before the subcommand.
|
||||
# @option options [Boolean] :allow_interactive
|
||||
# Allow interaction with svn command.
|
||||
#
|
||||
# @yield [line]
|
||||
# If a block is given, each line that the Subversion command writes to
|
||||
# standard output will be yielded to the calling block.
|
||||
# @yieldparam line [String]
|
||||
# Line of standard output.
|
||||
# @return [String]
|
||||
# The standard output from svn.
|
||||
# @raise [SvnExecError]
|
||||
# If the svn command errors out this exception is raised.
|
||||
def run_svn(subcommand, args, options = {}, &block)
|
||||
options[:global_args] ||= []
|
||||
options[:global_args] << "--non-interactive" unless options[:allow_interactive]
|
||||
command = [
|
||||
"svn",
|
||||
*options[:global_args],
|
||||
subcommand,
|
||||
*args,
|
||||
].compact
|
||||
|
||||
# Create pipes for standard output and standard error.
|
||||
stdout_rd, stdout_wr = IO.pipe
|
||||
stderr_rd, stderr_wr = IO.pipe
|
||||
|
||||
# Launch the svn subprocess using the pipes.
|
||||
spawn_options = {
|
||||
out: stdout_wr,
|
||||
err: stderr_wr,
|
||||
close_others: true,
|
||||
}
|
||||
spawn_options[:in] = :close unless options[:allow_interactive]
|
||||
svn_pid = Process.spawn(*command, spawn_options)
|
||||
|
||||
# Close write side of the pipes in the parent process.
|
||||
stdout_wr.close
|
||||
stderr_wr.close
|
||||
|
||||
stdout = ""
|
||||
stderr = ""
|
||||
stdout_yield = ""
|
||||
|
||||
loop do
|
||||
so = stdout_rd.read_nonblock(100000, exception: false)
|
||||
if so.is_a?(String)
|
||||
stdout += so
|
||||
stdout_yield += so
|
||||
end
|
||||
se = stderr_rd.read_nonblock(100000, exception: false)
|
||||
if se.is_a?(String)
|
||||
stderr += se
|
||||
end
|
||||
if so.nil? and se.nil?
|
||||
break
|
||||
end
|
||||
if block
|
||||
loop do
|
||||
index = stdout_yield.index("\n")
|
||||
break unless index
|
||||
line = stdout_yield[0, index]
|
||||
block[line]
|
||||
stdout_yield = stdout_yield[index + 1, stdout_yield.size]
|
||||
end
|
||||
end
|
||||
IO.select([stdout_rd, stderr_rd])
|
||||
end
|
||||
|
||||
Process.waitpid(svn_pid)
|
||||
|
||||
if stderr != ""
|
||||
raise SvnExecError.new(stderr)
|
||||
end
|
||||
|
||||
stdout
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,48 +0,0 @@
|
||||
module Svi
|
||||
module Util
|
||||
|
||||
class << self
|
||||
|
||||
def is_path_ignored?(path, config)
|
||||
config.ignores.find do |ignore_pattern|
|
||||
ignore_pattern.chomp!("/")
|
||||
if ignore_pattern.start_with?("/")
|
||||
ignore_pattern = ignore_pattern[1, ignore_pattern.size]
|
||||
elsif not ignore_pattern["/"]
|
||||
path = File.basename(path)
|
||||
end
|
||||
File.fnmatch(ignore_pattern, path, File::FNM_PATHNAME)
|
||||
end
|
||||
end
|
||||
|
||||
def format_time(time)
|
||||
if time < 10
|
||||
sprintf("%.3fs", time)
|
||||
else
|
||||
days = (time / (60 * 60 * 24)).to_i
|
||||
time -= days * (60 * 60 * 24)
|
||||
hours = (time / (60 * 60)).to_i
|
||||
time -= hours * (60 * 60)
|
||||
minutes = (time / 60).to_i
|
||||
time -= minutes * 60
|
||||
incl = false
|
||||
formatted = ""
|
||||
if days != 0
|
||||
incl = true
|
||||
formatted += "#{days}d"
|
||||
end
|
||||
if hours != 0 or incl
|
||||
incl = true
|
||||
formatted += "#{hours}h"
|
||||
end
|
||||
if minutes != 0 or incl
|
||||
formatted += "#{minutes}m"
|
||||
end
|
||||
formatted += "#{time.round}s"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,3 +0,0 @@
|
||||
module Svi
|
||||
VERSION = "0.1.0"
|
||||
end
|
8
src/main.d
Normal file
8
src/main.d
Normal file
@ -0,0 +1,8 @@
|
||||
import svn_runner;
|
||||
import std.stdio;
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
auto r = run_svn(["ls", "https://vcs.gentex.com/svn/embedded_sw"]);
|
||||
return 0;
|
||||
}
|
36
src/svn_runner.d
Normal file
36
src/svn_runner.d
Normal file
@ -0,0 +1,36 @@
|
||||
import std.process;
|
||||
import std.stdio;
|
||||
|
||||
struct Result
|
||||
{
|
||||
int status;
|
||||
char[] stdout;
|
||||
char[] stderr;
|
||||
}
|
||||
|
||||
private File nullfd()
|
||||
{
|
||||
return File("/dev/null", "r");
|
||||
}
|
||||
|
||||
Result run_svn(string[] args)
|
||||
{
|
||||
string[] command = ["svn", "--non-interactive", "--no-auth-cache"] ~ args;
|
||||
auto stdout_pipe = pipe();
|
||||
auto stderr_pipe = pipe();
|
||||
auto pid = spawnProcess(command, nullfd(), stdout_pipe.writeEnd,
|
||||
stderr_pipe.writeEnd);
|
||||
char[] stdout;
|
||||
char[] stderr;
|
||||
char[] buf;
|
||||
while (stdout_pipe.readEnd.readln(buf))
|
||||
{
|
||||
stdout ~= buf;
|
||||
}
|
||||
while (stderr_pipe.readEnd.readln(buf))
|
||||
{
|
||||
stderr ~= buf;
|
||||
}
|
||||
int status = wait(pid);
|
||||
return Result(status, stdout, stderr);
|
||||
}
|
34
svi.gemspec
34
svi.gemspec
@ -1,34 +0,0 @@
|
||||
# coding: utf-8
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'svi/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "svi"
|
||||
spec.version = Svi::VERSION
|
||||
spec.authors = ["Josh Holtrop"]
|
||||
spec.email = ["jholtrop@gmail.com"]
|
||||
|
||||
spec.summary = %q{Subversion Improved}
|
||||
spec.description = %q{Subversion Improved.}
|
||||
spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
||||
spec.license = "MIT"
|
||||
|
||||
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
||||
# delete this section to allow pushing this gem to any host.
|
||||
if spec.respond_to?(:metadata)
|
||||
spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
||||
else
|
||||
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
||||
end
|
||||
|
||||
spec.files = Dir['{bin,exe,ext,assets,integration,lib,spec,doc}/**/*', '*.gemspec', '.rspec']
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.extensions = %w[ext/svi/extconf.rb]
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_dependency "yawpa", "~> 1.0"
|
||||
spec.add_development_dependency "bundler", "~> 1.10"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user