zizu 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ zizu*.gem
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "colorize"
6
+ gem "github_api"
7
+ gem "haml"
8
+ gem "highline"
9
+ gem "rack"
10
+ gem "thin"
11
+ gem "thor"
12
+ gem "tilt"
13
+ gem "zip"
14
+
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ zizu (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ colorize (0.5.8)
10
+ daemons (1.1.9)
11
+ eventmachine (1.0.0)
12
+ faraday (0.8.4)
13
+ multipart-post (~> 1.1)
14
+ github_api (0.8.4)
15
+ faraday (~> 0.8.1)
16
+ hashie (~> 1.2.0)
17
+ multi_json (~> 1.4)
18
+ nokogiri (~> 1.5.2)
19
+ oauth2
20
+ haml (3.1.7)
21
+ hashie (1.2.0)
22
+ highline (1.6.15)
23
+ httpauth (0.2.0)
24
+ jwt (0.1.5)
25
+ multi_json (>= 1.0)
26
+ multi_json (1.5.0)
27
+ multipart-post (1.1.5)
28
+ nokogiri (1.5.6)
29
+ oauth2 (0.8.0)
30
+ faraday (~> 0.8)
31
+ httpauth (~> 0.1)
32
+ jwt (~> 0.1.4)
33
+ multi_json (~> 1.0)
34
+ rack (~> 1.2)
35
+ rack (1.4.1)
36
+ thin (1.5.0)
37
+ daemons (>= 1.0.9)
38
+ eventmachine (>= 0.12.6)
39
+ rack (>= 1.0.0)
40
+ thor (0.16.0)
41
+ tilt (1.3.3)
42
+ zip (2.0.2)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ colorize
49
+ github_api
50
+ haml
51
+ highline
52
+ rack
53
+ thin
54
+ thor
55
+ tilt
56
+ zip
57
+ zizu!
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2012 Stephen Hu
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
+ of the Software, and to permit persons to whom the Software is furnished to do
9
+ so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # zizu
2
+
3
+ zizu is an opinionated static site generator based on bootstrap, haml, html5,
4
+ and github.
5
+
6
+ ## requirements
7
+
8
+ * bundler
9
+ * github_api
10
+ * haml
11
+ * thor
12
+ * tilt
13
+
14
+ ## installation
15
+
16
+ ### gem package
17
+
18
+ 1. install the gem from rubygems
19
+ ```gem install zizu-0.0.1.gem```
20
+
21
+ ### from source
22
+
23
+ 1. clone the repository
24
+ ```git clone git@github.com:stephenhu/zizu```
25
+ 2. install dependencies
26
+ ```bundle install # from the zizu root directory```
27
+ 3. build the gem
28
+ ```gem build zizu.gemspec # from the zizu root directory```
29
+ 4. install the local gem
30
+ ```gem install zizu-0.0.1.gem```
31
+
32
+ ## faq
33
+
34
+ 1. what does zizu stand for?
35
+ ```zizu is the chinese pin yin for spider, technically it should be zhizhu,
36
+ but that's a bit harder to type.```
37
+ 2. are sinatra haml files compatible with zizu?
38
+ ```technically i tried to keep the syntax consistent with sinatra since i'm
39
+ a big fan, so i mocked _haml_ and _url_ functions.```
40
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "zizu"
3
+ Zizu::CLI.start
4
+
@@ -0,0 +1,3 @@
1
+ templates: layout.haml, navbar.haml, footer.haml
2
+
3
+
@@ -0,0 +1,6 @@
1
+ - add rspec tests
2
+ - prompt user on overwriting existing files
3
+ - add in configuration file to ~/.zizu
4
+ - download prettify
5
+ - download jqueryui
6
+
@@ -0,0 +1,33 @@
1
+ zizu is an opinionated static site generator based on bootstrap, haml, html5,
2
+ and github.
3
+
4
+ zizu can:
5
+
6
+ 1. fork site skeleton (fork generic templates)
7
+ 2. create repositories on github
8
+ 3. generate static site files
9
+ 4. push/pull sources to github
10
+ 5. deploy to github pages
11
+
12
+ x. deploy to bitbucket
13
+ x. pull sources from local git
14
+ x. deploy to other hosts via sftp
15
+ x. run locally for development purposes
16
+
17
+ repositories:
18
+
19
+ 1. source (haml)
20
+ 2. destination (html)
21
+
22
+ commands:
23
+
24
+ # creates remote repository and syncs
25
+ zizu create NAME
26
+
27
+ # converts haml to html
28
+ zizu compile
29
+
30
+ # push to remote repository
31
+ zizu deploy REPOSITORY
32
+
33
+
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "base64"
4
+ require "colorize"
5
+ require "github_api"
6
+ require "haml"
7
+ require "highline/import"
8
+ require "open3"
9
+ require "open-uri"
10
+ require "rack"
11
+ require "thor"
12
+ require "tilt"
13
+ require "yaml"
14
+ require "zip/zip"
15
+
16
+ require File.join( File.dirname(__FILE__), "zizu", "githublib" )
17
+ require File.join( File.dirname(__FILE__), "zizu", "gitlib" )
18
+ require File.join( File.dirname(__FILE__), "zizu", "version" )
19
+ require File.join( File.dirname(__FILE__), "zizu", "cli" )
20
+
21
+ def haml(file)
22
+ return Tilt.new("#{file}.haml").render
23
+ end
24
+
25
+ def url(location)
26
+ # TODO add some logic to filter the url address
27
+ return location
28
+ end
29
+
30
+ module Zizu
31
+
32
+ USER = "stephenhu"
33
+ REPOSITORY = "bootstrap-haml"
34
+ EXCLUDES = [ "layout.haml", "navbar.haml", "footer.haml" ]
35
+ BOOTSTRAP = "http://twitter.github.com/bootstrap/assets/bootstrap.zip"
36
+
37
+ def self.fatal(msg)
38
+ puts msg.red
39
+ exit
40
+ end
41
+
42
+ def self.warning(msg)
43
+ puts msg.yellow
44
+ end
45
+
46
+ def self.info(msg)
47
+ puts msg
48
+ end
49
+
50
+ def self.success(msg)
51
+ puts msg.green
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,246 @@
1
+ module Zizu
2
+
3
+ class CLI < Thor
4
+
5
+ desc( "create NAME", "create site skeleton" )
6
+ method_option :fork, :aliases => "-f", :type => :boolean,
7
+ :default => false, :required => false, :desc => "fork repo"
8
+ def create(name)
9
+
10
+ if File.directory?(name)
11
+ Zizu::fatal("directory already exists, init aborted")
12
+ else
13
+
14
+ if options[:fork]
15
+ fork(name)
16
+ else
17
+ create_skeleton(name)
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ desc( "compile", "compile template files to .html" )
25
+ method_option :exclude, :aliases => "-x", :type => :string,
26
+ :required => false, :desc => "files to exclude from compilation, " +
27
+ "use comma separate multiple files"
28
+ method_option :output, :aliases => "-o", :type => :string,
29
+ :required => false, :desc => "output path for compiled files"
30
+ def compile
31
+
32
+ excludes = check_exclusions(options[:exclude])
33
+
34
+ unless options[:output].nil?
35
+ dir = create_directory(options[:output])
36
+ end
37
+
38
+ basedir = "."
39
+
40
+ haml_files = Dir.glob("*.haml")
41
+
42
+ init_templates
43
+
44
+ haml_files.each do |f|
45
+
46
+ if excludes.include?(f)
47
+ next
48
+ else
49
+
50
+ html = haml_to_html(f)
51
+
52
+ if dir.nil?
53
+ html_name = f.chomp(".haml") + ".html"
54
+ else
55
+ html_name = dir + f.chomp(".haml") + ".html"
56
+ end
57
+
58
+ f = File.open( html_name, "w" )
59
+ f.write(html)
60
+ f.close
61
+
62
+ Zizu::success("created #{html_name}")
63
+
64
+ end
65
+
66
+ end
67
+
68
+ unless dir.nil?
69
+
70
+ FileUtils.cp_r( "scripts", dir )
71
+ Zizu::success("copying scripts directory")
72
+
73
+ FileUtils.cp_r( "styles", dir )
74
+ Zizu::success("copying styles directory")
75
+
76
+ FileUtils.cp_r( "images", dir )
77
+ Zizu::success("copying images directory")
78
+
79
+ end
80
+
81
+ end
82
+
83
+ desc( "stage", "stage environment locally for testing")
84
+ def stage
85
+
86
+ get_rack
87
+ get_bootstrap
88
+
89
+ Zizu::info("run the 'rackup' command to start the server locally")
90
+
91
+ end
92
+
93
+ #desc( "deploy", "deploy static files to production" )
94
+ #method_option :target, :aliases => "-t", :required => false
95
+ #def deploy
96
+
97
+ # g = GithubLib.new
98
+
99
+ # latest = g.get_latest_commit()
100
+
101
+ # tree = g.get_tree(latest)
102
+ # g.download_files(tree)
103
+
104
+ #end
105
+
106
+ no_tasks do
107
+
108
+ def fork(name)
109
+
110
+ g = GithubLib.new
111
+
112
+ # TODO make atomic
113
+ git_url = g.fork( USER, REPOSITORY )
114
+
115
+ unless git_url.nil?
116
+
117
+ new_url = g.set_repository_name( REPOSITORY, name )
118
+
119
+ if g.check_exists?(name)
120
+ # TODO retry if it doesn't exist since this is asynch
121
+ if Gitlib.clone( new_url, name )
122
+ Zizu::success("repository cloned to local")
123
+ else
124
+
125
+ Zizu::fatal("unable clone remote repository")
126
+ g.delete_repository(REPOSITORY)
127
+
128
+ end
129
+
130
+ else
131
+ Zizu::fatal("repository rename failed, aborting operation")
132
+ g.delete_repository(REPOSITORY)
133
+ end
134
+
135
+ else
136
+ Zizu::fatal("fork failed, aborting operation")
137
+ end
138
+
139
+ end
140
+
141
+ def get_rack
142
+
143
+ configru = File.join( File.dirname(__FILE__), "config.ru" )
144
+ FileUtils.cp( configru, "." ) unless File.exists?("./config.ru")
145
+
146
+ Zizu::success("rackup configuration (config.ru) installed")
147
+
148
+ end
149
+
150
+ def get_bootstrap
151
+
152
+ bootstrap = open(BOOTSTRAP)
153
+
154
+ Zip::ZipFile.open(bootstrap.path) do |z|
155
+
156
+ z.each do |f|
157
+ path = File.join( ".", f.name )
158
+ FileUtils.mkdir_p(File.dirname(path))
159
+ z.extract( f, path ) unless File.exist?(path)
160
+ end
161
+
162
+ end
163
+
164
+ Zizu::success("bootstrap latest installed")
165
+
166
+ end
167
+
168
+ def get_dependencies
169
+
170
+ # bootstrap
171
+
172
+ end
173
+
174
+ def check_exclusions(excludes_str)
175
+
176
+ if excludes_str.nil?
177
+ return EXCLUDES
178
+ else
179
+
180
+ user_excludes = excludes_str.split(",").each { |e| e.strip! }
181
+ user_excludes = user_excludes + EXCLUDES
182
+ user_excludes.uniq!
183
+ return user_excludes
184
+
185
+ end
186
+
187
+ end
188
+
189
+ def create_skeleton(name)
190
+
191
+ dirs = [ "images", "styles", "scripts", "conf" ]
192
+
193
+ dirs.each do |d|
194
+ create_directory( "#{name}/#{d}")
195
+ end
196
+
197
+ # copy from github or store locally? if no network?
198
+ # TODO how about a reverse html to haml?
199
+
200
+ g = GithubLib.new
201
+
202
+ latest = g.get_latest_commit()
203
+
204
+ tree = g.get_tree(latest)
205
+ g.download_files( tree, name )
206
+
207
+ end
208
+
209
+ def create_directory(name)
210
+
211
+ if name.nil?
212
+ Zizu::fatal("no directory name specified")
213
+ else
214
+
215
+ FileUtils.mkdir_p(name) unless File.directory?(name)
216
+ Zizu::success("#{name} directory created")
217
+
218
+ end
219
+
220
+ end
221
+
222
+ def init_templates
223
+
224
+ @layout = Tilt.new("layout.haml") if File.exists?("layout.haml")
225
+ @navbar = Tilt.new("navbar.haml") if File.exists?("navbar.haml")
226
+ @footer = Tilt.new("footer.haml") if File.exists?("footer.haml")
227
+
228
+ end
229
+
230
+ def haml_to_html(template)
231
+
232
+ unless @layout.nil?
233
+ html = @layout.render { Tilt.new(template).render }
234
+ return html
235
+ end
236
+
237
+ Zizu::fatal("layout.haml template is missing, aborting compile")
238
+
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+
@@ -0,0 +1,2 @@
1
+ run Rack::Directory.new(".")
2
+
@@ -0,0 +1,192 @@
1
+ module Zizu
2
+
3
+ class GithubLib
4
+
5
+ attr_accessor :api
6
+
7
+ def initialize
8
+ login
9
+ end
10
+
11
+
12
+ def login
13
+
14
+ if @api.nil?
15
+
16
+ config = Gitlib.config
17
+
18
+ @login = ENV["ZIZU_GIT_LOGIN"] || config["user.name"]
19
+
20
+ if @login == USER
21
+ Zizu::fatal("cannot fork a project that you own, aborting")
22
+ end
23
+
24
+ @password = ENV["ZIZU_GIT_PASSWORD"] ||
25
+ ask("git password: ") { |q| q.echo = "*" }
26
+
27
+ if @login.nil? or @password.nil?
28
+ Zizu::fatal("please set git config variable user.name or env variable ZIZU_GIT_LOGIN.")
29
+ end
30
+
31
+ @api = Github.new( login:@login, password:@password )
32
+
33
+ if @api.nil?
34
+ Zizu::fatal("login failed")
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ def fork( user, repo )
42
+
43
+ unless fork_exists?("#{user}/#{repo}")
44
+
45
+ response = @api.repos.forks.create( user, repo )
46
+
47
+ if response.success?
48
+ Zizu::success("repository forked to: #{response[:git_url]}")
49
+ return response[:git_url]
50
+ end
51
+
52
+ end
53
+
54
+ return nil
55
+
56
+ end
57
+
58
+ def fork_exists?(full_name)
59
+
60
+ response = @api.repos.list( :user => @login )
61
+
62
+ response.each do |r|
63
+
64
+ user, repo = r[:full_name].split("/")
65
+ get_r = @api.repos.get( user, repo )
66
+
67
+ if get_r.success?
68
+
69
+ if get_r[:fork] and get_r[:parent][:full_name] == full_name
70
+ Zizu::fatal(
71
+ "repository has already been forked: #{get_r[:git_url]}")
72
+ return true
73
+ end
74
+
75
+ else
76
+ Zizu::fatal("Error: #{get_r.status}")
77
+ Zizu::fatal(get_r.body)
78
+ end
79
+
80
+ end
81
+
82
+ return false
83
+
84
+ end
85
+
86
+ def check_exists?(repo)
87
+
88
+ response = @api.repos.get( @login, repo )
89
+
90
+ if response.success?
91
+ return true
92
+ else
93
+ return false
94
+ end
95
+
96
+ end
97
+
98
+ def set_repository_name( repository, new_name )
99
+
100
+ response = @api.repos.edit( @login, repository, :name => new_name )
101
+
102
+ if response.success?
103
+ Zizu::success("Repository successfully renamed to #{new_name}")
104
+ Zizu::success(response[:git_url])
105
+ return response[:git_url]
106
+ else
107
+ Zizu::fatal("GithubLib Error: #{response.status}")
108
+ return nil
109
+ end
110
+
111
+ end
112
+
113
+ def delete_repository(name)
114
+
115
+ puts @login
116
+ puts "rollback"
117
+
118
+ #response = @api.repos.delete( @login, name )
119
+
120
+ end
121
+
122
+ def get_latest_commit
123
+
124
+ response = @api.repos.commits.list( USER, REPOSITORY )
125
+
126
+ if response.success?
127
+ return response[0][:sha]
128
+ else
129
+ fatal("unable to retrieve latest commit hash")
130
+ end
131
+
132
+ end
133
+
134
+ def get_tree(id)
135
+
136
+ #latest = get_latest_commit
137
+
138
+ response = @api.git_data.trees.get( USER, REPOSITORY, id )
139
+
140
+ if response.success?
141
+ return response[:tree]
142
+ else
143
+ fatal("unable to retrieve tree for #{id}")
144
+ end
145
+
146
+ end
147
+
148
+ def download_files( tree, dir=nil )
149
+
150
+ tree.each do |t|
151
+
152
+ if t["type"] == "tree"
153
+
154
+ if dir.nil?
155
+ new_path = t["path"]
156
+ else
157
+ new_path = "#{dir}/#{t["path"]}"
158
+ end
159
+
160
+ FileUtils.mkdir_p(new_path)
161
+ download_files( get_tree(t["sha"]), new_path )
162
+ next
163
+
164
+ else
165
+
166
+ data = open(t["url"])
167
+ json = JSON.parse(data.read())
168
+
169
+ unless json["content"].nil?
170
+
171
+ if dir.nil?
172
+ f = File.open( t["path"], "w" )
173
+ else
174
+ f = File.open( "#{dir}/#{t["path"]}", "w" )
175
+ end
176
+
177
+ contents = Base64.decode64(json["content"])
178
+ f.write(contents)
179
+ f.close
180
+
181
+ end
182
+
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+
@@ -0,0 +1,66 @@
1
+ module Zizu
2
+
3
+ module Gitlib
4
+
5
+ def self.cmd(str)
6
+
7
+ stdin, stdout, stderr = Open3.popen3(str)
8
+
9
+ err = stderr.read.chomp
10
+
11
+ return { :stdout => stdout.read.chomp, :stderr => err,
12
+ :status => err.length > 0 ? false : true }
13
+
14
+ end
15
+
16
+ def self.version
17
+
18
+ r = cmd("git --version")
19
+ tokens = r[:stdout].split(" ")
20
+ return tokens[-1]
21
+
22
+ end
23
+
24
+ def self.config
25
+
26
+ configs = Hash.new
27
+
28
+ r = cmd("git config -l")
29
+
30
+ if r[:status]
31
+
32
+ # TODO windows and mac support
33
+ params = r[:stdout].split("\n")
34
+
35
+ params.each do |p|
36
+ key, value = p.split("=")
37
+ configs[key.strip] = value.strip
38
+ end
39
+
40
+ return configs
41
+
42
+ else
43
+ Zizu::fatal("command failed")
44
+ return nil
45
+ end
46
+
47
+ end
48
+
49
+ def self.clone( url, dest=nil )
50
+
51
+ r = cmd("git clone #{url} #{dest}")
52
+
53
+ if r[:status]
54
+ Zizu::success(r[:stdout])
55
+ else
56
+ Zizu::fatal(r[:stderr])
57
+ end
58
+
59
+ return r[:status]
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
@@ -0,0 +1,3 @@
1
+ module Zizu
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/zizu/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.name = "zizu"
7
+ gem.platform = Gem::Platform::RUBY
8
+ gem.version = Zizu::VERSION
9
+ gem.license = "MIT"
10
+ gem.date = "2012-12-23"
11
+ gem.summary = "zizu cli"
12
+ gem.description = "static site generator and deployment tool."
13
+ gem.authors = %w(stephenhu)
14
+ gem.email = "epynonymous@outlook.com"
15
+ gem.homepage = "http://github.com/stephenhu/zizu"
16
+
17
+ gem.require_paths = %w(lib)
18
+ gem.bindir = "bin"
19
+ gem.files = `git ls-files`.split($\)
20
+ #gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.executables = %w(zizu)
22
+
23
+ #gem.add_dependency "rgit"
24
+
25
+ end
26
+
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zizu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - stephenhu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: static site generator and deployment tool.
15
+ email: epynonymous@outlook.com
16
+ executables:
17
+ - zizu
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - Gemfile.lock
24
+ - LICENSE
25
+ - README.md
26
+ - Rakefile
27
+ - bin/zizu
28
+ - conf/zizu.yaml
29
+ - docs/backlog
30
+ - docs/design
31
+ - lib/zizu.rb
32
+ - lib/zizu/cli.rb
33
+ - lib/zizu/config.ru
34
+ - lib/zizu/githublib.rb
35
+ - lib/zizu/gitlib.rb
36
+ - lib/zizu/version.rb
37
+ - zizu.gemspec
38
+ homepage: http://github.com/stephenhu/zizu
39
+ licenses:
40
+ - MIT
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.23
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: zizu cli
63
+ test_files: []