Skip to content

Commit 947f167

Browse files
committed
fix: fixed alias resource and ssh-add
1 parent 80a0f31 commit 947f167

4 files changed

Lines changed: 102 additions & 33 deletions

File tree

src/resources/shell/alias/alias-resource.ts

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { CreatePlan, Resource, ResourceSettings } from 'codify-plugin-lib';
1+
import { CreatePlan, DestroyPlan, ModifyPlan, ParameterChange, Resource, ResourceSettings } from 'codify-plugin-lib';
22
import { StringIndexedObject } from 'codify-schemas';
3+
import fs from 'node:fs/promises';
4+
import os from 'node:os';
5+
import path from 'node:path';
36

47
import { SpawnStatus, codifySpawn } from '../../../utils/codify-spawn.js';
58
import { FileUtils } from '../../../utils/file-utils.js';
9+
import { Utils } from '../../../utils/index.js';
610
import Schema from './alias-schema.json';
711

812
export interface AliasConfig extends StringIndexedObject {
@@ -16,7 +20,7 @@ export class AliasResource extends Resource<AliasConfig> {
1620
id: 'alias',
1721
schema: Schema,
1822
parameterSettings: {
19-
value: { canModify: true }
23+
value: { canModify: true, inputTransformation: (input) => Utils.shellEscape(input) }
2024
},
2125
}
2226
}
@@ -42,24 +46,103 @@ export class AliasResource extends Resource<AliasConfig> {
4246

4347
const [name, value] = matchedAlias.split('=');
4448

49+
let processedValue = value.trim()
50+
if ((processedValue.startsWith('\'') && processedValue.endsWith('\'')) || (processedValue.startsWith('"') && processedValue.endsWith('"'))) {
51+
processedValue = processedValue.slice(1, -1)
52+
}
53+
4554
return {
4655
alias: name,
47-
value,
56+
value: processedValue,
4857
}
4958
}
5059

5160
override async create(plan: CreatePlan<AliasConfig>): Promise<void> {
61+
const zshrcPath = path.join(os.homedir(), '.zshrc');
62+
63+
if (!(await FileUtils.fileExists(zshrcPath))) {
64+
await fs.writeFile(zshrcPath, '', { encoding: 'utf8' });
65+
}
66+
5267
const { alias, value } = plan.desiredConfig;
68+
const aliasString = this.aliasString(alias, value);
69+
await fs.appendFile(zshrcPath, '\n\n' + aliasString, { encoding: 'utf8' });
70+
}
71+
72+
async modify(pc: ParameterChange<AliasConfig>, plan: ModifyPlan<AliasConfig>): Promise<void> {
73+
if (pc.name !== 'value') {
74+
return;
75+
}
76+
77+
const { alias, value } = plan.currentConfig;
78+
const aliasInfo = await this.findAlias(alias, value);
79+
if (!aliasInfo) {
80+
throw new Error(`Unable to find alias: ${alias} on the system. Codify isn't able to search all locations on the system. Please delete the alias manually and re-run Codify.`);
81+
}
82+
83+
const aliasString = this.aliasString(alias, value);
84+
const aliasStringShort = this.aliasStringShort(alias, value);
85+
86+
const lines = aliasInfo.contents
87+
.split(/\n/)
5388

54-
await FileUtils.addAliasToZshrc(alias, value);
89+
const aliasLineNum = lines
90+
.findIndex((l) => l.trim() === aliasStringShort || l.trim() === aliasString);
91+
if (!aliasLineNum) {
92+
throw new Error(`Unable to modify Alias. Cannot find line ${aliasString} in ${aliasInfo.path}. Please delete the alias manually and re-run Codify.`);
93+
}
94+
95+
const newAlias = this.aliasString(plan.desiredConfig.alias, plan.desiredConfig.value);
96+
lines.splice(aliasLineNum, 1, newAlias);
97+
98+
await fs.writeFile(lines.join('\n'), 'utf8');
99+
}
100+
101+
async destroy(plan: DestroyPlan<AliasConfig>): Promise<void> {
102+
const { alias, value } = plan.currentConfig;
103+
const aliasInfo = await this.findAlias(alias, value);
104+
if (!aliasInfo) {
105+
throw new Error(`Unable to find alias: ${alias} on the system. Codify isn't able to search all locations on the system. Please delete the alias manually and re-run Codify.`);
106+
}
107+
108+
const aliasString = this.aliasString(alias, value);
109+
const aliasStringShort = this.aliasStringShort(alias, value);
110+
111+
await FileUtils.removeLineFromFile(aliasInfo.path, aliasString);
112+
await FileUtils.removeLineFromFile(aliasInfo.path, aliasStringShort);
55113
}
56114

