Skip to content

Commit

Permalink
feat(fig): port "shell" role to Fig
Browse files Browse the repository at this point in the history
Very simple "line" operation here, a subset of Ansible's "lineinfile"
functionality.
  • Loading branch information
wincent committed Apr 29, 2020
1 parent 16c5242 commit ce35f52
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 17 deletions.
12 changes: 8 additions & 4 deletions aspects/iterm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ task('set up switchable symbolic links', async () => {

assert.JSONArray(retina);

const sources = path.home.join('Library/Application Support/iTerm2/Sources');
const profiles = path.home.join('Library/Application Support/iTerm2/DynamicProfiles');
const sources = path.home.join(
'Library/Application Support/iTerm2/Sources'
);
const profiles = path.home.join(
'Library/Application Support/iTerm2/DynamicProfiles'
);

for (const config of retina) {
assert.JSONObject(config);
Expand All @@ -71,10 +75,10 @@ task('set up switchable symbolic links', async () => {
await file({
path: dest,
src,
state: 'link'
state: 'link',
});
} else {
skip(`path ${stringify(dest)} exists`)
skip(`path ${stringify(dest)} exists`);
}
}
});
3 changes: 3 additions & 0 deletions aspects/shell/aspect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"description": "Sets the use shell to zsh"
}
11 changes: 11 additions & 0 deletions aspects/shell/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {line, task} from 'fig';

task('add /usr/local/bin/zsh to /etc/shells', async () => {
await line({
group: 'wheel',
line: '/usr/local/bin/zsh',
owner: 'root',
path: '/etc/shells',
sudo: true,
});
});
2 changes: 0 additions & 2 deletions darwin.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
---
- hosts: localhost
roles:
- { role: iterm, tags: iterm }
- { role: dotfiles, tags: dotfiles }
- { role: shell, tags: shell }
- { role: vim, tags: vim }
- { role: ssh, tags: ssh }
- { role: misc, tags: misc }
Expand Down
90 changes: 90 additions & 0 deletions fig/dsl/operations/line.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import ErrorWithMetadata from '../../ErrorWithMetadata.js';
import Scanner from '../../Scanner.js';
import {log} from '../../console.js';
import {promises as fs} from '../../fs.js';
import stat from '../../fs/stat.js';
import path from '../../path.js';
import file from './file.js';

/**
* @see https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html
*/
export default async function line({
path: dest,
group,
line,
mode,
owner,
state = 'present',
sudo,
}: {
path: string;
group?: string;
line: string;
mode?: Mode;
owner?: string;
state?: 'absent' | 'present';
sudo?: boolean;
}): Promise<void> {
log.debug(`Line \`${line}\` in \`${path}\``);

const normalized = `${line.trimEnd()}\n`;

const target = path(dest).resolve;

const stats = await stat(target);

let contents = state === 'present' ? normalized : '';

if (stats instanceof Error) {
throw stats;
} else if (stats !== null) {
if (stats.type !== 'file') {
// TODO: make this work with symlinks (problem with file)
throw new ErrorWithMetadata(
`Cannot replace line in ${dest} because its type is ${stats.type}`
);
}

// TODO: deal with potential permission issue here (eg. file only readable by root)
const scanner = new Scanner(await fs.readFile(dest, 'utf8'));

contents = '';

let found = false;

while (!scanner.atEnd()) {
const current = scanner.scan(/[^\r\n]*/);

if (current === line) {
found = true;

if (state === 'present') {
contents += current;
} else {
// Slurp unwanted line, plus the following newline.
scanner.scan(/\r?\n/);
continue;
}
} else {
contents += current || '';
}

contents += scanner.scan(/[\r\n]+/) || '';
}

if (!found && state === 'present') {
contents = contents.replace(/(\r?\n|)$/, `\n${normalized}`);
}
}

await file({
contents,
group,
mode,
owner,
path: target,
state: 'file',
sudo,
});
}
1 change: 1 addition & 0 deletions fig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {default as cron} from './dsl/operations/cron.js';
export {default as defaults} from './dsl/operations/defaults.js';
export {default as fetch} from './dsl/operations/fetch.js';
export {default as file} from './dsl/operations/file.js';
export {default as line} from './dsl/operations/line.js';
export {default as template} from './dsl/operations/template.js';
export * as resource from './dsl/resource.js';
export {default as root} from './dsl/root.js';
Expand Down
3 changes: 3 additions & 0 deletions fig/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ async function loadAspect(aspect: Aspect): Promise<void> {
case 'meta':
await import('../aspects/meta/index.js');
break;
case 'shell':
await import('../aspects/shell/index.js');
break;
case 'terminfo':
await import('../aspects/terminfo/index.js');
break;
Expand Down
4 changes: 2 additions & 2 deletions fig/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export default async function run(
};

const child = spawn(
final[0],
final.slice(1),
final[0].toString(),
final.slice(1).map(String),
options.chdir ? {cwd: options.chdir.toString()} : {}
);

Expand Down
1 change: 1 addition & 0 deletions project.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"iterm",
"karabiner",
"launchd",
"shell",
"terminfo",
"vim",
"cron",
Expand Down
1 change: 0 additions & 1 deletion roles/shell/description

This file was deleted.

8 changes: 0 additions & 8 deletions roles/shell/tasks/main.yml

This file was deleted.

0 comments on commit ce35f52

Please sign in to comment.