diff options
-rw-r--r-- | README.md | 73 | ||||
-rw-r--r-- | Rakefile | 51 | ||||
-rw-r--r-- | lib/pgxn_utils.rb | 1 | ||||
-rw-r--r-- | lib/pgxn_utils/cli.rb | 45 | ||||
-rw-r--r-- | lib/pgxn_utils/no_tasks.rb | 48 | ||||
-rw-r--r-- | lib/pgxn_utils/templates/root/.gitignore.tt | 8 | ||||
-rw-r--r-- | pgxn_utils.gemspec | 4 | ||||
-rw-r--r-- | spec/cli_spec.rb | 32 | ||||
-rw-r--r-- | spec/no_tasks_spec.rb | 2 | ||||
-rw-r--r-- | spec/spec_helper.rb | 25 |
10 files changed, 232 insertions, 57 deletions
@@ -4,7 +4,7 @@ pgxn utils What is it? -------- -It aims to be a set of task to help PostgreSQL extension's developers to focus more on the problem that they wants to solve than in all structure and files and control files need to PGXS to build the extension. +It aims to be a set of task to help PostgreSQL extension's developers to focus more on the problem that they wants to solve than in the structure and files and control files need to PGXS to build the extension. How to install it? ------------------ @@ -17,24 +17,27 @@ Or you can install it by rubygems: gem install pgxn_utils + How it works? ------------- -It is all about tasks. Let's see what tasks we have: +It is all about tasks. Let's see them: - $ pgxn_utils help + $ pgxn-utils help Tasks: - pgxn_utils bundle [extension_name] # Bundles an extension. - pgxn_utils change [extension_name] # Change META's attributes in current extension. - pgxn_utils help [TASK] # Describe available tasks or one specific task - pgxn_utils release filename # Release a extension - pgxn_utils skeleton extension_name # Creates an extension skeleton in current directory. + pgxn-utils bundle [extension_name] # Bundles the extension in a zip file + pgxn-utils change [extension_name] # Changes META's attributes in current extension + pgxn-utils help [TASK] # Describe available tasks or one specific task + pgxn-utils release filename # Release an extension to PGXN + pgxn-utils skeleton extension_name # Creates an extension skeleton in current directory + # Creating a new extension - $ pgxn_utils skeleton my_cool_extension + $ pgxn-utils skeleton my_cool_extension create my_cool_extension create my_cool_extension/my_cool_extension.control + create my_cool_extension/.gitignore create my_cool_extension/META.json create my_cool_extension/Makefile create my_cool_extension/README.md @@ -44,13 +47,32 @@ It is all about tasks. Let's see what tasks we have: create my_cool_extension/test/expected/base.out create my_cool_extension/test/sql/base.sql +You can start creating an extension with or without version control. By default `pgxn-utils` +supports [git](http://git-scm.org) but it will not create a repository unless you use `--git` +option in the skeleton task. + + $ pgxn-utils skeleton my_cool_versioned_extension --git + create my_cool_versioned_extension + create my_cool_versioned_extension/my_cool_versioned_extension.control + create my_cool_versioned_extension/.gitignore + create my_cool_versioned_extension/META.json + create my_cool_versioned_extension/Makefile + create my_cool_versioned_extension/README.md + create my_cool_versioned_extension/doc/my_cool_versioned_extension.md + create my_cool_versioned_extension/sql/my_cool_versioned_extension.sql + create my_cool_versioned_extension/sql/uninstall_my_cool_versioned_extension.sql + create my_cool_versioned_extension/test/expected/base.out + create my_cool_versioned_extension/test/sql/base.sql + init /tmp/my_cool_versioned_extension + commit initial commit + Thats it! Just start coding! ":) # Changing something Well suppose you want to change the default maintainer's name and the license, well just do: - $ pgxn_utils change my_cool_extension --maintainer "Dickson Guedes" --license bsd + $ pgxn-utils change my_cool_extension --maintainer "Dickson Guedes" --license bsd conflict META.json Overwrite /tmp/my_cool_extension/META.json? (enter "h" for help) [Ynaqdh] d { @@ -82,16 +104,17 @@ Well suppose you want to change the default maintainer's name and the license, w } Retrying... Overwrite /tmp/my_cool_extension/META.json? (enter "h" for help) [Ynaqdh] Y - force META.json + force META.json identical my_cool_extension.control + It will wait you decide what to do. For all switches that you can use with *change*, type: - $ pgxn_utils help change + $ pgxn-utils help change Usage: - pgxn_utils change [extension_name] + pgxn-utils change [extension_name] Options: -p, [--target=TARGET] # Define the target directory @@ -105,7 +128,8 @@ For all switches that you can use with *change*, type: -t, [--tags=one two three] # Defines extension's tags -r, [--release-status=RELEASE_STATUS] # Initial extension's release status - Change META's attributes in current extension. + Changes META's attributes in current extension + # Bundling and Releasing! @@ -113,12 +137,12 @@ Well, since you finished your work you can bundle it to send to [PGXN](http://pg Bundle it: - $ pgxn_utils bundle my_cool_extension - Extension generated at: /home/guedes/extensions/my_cool_extension-0.0.1.zip + $ pgxn-utils bundle my_cool_extension + create /home/guedes/extensions/my_cool_extension-0.0.1.zip and release it: - $ pgxn_utils release my_cool_extension-0.0.1.zip + $ pgxn-utils release my_cool_extension-0.0.1.zip Enter your PGXN username: guedes Enter your PGXN password: ****** Trying to release my_cool_extension-0.0.1.zip ... released successfully! @@ -127,9 +151,22 @@ and release it: You can export `PGXN_USER` and `PGXN_PASSWORD` environment variables to avoid type username and password everytime. +# Git support + +You can start a new extension with git support calling `skeleton` task with +`--git` flag, then in addition to create skeleton, `pgxn-utils` will initialize +a git repository and do a initial commit. + +Once you have your extension in a git repository your `bundle` will use only the +commited files to create the archive, but if your repository is dirty then `pgxn-utils` +will hint you to commit or stash your changes, before bundle. + +You must be careful with new files not added to repository, because they will NOT +be archived. + # Working in progress -* [git](http://git-scm.org) support +* improve [git](http://git-scm.org) support * proxy support * custom templates @@ -4,7 +4,7 @@ Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' def format_cmd_output(cmd) - `./bin/pgxn_utils #{cmd} | sed 's/^/ /' | sed 's/\[[0-9]*m//g'` + `./bin/pgxn-utils #{cmd} | sed 's/^/ /' | sed 's/\[[0-9]*m//g'` end desc "Run RSpec" @@ -22,6 +22,7 @@ end desc "Generate README.md" task :generate_readme do rm_r "/tmp/my_cool_extension" if File.exist?("/tmp/my_cool_extension") + rm_r "/tmp/my_cool_versioned_extension" if File.exist?("/tmp/my_cool_versioned_extension") readme = File.new("README.md.new", 'w') readme.puts <<-README pgxn utils @@ -30,40 +31,53 @@ pgxn utils What is it? -------- -It aims to be a set of task to help PostgreSQL extension's developers to focus more on the problem that they wants to solve than in the all structure and files and control files need to PGXS to build the extension. +It aims to be a set of task to help PostgreSQL extension's developers to focus more on the problem that they wants to solve than in all structure and files and control files need to PGXS to build the extension. How to install it? ------------------ +If you have pgxn client installed you can do: + + pgxn install pgxn_utils + +Or you can install it by rubygems: + gem install pgxn_utils How it works? ------------- -It is all about tasks. Let's see what tasks we have: +It is all about tasks. Let's see them: - $ pgxn_utils help + $ pgxn-utils help #{format_cmd_output("help")} # Creating a new extension - $ pgxn_utils skeleton my_cool_extension + $ pgxn-utils skeleton my_cool_extension #{format_cmd_output("skeleton my_cool_extension -p /tmp")} +You can start creating an extension with or without version control. By default `pgxn-utils` +supports [git](http://git-scm.org) but it will not create a repository unless you use `--git` +option in the skeleton task. + + $ pgxn-utils skeleton my_cool_versioned_extension --git +#{format_cmd_output("skeleton my_cool_versioned_extension --git -p /tmp")} + Thats it! Just start coding! ":) # Changing something -Well suppose you want to change the default maintainer's name and the license, well just do: +Well suppose you want to change the default maintainer's name and the license, just do: - $ pgxn_utils change my_cool_extension --maintainer "Dickson Guedes" --license bsd + $ pgxn-utils change my_cool_extension --maintainer "Dickson Guedes" --license bsd #{format_cmd_output("change my_cool_extension -p /tmp --maintainer 'Dickson Guedes' --license bsd")} It will wait you decide what to do. For all switches that you can use with *change*, type: - $ pgxn_utils help change + $ pgxn-utils help change #{format_cmd_output("help change")} # Bundling and Releasing! @@ -72,12 +86,12 @@ Well, since you finished your work you can bundle it to send to [PGXN](http://pg Bundle it: - $ pgxn_utils bundle my_cool_extension - Extension generated at: /home/guedes/extensions/my_cool_extension-0.0.1.zip + $ pgxn-utils bundle my_cool_extension + create /home/guedes/extensions/my_cool_extension-0.0.1.zip and release it: - $ pgxn_utils release my_cool_extension-0.0.1.zip + $ pgxn-utils release my_cool_extension-0.0.1.zip Enter your PGXN username: guedes Enter your PGXN password: ****** Trying to release my_cool_extension-0.0.1.zip ... released successfully! @@ -86,9 +100,22 @@ and release it: You can export `PGXN_USER` and `PGXN_PASSWORD` environment variables to avoid type username and password everytime. +# Git support + +You can start a new extension with git support calling `skeleton` task with +`--git` flag, then in addition to create skeleton, `pgxn-utils` will initialize +a git repository and do a initial commit. + +Once you have your extension in a git repository your `bundle` will use only the +commited files to create the archive, but if your repository is dirty then `pgxn-utils` +will hint you to commit or stash your changes, before bundle. + +You must be careful with new files not added to repository, because they will NOT +be archived. + # Working in progress -* [git](http://git-scm.org) support +* improve [git](http://git-scm.org) support * proxy support * custom templates diff --git a/lib/pgxn_utils.rb b/lib/pgxn_utils.rb index 0fea754..337f2f2 100644 --- a/lib/pgxn_utils.rb +++ b/lib/pgxn_utils.rb @@ -5,6 +5,7 @@ require 'zippy' require 'net/http/post/multipart' require 'net/https' require 'highline/import' +require 'grit' module PgxnUtils autoload :CLI, 'pgxn_utils/cli' diff --git a/lib/pgxn_utils/cli.rb b/lib/pgxn_utils/cli.rb index fc22c1d..cbec0ee 100644 --- a/lib/pgxn_utils/cli.rb +++ b/lib/pgxn_utils/cli.rb @@ -23,20 +23,22 @@ module PgxnUtils method_option :generated_by, :aliases => "-b", :type => :string, :desc => "Name of extension's generator" method_option :tags, :aliases => "-t", :type => :array, :desc => "Defines extension's tags" method_option :release_status, :aliases => "-r", :type => :string, :desc => "Initial extension's release status" + method_option :git, :type => :boolean, :default => false, :desc => "Initialize a git repository after create the extension" def skeleton(extension_name,target=nil) self.target = options[:target] || target || "." if is_extension?("#{self.target}/#{extension_name}") say "'#{extension_name}' already exists. Please, use 'change' instead 'skeleton'.", :red - elsif is_extension?(".") + elsif is_extension?(self.target) say "You are inside a extension directory, already. Consider use 'change' instead.", :red elsif is_dir?("#{self.target}/#{extension_name}") say "Can't create an extension overwriting an existing directory.", :red else self.set_accessors extension_name - directory "root", extension_name + + init_repository("#{self.target}/#{extension_name}") if options[:git] end end @@ -83,21 +85,30 @@ module PgxnUtils self.target = path archive_name = "#{path}-#{config_options['version']}" - ext = "zip" - archive = "#{archive_name}.#{ext}" - - if can_zip?(archive) - make_dist_clean(path) - - Zippy.create(archive) do |zip| - Dir["#{path}/**/**"].each do |file| - zip["#{extension_name}-#{config_options['version']}/#{file.sub(path+'/','')}"] = File.open(file) unless File.directory?(file) - end - end - say_status :create, archive, :green - end - end - end + prefix_name = "#{extension_name}-#{config_options['version']}/" + + archive = "#{archive_name}.zip" + archived = false + + if has_scm?(path) + if is_dirty?(path) + say "Your repository is dirty! You should commit or stash before continue.", :red + else + if can_zip?(archive) + scm_archive(path, archive, prefix_name) + archived = true + end + end + else + if can_zip?(archive) + make_dist_clean(path) + zip_archive(path, archive, prefix_name) + archived = true + end + end + say_status(:create, archive, :green) if archived + end + end desc "release filename", "Release an extension to PGXN" diff --git a/lib/pgxn_utils/no_tasks.rb b/lib/pgxn_utils/no_tasks.rb index a066998..9dc62ad 100644 --- a/lib/pgxn_utils/no_tasks.rb +++ b/lib/pgxn_utils/no_tasks.rb @@ -2,6 +2,33 @@ module PgxnUtils module NoTasks include PgxnUtils::Constants + include Grit + + def init_repository(extension_dir) + repo = Repo.init(extension_dir) + original_dir = File.expand_path "." + + if repo + say_status :init, "#{extension_dir}", :green + + FileUtils.chdir extension_dir + repo.add "." + if repo.commit_index("initial commit") + say_status :commit, "initial commit", :green + else + say_status :failed, "initial commit", :red + end + else + say_status :error, " initializing #{extension_dir}", :red + end + + FileUtils.chdir original_dir + end + + def is_dirty?(extension_dir) + repo = Repo.init(extension_dir) + repo.status.map(&:type).uniq != [nil] + end def make_dist_clean(path) inside path do @@ -77,6 +104,27 @@ module PgxnUtils [ extension_path, extension_name ] end + def has_scm?(path) + begin + Repo.new(path) + rescue Grit::InvalidGitRepositoryError + false + end + end + + def scm_archive(path, archive, prefix, treeish='master') + git = Git.new(Repo.new(path).path) + git.archive({:format => "zip", :prefix => prefix, :output => archive}, treeish) + end + + def zip_archive(path, archive, prefix) + Zippy.create(archive) do |zip| + Dir["#{path}/**/**"].each do |file| + zip["#{prefix}#{file.sub(path+'/','')}"] = File.open(file) unless File.directory?(file) + end + end + end + def can_zip?(archive) can_zip = false diff --git a/lib/pgxn_utils/templates/root/.gitignore.tt b/lib/pgxn_utils/templates/root/.gitignore.tt new file mode 100644 index 0000000..7ccbc9d --- /dev/null +++ b/lib/pgxn_utils/templates/root/.gitignore.tt @@ -0,0 +1,8 @@ +results/ +*.so +tmp/ +*.o +regression.diffs +regression.out +/sql/<%= extension_name =>--* +!/sql/<%= extension_name =>--*--*.sql diff --git a/pgxn_utils.gemspec b/pgxn_utils.gemspec index 17c04b1..a91a608 100644 --- a/pgxn_utils.gemspec +++ b/pgxn_utils.gemspec @@ -57,7 +57,9 @@ Gem::Specification.new do |s| s.add_runtime_dependency "zippy", "~> 0.1.0" s.add_runtime_dependency "multipart-post", "~> 1.1.2" s.add_runtime_dependency "highline", "~> 1.6.2" + s.add_runtime_dependency "grit", "~> 2.4.1" s.add_development_dependency "rspec" + s.add_development_dependency "mocha" s.add_development_dependency "simplecov", "~> 0.4.0" else s.add_dependency "json", "~> 1.5.2" @@ -66,6 +68,7 @@ Gem::Specification.new do |s| s.add_dependency "zippy", "~> 0.1.0" s.add_dependency "multipart-post", "~> 1.1.2" s.add_dependency "highline", "~> 1.6.2" + s.add_dependency "grit", "~> 2.4.1" end else s.add_dependency "json", "~> 1.5.2" @@ -74,5 +77,6 @@ Gem::Specification.new do |s| s.add_dependency "zippy", "~> 0.1.0" s.add_dependency "multipart-post", "~> 1.1.2" s.add_dependency "highline", "~> 1.6.2" + s.add_dependency "grit", "~> 2.4.1" end end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index aa346ed..1201f72 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -2,21 +2,27 @@ require File.expand_path('spec/spec_helper') describe PgxnUtils::CLI do + before(:all) do + FileUtils.mv "META.json", "meta.json" + end + after(:all) do + FileUtils.mv "meta.json", "META.json" system "rm -rf /tmp/extension.*" system "rm -rf extension.*" end context "#skeleton" do before(:each) do - @cli = PgxnUtils::CLI.new + system "rm -rf /tmp/extension.*" + system "rm -rf extension.*" end it "should accepts or not a target" do expected_extension = next_extension File.should_not exist(expected_extension) - skeleton "#{expected_extension}", "-p /tmp" + skeleton "#{expected_extension}", %w|-p /tmp| File.should exist("/tmp/#{expected_extension}") File.should_not exist(expected_extension) @@ -30,10 +36,9 @@ describe PgxnUtils::CLI do expected_mail = "[email protected]" expected_abstract = "Short description" expected_description = "Very Long description for my cool extension" - expected_tags = "one two tree" expected_version = "1.0.0" - skeleton expected_extension, "-p /tmp -m #{expected_name} -e #{expected_mail} -t #{expected_tags} -a '#{expected_abstract}' -d '#{expected_description}' -v #{expected_version}" + skeleton expected_extension, %W|-p /tmp -m #{expected_name} -e #{expected_mail} -t one two tree -a #{expected_abstract} -d #{expected_description} -v #{expected_version}| meta = File.read("/tmp/#{expected_extension}/META.json") meta.should match(/"name": "#{expected_extension}"/) @@ -62,7 +67,8 @@ describe PgxnUtils::CLI do extension = next_extension skeleton extension - Dir["#{extension}/**/*"].sort.should == [ + Dir["#{extension}/**/{*,.gitignore}"].sort.should == [ + "#{extension}/.gitignore", "#{extension}/META.json", "#{extension}/Makefile", "#{extension}/README.md", @@ -80,7 +86,21 @@ describe PgxnUtils::CLI do ].sort end - it "should generates a git repo" + it "should generates a git repo with --git" do + expected_extension = next_extension + skeleton "#{expected_extension}", %w|--git| + + File.should exist("#{expected_extension}/.git") + lambda{ Grit::Repo.new(expected_extension) }.should_not raise_error + end + + it "should not generates a git repo with --no-git" do + expected_extension = next_extension + skeleton "#{expected_extension}", %w|--no-git| + + File.should_not exist("#{expected_extension}/.git") + lambda{ Grit::Repo.new(expected_extension) }.should raise_error + end end context "#change" do diff --git a/spec/no_tasks_spec.rb b/spec/no_tasks_spec.rb index 0117b72..9637882 100644 --- a/spec/no_tasks_spec.rb +++ b/spec/no_tasks_spec.rb @@ -7,6 +7,8 @@ describe PgxnUtils::NoTasks do after(:all) do rm_r("/tmp/teste") rm_r("/tmp/teste2") + + PgxnUtils::NoTasks.send(:remove_method, :options) end context "#resolve_extension_path_and_name" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3ad1c37..d698aba 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,19 +14,36 @@ BIN_PATH = File.expand_path('../../bin/pgxn-utils', __FILE__) DESTINATION_ROOT = File.expand_path('../pgxn_utils', __FILE__) FileUtils.rm_rf(DESTINATION_ROOT) +RSpec.configure do |config| + # capture method from https://github.com/wycats/thor/blob/master/spec/spec_helper.rb#L36-47 + def capture(stream) + begin + stream = stream.to_s + eval "$#{stream} = StringIO.new" + yield + result = eval("$#{stream}").string + ensure + eval("$#{stream} = #{stream.upcase}") + end + + result + end +end + + def next_extension $counter += 1 "extension.#{$counter}" end -def skeleton(extension_name, args=nil) - run_pgxn_utils(:skeleton, "#{extension_name} #{args}") +def skeleton(extension_name, args=[]) + capture(:stdout) { PgxnUtils::CLI.start([ "skeleton", extension_name ] + args) } end def change(extension_name, args=nil) - run_pgxn_utils(:skeleton, "#{extension_name} #{args}") + #run_pgxn_utils(:skeleton, "#{extension_name} #{args}") end def run_pgxn_utils(task, args) - system "#{BIN_PATH} #{task.to_s} #{args} >/dev/null" + #system "#{BIN_PATH} #{task.to_s} #{args}" end |