]> git.wincent.com - wincent.git/commitdiff
feat(fig): get chown working
authorGreg Hurrell <greg@hurrell.net>
Sat, 4 Apr 2020 22:38:18 +0000 (00:38 +0200)
committerGreg Hurrell <greg@hurrell.net>
Sat, 4 Apr 2020 22:39:15 +0000 (00:39 +0200)
src/Fig/compare.ts
src/Fig/operations/template.ts
src/chown.ts
src/run.ts
src/stat.ts

index f27bb6bbc236d29f6599c926d9426854e81b8bf5..890d521b7d26299a8b2e3ea7749fc6340664f183 100644 (file)
@@ -138,6 +138,14 @@ export default async function compare({
                 // specified "user"
             }
         }
+
+        if (group && stats.group !== group) {
+            diff.group = group;
+        }
+
+        if (owner && stats.owner !== owner) {
+            diff.owner = owner;
+        }
     } else if (state === 'directory') {
         if (stats.type === 'directory') {
             // Want "directory", have "directory": no state change required.
index f2d20a741eaab18793493de2f9452ccd295a00a3..f5e304b20d0eacf344717d83686d1e2ab03ae6cf 100644 (file)
@@ -1,9 +1,9 @@
 import * as fs from 'fs';
 
 import ErrorWithMetadata from '../../ErrorWithMetadata';
+import chown from '../../chown';
 import {log} from '../../console';
 import expand from '../../expand';
-import run from '../../run';
 import tempfile from '../../tempfile';
 import {compile, fill} from '../../template';
 import Context from '../Context';
@@ -40,15 +40,17 @@ export default async function template({
         state: 'file',
     });
 
-    if (owner && owner !== Context.attributes.username) {
-        log.debug(`Needs sudo: ${Context.attributes.username} -> ${owner}`);
-        const passphrase = await Context.sudoPassphrase;
-        const result = await run('ls', ['-l', '/var/audit'], {passphrase});
+    if (diff.state === 'file') {
+        // TODO: file does not exist — have to create it
+    } else if (diff.owner || diff.group) {
+        const result = await chown(target, {group, owner, sudo: true});
 
-        if (result.status !== 0) {
+        if (result instanceof Error) {
             throw new ErrorWithMetadata(`Failed command`, {
-                ...result,
-                error: result.error?.toString() ?? null,
+                error: result.toString(),
+                group: group ?? null,
+                owner: owner ?? null,
+                target,
             });
         }
     } else {
index 99ead6ae52c2f046f8608ff7973943cfa65c8986..ce6153d15b0ae035d150b9c62419bc3385b6e660 100644 (file)
@@ -5,7 +5,7 @@ import run from './run';
 type Options = {
     group?: string;
     sudo?: boolean;
-    user?: string;
+    owner?: string;
 };
 
 export default async function chown(
@@ -13,40 +13,31 @@ export default async function chown(
     options: Options = {}
 ): Promise<Error | null> {
     if (Context.attributes.platform === 'darwin') {
-        return null; // TODO finish
+        // Run one of:
+        //
+        //      chown owner path
+        //      chown :group path
+        //      chown owner:group path
+        //
+        // With or without `sudo` (in practice, if we are calling `chown()` at
+        // all, it will probably be with `sudo`).
+        //
+        const passphrase = options.sudo ? (await Context.sudoPassphrase) : undefined;
+
+        let ownerAndGroup = options.owner || '';
+
+        if (options.group) {
+            ownerAndGroup += `:${options.group}`;
+        }
+
+        const result = await run('chown', [ownerAndGroup, path], {passphrase});
+
+        if (result.status === 0) {
+            return null;
+        } else {
+            return result.error || new ErrorWithMetadata('chown failed');
+        }
     } else {
         throw new Error('TODO: implement');
     }
 }
-
-// TODO: decide whether to throw/catch or just return errors
-
-/*
-
-let result;
-
-try {
-  result = await chown(path, options);
-
-  // code that needs result (either here...)
-} catch (error) {
-  return null; // or re-throw
-}
-
-// (or...) code that needs result
-
-// vs
-
-const result = await chown(path, options);
-
-if (result instanceof Error) {
-  return null; // or re-throw etc
-}
-
-// code that needs result...
-
-// less nesting, possibly clearer control flow, less fighting against block
-// scope etc.
-// catch still might be better though if you ever need to use finally etc.
-
-*/
index 3cf4f0528c282d9596375be14d167766f5a3d270..29f24e054447af17d34f80574dcd39e61d171fc8 100644 (file)
@@ -23,10 +23,10 @@ export default async function run(
     args: Array<string>,
     options: Options = {}
 ): Promise<Result> {
-    return new Promise((resolve, reject) => {
+    return new Promise((resolve) => {
         const prompt = `sudo[${randomBytes(16).toString('hex')}]:`;
 
-        const final = options.passphrase
+        const final = options.passphrase !== undefined
             ? ['sudo', '-S', '-k', '-p', prompt, '--', command, ...args]
             : [command, ...args];
 
@@ -62,7 +62,7 @@ export default async function run(
         });
 
         child.on('error', (error) =>
-            reject({
+            resolve({
                 ...result,
                 error,
             })
@@ -77,7 +77,7 @@ export default async function run(
             } else if (signal) {
                 resolve({
                     ...result,
-                    status,
+                    signal,
                 });
             }
         });
index bf810b08156482d2f736da7be6ed694f9e6c7296..87fa7c98064ad51ca235d1dfc85eb453cefaf158 100644 (file)
@@ -7,7 +7,7 @@ type Stats = {
     mode: Mode;
     target?: string;
     type: 'directory' | 'file' | 'link' | 'socket' | 'special' | 'unknown';
-    user: string;
+    owner: string;
 };
 
 const TYPE_MAP = {
@@ -36,13 +36,13 @@ export default async function stat(
             newline: '%n',
             target: '%Y',
             type: '%HT',
-            user: '%Su',
+            owner: '%Su',
         };
 
         const formatString = [
             formats.mode,
             formats.type,
-            formats.user,
+            formats.owner,
             formats.group,
             formats.target,
         ].join(formats.newline);
@@ -60,7 +60,7 @@ export default async function stat(
             );
 
             if (status === 0) {
-                const [mode, type, user, group, target] = stdout.split('\n');
+                const [mode, type, owner, group, target] = stdout.split('\n');
 
                 const paddedMode = mode.padStart(4, '0');
 
@@ -71,7 +71,7 @@ export default async function stat(
                     mode: paddedMode,
                     target: target || undefined,
                     type: (TYPE_MAP as any)[type.toLowerCase()] || 'unknown',
-                    user,
+                    owner,
                 };
             }
 
@@ -88,7 +88,7 @@ export default async function stat(
         // a = mode
         // F = type
         // G = group name
-        // U = user name
+        // U = owner name
         // maybe %N (link target): prints 'src' -> 'target'
         // 644, 1777
         // regular file, directory, symbolic link, character special file