org-converge 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|