Skip to content

Commit 745c6da

Browse files
committed
COnverted ssh-add, homebrew, aws-cli, asdf to linux
1 parent c62ece8 commit 745c6da

15 files changed

Lines changed: 170 additions & 92 deletions

File tree

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ integration_individual_test_task:
4848

4949
integration_individual_test_linux_task:
5050
arm_container:
51-
image: node:22
51+
image: kevinwang5658/codify-test-linux:latest
5252
# node_modules_cache:
5353
# folder: node_modules
5454
# fingerprint_script: cat package-lock.json

package-lock.json

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
"ajv": "^8.12.0",
2727
"ajv-formats": "^2.1.1",
2828
"chalk": "^5.3.0",
29-
"codify-plugin-lib": "1.0.182-beta21",
30-
"codify-schemas": "1.0.86-beta5",
29+
"codify-plugin-lib": "1.0.182-beta27",
30+
"codify-schemas": "1.0.86-beta7",
3131
"debug": "^4.3.4",
3232
"lodash.isequal": "^4.5.0",
3333
"nanoid": "^5.0.9",
@@ -55,7 +55,7 @@
5555
"@types/node": "^18",
5656
"@types/plist": "^3.0.5",
5757
"@types/semver": "^7.5.4",
58-
"codify-plugin-test": "0.0.53-beta7",
58+
"codify-plugin-test": "0.0.53-beta11",
5959
"commander": "^12.1.0",
6060
"eslint": "^8.51.0",
6161
"eslint-config-oclif": "^5",

scripts/Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM node:22
2+
3+
USER root
4+
5+
# Install system dependencies without sudo
6+
RUN apt-get update && apt-get install -y \
7+
build-essential \
8+
curl \
9+
git \
10+
sudo \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
RUN echo "node ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/node
14+

src/resources/asdf/asdf.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,8 @@ export class AsdfResource extends Resource<AsdfConfig> {
6565
// eslint-disable-next-line no-template-curly-in-string
6666
await FileUtils.addToShellRc('export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"')
6767

68-
// TODO: Add filtering to resources for operating systems
69-
// os parameter
70-
// TODO: Move all utils to codify-plugin-lib
7168
// TODO: Move OsUtils to a separate name space? All things that have to do with the os.
7269
// TODO: Add a way to run multiple commands in sequence
73-
// TODO: Add a easier way to make sure path is on the path
7470
// TODO: Change all plugins to install to ~/.local/bin
7571

7672
await $.spawnSafe('which asdf', { interactive: true });

src/resources/aws-cli/cli/aws-cli.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Resource, ResourceSettings, getPty, SpawnStatus } from 'codify-plugin-lib';
1+
import { Resource, ResourceSettings, getPty, SpawnStatus, Utils } from 'codify-plugin-lib';
22
import { OS, StringIndexedObject } from 'codify-schemas';
33
import fs from 'node:fs/promises';
4+
import os from 'node:os';
5+
import path from 'node:path';
46

5-
import { Utils } from '../../../utils/index.js';
67
import Schema from './aws-cli-schema.json';
78

