org-converge 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +3 -0
- data/Gemfile +8 -0
- data/LICENSE +22 -0
- data/README.org +109 -0
- data/Rakefile +10 -0
- data/TODO +167 -0
- data/bin/org-converge +29 -0
- data/examples/apt-get-install/setup.org +11 -0
- data/examples/fluentd/setup.org +50 -0
- data/examples/macro-config/setup.org +14 -0
- data/examples/simple/setup.org +51 -0
- data/lib/org-converge/babel.rb +60 -0
- data/lib/org-converge/babel_output_buffer.rb +82 -0
- data/lib/org-converge/command.rb +58 -0
- data/lib/org-converge/version.rb +3 -0
- data/lib/org-converge.rb +37 -0
- data/org-converge.gemspec +19 -0
- data/spec/converge_examples/basic_tangle/conf.yml.expected +7 -0
- data/spec/converge_examples/basic_tangle/setup.org +18 -0
- data/spec/converge_spec.rb +28 -0
- data/spec/spec_helper.rb +6 -0
- metadata +94 -0
data/.gitmodules
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Waldemar Quevedo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.org
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# -*- mode: org; mode: auto-fill; -*-
|
2
|
+
#+TITLE: Org Converge
|
3
|
+
#+STARTUP: showeverything
|
4
|
+
|
5
|
+
* Description
|
6
|
+
|
7
|
+
This attempts to be an experiment of using Org mode syntax to
|
8
|
+
describe, configure and setting up something, borrowing some ideas
|
9
|
+
of what is possible to do with tools like =chef-solo=, =puppet=,
|
10
|
+
=ansible=, etc...
|
11
|
+
|
12
|
+
* Motivation
|
13
|
+
|
14
|
+
The Org babel syntax has proven to be flexible enough to produce
|
15
|
+
/reproducible research/ papers. Then, I believe that configuring and setting up
|
16
|
+
a server for example is something that could be also be done using
|
17
|
+
the same syntax, given that /converging/ the configuration is something
|
18
|
+
that one ought to be able to reproduce.
|
19
|
+
|
20
|
+
* Usage
|
21
|
+
|
22
|
+
: org-converge path/to/setup-file.org
|
23
|
+
|
24
|
+
* How it works
|
25
|
+
|
26
|
+
Org Converge uses an liberally extended version of Org Babel
|
27
|
+
features in order to give support for converging the configuration
|
28
|
+
of a server.
|
29
|
+
|
30
|
+
For example, using Org Babel and macros we can easily spread config
|
31
|
+
files on a server by writing the following on a ~server.org~ file.
|
32
|
+
|
33
|
+
#+begin_src org
|
34
|
+
,#+MACRO: multitenancy_enabled true
|
35
|
+
,#+MACRO: status_port true
|
36
|
+
|
37
|
+
,#+begin_src yaml :tangle /etc/component.yml
|
38
|
+
multitenant: false
|
39
|
+
status_port: 10004
|
40
|
+
,#+end_src
|
41
|
+
#+end_src
|
42
|
+
|
43
|
+
And then configure it by running it as follows, (considering we have
|
44
|
+
the correct permissions):
|
45
|
+
|
46
|
+
#+begin_src sh
|
47
|
+
org-converge server.org
|
48
|
+
#+end_src
|
49
|
+
|
50
|
+
This leverages on the syntax already provided by Org Babel, but one
|
51
|
+
difference here is that if we run it once again without changes...
|
52
|
+
|
53
|
+
#+begin_src sh
|
54
|
+
org-converge server.org
|
55
|
+
#+end_src
|
56
|
+
|
57
|
+
...it would finish soon since the configuration has already converged.
|
58
|
+
|
59
|
+
Next, let's say that we no only one want to set the configured templates,
|
60
|
+
but that we also want to install some packages. In that case, we
|
61
|
+
should be able to do the following:
|
62
|
+
|
63
|
+
#+begin_src org
|
64
|
+
,#+macro: multitenancy_enabled true
|
65
|
+
,#+macro: status_port true
|
66
|
+
,#+macro: project_path path/to/project
|
67
|
+
|
68
|
+
,* Configuring the component
|
69
|
+
|
70
|
+
,#+begin_src yaml :tangle /etc/component.yml
|
71
|
+
multitenant: false
|
72
|
+
status_port: 10004
|
73
|
+
,#+end_src
|
74
|
+
|
75
|
+
,* Installing the dependencies
|
76
|
+
|
77
|
+
Need the following so that ~bundle install~ can compile
|
78
|
+
the native extensions correctly.
|
79
|
+
|
80
|
+
,#+begin_src sh
|
81
|
+
apt-get install build-essentials -y
|
82
|
+
,#+end_src
|
83
|
+
|
84
|
+
Then the following should work:
|
85
|
+
|
86
|
+
,#+begin_src sh
|
87
|
+
cd {{{project_path}}}
|
88
|
+
bundle install
|
89
|
+
,#+end_src
|
90
|
+
#+end_src
|
91
|
+
|
92
|
+
As long as the repo has been already checked out in the directory,
|
93
|
+
the previous example will succeed.
|
94
|
+
|
95
|
+
#+begin_src sh
|
96
|
+
org-converge server.org
|
97
|
+
#+end_src
|
98
|
+
|
99
|
+
If that is not the case, then org-converge will fail
|
100
|
+
and pickup from that last step.
|
101
|
+
|
102
|
+
More practical examples can be found [[here]], more will be added as
|
103
|
+
long as dogfooding from this goes well.
|
104
|
+
|
105
|
+
* Contributing
|
106
|
+
|
107
|
+
The project is in very early development at this moment, but if you
|
108
|
+
feel that it is interesting enough, please create a ticket so start
|
109
|
+
the discussion.
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
9
|
+
spec.rspec_opts = ['--format', 'documentation', '--colour']
|
10
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# -*- mode: org; mode: auto-fill; -*-
|
2
|
+
#+TODO: TODO | DONE CANCELED
|
3
|
+
#+startup: showeverything
|
4
|
+
|
5
|
+
* [0/2] 0.0.3 version
|
6
|
+
|
7
|
+
- [ ] Macros can be loaded and applied to the configuration
|
8
|
+
- [ ] Actually support converging and idempotency
|
9
|
+
+ Do not do an operation unless it is required
|
10
|
+
+ Abort in case there was a failure in executing the script.
|
11
|
+
|
12
|
+
* [0/2] 0.0.2 version
|
13
|
+
|
14
|
+
Run the code blocks!
|
15
|
+
|
16
|
+
- [ ] Code blocks are executed by default after tangling
|
17
|
+
Only when :shebang exists as an argument
|
18
|
+
- [ ] Display how the run would look like
|
19
|
+
: org-converge setupfile.org --dry-run
|
20
|
+
|
21
|
+
* [3/3] 0.0.1 version
|
22
|
+
|
23
|
+
Need some basic functionality of what Org babel offers first.
|
24
|
+
|
25
|
+
- [X] Display files that were captured with command
|
26
|
+
: org-converge setupfile.org --showfiles
|
27
|
+
- [X] ~:tangle~ puts the files in the correct path
|
28
|
+
- [X] ~--tangle~ flag
|
29
|
+
- [X] Support a root dir for when not running relative to the directory
|
30
|
+
|
31
|
+
* [2/14] Ideas
|
32
|
+
** CANCELED We don't need to create the directories in most cases
|
33
|
+
|
34
|
+
Something like this is not required because the ~:tangle~ blocks
|
35
|
+
would create the necessary directories behind the scenes.
|
36
|
+
|
37
|
+
#+begin_src org
|
38
|
+
,We need to prepare some directories for the configuration:
|
39
|
+
|
40
|
+
,#+begin_src converge
|
41
|
+
,mkdir -p etc/fluentd/config
|
42
|
+
,#+end_src
|
43
|
+
#+end_src
|
44
|
+
|
45
|
+
** CANCELED How to set the permissions from the directory from the file that is being tangled when it does not exists?
|
46
|
+
|
47
|
+
By default, this would be 0644, but we also need to specify the
|
48
|
+
user:group of it so the syntax would have to be:
|
49
|
+
|
50
|
+
#+begin_src conf :tangle etc/something/config/path :chmod 0664 :user td-agent :group
|
51
|
+
hmm dont't like this syntax for folders
|
52
|
+
#+end_src
|
53
|
+
|
54
|
+
Let's keep it simple and just use a babel block that shells out to create the directories
|
55
|
+
until I can think of something better.
|
56
|
+
|
57
|
+
** TODO By default, it should use current dir for tangling
|
58
|
+
** TODO Converging: Only do an operation when it didn't finish
|
59
|
+
|
60
|
+
Hence the name of the project
|
61
|
+
|
62
|
+
** TODO For now we keep the indentation of the code blocks
|
63
|
+
|
64
|
+
The indentatin of the ~#+begin_src~ should always
|
65
|
+
be at the beginning of the file, not at the indentation
|
66
|
+
level from when the file was written in Org mode.
|
67
|
+
|
68
|
+
** TODO We should have a whitelist of languages that can be executed and ignore everything
|
69
|
+
|
70
|
+
Under which heuristics or configuration should we decide which will be the
|
71
|
+
binary that should be used to execute the block? Specify with shebang?
|
72
|
+
|
73
|
+
- For now, unless shebang is specified it will be executed.
|
74
|
+
Then in the scripts buffer used ~index::shebang~ as a key.
|
75
|
+
|
76
|
+
** TODO Setting permissions from a tangled file
|
77
|
+
|
78
|
+
It should be possible to tangle the files this way:
|
79
|
+
|
80
|
+
#+begin_src conf :tangle etc/something/config/path :chmod 0664 :user td-agent :group td-agent
|
81
|
+
<source>
|
82
|
+
</source>
|
83
|
+
#+end_src
|
84
|
+
|
85
|
+
** TODO Adopting the ~#+SETUPFILE~ for loading a config that is done JSON or YAML
|
86
|
+
|
87
|
+
It seems that there is support for a ~#+SETUPFILE~
|
88
|
+
|
89
|
+
** TODO Something more flexible than macros:
|
90
|
+
|
91
|
+
One example of syntax that we could use instead of macros:
|
92
|
+
|
93
|
+
#+begin_src yaml :session
|
94
|
+
fluentd:
|
95
|
+
port: 4224
|
96
|
+
path: here.log
|
97
|
+
#+end_src
|
98
|
+
|
99
|
+
But need to
|
100
|
+
|
101
|
+
** TODO Using the :tags: to setup the things to run right away
|
102
|
+
|
103
|
+
Kind of like the chef-solo runlist, a headline like this...
|
104
|
+
|
105
|
+
#+begin_src org
|
106
|
+
,* Everything in its right place :config:
|
107
|
+
|
108
|
+
,#+begin_src conf :tangle etc/this.yml
|
109
|
+
,hello: "world"
|
110
|
+
,#+end_src
|
111
|
+
#+end_src
|
112
|
+
|
113
|
+
...could be called like this
|
114
|
+
|
115
|
+
#+begin_src sh
|
116
|
+
org-converge fluentd.org -t config
|
117
|
+
#+end_src
|
118
|
+
|
119
|
+
** TODO Managing dependencies: could be handled with ~#+include~ directives
|
120
|
+
|
121
|
+
One idea is that it would be useful to compile different manuals
|
122
|
+
that are dependent among each other.
|
123
|
+
|
124
|
+
For example, the centralized logs server would require fluentd.
|
125
|
+
We should be able to express that dependency somehow:
|
126
|
+
|
127
|
+
#+begin_src org :tangle logserver.org
|
128
|
+
,#+include: "fluentd.org"
|
129
|
+
|
130
|
+
,* Setup the centralized logserver :setup:
|
131
|
+
|
132
|
+
,Once the fluentd.org has been converged, we can build upon this and
|
133
|
+
,override the original config to make it particular for this logserver
|
134
|
+
#+end_src
|
135
|
+
|
136
|
+
# But one problem, is that once I have included something, sometimes we
|
137
|
+
# will want "reopen" the previous manuals?
|
138
|
+
|
139
|
+
** TODO Loading all the Org mode files first and then setup only one part
|
140
|
+
|
141
|
+
So maybe, each one of these tags would have to be namespaces under the
|
142
|
+
name of the file:
|
143
|
+
|
144
|
+
: org-converge logserver.org -t "fluentd::setup, fluentd::config, logserver::setup"
|
145
|
+
|
146
|
+
** TODO Choosing a templating language: default for now is mustache
|
147
|
+
|
148
|
+
We could implement the macro systems, but it seems that it may not be
|
149
|
+
strong enough for providing with all the cases we may run into.
|
150
|
+
|
151
|
+
** TODO Chaining resources with ~#+NAME:~ directives and ~:notify~ argument
|
152
|
+
|
153
|
+
One idea is to be able to notify resources by naming the code blocks.
|
154
|
+
|
155
|
+
Example: Here first the td-agent service would try to start,
|
156
|
+
and if it succeeds, then it would execute the script defined in the
|
157
|
+
~announce-availability~ resource.
|
158
|
+
|
159
|
+
#+name: td-agent-start
|
160
|
+
#+begin_src sh :notify announce-availability
|
161
|
+
sudo service td-agent start
|
162
|
+
#+end_src
|
163
|
+
|
164
|
+
#+name: announce-availability
|
165
|
+
#+begin_src sh
|
166
|
+
sudo /etc/register-to-balancer
|
167
|
+
#+end_src
|
data/bin/org-converge
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby -*-
|
3
|
+
require 'docopt'
|
4
|
+
require 'org-converge'
|
5
|
+
|
6
|
+
doc = <<OPTIONS
|
7
|
+
|
8
|
+
org-converge: A light configuration management tool for Org mode
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
org-converge <org_file> [--tangle] [--showfiles] [--log=<logfile>] [--root-dir=<root_dir>]
|
12
|
+
|
13
|
+
Options:
|
14
|
+
|
15
|
+
-h --help Show this screen.
|
16
|
+
|
17
|
+
OPTIONS
|
18
|
+
|
19
|
+
begin
|
20
|
+
require "pp"
|
21
|
+
cmd = Docopt::docopt(doc)
|
22
|
+
rescue Docopt::Exit => e
|
23
|
+
puts e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
exit 1 unless cmd
|
27
|
+
|
28
|
+
o = OrgConverge::Command.new(cmd)
|
29
|
+
o.execute!
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- mode: org; auto-fill-mode -*-
|
2
|
+
#+TITLE: Setting up Fluentd
|
3
|
+
#+startup: showeverything
|
4
|
+
|
5
|
+
* Introduction
|
6
|
+
|
7
|
+
The following will setup and configure ~fluentd~ on a node.
|
8
|
+
|
9
|
+
* Settings
|
10
|
+
|
11
|
+
These can be defined with Org mode macro directives, but thinking on
|
12
|
+
other ways to do it as well.
|
13
|
+
|
14
|
+
#+macro: fluentd_port 4224
|
15
|
+
#+macro: fluentd_filepath here.log
|
16
|
+
|
17
|
+
* Setup :setup:
|
18
|
+
|
19
|
+
Fluentd can be installed either as a package from treasure data or
|
20
|
+
just as a gem. In this example, we only install as a gem:
|
21
|
+
|
22
|
+
#+begin_src sh
|
23
|
+
gem install fluentd
|
24
|
+
#+end_src
|
25
|
+
|
26
|
+
* Configuration :config:
|
27
|
+
|
28
|
+
Set Fluentd to listen on the ~fluentd_port~ port and to write logs
|
29
|
+
to ~fluentd_filepath~.
|
30
|
+
|
31
|
+
#+begin_src conf :tangle etc/fluentd.conf
|
32
|
+
<source>
|
33
|
+
type forward
|
34
|
+
port {{{ fluentd_port }}}
|
35
|
+
</source>
|
36
|
+
|
37
|
+
<match **>
|
38
|
+
type file
|
39
|
+
path {{{ fluentd_filepath }}}
|
40
|
+
</match>
|
41
|
+
#+end_src
|
42
|
+
|
43
|
+
# A script to daemonize the process could be written here, as well as
|
44
|
+
# any /etc/default/* required settings...
|
45
|
+
|
46
|
+
* Start which was configured here :start:
|
47
|
+
|
48
|
+
#+begin_src sh
|
49
|
+
fluentd -c etc/fluentd.conf -vvv
|
50
|
+
#+end_src
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#+TITLE: Using macros for configuration
|
2
|
+
|
3
|
+
#+macro: hello world
|
4
|
+
#+macro: application my-application
|
5
|
+
|
6
|
+
Hello {{{hello}}}, this will setup something for {{{application}}}.
|
7
|
+
|
8
|
+
#+begin_src sh :results output
|
9
|
+
echo "Hello for {{{application}}}" > /tmp/hello
|
10
|
+
#+end_src
|
11
|
+
|
12
|
+
#+RESULTS:
|
13
|
+
: Hello for {{{application}}}
|
14
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#+TITLE: Simple example
|
2
|
+
|
3
|
+
It does not get more simple than this:
|
4
|
+
|
5
|
+
- First, configure something:
|
6
|
+
|
7
|
+
#+begin_src conf :tangle /tmp/my-conf
|
8
|
+
<source>
|
9
|
+
bind 0.0.0.0
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
port 4224
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
</source>
|
19
|
+
|
20
|
+
<match **>
|
21
|
+
type file
|
22
|
+
|
23
|
+
|
24
|
+
path /var/tmp/a.log
|
25
|
+
</match>
|
26
|
+
#+end_src
|
27
|
+
|
28
|
+
- And then run something
|
29
|
+
|
30
|
+
#+begin_src sh
|
31
|
+
echo "1) Hello world" > /tmp/my-echo
|
32
|
+
echo "2) Hello world" > /tmp/my-echo
|
33
|
+
echo "3) Hello world" > /tmp/my-echo
|
34
|
+
#+end_src
|
35
|
+
|
36
|
+
- Then configure something again
|
37
|
+
|
38
|
+
#+begin_src sh
|
39
|
+
echo "4) Hello world" >> /tmp/my-echo
|
40
|
+
echo "5) Hello world" >> /tmp/my-echo
|
41
|
+
echo "6) Hello world" >> /tmp/my-echo
|
42
|
+
|
43
|
+
echo "7) Hello world" >> /tmp/my-echo
|
44
|
+
#+end_src
|
45
|
+
|
46
|
+
- And send something to babel once again
|
47
|
+
|
48
|
+
#+begin_src conf :tangle /tmp/hello-conf.yml
|
49
|
+
status_port: 9090
|
50
|
+
no_tab_after_that: true
|
51
|
+
#+end_src
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Class with util methods that try to reimplement some of
|
3
|
+
# the functionality provided by Org Babel from Emacs
|
4
|
+
#
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Orgmode
|
8
|
+
class Babel
|
9
|
+
attr_reader :ob # Babel Output Buffer with parsed contents
|
10
|
+
attr_reader :logger
|
11
|
+
|
12
|
+
def initialize(babel_output_buffer, options={})
|
13
|
+
@ob = babel_output_buffer
|
14
|
+
@options = options
|
15
|
+
@logger = options[:logger] || Logger.new(STDOUT)
|
16
|
+
@root_dir = options[:root_dir]
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: should be able to tangle relatively to a dir
|
20
|
+
def tangle!
|
21
|
+
logger.info "Tangling #{ob.tangle.keys.count} files..."
|
22
|
+
|
23
|
+
ob.tangle.each do |tangle_file, lines|
|
24
|
+
file = if @root_dir
|
25
|
+
File.join(@root_dir, tangle_file)
|
26
|
+
else
|
27
|
+
tangle_file
|
28
|
+
end
|
29
|
+
|
30
|
+
logger.info "BEGIN(#{tangle_file}): Tangling #{lines.count} lines at '#{file}'"
|
31
|
+
# TODO: should abort when the directory does not exists
|
32
|
+
# TODO: should abort when the directory failed because of permissions
|
33
|
+
if not Dir.exists?(File.dirname(file))
|
34
|
+
logger.error "Could not tangle #{file} because directory does not exists!"
|
35
|
+
raise TangleError
|
36
|
+
end
|
37
|
+
|
38
|
+
if File.exists?(file)
|
39
|
+
logger.warn "File already exists at #{file}, it will be overwritten"
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
File.open(file, 'w') do |f|
|
44
|
+
lines.each do |line|
|
45
|
+
f.puts line
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue => e
|
49
|
+
logger.error "Problem while writing to '#{file}': #{e}"
|
50
|
+
raise TangleError
|
51
|
+
end
|
52
|
+
logger.info "END(#{file}): done."
|
53
|
+
end
|
54
|
+
|
55
|
+
logger.info "Tangling succeeded!".green
|
56
|
+
end
|
57
|
+
|
58
|
+
class TangleError < Exception; end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Orgmode
|
2
|
+
class BabelOutputBuffer < OutputBuffer
|
3
|
+
attr_reader :tangle
|
4
|
+
attr_reader :scripts
|
5
|
+
|
6
|
+
def initialize(output)
|
7
|
+
super(output)
|
8
|
+
|
9
|
+
# ~@tangle~ files are put in the right path
|
10
|
+
# : @tangle['/path'] = [Lines]
|
11
|
+
@tangle = Hash.new {|h,k| h[k] = []}
|
12
|
+
|
13
|
+
# ~@scripts~ are tangled in order and ran
|
14
|
+
# : @scripts = [text, text, ...]
|
15
|
+
@scripts = []
|
16
|
+
|
17
|
+
@buffer = ''
|
18
|
+
end
|
19
|
+
|
20
|
+
def push_mode(mode, indent)
|
21
|
+
super(mode, indent)
|
22
|
+
end
|
23
|
+
|
24
|
+
def pop_mode(mode = nil)
|
25
|
+
m = super(mode)
|
26
|
+
@list_indent_stack.pop
|
27
|
+
m
|
28
|
+
end
|
29
|
+
|
30
|
+
def insert(line)
|
31
|
+
# We try to get the lang from #+BEGIN_SRC blocks
|
32
|
+
if line.begin_block?
|
33
|
+
@block_lang = line.block_lang
|
34
|
+
if line.block_header_arguments[':tangle']
|
35
|
+
@current_tangle = line.block_header_arguments[':tangle']
|
36
|
+
else
|
37
|
+
@shebang = line.block_header_arguments[':shebang']
|
38
|
+
@current_tangle = nil
|
39
|
+
@buffer = ''
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
case
|
44
|
+
when (line.assigned_paragraph_type == :code and @current_tangle)
|
45
|
+
# Need to keep track of the current tangle to buffer its lines
|
46
|
+
@tangle[@current_tangle] << line
|
47
|
+
when (line.assigned_paragraph_type == :code)
|
48
|
+
# When a tangle is not going on, it means that the lines would go
|
49
|
+
# into a runnable script
|
50
|
+
@buffer << line.output_text << "\n"
|
51
|
+
when (!@buffer.empty? and not (line.begin_block? or line.assigned_paragraph_type == :code))
|
52
|
+
# Fix indentation and remove pre fix commas from Org mode before flushing
|
53
|
+
strip_code_block!
|
54
|
+
@scripts << @buffer
|
55
|
+
@buffer = ''
|
56
|
+
end
|
57
|
+
|
58
|
+
@output_type = line.assigned_paragraph_type || line.paragraph_type
|
59
|
+
end
|
60
|
+
|
61
|
+
# Flushing is a bit different since we still need the metadata
|
62
|
+
# from lines in order to flush to correct tangle buffer
|
63
|
+
# TODO: Should be in the parent class as well...
|
64
|
+
def flush!; false; end
|
65
|
+
|
66
|
+
# TODO: This should be in the parent class....
|
67
|
+
def output_footnotes!; false; end
|
68
|
+
|
69
|
+
def strip_code_block!
|
70
|
+
if @code_block_indent and @code_block_indent > 0
|
71
|
+
strip_regexp = Regexp.new("^" + " " * @code_block_indent)
|
72
|
+
@buffer.gsub!(strip_regexp, "")
|
73
|
+
end
|
74
|
+
@code_block_indent = nil
|
75
|
+
|
76
|
+
# Strip proctective commas generated by Org mode (C-c ')
|
77
|
+
@buffer.gsub! /^(\s*)(,)(\s*)([*]|#\+)/ do |match|
|
78
|
+
"#{$1}#{$3}#{$4}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module OrgConverge
|
2
|
+
class Command
|
3
|
+
attr_reader :dotorg
|
4
|
+
attr_reader :logger
|
5
|
+
attr_reader :ob
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
@dotorg = options['<org_file>']
|
10
|
+
@logger = Logger.new(options['--log'] || STDOUT)
|
11
|
+
@root_dir = options['--root-dir']
|
12
|
+
@ob = Orgmode::Parser.new(File.read(dotorg)).babelize
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute!
|
16
|
+
case
|
17
|
+
when @options['--showfiles']
|
18
|
+
|
19
|
+
showfiles
|
20
|
+
when @options['--tangle']
|
21
|
+
tangle!
|
22
|
+
else
|
23
|
+
converge!
|
24
|
+
end
|
25
|
+
|
26
|
+
true
|
27
|
+
rescue => e
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def converge!
|
32
|
+
tangle!
|
33
|
+
end
|
34
|
+
|
35
|
+
def tangle!
|
36
|
+
begin
|
37
|
+
babel = Orgmode::Babel.new(ob, { :logger => logger, :root_dir => @root_dir })
|
38
|
+
results = babel.tangle!
|
39
|
+
rescue Orgmode::Babel::TangleError
|
40
|
+
logger.error "Cannot converge because there were errors during tangle step".red
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def showfiles
|
45
|
+
ob.tangle.each do |file, lines|
|
46
|
+
puts "---------- #{file} --------------".green
|
47
|
+
lines.each do |line|
|
48
|
+
puts line
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ob.scripts.each_with_index do |script, index|
|
53
|
+
puts "---------- script: #{index} --------------".green
|
54
|
+
puts script
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/org-converge.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'org-ruby'
|
2
|
+
require 'org-converge/babel_output_buffer'
|
3
|
+
require 'org-converge/babel'
|
4
|
+
require 'org-converge/command'
|
5
|
+
require 'org-converge/version'
|
6
|
+
|
7
|
+
module Orgmode
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
# This would return a babel output buffer which has the methods
|
11
|
+
# needed in order to be able to tangle the files
|
12
|
+
def babelize
|
13
|
+
# Feed the parsed contens and create the necessary internal structures
|
14
|
+
# for doing babel like features
|
15
|
+
output = ''
|
16
|
+
ob = BabelOutputBuffer.new(output)
|
17
|
+
translate(@header_lines, ob)
|
18
|
+
@headlines.each do |headline|
|
19
|
+
translate(headline.body_lines, ob)
|
20
|
+
end
|
21
|
+
|
22
|
+
ob
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module StringWithColors
|
28
|
+
def red; colorize("\e[0m\e[31m"); end
|
29
|
+
def green; colorize("\e[0m\e[32m"); end
|
30
|
+
def yellow; colorize("\e[0m\e[33m"); end
|
31
|
+
def bold; colorize("\e[0m\e[1m"); end
|
32
|
+
def colorize(color_code); "#{color_code}#{self}\e[0m"; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class String
|
36
|
+
include StringWithColors
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/org-converge/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Waldemar Quevedo"]
|
6
|
+
gem.email = ["waldemar.quevedo@gmail.com"]
|
7
|
+
gem.description = %q{A light configuration management tool for Org mode}
|
8
|
+
gem.summary = %q{Provides an 'org-converge' command which can be used for tangling and running Org mode code blocks}
|
9
|
+
gem.homepage = "https://github.com/wallyqs/org-converge"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "org-converge"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = OrgConverge::VERSION
|
17
|
+
gem.add_runtime_dependency('docopt', '0.5.0')
|
18
|
+
gem.add_runtime_dependency('org-ruby', '~> 0.9.2')
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#+TITLE: Relative Tangle
|
2
|
+
|
3
|
+
In this test, tangles files will exist relative to this file.
|
4
|
+
|
5
|
+
#+begin_src yaml :tangle conf.yml
|
6
|
+
bind: 0.0.0.0
|
7
|
+
port: 2442
|
8
|
+
#+end_src
|
9
|
+
|
10
|
+
e.g. including the connections to the database in the same file:
|
11
|
+
|
12
|
+
#+begin_src yaml :tangle conf.yml
|
13
|
+
|
14
|
+
mysql:
|
15
|
+
db: users
|
16
|
+
host: somewhere-example.local
|
17
|
+
password: 111111111
|
18
|
+
#+end_src
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OrgConverge::Command do
|
4
|
+
context "when converging 'basic_tangle'" do
|
5
|
+
example_dir = File.join(EXAMPLES_DIR, 'basic_tangle')
|
6
|
+
|
7
|
+
it "should tangle a 'conf.yml' file" do
|
8
|
+
setup_file = File.join(example_dir, 'setup.org')
|
9
|
+
o = OrgConverge::Command.new({
|
10
|
+
'<org_file>' => setup_file,
|
11
|
+
'--root-dir' => example_dir
|
12
|
+
})
|
13
|
+
success = o.execute!
|
14
|
+
success.should == true
|
15
|
+
|
16
|
+
expected_contents = File.read(File.join(example_dir, 'conf.yml.expected'))
|
17
|
+
|
18
|
+
resulting_file = File.join(example_dir, 'conf.yml')
|
19
|
+
File.exists?(resulting_file).should == true
|
20
|
+
|
21
|
+
result = File.read(resulting_file)
|
22
|
+
|
23
|
+
result.should == expected_contents
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: org-converge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Waldemar Quevedo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: docopt
|
16
|
+
requirement: &70317759431040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.5.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70317759431040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: org-ruby
|
27
|
+
requirement: &70317759430520 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.9.2
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70317759430520
|
36
|
+
description: A light configuration management tool for Org mode
|
37
|
+
email:
|
38
|
+
- waldemar.quevedo@gmail.com
|
39
|
+
executables:
|
40
|
+
- org-converge
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitmodules
|
45
|
+
- Gemfile
|
46
|
+
- LICENSE
|
47
|
+
- README.org
|
48
|
+
- Rakefile
|
49
|
+
- TODO
|
50
|
+
- bin/org-converge
|
51
|
+
- examples/apt-get-install/setup.org
|
52
|
+
- examples/fluentd/setup.org
|
53
|
+
- examples/macro-config/setup.org
|
54
|
+
- examples/simple/setup.org
|
55
|
+
- lib/org-converge.rb
|
56
|
+
- lib/org-converge/babel.rb
|
57
|
+
- lib/org-converge/babel_output_buffer.rb
|
58
|
+
- lib/org-converge/command.rb
|
59
|
+
- lib/org-converge/version.rb
|
60
|
+
- org-converge.gemspec
|
61
|
+
- spec/converge_examples/basic_tangle/conf.yml.expected
|
62
|
+
- spec/converge_examples/basic_tangle/setup.org
|
63
|
+
- spec/converge_spec.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
homepage: https://github.com/wallyqs/org-converge
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.10
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Provides an 'org-converge' command which can be used for tangling and running
|
89
|
+
Org mode code blocks
|
90
|
+
test_files:
|
91
|
+
- spec/converge_examples/basic_tangle/conf.yml.expected
|
92
|
+
- spec/converge_examples/basic_tangle/setup.org
|
93
|
+
- spec/converge_spec.rb
|
94
|
+
- spec/spec_helper.rb
|