diff options
author | Andrew Cady <d@jerkface.net> | 2018-07-09 18:41:36 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2018-07-09 19:29:37 -0400 |
commit | db279470ddaafcafd9e9c28ef41f2eab1a904ead (patch) | |
tree | a86b102820fd9b70469a6f120a23a6b85c7380cd | |
parent | 31aa1e527dd9439f4c74a9c88347806e4d65f94e (diff) |
werks purty good
-rw-r--r-- | fsmgr.hs | 66 |
1 files changed, 37 insertions, 29 deletions
@@ -1,4 +1,4 @@ | |||
1 | {-# LANGUAGE ScopedTypeVariables #-} | 1 | {-# LANGUAGE ScopedTypeVariables #-} |
2 | {-# OPTIONS_GHC -fno-warn-type-defaults #-} | 2 | {-# OPTIONS_GHC -fno-warn-type-defaults #-} |
3 | {-# LANGUAGE ExtendedDefaultRules #-} | 3 | {-# LANGUAGE ExtendedDefaultRules #-} |
4 | {-# LANGUAGE InstanceSigs #-} | 4 | {-# LANGUAGE InstanceSigs #-} |
@@ -14,14 +14,14 @@ import Rebase.Prelude hiding (bool, hash, (<.>)) | |||
14 | import Crypto.Hash | 14 | import Crypto.Hash |
15 | import Crypto.Hash.Types.Digest.Read () | 15 | import Crypto.Hash.Types.Digest.Read () |
16 | import Data.Yaml.Combinators | 16 | import Data.Yaml.Combinators |
17 | import qualified Rebase.Data.Set as Set | ||
18 | import qualified Rebase.Data.Vector as Vector | ||
19 | |||
20 | import Development.Shake | 17 | import Development.Shake |
21 | import Development.Shake.Command() | 18 | import Development.Shake.Command () |
22 | import Development.Shake.FilePath | 19 | import Development.Shake.FilePath |
23 | import Rebase.Data.Text (pack, unpack) | 20 | import qualified Rebase.Data.Set as Set |
21 | import qualified Rebase.Data.Text as Text | ||
22 | ;import Rebase.Data.Text (pack, unpack) | ||
24 | import Rebase.Data.Text.Encoding | 23 | import Rebase.Data.Text.Encoding |
24 | import qualified Rebase.Data.Vector as Vector | ||
25 | {- | 25 | {- |
26 | 26 | ||
27 | Basic idea is to have a fs.yaml that specifies the build procedure. Analogous to | 27 | Basic idea is to have a fs.yaml that specifies the build procedure. Analogous to |
@@ -56,11 +56,11 @@ data BaseImageSpecification | |||
56 | deriving (Show, Read) | 56 | deriving (Show, Read) |
57 | 57 | ||
58 | data DiskImageConfig = DiskImageConfig { | 58 | data DiskImageConfig = DiskImageConfig { |
59 | initialImage :: BaseImageSpecification | 59 | initialImage :: BaseImageSpecification |
60 | , packages :: Set Package | 60 | , packages :: Set Package |
61 | -- , debconfConfig :: DebconfConfig | 61 | -- , debconfConfig :: DebconfConfig |
62 | , unpackOnly :: Bool | 62 | , unpackOnly :: Bool |
63 | -- , patched :: Vector Text -- :: [Patch] | 63 | , binaries :: Vector Text -- :: [Patch] |
64 | } deriving (Show, Read) | 64 | } deriving (Show, Read) |
65 | 65 | ||
66 | parsePackageName :: Text -> Package | 66 | parsePackageName :: Text -> Package |
@@ -70,10 +70,10 @@ diskImageConfigParser :: Parser DiskImageConfig | |||
70 | diskImageConfigParser = object $ | 70 | diskImageConfigParser = object $ |
71 | DiskImageConfig | 71 | DiskImageConfig |
72 | <$> field "parent" ((EmptyImageOfBytes <$> integer) <> (ParentImageConfigFile . unpack <$> string)) | 72 | <$> field "parent" ((EmptyImageOfBytes <$> integer) <> (ParentImageConfigFile . unpack <$> string)) |
73 | <*> (Set.fromList . toList . fmap parsePackageName <$> (field "packages" (array string))) | 73 | <*> (Set.fromList . toList . fmap parsePackageName <$> defaultField "packages" Vector.empty (array string)) |
74 | -- <*> pure (DebconfConfig "") | 74 | -- <*> pure (DebconfConfig "") |
75 | <*> defaultField "configure" True bool | 75 | <*> defaultField "unpack-only" False bool |
76 | -- <*> ((fromMaybe Vector.empty) <$> (optField "patches" (array string))) | 76 | <*> defaultField "binaries" Vector.empty (array string) |
77 | 77 | ||
78 | diskImageFilename :: DiskImageConfig -> FilePath | 78 | diskImageFilename :: DiskImageConfig -> FilePath |
79 | diskImageFilename = (++ ".btrfs") . show . sha1 . show | 79 | diskImageFilename = (++ ".btrfs") . show . sha1 . show |
@@ -81,27 +81,20 @@ diskImageFilename = (++ ".btrfs") . show . sha1 . show | |||
81 | sha1 :: String -> Digest SHA1 | 81 | sha1 :: String -> Digest SHA1 |
82 | sha1 = hash . encodeUtf8 . pack | 82 | sha1 = hash . encodeUtf8 . pack |
83 | 83 | ||
84 | targetConfig :: DiskImageConfig | ||
85 | targetConfig = undefined | ||
86 | |||
87 | targetFilename :: FilePath | ||
88 | targetFilename = diskImageFilename targetConfig | ||
89 | |||
90 | readCfg :: FilePath -> Action DiskImageConfig | 84 | readCfg :: FilePath -> Action DiskImageConfig |
91 | readCfg yaml = either error id . parse diskImageConfigParser . encodeUtf8 . pack <$> readFile' yaml | 85 | readCfg yaml = either error id . parse diskImageConfigParser . encodeUtf8 . pack <$> readFile' yaml |
92 | 86 | ||
93 | s :: String -> String | ||
94 | s = id | ||
95 | |||
96 | buildRoot :: DiskImageConfig -> FilePath -> Action () | 87 | buildRoot :: DiskImageConfig -> FilePath -> Action () |
97 | buildRoot DiskImageConfig{..} finalOut = do | 88 | buildRoot DiskImageConfig{..} finalOut = do |
98 | let out = finalOut <.> "tmp" | 89 | let out = finalOut <.> "tmp" |
99 | mountpoint = finalOut <.> "mnt" | 90 | mountpoint = finalOut <.> "mnt" |
91 | cmd_ "sh -c" ["! mountpoint -q \"$0\" || umount \"$0\" ", mountpoint] | ||
100 | case initialImage of | 92 | case initialImage of |
101 | ParentImageConfigFile f -> do | 93 | ParentImageConfigFile f -> do |
102 | let parent = f -<.> "yaml" <.> "btrfs" | 94 | let parent = "_build" </> f -<.> "btrfs" |
103 | need [parent] | 95 | need [parent] |
104 | cmd_ "cp --reflink" [parent, out] | 96 | cmd_ "cp --reflink" [parent, out] |
97 | cmd_ "mkdir -p" [mountpoint] | ||
105 | cmd_ "mount -t btrfs" [out] mountpoint | 98 | cmd_ "mount -t btrfs" [out] mountpoint |
106 | EmptyImageOfBytes n -> do | 99 | EmptyImageOfBytes n -> do |
107 | cmd_ "truncate -s" [show n] [out] | 100 | cmd_ "truncate -s" [show n] [out] |
@@ -109,29 +102,44 @@ buildRoot DiskImageConfig{..} finalOut = do | |||
109 | cmd_ "mkdir -p" [mountpoint] | 102 | cmd_ "mkdir -p" [mountpoint] |
110 | cmd_ "mount -t btrfs" [out] mountpoint | 103 | cmd_ "mount -t btrfs" [out] mountpoint |
111 | createDefaultSubvolume mountpoint | 104 | createDefaultSubvolume mountpoint |
105 | cmd_ (Cwd mountpoint) "mkdir -p root/var/cache/apt" | ||
112 | cmd_ (Cwd mountpoint) "btrfs subvolume create root/var/cache/apt/archives" | 106 | cmd_ (Cwd mountpoint) "btrfs subvolume create root/var/cache/apt/archives" |
113 | cmd_ "selfstrap" (("--unpack" `consWhen` unpackOnly) ["-t", mountpoint]) (unpack . coerce <$> toList packages) | 107 | -- TODO: catch errors and umount, rmdir mountpoint |
108 | cmd_ "selfstrap --skip-update" | ||
109 | (("--unpack" `consWhen` unpackOnly) ["-t", mountpoint]) (unpack . coerce <$> toList packages) | ||
110 | cmd_ "mv" [out, finalOut] | ||
114 | 111 | ||
115 | consWhen :: a -> Bool -> [a] -> [a] | 112 | consWhen :: a -> Bool -> [a] -> [a] |
116 | a `consWhen` c = if c then (a:) else id | 113 | a `consWhen` c = if c then (a:) else id |
117 | 114 | ||
115 | strip :: String -> String | ||
116 | strip = unpack . Text.strip . pack | ||
117 | |||
118 | createDefaultSubvolume :: FilePath -> Action () | 118 | createDefaultSubvolume :: FilePath -> Action () |
119 | createDefaultSubvolume mountpoint = do | 119 | createDefaultSubvolume mountpoint = do |
120 | cmd_ (Cwd mountpoint) "btrfs subvolume create root" | 120 | cmd_ (Cwd mountpoint) "btrfs subvolume create root" |
121 | Stdout (subvolId::String) <- cmd (Cwd mountpoint) "sh -c" | 121 | Stdout subvolIdLine <- cmd (Cwd mountpoint) "sh -c" |
122 | ["btrfs subvolume show root | sed -n -e 's/^[ \t]*Subvolume ID:[ \t]*//p; s/.*is toplevel subvolume/5/p'"] | 122 | ["btrfs subvolume show root | sed -n -e 's/^[ \t]*Subvolume ID:[ \t]*//p; s/.*is toplevel subvolume/5/p'"] |
123 | let subvolId = strip subvolIdLine | ||
123 | when (null subvolId) $ fail "could not obtain btrfs Subvolume ID" | 124 | when (null subvolId) $ fail "could not obtain btrfs Subvolume ID" |
124 | cmd_ "btrfs subvolume set-default" [subvolId, mountpoint] | 125 | cmd_ "btrfs subvolume set-default" [strip subvolId, mountpoint] |
125 | 126 | ||
127 | defaultImageName :: String | ||
128 | defaultImageName = "minbase" | ||
126 | 129 | ||
130 | stripSuffix :: Text -> String -> String | ||
131 | stripSuffix s t = fromMaybe t $ fmap unpack $ Text.stripSuffix s $ pack t | ||
127 | 132 | ||
128 | main :: IO () | 133 | main :: IO () |
129 | main = do | 134 | main = do |
135 | -- TODO: Fail early on: | ||
136 | -- 1. not running as root | ||
137 | -- 2. no "selfstrap" in PATH | ||
130 | args <- getArgs | 138 | args <- getArgs |
131 | case args of | 139 | case args of |
132 | [target] -> shakeBuildOneImage target | 140 | [target] -> shakeBuildOneImage (stripSuffix ".yaml" target) |
133 | [] -> shakeBuildOneImage "image" | 141 | [] -> shakeBuildOneImage defaultImageName |
134 | _ -> error "usage" | 142 | _ -> error "usage" |
135 | 143 | ||
136 | shakeBuildOneImage :: FilePath -> IO () | 144 | shakeBuildOneImage :: FilePath -> IO () |
137 | shakeBuildOneImage target = | 145 | shakeBuildOneImage target = |