57-
// TODO: Implement updating an alias
58-
override async modify(): Promise<void> {
59-
throw new Error('Unsupported for now. Un-able to update an alias value for now')
115+
private async findAlias(alias: string, value: string): Promise<{ path: string; contents: string; } | null> {
116+
const paths = [
117+
path.join(os.homedir(), '.zshrc'),
118+
path.join(os.homedir(), '.zprofile'),
119+
path.join(os.homedir(), '.zshenv'),
120+
];
121+
122+
const aliasString = this.aliasString(alias, value);
123+
const aliasStringShort = this.aliasStringShort(alias, value);
124+
125+
for (const path of paths) {
126+
if (await FileUtils.fileExists(path)) {
127+
const fileContents = await fs.readFile(path, 'utf8');
128+
129+
if (fileContents.includes(aliasString) || fileContents.includes(aliasStringShort)) {
130+
return {
131+
path,
132+
contents: fileContents,
133+
}
134+
}
135+
}
136+
}
137+
138+
return null;
60139
}
61140

62-
// TODO: Implement destroy some time in the future
63-
override async destroy(): Promise<void> {}
141+
private aliasString(alias: string, value: string): string {
142+
return `alias ${alias}='${value}'`
143+
}
64144

145+
private aliasStringShort(alias: string, value: string): string {
146+
return `alias ${alias}=${value}`
147+
}
65148
}

src/resources/ssh/ssh-add.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,19 @@ export class SshAddResource extends Resource<SshAddConfig> {
3131
}
3232

3333
async refresh(parameters: Partial<SshAddConfig>): Promise<Partial<SshAddConfig> | null> {
34-
const { data } = await codifySpawn('which ssh-add');
35-
if (data.trim() !== '/usr/bin/ssh-add') {
36-
throw new Error('Not using the default macOS ssh-add.')
37-
}
38-
39-
const path = parameters.path!;
40-
if (!(await FileUtils.fileExists(path))) {
34+
const sshPath = parameters.path!;
35+
if (!(await FileUtils.fileExists(sshPath))) {
4136
return null;
4237
}
4338

4439
await codifySpawn('eval "$(ssh-agent -s)"')
4540

46-
const { data: keyFingerprint, status: keygenStatus } = await codifySpawn(`ssh-keygen -lf ${path}`, { throws: false });
41+
const { data: keyFingerprint, status: keygenStatus } = await codifySpawn(`ssh-keygen -lf ${sshPath}`, { throws: false });
4742
if (keygenStatus === SpawnStatus.ERROR) {
4843
return null;
4944
}
5045

51-
const { data: loadedSshKeys, status: sshAddStatus } = await codifySpawn('ssh-add -l', { throws: false });
46+
const { data: loadedSshKeys, status: sshAddStatus } = await codifySpawn('/usr/bin/ssh-add -l', { throws: false });
5247
if (sshAddStatus === SpawnStatus.ERROR) {
5348
return null;
5449
}
@@ -65,11 +60,11 @@ export class SshAddResource extends Resource<SshAddConfig> {
6560

6661
let appleUseKeychain: boolean | undefined;
6762
if (parameters.appleUseKeychain) {
68-
appleUseKeychain = await this.isKeyLoadedInKeychain(path);
63+
appleUseKeychain = await this.isKeyLoadedInKeychain(sshPath);
6964
}
7065

7166
return {
72-
path,
67+
path: sshPath,
7368
appleUseKeychain,
7469
};
7570
}
@@ -78,18 +73,18 @@ export class SshAddResource extends Resource<SshAddConfig> {
7873
const { appleUseKeychain, path } = plan.desiredConfig;
7974

8075
await codifySpawn('eval "$(ssh-agent -s)"')
81-
await codifySpawn(`ssh-add ${appleUseKeychain ? '--apple-use-keychain ' : ''}${path}`, { requestsTTY: true })
76+
await codifySpawn(`/usr/bin/ssh-add ${appleUseKeychain ? '--apple-use-keychain ' : ''}${path}`, { requestsTTY: true })
8277
}
8378

8479
async destroy(plan: DestroyPlan<SshAddConfig>): Promise<void> {
8580
const { path } = plan.currentConfig;
8681

8782
await codifySpawn('eval "$(ssh-agent -s)"')
88-
await codifySpawn(`ssh-add -d ${path}`)
83+
await codifySpawn(`/usr/bin/ssh-add -d ${path}`)
8984
}
9085

9186
private async isKeyLoadedInKeychain(keyPath: string): Promise<boolean> {
92-
const { data: keychainKeys, status } = await codifySpawn('ssh-add --apple-load-keychain', { throws: false });
87+
const { data: keychainKeys, status } = await codifySpawn('/usr/bin/ssh-add --apple-load-keychain', { throws: false });
9388
if (status === SpawnStatus.ERROR) {
9489
return false;
9590
}

src/utils/file-utils.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ import { Utils } from './index.js';
88
import { untildify } from './untildify.js';
99

1010
export const FileUtils = {
11-
async addAliasToZshrc(alias: string, value?: string): Promise<void> {
12-
if (!value) {
13-
return;
14-
}
15-
16-
const escapedValue = Utils.shellEscape(value);
17-
await codifySpawn(`echo "alias ${alias}=${escapedValue}" >> $HOME/.zshrc`)
18-
},
19-
2011
async addToStartupFile(line: string): Promise<void> {
2112
const lineToInsert = addLeadingSpacer(
2213
addTrailingSpacer(line)

test/shell/alias.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('Alias resource integration tests', async () => {
1616
alias: 'test',
1717
value: 'ls'
1818
}
19-
], true);
19+
]);
2020
})
2121

2222
it('Validates against invalid alias', { timeout: 300000 }, async () => {

0 commit comments

Comments
 (0)