Compare commits
No commits in common. "d" and "master" have entirely different histories.
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,3 +1,8 @@
|
|||||||
/.rscons*
|
/.bundle/
|
||||||
/build/
|
/.yardoc
|
||||||
/svi
|
/_yardoc/
|
||||||
|
/coverage/
|
||||||
|
/doc/
|
||||||
|
/pkg/
|
||||||
|
/spec/reports/
|
||||||
|
/tmp/
|
||||||
|
4
Gemfile
Normal file
4
Gemfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
# Specify your gem's dependencies in svi.gemspec
|
||||||
|
gemspec
|
22
Gemfile.lock
Normal file
22
Gemfile.lock
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
Normal file
21
LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# 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
10
Rsconscript
@ -1,10 +0,0 @@
|
|||||||
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
Executable file
9
exe/svi
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require "svi"
|
||||||
|
|
||||||
|
args = ARGV.dup
|
||||||
|
|
||||||
|
cli = Svi::Cli.new
|
||||||
|
|
||||||
|
exit(cli.run(args))
|
6
ext/svi/extconf.rb
Normal file
6
ext/svi/extconf.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
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"
|
30
ext/svi/svi.c
Normal file
30
ext/svi/svi.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#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);
|
||||||
|
}
|
8
lib/svi.rb
Normal file
8
lib/svi.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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"
|
21
lib/svi/ansi.rb
Normal file
21
lib/svi/ansi.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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
|
105
lib/svi/application.rb
Normal file
105
lib/svi/application.rb
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
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
|
80
lib/svi/cli.rb
Normal file
80
lib/svi/cli.rb
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
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
|
47
lib/svi/config.rb
Normal file
47
lib/svi/config.rb
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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
|
99
lib/svi/svn_runner.rb
Normal file
99
lib/svi/svn_runner.rb
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
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
|
48
lib/svi/util.rb
Normal file
48
lib/svi/util.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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
|
3
lib/svi/version.rb
Normal file
3
lib/svi/version.rb
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module Svi
|
||||||
|
VERSION = "0.1.0"
|
||||||
|
end
|
@ -1,8 +0,0 @@
|
|||||||
import svn_runner;
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
int main(string[] args)
|
|
||||||
{
|
|
||||||
auto r = run_svn(["ls", "https://vcs.gentex.com/svn/embedded_sw"]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
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
Normal file
34
svi.gemspec
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# 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