89
export interface AwsCliConfig extends StringIndexedObject {
@@ -16,7 +17,7 @@ export class AwsCliResource extends Resource<AwsCliConfig> {
1617
getSettings(): ResourceSettings<AwsCliConfig> {
1718
return {
1819
schema: Schema,
19-
operatingSystems: [OS.Darwin],
20+
operatingSystems: [OS.Darwin, OS.Linux],
2021
id: 'aws-cli',
2122
};
2223
}
@@ -41,22 +42,24 @@ export class AwsCliResource extends Resource<AwsCliConfig> {
4142
// Amazon has not released a standalone way to install arm aws-cli. See: https://github.com/aws/aws-cli/issues/7252
4243
// Prefer the homebrew version on M1
4344
const isArmArch = await Utils.isArmArch();
44-
const isRosettaInstalled = await Utils.isRosetta2Installed()
45-
const isHomebrewInstalled = await Utils.isHomebrewInstalled();
46-
47-
if (isArmArch && isHomebrewInstalled) {
48-
console.log('Resource: \'aws-cli\'. Detected that mac is aarch64. Installing AWS-CLI via homebrew')
49-
await $.spawn('HOMEBREW_NO_AUTO_UPDATE=1 brew install awscli', { interactive: true })
50-
51-
} else if (!isArmArch || isRosettaInstalled) {
52-
console.log('Resource: \'aws-cli\'. Detected that mac is not ARM or Rosetta is installed. Installing AWS-CLI standalone version')
53-
await $.spawn('curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"');
54-
await $.spawn('installer -pkg ./AWSCLIV2.pkg -target /', { requiresRoot: true })
55-
await fs.rm('./AWSCLIV2.pkg', { recursive: true, force: true });
56-
57-
} else {
58-
// This covers arm arch + Homebrew is not installed
59-
throw new Error(`Resource: 'aws-cli'. This plugin prefers installing AWS-CLI via homebrew for M1 macs.
45+
46+
if (Utils.isMacOS()) {
47+
const isRosettaInstalled = await Utils.isRosetta2Installed()
48+
const isHomebrewInstalled = await Utils.isHomebrewInstalled();
49+
50+
if (isArmArch && isHomebrewInstalled) {
51+
console.log('Resource: \'aws-cli\'. Detected that mac is aarch64. Installing AWS-CLI via homebrew')
52+
await $.spawn('HOMEBREW_NO_AUTO_UPDATE=1 brew install awscli', { interactive: true })
53+
54+
} else if (!isArmArch || isRosettaInstalled) {
55+
console.log('Resource: \'aws-cli\'. Detected that mac is not ARM or Rosetta is installed. Installing AWS-CLI standalone version')
56+
await $.spawn('curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"');
57+
await $.spawn('installer -pkg ./AWSCLIV2.pkg -target /', { requiresRoot: true })
58+
await fs.rm('./AWSCLIV2.pkg', { recursive: true, force: true });
59+
60+
} else {
61+
// This covers arm arch + Homebrew is not installed
62+
throw new Error(`Resource: 'aws-cli'. This plugin prefers installing AWS-CLI via homebrew for M1 macs.
6063
AWS has not updated the standalone installer to support M1 macs. See: https://github.com/aws/aws-cli/issues/7252.
6164
6265
Homebrew can be installed by adding:
@@ -68,6 +71,14 @@ Or enable rosetta 2 using the below command and re-run:
6871
6972
softwareupdate --install-rosetta
7073
`);
74+
}
75+
} else if (Utils.isLinux()) {
76+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'codify-aws-cli'));
77+
78+
await $.spawn('curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"', { cwd: tmpDir });
79+
await $.spawn('unzip -q awscliv2.zip', { cwd: tmpDir });
80+
await $.spawn('./aws/install', { cwd: tmpDir, requiresRoot: true });
81+
await fs.rm(tmpDir, { recursive: true, force: true });
7182
}
7283
}
7384

@@ -78,17 +89,17 @@ softwareupdate --install-rosetta
7889
if (!installLocation) {
7990
return;
8091
}
81-
92+
8293
if (installLocation.includes('homebrew')) {
8394
await $.spawn('brew uninstall awscli', { interactive: true, env: { HOMEBREW_NO_AUTO_UPDATE: 1 } });
8495
return;
8596
}
86-
97+
8798
await $.spawn(`rm ${installLocation}`, { requiresRoot: true });
8899
await $.spawn(`rm ${installLocation}_completer`, { requiresRoot: true });
89100
await $.spawn('rm -rf $HOME/.aws/');
90101
}
91-
102+
92103
private async findInstallLocation(): Promise<null | string> {
93104
const $ = getPty();
94105
const query = await $.spawnSafe('which aws', { interactive: true });

src/resources/homebrew/casks-parameter.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ParameterSetting, Plan, SpawnStatus, StatefulParameter, getPty } from 'codify-plugin-lib';
1+
import { ParameterSetting, Plan, SpawnStatus, StatefulParameter, Utils, getPty } from 'codify-plugin-lib';
22
import path from 'node:path';
33

44
import { FileUtils } from '../../utils/file-utils.js';
@@ -42,7 +42,7 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
4242

4343
// This serves a secondary purpose as well. It checks that each cask to install is valid (alerting the user
4444
// in the plan instead of in the apply)
45-
const casksWithConflicts = await this.findConflicts(notInstalledCasks);
45+
const casksWithConflicts = await this.findConflictsOnMacOs(notInstalledCasks);
4646
if (casksWithConflicts.length > 0) {
4747
// To avoid errors, we pretend that those programs were already installed by homebrew (even though it was installed outside)
4848
if (config?.skipAlreadyInstalledCasks) {
@@ -82,7 +82,7 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
8282
}
8383

8484
const $ = getPty();
85-
const conflicts = await this.findConflicts(casks);
85+
const conflicts = await this.findConflictsOnMacOs(casks);
8686
const casksToInstall = casks.filter((c) => !conflicts.includes(c))
8787

8888
if (conflicts.length > 0) {
@@ -132,9 +132,13 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
132132
}
133133
}
134134

135-
private async findConflicts(casks: string[]): Promise<string[]> {
136-
const $ = getPty();
135+
private async findConflictsOnMacOs(casks: string[]): Promise<string[]> {
136+
// if (!Utils.isMacOS()) {
137+
return [];
138+
// }
139+
137140

141+
const $ = getPty();
138142
const result = (await $.spawn(
139143
`brew info -q --json=v2 ${casks.map((c) => `"${c}"`).join(' ')}`, {
140144
interactive: true,

src/resources/homebrew/homebrew.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class HomebrewResource extends Resource<HomebrewConfig> {
7373
)
7474

7575
const brewPath = Utils.isLinux() ? '/home/linuxbrew/.linuxbrew/bin/brew' : '/opt/homebrew/bin/brew';
76-
await FileUtils.addToStartupFile(`eval "$(${brewPath} shellenv)"`);
76+
await FileUtils.addToShellRc(`eval "$(${brewPath} shellenv)"`);
7777

7878
// TODO: Add a check here to see if homebrew is writable
7979
// Either add a warning or a parameter to edit the permissions on /opt/homebrew
@@ -94,7 +94,7 @@ export class HomebrewResource extends Resource<HomebrewConfig> {
9494
}
9595

9696
// Delete eval from .zshrc
97-
await FileUtils.removeLineFromPrimaryShellRc(`eval "$(${homebrewDirectory}/bin/brew shellenv)"`)
97+
await FileUtils.removeLineFromShellRc(`eval "$(${homebrewDirectory}/bin/brew shellenv)"`)
9898
}
9999

100100
private async installBrewInCustomDir(dir: string): Promise<void> {
@@ -119,7 +119,7 @@ export class HomebrewResource extends Resource<HomebrewConfig> {
119119
})
120120

121121
// Update shell startup scripts
122-
await FileUtils.addToStartupFile(`eval "$(${absoluteDir}/bin/brew shellenv)"`);
122+
await FileUtils.addToShellRc(`eval "$(${absoluteDir}/bin/brew shellenv)"`);
123123
}
124124

125125
// Ex:

src/resources/ssh/ssh-add.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CreatePlan, DestroyPlan, getPty, Resource, ResourceSettings, SpawnStatus } from 'codify-plugin-lib';
1+
import { CreatePlan, DestroyPlan, getPty, Resource, ResourceSettings, SpawnStatus, Utils } from 'codify-plugin-lib';
22
import { OS, StringIndexedObject } from 'codify-schemas';
33
import path from 'node:path';
44

@@ -16,7 +16,7 @@ export class SshAddResource extends Resource<SshAddConfig> {
1616
getSettings(): ResourceSettings<SshAddConfig> {
1717
return {
1818
id: 'ssh-add',
19-
operatingSystems: [OS.Darwin],
19+
operatingSystems: [OS.Darwin, OS.Linux],
2020
schema: Schema,
2121
parameterSettings: {
2222
path: {
@@ -41,12 +41,12 @@ export class SshAddResource extends Resource<SshAddConfig> {
4141
return null;
4242
}
4343

44-
const { data: keyFingerprint, status: keygenStatus } = await $.spawnSafe(`eval "$(ssh-agent -s)"; ssh-keygen -lf ${sshPath}`);
44+
const { data: keyFingerprint, status: keygenStatus } = await $.spawnSafe(`ssh-keygen -lf ${sshPath}`);
4545
if (keygenStatus === SpawnStatus.ERROR) {
4646
return null;
4747
}
4848

49-
const { data: loadedSshKeys, status: sshAddStatus } = await $.spawnSafe('eval "$(ssh-agent -s)"; /usr/bin/ssh-add -l');
49+
const { data: loadedSshKeys, status: sshAddStatus } = await $.spawnSafe('/usr/bin/ssh-add -l');
5050
if (sshAddStatus === SpawnStatus.ERROR) {
5151
return null;
5252
}
@@ -63,7 +63,7 @@ export class SshAddResource extends Resource<SshAddConfig> {
6363

6464
let appleUseKeychain: boolean | undefined;
6565
if (parameters.appleUseKeychain) {
66-
appleUseKeychain = await this.isKeyLoadedInKeychain(sshPath);
66+
appleUseKeychain = Utils.isMacOS() ? (await this.isKeyLoadedInKeychain(sshPath)) : parameters.appleUseKeychain;
6767
}
6868

6969
return {
@@ -74,20 +74,43 @@ export class SshAddResource extends Resource<SshAddConfig> {
7474

7575
async create(plan: CreatePlan<SshAddConfig>): Promise<void> {
7676
const { appleUseKeychain, path } = plan.desiredConfig;
77-
7877
const $ = getPty();
79-
await $.spawn(`eval "$(ssh-agent -s)"; /usr/bin/ssh-add ${appleUseKeychain ? '--apple-use-keychain ' : ''}${path}`, { interactive: true, stdin: true })
78+
79+
if (Utils.isLinux()) {
80+
if ((await $.spawnSafe('ssh-agent -l')).status === SpawnStatus.ERROR) {
81+
await FileUtils.addToStartupFile('eval "$(ssh-agent -s)"');
82+
}
83+
84+
await FileUtils.addToStartupFile('ssh-add ' + path);
85+
}
86+
87+
if (Utils.isMacOS()) {
88+
await $.spawn(`/usr/bin/ssh-add ${appleUseKeychain && Utils.isMacOS() ? '--apple-use-keychain ' : ''}${path}`, {
89+
interactive: true,
90+
stdin: true
91+
});
92+
}
8093
}
8194

8295
async destroy(plan: DestroyPlan<SshAddConfig>): Promise<void> {
8396
const { path } = plan.currentConfig;
8497

98+
if (Utils.isLinux()) {
99+
await FileUtils.removeLineFromStartupFile('ssh-add ' + path);
100+
}
101+
102+
85103
const $ = getPty();
86-
await $.spawn(`eval "$(ssh-agent -s)"; /usr/bin/ssh-add -d ${path}`, { interactive: true })
104+
await $.spawnSafe(`/usr/bin/ssh-add -d ${path}`, { interactive: true });
87105
}
88106

89107
private async isKeyLoadedInKeychain(keyPath: string): Promise<boolean> {
90-
const $ = getPty();
108+
if (!Utils.isMacOS()) {
109+
return false;
110+
}
111+
112+
113+
const $ = getPty();
91114
const { data: keychainKeys, status } = await $.spawnSafe('/usr/bin/ssh-add --apple-load-keychain', { interactive: true });
92115
if (status === SpawnStatus.ERROR) {
93116
return false;

0 commit comments

Comments
 (0)