Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
+13
View File
@@ -0,0 +1,13 @@
source "http://rubygems.org"
gemspec
gem "rake"
group :test do
gem "minitest"
end
install_if -> { ENV["FARADAY_VERSION"] } do
gem "faraday", ENV["FARADAY_VERSION"]
end
+20
View File
@@ -0,0 +1,20 @@
Copyright (c) 2011 rick olson
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.
+62
View File
@@ -0,0 +1,62 @@
# Sawyer
Sawyer is an experimental hypermedia agent for Ruby built on top of [Faraday][faraday].
[faraday]: https://github.com/lostisland/faraday
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'sawyer'
```
And then execute:
```sh
bundle
```
Or install it yourself as:
```sh
gem install sawyer
```
## Usage
```ruby
require "sawyer"
# Create a Sawyer agent
agent = Sawyer::Agent.new("https://api.github.com",
links_parser: Sawyer::LinkParsers::Simple.new)
# Fetch the root of the API
root = agent.root.data
# Access a resource directly
contributors = agent.call(:get, "repos/lostisland/sawyer/contributors").data
# Load a hypermedia relation
top_contributor = contributors.first
followers = top_contributor.rels[:followers].get.data
```
For more information, check out the [documentation](http://www.rubydoc.info/gems/sawyer/).
## Development
After checking out the repo, run `script/test` to bootstrap the project and run the tests.
You can also run `script/console` for an interactive prompt that will allow you to experiment.
To package the gem, run `script/package`. To release a new version, update the version number in [`lib/sawyer.rb`](lib/sawyer.rb), and then run `script/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
Check out the [contributing guide](CONTRIBUTING.md) for more information on contributing.
## License
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
+12
View File
@@ -0,0 +1,12 @@
require 'rubygems'
require 'rake'
task :default => :test
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
+17
View File
@@ -0,0 +1,17 @@
module Sawyer
VERSION = "0.9.2"
class Error < StandardError; end
end
require 'set'
%w(
resource
relation
response
serializer
agent
link_parsers/hal
link_parsers/simple
).each { |f| require File.expand_path("../sawyer/#{f}", __FILE__) }
+163
View File
@@ -0,0 +1,163 @@
require 'faraday'
require 'addressable/template'
module Sawyer
class Agent
NO_BODY = Set.new([:get, :head])
attr_accessor :links_parser
attr_accessor :allow_undefined_methods
class << self
attr_writer :serializer
end
def self.serializer
@serializer ||= Serializer.any_json
end
def self.encode(data)
serializer.encode(data)
end
def self.decode(data)
serializer.decode(data)
end
# Agents handle making the requests, and passing responses to
# Sawyer::Response.
#
# endpoint - String URI of the API entry point.
# options - Hash of options.
# :allow_undefined_methods - Allow relations to call all the HTTP verbs,
# not just the ones defined.
# :faraday - Optional Faraday::Connection to use.
# :links_parser - Optional parser to parse link relations
# Defaults: Sawyer::LinkParsers::Hal.new
# :serializer - Optional serializer Class. Defaults to
# self.serializer_class.
#
# Yields the Faraday::Connection if a block is given.
def initialize(endpoint, options = nil)
@endpoint = endpoint
@conn = (options && options[:faraday]) || Faraday.new
@serializer = (options && options[:serializer]) || self.class.serializer
@links_parser = (options && options[:links_parser]) || Sawyer::LinkParsers::Hal.new
@allow_undefined_methods = (options && options[:allow_undefined_methods])
@conn.url_prefix = @endpoint
yield @conn if block_given?
end
# Public: Close the underlying connection.
def close
@conn.close if @conn.respond_to?(:close)
end
# Public: Retains a reference to the root relations of the API.
#
# Returns a Sawyer::Relation::Map.
def rels
@rels ||= root.data._rels
end
# Public: Retains a reference to the root response of the API.
#
# Returns a Sawyer::Response.
def root
@root ||= start
end
# Public: Hits the root of the API to get the initial actions.
#
# Returns a Sawyer::Response.
def start
call :get, @endpoint
end
# Makes a request through Faraday.
#
# method - The Symbol name of an HTTP method.
# url - The String URL to access. This can be relative to the Agent's
# endpoint.
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
#
# Returns a Sawyer::Response.
def call(method, url, data = nil, options = nil)
if NO_BODY.include?(method)
options ||= data
data = nil
end
options ||= {}
url = expand_url(url, options[:uri])
started = nil
res = @conn.send method, url do |req|
if data
req.body = data.is_a?(String) ? data : encode_body(data)
end
if params = options[:query]
req.params.update params
end
if headers = options[:headers]
req.headers.update headers
end
started = Time.now
end
Response.new self, res, :sawyer_started => started, :sawyer_ended => Time.now
end
# Encodes an object to a string for the API request.
#
# data - The Hash or Resource that is being sent.
#
# Returns a String.
def encode_body(data)
@serializer.encode(data)
end
# Decodes a String response body to a resource.
#
# str - The String body from the response.
#
# Returns an Object resource (Hash by default).
def decode_body(str)
@serializer.decode(str)
end
def parse_links(data)
@links_parser.parse(data)
end
def expand_url(url, options = nil)
tpl = url.respond_to?(:expand) ? url : Addressable::Template.new(url.to_s)
tpl.expand(options || {}).to_s
end
def allow_undefined_methods?
!!@allow_undefined_methods
end
def inspect
%(<#{self.class} #{@endpoint}>)
end
# private
def to_yaml_properties
[:@endpoint]
end
def marshal_dump
[@endpoint]
end
def marshal_load(dumped)
@endpoint = *dumped.shift(1)
end
end
end
@@ -0,0 +1,15 @@
module Sawyer
module LinkParsers
class Hal
def parse(data)
links = data.delete(:_links)
return data, links
end
end
end
end
@@ -0,0 +1,29 @@
module Sawyer
module LinkParsers
class Simple
LINK_REGEX = /_?url$/
# Public: Parses simple *_url style links on resources
#
# data - Hash of resource data
#
# Returns a Hash of data with separate links Hash
def parse(data)
links = {}
inline_links = data.keys.select {|k| k.to_s[LINK_REGEX] }
inline_links.each do |key|
rel_name = key.to_s == 'url' ? 'self' : key.to_s.gsub(LINK_REGEX, '')
links[rel_name.to_sym] = data[key]
end
return data, links
end
end
end
end
@@ -0,0 +1,272 @@
module Sawyer
class Relation
class Map
# Tracks the available next actions for a resource, and
# issues requests for them.
def initialize
@map = {}
end
# Adds a Relation to the map.
#
# rel - A Relation.
#
# Returns nothing.
def <<(rel)
@map[rel.name] = rel if rel
end
# Gets the raw Relation by its name.
#
# key - The Symbol name of the Relation.
#
# Returns a Relation.
def [](key)
@map[key.to_sym]
end
# Gets the number of mapped Relations.
#
# Returns an Integer.
def size
@map.size
end
# Gets a list of the Relation names.
#
# Returns an Array of Symbols in no specific order.
def keys
@map.keys
end
def to_hash
pairs = @map.map do |k, v|
[(k.to_s + "_url").to_sym, v.href]
end
Hash[pairs]
end
alias :to_h :to_hash
def inspect
hash = to_hash
hash.respond_to?(:pretty_inspect) ? hash.pretty_inspect : hash.inspect
end
end
attr_reader :agent,
:name,
:href_template,
:method,
:available_methods
# Public: Builds an index of Relations from the value of a `_links`
# property in a resource. :get is the default method. Any links with
# multiple specified methods will get multiple relations created.
#
# index - The Hash mapping Relation names to the Hash Relation
# options.
# rels - A Relation::Map to store the Relations.
#
# Returns a Relation::Map
def self.from_links(agent, index, rels = Map.new)
if index.is_a?(Array)
raise ArgumentError, "Links must be a hash of rel => {_href => '...'}: #{index.inspect}"
end
index.each do |name, options|
rels << from_link(agent, name, options)
end if index
rels
end
# Public: Builds a single Relation from the given options. These are
# usually taken from a `_links` property in a resource.
#
# agent - The Sawyer::Agent that made the request.
# name - The Symbol name of the Relation.
# options - A Hash containing the other Relation properties.
# :href - The String URL of the next action's location.
# :method - The optional String HTTP method.
#
# Returns a Relation.
def self.from_link(agent, name, options)
case options
when Hash
new agent, name, options[:href], options[:method]
when String
new agent, name, options
end
end
# A Relation represents an available next action for a resource.
#
# agent - The Sawyer::Agent that made the request.
# name - The Symbol name of the relation.
# href - The String URL of the location of the next action.
# method - The Symbol HTTP method. Default: :get
def initialize(agent, name, href, method = nil)
@agent = agent
@name = name.to_sym
@href = href
@href_template = Addressable::Template.new(href.to_s)
methods = nil
if method.is_a? String
if method.size.zero?
method = nil
else
method.downcase!
methods = method.split(',').map! do |m|
m.strip!
m.to_sym
end
method = methods.first
end
end
@method = (method || :get).to_sym
@available_methods = Set.new methods || [@method]
end
# Public: Makes an API request with the curent Relation using HEAD.
#
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def head(options = nil)
options ||= {}
options[:method] = :head
call options
end
# Public: Makes an API request with the curent Relation using GET.
#
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def get(options = nil)
options ||= {}
options[:method] = :get
call options
end
# Public: Makes an API request with the curent Relation using POST.
#
# data - The Optional Hash or Resource body to be sent.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def post(data = nil, options = nil)
options ||= {}
options[:method] = :post
call data, options
end
# Public: Makes an API request with the curent Relation using PUT.
#
# data - The Optional Hash or Resource body to be sent.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def put(data = nil, options = nil)
options ||= {}
options[:method] = :put
call data, options
end
# Public: Makes an API request with the curent Relation using PATCH.
#
# data - The Optional Hash or Resource body to be sent.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def patch(data = nil, options = nil)
options ||= {}
options[:method] = :patch
call data, options
end
# Public: Makes an API request with the curent Relation using DELETE.
#
# data - The Optional Hash or Resource body to be sent.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def delete(data = nil, options = nil)
options ||= {}
options[:method] = :delete
call data, options
end
# Public: Makes an API request with the curent Relation using OPTIONS.
#
# data - The Optional Hash or Resource body to be sent.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Returns a Sawyer::Response.
def options(data = nil, opt = nil)
opt ||= {}
opt[:method] = :options
call data, opt
end
def href(options = nil)
return @href if @href_template.nil?
@href_template.expand(options || {}).to_s
end
# Public: Makes an API request with the curent Relation.
#
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
# :method - Symbol HTTP method.
#
# Raises ArgumentError if the :method value is not in @available_methods.
# Returns a Sawyer::Response.
def call(data = nil, options = nil)
m = options && options[:method]
if m && !@agent.allow_undefined_methods? && !@available_methods.include?(m == :head ? :get : m)
raise ArgumentError, "method #{m.inspect} is not available: #{@available_methods.to_a.inspect}"
end
@agent.call m || @method, @href_template, data, options
end
def inspect
%(#<#{self.class}: #{@name}: #{@method} #{@href_template}>)
end
end
end
@@ -0,0 +1,160 @@
require 'forwardable'
module Sawyer
class Resource
SPECIAL_METHODS = Set.new(%w(agent rels fields))
attr_reader :_agent, :_rels, :_fields
attr_reader :attrs
include Enumerable
extend Forwardable
# Initializes a Resource with the given data.
#
# agent - The Sawyer::Agent that made the API request.
# data - Hash of key/value properties.
def initialize(agent, data = {})
@_agent = agent
data, links = agent.parse_links(data)
@_rels = Relation.from_links(agent, links)
@_fields = Set.new
@_metaclass = (class << self; self; end)
@attrs = {}
data.each do |key, value|
@_fields << key
@attrs[key.to_sym] = process_value(value)
end
@_metaclass.send(:attr_accessor, *data.keys)
end
# Processes an individual value of this resource. Hashes get exploded
# into another Resource, and Arrays get their values processed too.
#
# value - An Object value of a Resource's data.
#
# Returns an Object to set as the value of a Resource key.
def process_value(value)
case value
when Hash then self.class.new(@_agent, value)
when Array then value.map { |v| process_value(v) }
else value
end
end
# Checks to see if the given key is in this resource.
#
# key - A Symbol key.
#
# Returns true if the key exists, or false.
def key?(key)
@_fields.include? key
end
# Allow fields to be retrieved via Hash notation
#
# method - key name
#
# Returns the value from attrs if exists
def [](method)
send(method.to_sym)
rescue NoMethodError
nil
end
# Allow fields to be set via Hash notation
#
# method - key name
# value - value to set for the attr key
#
# Returns - value
def []=(method, value)
send("#{method}=", value)
rescue NoMethodError
nil
end
def_delegators :attrs, :dig, :fetch
ATTR_SETTER = '='.freeze
ATTR_PREDICATE = '?'.freeze
# Provides access to a resource's attributes.
def method_missing(method, *args)
attr_name, suffix = method.to_s.scan(/([a-z0-9\_]+)(\?|\=)?$/i).first
if suffix == ATTR_SETTER
@_metaclass.send(:attr_accessor, attr_name)
@_fields << attr_name.to_sym
send(method, args.first)
elsif attr_name && @_fields.include?(attr_name.to_sym)
value = @attrs[attr_name.to_sym]
case suffix
when nil
@_metaclass.send(:attr_accessor, attr_name)
value
when ATTR_PREDICATE then !!value
end
elsif suffix.nil? && SPECIAL_METHODS.include?(attr_name)
instance_variable_get "@_#{attr_name}"
elsif attr_name && !@_fields.include?(attr_name.to_sym)
nil
else
super
end
end
# Wire up accessor methods to pull from attrs
def self.attr_accessor(*attrs)
attrs.each do |attribute|
class_eval do
define_method attribute do
@attrs[attribute.to_sym]
end
define_method "#{attribute}=" do |value|
@attrs[attribute.to_sym] = value
end
define_method "#{attribute}?" do
!!@attrs[attribute.to_sym]
end
end
end
end
def inspect
to_attrs.respond_to?(:pretty_inspect) ? to_attrs.pretty_inspect : to_attrs.inspect
end
def each(&block)
@attrs.each(&block)
end
# private
def to_yaml_properties
[:@attrs, :@_fields, :@_rels]
end
def to_attrs
hash = self.attrs.clone
hash.keys.each do |k|
if hash[k].is_a?(Sawyer::Resource)
hash[k] = hash[k].to_attrs
elsif hash[k].is_a?(Array) && hash[k].all?{|el| el.is_a?(Sawyer::Resource)}
hash[k] = hash[k].collect{|el| el.to_attrs}
end
end
hash
end
alias to_hash to_attrs
alias to_h to_attrs
def marshal_dump
[@attrs, @_fields, @_rels]
end
def marshal_load(dumped)
@attrs, @_fields, @_rels = *dumped.shift(3)
@_metaclass = (class << self; self; end)
end
end
end
@@ -0,0 +1,72 @@
module Sawyer
class Response
attr_reader :agent,
:status,
:headers,
:env,
:body,
:rels
# Builds a Response after a completed request.
#
# agent - The Sawyer::Agent that is managing the API connection.
# res - A Faraday::Response.
def initialize(agent, res, options = {})
@agent = agent
@status = res.status
@headers = res.headers
@env = res.env
@body = res.body
@rels = process_rels
@started = options[:sawyer_started]
@ended = options[:sawyer_ended]
end
def data
@data ||= begin
return(body) unless (headers[:content_type] =~ /json|msgpack/)
process_data(agent.decode_body(body))
end
end
# Turns parsed contents from an API response into a Resource or
# collection of Resources.
#
# data - Either an Array or Hash parsed from JSON.
#
# Returns either a Resource or Array of Resources.
def process_data(data)
case data
when Hash then Resource.new(agent, data)
when Array then data.map { |hash| process_data(hash) }
when nil then nil
else data
end
end
# Finds link relations from 'Link' response header
#
# Returns an array of Relations
def process_rels
links = ( @headers["Link"] || "" ).split(', ').map do |link|
href, name = link.match(/<(.*?)>; rel="(\w+)"/).captures
[name.to_sym, Relation.from_link(@agent, name, :href => href)]
end
Hash[*links.flatten]
end
def timing
@timing ||= @ended - @started
end
def time
@ended
end
def inspect
%(#<#{self.class}: #{@status} @rels=#{@rels.inspect} @data=#{data.inspect}>)
end
end
end
@@ -0,0 +1,129 @@
require 'date'
require 'time'
module Sawyer
class Serializer
def self.any_json
yajl || multi_json || json || begin
raise RuntimeError, "Sawyer requires a JSON gem: yajl, multi_json, or json"
end
end
def self.yajl
require 'yajl'
new(Yajl)
rescue LoadError
end
def self.json
require 'json'
new(JSON)
rescue LoadError
end
def self.multi_json
require 'multi_json'
new(MultiJson)
rescue LoadError
end
def self.message_pack
require 'msgpack'
new(MessagePack, :pack, :unpack)
rescue LoadError
end
# Public: Wraps a serialization format for Sawyer. Nested objects are
# prepared for serialization (such as changing Times to ISO 8601 Strings).
# Any serialization format that responds to #dump and #load will work.
def initialize(format, dump_method_name = nil, load_method_name = nil)
@format = format
@dump = @format.method(dump_method_name || :dump)
@load = @format.method(load_method_name || :load)
end
# Public: Encodes an Object (usually a Hash or Array of Hashes).
#
# data - Object to be encoded.
#
# Returns an encoded String.
def encode(data)
@dump.call(encode_object(data))
end
alias dump encode
# Public: Decodes a String into an Object (usually a Hash or Array of
# Hashes).
#
# data - An encoded String.
#
# Returns a decoded Object.
def decode(data)
return nil if data.nil? || data.strip.empty?
decode_object(@load.call(data))
end
alias load decode
def encode_object(data)
case data
when Hash then encode_hash(data)
when Array then data.map { |o| encode_object(o) }
else data
end
end
def encode_hash(hash)
hash.keys.each do |key|
case value = hash[key]
when Date then hash[key] = value.to_time.utc.xmlschema
when Time then hash[key] = value.utc.xmlschema
when Hash then hash[key] = encode_hash(value)
end
end
hash
end
def decode_object(data)
case data
when Hash then decode_hash(data)
when Array then data.map { |o| decode_object(o) }
else data
end
end
def decode_hash(hash)
hash.keys.each do |key|
hash[key.to_sym] = decode_hash_value(key, hash.delete(key))
end
hash
end
def decode_hash_value(key, value)
if time_field?(key, value)
if value.is_a?(String)
begin
Time.parse(value)
rescue ArgumentError
value
end
elsif value.is_a?(Integer) || value.is_a?(Float)
Time.at(value)
else
value
end
elsif value.is_a?(Hash)
decode_hash(value)
elsif value.is_a?(Array)
value.map { |o| decode_hash_value(key, o) }
else
value
end
end
def time_field?(key, value)
value && (key =~ /_(at|on)\z/ || key =~ /(\A|_)date\z/)
end
end
end
+31
View File
@@ -0,0 +1,31 @@
lib = "sawyer"
lib_file = File.expand_path("../lib/#{lib}.rb", __FILE__)
File.read(lib_file) =~ /\bVERSION\s*=\s*["'](.+?)["']/
version = $1
Gem::Specification.new do |spec|
spec.specification_version = 2 if spec.respond_to? :specification_version=
spec.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if spec.respond_to? :required_rubygems_version=
spec.name = lib
spec.version = version
spec.summary = "Secret User Agent of HTTP"
spec.authors = ["Rick Olson", "Wynn Netherland"]
spec.email = 'technoweenie@gmail.com'
spec.homepage = 'https://github.com/lostisland/sawyer'
spec.licenses = ['MIT']
spec.add_dependency 'faraday', '>= 0.17.3', '< 3'
spec.add_dependency 'addressable', ['>= 2.3.5']
spec.files = %w(Gemfile LICENSE.md README.md Rakefile)
spec.files << "#{lib}.gemspec"
spec.files += Dir.glob("lib/**/*.rb")
spec.files += Dir.glob("script/*")
dev_null = File.exist?('/dev/null') ? '/dev/null' : 'NUL'
git_files = `git ls-files -z 2>#{dev_null}`
spec.files &= git_files.split("\0") if $?.success?
end
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
set -e
bundle install --quiet "$@"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Usage: script/console
# Starts an IRB console with this library loaded.
gemspec="$(ls *.gemspec | head -1)"
exec bundle exec irb -r "${gemspec%.*}"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Usage: script/gem
# Updates the gemspec and builds a new gem in the pkg directory.
mkdir -p pkg
gem build *.gemspec
mv *.gem pkg
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Usage: script/release
# Build the package, tag a commit, push it to origin, and then release the
# package publicly.
set -e
version="$(script/package | grep Version: | awk '{print $2}')"
[ -n "$version" ] || exit 1
git commit --allow-empty -a -m "Release $version"
git tag "v$version"
git push origin
git push origin "v$version"
gem push pkg/*-${version}.gem
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Usage: script/test
# Runs the library's test suite.
script/bootstrap
bundle exec rake test