debianization and a wrapper program for /usr/bin
This commit is contained in:
parent
167609746d
commit
42f207232b
27
Makefile
27
Makefile
|
@ -1,20 +1,30 @@
|
||||||
run: build
|
CABAL?=cabal
|
||||||
|
|
||||||
|
run: deps build
|
||||||
./propellor
|
./propellor
|
||||||
|
|
||||||
dev: build tags
|
dev: build tags
|
||||||
|
|
||||||
build: deps dist/setup-config
|
build: dist/setup-config
|
||||||
if ! cabal build; then cabal configure; cabal build; fi
|
if ! $(CABAL) build; then $(CABAL) configure; $(CABAL) build; fi
|
||||||
ln -sf dist/build/propellor/propellor
|
ln -sf dist/build/config/config propellor
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
@if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-async-dev; fi || true
|
@if [ $$(whoami) = root ]; then apt-get -y install gnupg ghc cabal-install libghc-missingh-dev libghc-ansi-terminal-dev libghc-ifelse-dev libghc-unix-compat-dev libghc-hslogger-dev libghc-network-dev libghc-async-dev; fi || true
|
||||||
|
|
||||||
dist/setup-config: propellor.cabal
|
dist/setup-config: propellor.cabal
|
||||||
cabal configure
|
if [ "$(CABAL)" = ./Setup ]; then ghc --make Setup; fi
|
||||||
|
$(CABAL) configure
|
||||||
|
|
||||||
|
install:
|
||||||
|
install -d $(DESTDIR)/usr/bin $(DESTDIR)/usr/src/propellor
|
||||||
|
install -s dist/build/propellor/propellor $(DESTDIR)/usr/bin
|
||||||
|
$(CABAL) sdist
|
||||||
|
cat dist/propellor-*.tar.gz | \
|
||||||
|
(cd $(DESTDIR)/usr/src/propellor && tar zx --strip-components=1)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf dist Setup tags propellor privdata/local
|
rm -rf dist Setup tags propellor propellor-wrapper privdata/local
|
||||||
find -name \*.o -exec rm {} \;
|
find -name \*.o -exec rm {} \;
|
||||||
find -name \*.hi -exec rm {} \;
|
find -name \*.hi -exec rm {} \;
|
||||||
|
|
||||||
|
@ -22,4 +32,9 @@ clean:
|
||||||
tags:
|
tags:
|
||||||
find . | grep -v /.git/ | grep -v /tmp/ | grep -v /dist/ | grep -v /doc/ | egrep '\.hs$$' | xargs hothasktags > tags 2>/dev/null
|
find . | grep -v /.git/ | grep -v /tmp/ | grep -v /dist/ | grep -v /doc/ | egrep '\.hs$$' | xargs hothasktags > tags 2>/dev/null
|
||||||
|
|
||||||
|
# Upload to hackage.
|
||||||
|
hackage:
|
||||||
|
@cabal sdist
|
||||||
|
@cabal upload dist/*.tar.gz
|
||||||
|
|
||||||
.PHONY: tags
|
.PHONY: tags
|
||||||
|
|
|
@ -74,9 +74,10 @@ defaultMain getprops = do
|
||||||
headMaybe $ catMaybes $ map (\get -> get host) getprops
|
headMaybe $ catMaybes $ map (\get -> get host) getprops
|
||||||
|
|
||||||
unknownhost :: HostName -> IO a
|
unknownhost :: HostName -> IO a
|
||||||
unknownhost h = errorMessage $ unwords
|
unknownhost h = errorMessage $ unlines
|
||||||
[ "Unknown host:", h
|
[ "Unknown host: " ++ h
|
||||||
, "(perhaps you should specify the real hostname on the command line?)"
|
, "(Perhaps you should specify the real hostname on the command line?)"
|
||||||
|
, "(Or, edit propellor's config.hs to configure this host)"
|
||||||
]
|
]
|
||||||
|
|
||||||
buildFirst :: CmdLine -> IO () -> IO ()
|
buildFirst :: CmdLine -> IO () -> IO ()
|
||||||
|
|
70
README.md
70
README.md
|
@ -1,18 +1,18 @@
|
||||||
This is a work in progress configuration management system using Haskell
|
This is a configuration management system using Haskell and Git.
|
||||||
and Git.
|
|
||||||
|
|
||||||
Propellor enures that the system it's run in satisfies a list of
|
Propellor enures that the system it's run against satisfies a list of
|
||||||
properties, taking action as necessary when a property is not yet met.
|
properties, taking action as necessary when a property is not yet met.
|
||||||
|
|
||||||
The design is intentionally very minimal.
|
Propellor is configured via a git repository, which typically lives
|
||||||
|
in ~/.propellor/. The git repository contains a config.hs file,
|
||||||
|
and also the entire source code to propellor.
|
||||||
|
|
||||||
Propellor lives in a git repository. You'll typically want to have
|
You typically want to have the repository checked out on a laptop, in order
|
||||||
the repository checked out on a laptop, in order to make changes and push
|
to make changes and push them out to hosts. Each host will also have a
|
||||||
them out to hosts. Each host will also have a clone of the repository,
|
clone of the repository, and in that clone "make" can be used to build and
|
||||||
and in that clone "make" can be used to build and run propellor.
|
run propellor. This can be done by a cron job (which propellor can set up),
|
||||||
This can be done by a cron job (which propellor can set up),
|
or a remote host can be triggered to update by running propellor on your
|
||||||
or a remote host can be triggered to update by running propellor
|
laptop: propellor --spin $host
|
||||||
on your laptop: propellor --spin $host
|
|
||||||
|
|
||||||
Properties are defined using Haskell. Edit config.hs to get started.
|
Properties are defined using Haskell. Edit config.hs to get started.
|
||||||
|
|
||||||
|
@ -28,35 +28,41 @@ and so it's easy to factor out things like classes of hosts as desired.
|
||||||
|
|
||||||
## quick start
|
## quick start
|
||||||
|
|
||||||
1. Clone propellor's git repository to your laptop (or whatever).
|
1. Get propellor installed
|
||||||
git clone git://git.kitenet.net/propellor
|
`cabal install propellor`
|
||||||
or joeyh/propellor on github
|
or
|
||||||
2. Run: sudo make deps # installs build dependencies
|
`apt-get install propellor`
|
||||||
3. Run: make build
|
2. Run propellor for the first time. It will set up a `~/.propellor/` git
|
||||||
4. If you don't have a gpg private key, generate one: gpg --gen-key
|
repository for you.
|
||||||
5. Run: ./propellor --add-key $KEYID
|
3. In `~/.propellor/`, use git to push the repository to a central
|
||||||
7. Pick a host and run: ./propellor --spin $HOST
|
server (github, or your own git server). Configure that central
|
||||||
8. Now you have a simple propellor deployment, but it doesn't do anything
|
server as the origin remote of the repository.
|
||||||
to the host yet, besides installing propellor.
|
4. If you don't have a gpg private key, generate one: `gpg --gen-key`
|
||||||
|
5. Run: `propellor --add-key $KEYID`
|
||||||
|
6. Edit `~/.propellor/config.hs`, and add a host you want to manage.
|
||||||
|
You can start by not adding any properties, or only a few.
|
||||||
|
7. Pick a host and run: `propellor --spin $HOST`
|
||||||
|
8. Now you have a simple propellor deployment, but it doesn't do
|
||||||
|
much to the host yet, besides installing propellor.
|
||||||
|
|
||||||
So, edit config.hs to configure the host (maybe start with a few simple
|
So, edit `~/.propellor/config.hs` to configure the host (maybe
|
||||||
properties), and re-run step 7. Repeat until happy and move on to the
|
start with a few simple properties), and re-run step 7.
|
||||||
next host. :)
|
Repeat until happy and move on to the next host. :)
|
||||||
9. To move beyond manually running propellor --spin against hosts
|
9. To move beyond manually running propellor --spin against hosts
|
||||||
when you change configuration, add a property to your hosts
|
when you change configuration, add a property to your hosts
|
||||||
like: Cron.runPropellor "30 * * * *"
|
like: `Cron.runPropellor "30 * * * *"`
|
||||||
|
|
||||||
Now they'll automatically update every 30 minutes, and you can
|
Now they'll automatically update every 30 minutes, and you can
|
||||||
`git commit -S` and `git push` changes that affect any number of
|
`git commit -S` and `git push` changes that affect any number of
|
||||||
hosts.
|
hosts.
|
||||||
10. Write some neat new properties and send patches to propellor@joeyh.name!
|
8. Write some neat new properties and send patches to <propellor@joeyh.name>!
|
||||||
|
|
||||||
## security
|
## security
|
||||||
|
|
||||||
Propellor's security model is that the hosts it's used to deploy are
|
Propellor's security model is that the hosts it's used to deploy are
|
||||||
untrusted, and that the central git repository server is untrusted.
|
untrusted, and that the central git repository server is untrusted.
|
||||||
|
|
||||||
The only trusted machine is the laptop where you run propellor --spin
|
The only trusted machine is the laptop where you run `propellor --spin`
|
||||||
to connect to a remote host. And that one only because you have a ssh key
|
to connect to a remote host. And that one only because you have a ssh key
|
||||||
or login password to the host.
|
or login password to the host.
|
||||||
|
|
||||||
|
@ -73,7 +79,7 @@ That is only done when privdata/keyring.gpg exists. To set it up:
|
||||||
gpg --gen-key # only if you don't already have a gpg key
|
gpg --gen-key # only if you don't already have a gpg key
|
||||||
propellor --add-key $MYKEYID
|
propellor --add-key $MYKEYID
|
||||||
|
|
||||||
In order to be secure from the beginning, when propellor --spin is used
|
In order to be secure from the beginning, when `propellor --spin` is used
|
||||||
to bootstrap propellor on a new host, it transfers the local git repositry
|
to bootstrap propellor on a new host, it transfers the local git repositry
|
||||||
to the remote host over ssh. After that, the remote host knows the
|
to the remote host over ssh. After that, the remote host knows the
|
||||||
gpg key, and will use it to verify git fetches.
|
gpg key, and will use it to verify git fetches.
|
||||||
|
@ -81,17 +87,17 @@ gpg key, and will use it to verify git fetches.
|
||||||
Since the propoellor git repository is public, you can't store
|
Since the propoellor git repository is public, you can't store
|
||||||
in cleartext private data such as passwords, ssh private keys, etc.
|
in cleartext private data such as passwords, ssh private keys, etc.
|
||||||
|
|
||||||
Instead, propellor --spin $host looks for a privdata/$host.gpg file and
|
Instead, `propellor --spin $host` looks for a `~/.propellor/privdata/$host.gpg` file and
|
||||||
if found decrypts it and sends it to the remote host using ssh. This lets
|
if found decrypts it and sends it to the remote host using ssh. This lets
|
||||||
a remote host know its own private data, without seeing all the rest.
|
a remote host know its own private data, without seeing all the rest.
|
||||||
|
|
||||||
To securely store private data, use: propellor --set $host $field
|
To securely store private data, use: `propellor --set $host $field`
|
||||||
The field name will be something like 'Password "root"'; see PrivData.hs
|
The field name will be something like 'Password "root"'; see PrivData.hs
|
||||||
for available fields.
|
for available fields.
|
||||||
|
|
||||||
## debugging
|
## debugging
|
||||||
|
|
||||||
Set PROPELLOR_DEBUG=1 to make propellor print out all the commands it runs
|
Set `PROPELLOR_DEBUG=1` to make propellor print out all the commands it runs
|
||||||
and any other debug messages Properties choose to emit.
|
and any other debug messages that Properties choose to emit.
|
||||||
|
|
||||||
[1] http://reclass.pantsfullofunix.net/
|
[1] http://reclass.pantsfullofunix.net/
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
propellor (0.2) unstable; urgency=low
|
propellor (0.2.1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* First release with Debian package.
|
||||||
|
|
||||||
|
-- Joey Hess <joeyh@debian.org> Thu, 03 Apr 2014 01:43:14 -0400
|
||||||
|
|
||||||
|
propellor (0.2.0) unstable; urgency=low
|
||||||
|
|
||||||
* Added support for provisioning Docker containers.
|
* Added support for provisioning Docker containers.
|
||||||
* Bootstrap deployment now pushes the git repo to the remote host
|
* Bootstrap deployment now pushes the git repo to the remote host
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
9
|
|
@ -0,0 +1,36 @@
|
||||||
|
Source: propellor
|
||||||
|
Section: admin
|
||||||
|
Priority: optional
|
||||||
|
Build-Depends:
|
||||||
|
debhelper (>= 9),
|
||||||
|
ghc (>= 7.4),
|
||||||
|
cabal-install,
|
||||||
|
libghc-async-dev,
|
||||||
|
libghc-missingh-dev,
|
||||||
|
libghc-hslogger-dev,
|
||||||
|
libghc-unix-compat-dev,
|
||||||
|
libghc-ansi-terminal-dev,
|
||||||
|
libghc-ifelse-dev,
|
||||||
|
Maintainer: Joey Hess <joeyh@debian.org>
|
||||||
|
Standards-Version: 3.9.5
|
||||||
|
Vcs-Git: git://git.kitenet.net/propellor
|
||||||
|
Homepage: http://joeyh.name/code/propellor/
|
||||||
|
|
||||||
|
Package: propellor
|
||||||
|
Architecture: any
|
||||||
|
Section: admin
|
||||||
|
Depends: ${misc:Depends}, ${shlibs:Depends},
|
||||||
|
ghc (>= 7.4),
|
||||||
|
cabal-install,
|
||||||
|
libghc-async-dev,
|
||||||
|
libghc-missingh-dev,
|
||||||
|
libghc-hslogger-dev,
|
||||||
|
libghc-unix-compat-dev,
|
||||||
|
libghc-ansi-terminal-dev,
|
||||||
|
libghc-ifelse-dev,
|
||||||
|
git,
|
||||||
|
Description: property-based host configuration management in haskell
|
||||||
|
Propellor enures that the system it's run in satisfies a list of
|
||||||
|
properties, taking action as necessary when a property is not yet met.
|
||||||
|
.
|
||||||
|
It is configured using haskell.
|
|
@ -0,0 +1,11 @@
|
||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Source: native package
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: © 2010-2014 Joey Hess <joey@kitenet.net>
|
||||||
|
License: GPL-3+
|
||||||
|
|
||||||
|
License: GPL-3+
|
||||||
|
The full text of version 3 of the GPL is distributed as GPL in
|
||||||
|
this package's source, or in /usr/share/common-licenses/GPL-3 on
|
||||||
|
Debian systems.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# These files are used in a git repository that propellor sets up.
|
||||||
|
propellor: package-contains-vcs-control-file usr/src/propellor/.gitignore
|
||||||
|
propellor: extra-license-file usr/src/propellor/GPL
|
|
@ -0,0 +1,15 @@
|
||||||
|
.\" -*- nroff -*-
|
||||||
|
.TH propellor 1 "Commands"
|
||||||
|
.SH NAME
|
||||||
|
propellor \- property-based host configuration management in haskell
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B propellor [options] host
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I propellor
|
||||||
|
is a property-based host configuration management program written
|
||||||
|
and configured in haskell.
|
||||||
|
.PP
|
||||||
|
The first time you run propellor, it will set up a ~/.propellor/
|
||||||
|
repository. Edit ~/.propellor/config.hs to configure it.
|
||||||
|
.SH AUTHOR
|
||||||
|
Joey Hess <joey@kitenet.net>
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
# Avoid using cabal, as it writes to $HOME
|
||||||
|
export CABAL=./Setup
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
$(MAKE) build
|
||||||
|
override_dh_installdocs:
|
||||||
|
dh_installdocs README.md TODO
|
||||||
|
override_dh_installman:
|
||||||
|
dh_installman debian/propellor.1
|
|
@ -1,5 +1,5 @@
|
||||||
Name: propellor
|
Name: propellor
|
||||||
Version: 0.2.0
|
Version: 0.2.1
|
||||||
Cabal-Version: >= 1.6
|
Cabal-Version: >= 1.6
|
||||||
License: GPL
|
License: GPL
|
||||||
Maintainer: Joey Hess <joey@kitenet.net>
|
Maintainer: Joey Hess <joey@kitenet.net>
|
||||||
|
@ -14,20 +14,35 @@ Extra-Source-Files:
|
||||||
README.md
|
README.md
|
||||||
TODO
|
TODO
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
simple-config.hs
|
||||||
Makefile
|
Makefile
|
||||||
debian/changelog
|
debian/changelog
|
||||||
debian/README
|
debian/README.Debian
|
||||||
simple-config.hs
|
debian/propellor.1
|
||||||
|
debian/compat
|
||||||
|
debian/control
|
||||||
|
debian/copyright
|
||||||
|
debian/rules
|
||||||
|
debian/lintian-overrides
|
||||||
|
.gitignore
|
||||||
Synopsis: property-based host configuration management in haskell
|
Synopsis: property-based host configuration management in haskell
|
||||||
Description:
|
Description:
|
||||||
Propellor enures that the system it's run in satisfies a list of
|
Propellor enures that the system it's run in satisfies a list of
|
||||||
properties, taking action as necessary when a property is not yet met.
|
properties, taking action as necessary when a property is not yet met.
|
||||||
.
|
.
|
||||||
While Propellor can be installed from hackage, to customize and use it
|
It is configured using haskell.
|
||||||
you should fork its git repository and modify it from there:
|
|
||||||
git clone git://git.kitenet.net/propellor
|
|
||||||
|
|
||||||
Executable propellor
|
Executable propellor
|
||||||
|
Main-Is: propellor.hs
|
||||||
|
GHC-Options: -Wall
|
||||||
|
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
||||||
|
IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal,
|
||||||
|
containers, network, async
|
||||||
|
|
||||||
|
if (! os(windows))
|
||||||
|
Build-Depends: unix
|
||||||
|
|
||||||
|
Executable config
|
||||||
Main-Is: config.hs
|
Main-Is: config.hs
|
||||||
GHC-Options: -Wall -threaded
|
GHC-Options: -Wall -threaded
|
||||||
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
||||||
|
@ -38,7 +53,7 @@ Executable propellor
|
||||||
Build-Depends: unix
|
Build-Depends: unix
|
||||||
|
|
||||||
Library
|
Library
|
||||||
GHC-Options: -Wall -threaded
|
GHC-Options: -Wall
|
||||||
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
Build-Depends: MissingH, directory, filepath, base >= 4.5, base < 5,
|
||||||
IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal,
|
IfElse, process, bytestring, hslogger, unix-compat, ansi-terminal,
|
||||||
containers, network, async
|
containers, network, async
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
-- | Wrapper program for propellor distribution.
|
||||||
|
--
|
||||||
|
-- Distributions should install this program into PATH.
|
||||||
|
-- (Cabal builds it as dict/build/propellor.
|
||||||
|
--
|
||||||
|
-- This is not the propellor main program (that's config.hs)
|
||||||
|
--
|
||||||
|
-- This installs propellor's source into ~/.propellor,
|
||||||
|
-- uses it to build the real propellor program (if not already built),
|
||||||
|
-- and runs it.
|
||||||
|
--
|
||||||
|
-- The source is either copied from /usr/src/propellor, or is cloned from
|
||||||
|
-- git over the network.
|
||||||
|
|
||||||
|
import Utility.UserInfo
|
||||||
|
import Utility.Monad
|
||||||
|
import Utility.Process
|
||||||
|
import Utility.SafeCommand
|
||||||
|
import Utility.Directory
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IfElse
|
||||||
|
import System.Directory
|
||||||
|
import System.FilePath
|
||||||
|
import System.Environment (getArgs)
|
||||||
|
import System.Exit
|
||||||
|
import System.Posix.Directory
|
||||||
|
|
||||||
|
srcdir :: FilePath
|
||||||
|
srcdir = "/usr/src/propellor"
|
||||||
|
|
||||||
|
-- Using the github mirror of the main propellor repo because
|
||||||
|
-- it is accessible over https for better security.
|
||||||
|
srcrepo :: String
|
||||||
|
srcrepo = "https://github.com/joeyh/propellor.git"
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
args <- getArgs
|
||||||
|
home <- myHomeDir
|
||||||
|
let propellordir = home </> ".propellor"
|
||||||
|
let propellorbin = propellordir </> "propellor"
|
||||||
|
wrapper args propellordir propellorbin
|
||||||
|
|
||||||
|
wrapper :: [String] -> FilePath -> FilePath -> IO ()
|
||||||
|
wrapper args propellordir propellorbin = do
|
||||||
|
unlessM (doesDirectoryExist propellordir) $
|
||||||
|
makeRepo
|
||||||
|
buildruncfg
|
||||||
|
where
|
||||||
|
chain = do
|
||||||
|
(_, _, _, pid) <- createProcess (proc propellorbin args)
|
||||||
|
exitWith =<< waitForProcess pid
|
||||||
|
makeRepo = do
|
||||||
|
putStrLn $ "Setting up your propellor repo in " ++ propellordir
|
||||||
|
putStrLn ""
|
||||||
|
ifM (doesDirectoryExist srcdir)
|
||||||
|
( do
|
||||||
|
void $ boolSystem "cp" [Param "-a", File srcdir, File propellordir]
|
||||||
|
changeWorkingDirectory propellordir
|
||||||
|
void $ boolSystem "git" [Param "init"]
|
||||||
|
void $ boolSystem "git" [Param "add", Param "."]
|
||||||
|
setuprepo True
|
||||||
|
, do
|
||||||
|
void $ boolSystem "git" [Param "clone", Param srcrepo, File propellordir]
|
||||||
|
void $ boolSystem "git" [Param "remote", Param "rm", Param "origin"]
|
||||||
|
setuprepo False
|
||||||
|
)
|
||||||
|
setuprepo fromsrcdir = do
|
||||||
|
changeWorkingDirectory propellordir
|
||||||
|
whenM (doesDirectoryExist "privdata") $
|
||||||
|
mapM_ nukeFile =<< dirContents "privdata"
|
||||||
|
void $ boolSystem "git" [Param "commit", Param "--allow-empty", Param "--quiet", Param "-m", Param "setting up propellor git repository"]
|
||||||
|
void $ boolSystem "git" [Param "remote", Param "add", Param "upstream", Param srcrepo]
|
||||||
|
-- Connect synthetic git repo with upstream history so
|
||||||
|
-- merging with upstream will work going forward.
|
||||||
|
-- Note -s outs is used to avoid getting any divergent
|
||||||
|
-- changes from upstream.
|
||||||
|
when fromsrcdir $ do
|
||||||
|
void $ boolSystem "git" [Param "fetch", Param "upstream"]
|
||||||
|
version <- readProcess "dpkg-query" ["--showformat", "${Version}", "--show", "propellor"]
|
||||||
|
void $ boolSystem "git" [Param "merge", Param "-s", Param "ours", Param version]
|
||||||
|
buildruncfg = do
|
||||||
|
changeWorkingDirectory propellordir
|
||||||
|
ifM (boolSystem "make" [Param "build"])
|
||||||
|
( do
|
||||||
|
putStrLn ""
|
||||||
|
putStrLn ""
|
||||||
|
chain
|
||||||
|
, error "Propellor build failed."
|
||||||
|
)
|
Loading…
Reference in New Issue