Skip to content

Commit 1f73f03

Browse files
committed
fix: Bug fixes and improvements:
- fixed bug with alias not parsing for values with equal signs - fixed formulae and casks not working with fully qualified name (tap + package name) - fixed formulae and casks uninstalling before installing - fixed pip resource not activating virtual environments properly - fixed pip-sync not activating virtual environments properly - fixed pip-sync not installing dependencies to a virtualenv - fixed xcode-tools not installing the latest version.
1 parent becb461 commit 1f73f03

8 files changed

Lines changed: 81 additions & 28 deletions

File tree

src/resources/homebrew/casks-parameter.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
1313
return {
1414
type: 'array',
1515
isElementEqual(desired, current) {
16+
if (desired === current) {
17+
return true;
18+
}
19+
1620
// Handle the case where the name is fully qualified (tap + name)
1721
if (desired.includes('/')) {
1822
const formulaName = desired.split('/').at(-1);
1923
return formulaName === current;
2024
}
2125

22-
return desired === current;
26+
return false;
2327
},
2428
}
2529
}
2630

2731
async refresh(desired: string[], config: Partial<HomebrewConfig> | null): Promise<null | string[]> {
2832
const $ = getPty();
2933

30-
const caskQuery = await $.spawnSafe('brew list --casks -1')
34+
const caskQuery = await $.spawnSafe('brew list --casks -1 --full-name')
3135

3236
if (caskQuery.status === SpawnStatus.SUCCESS && caskQuery.data !== null && caskQuery.data !== undefined) {
3337
const installedCasks = caskQuery.data
@@ -66,8 +70,9 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
6670
const casksToUninstall = previousValue.filter((x: string) => !newValue.includes(x));
6771

6872
const skipAlreadyInstalledCasks = plan.desiredConfig?.skipAlreadyInstalledCasks ?? plan.currentConfig?.skipAlreadyInstalledCasks;
69-
await this.installCasks(casksToInstall, skipAlreadyInstalledCasks!);
73+
7074
await this.uninstallCasks(casksToUninstall);
75+
await this.installCasks(casksToInstall, skipAlreadyInstalledCasks!);
7176
}
7277

7378
override async remove(valueToRemove: string[]): Promise<void> {
@@ -94,7 +99,7 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
9499
return;
95100
}
96101

97-
const result = await codifySpawn(`SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew install --casks ${casksToInstall.join(' ')}`, { throws: false })
102+
const result = await codifySpawn(`HOMEBREW_NO_AUTO_UPDATE=1 SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew install --casks ${casksToInstall.join(' ')}`, { throws: false })
98103
if (result.status === SpawnStatus.SUCCESS) {
99104
// Casks can't detect if a program was installed by other means. If it returns this message, throw an error
100105
if (result.data.includes('It seems there is already an App at')) {

src/resources/homebrew/formulae-parameter.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,24 @@ export class FormulaeParameter extends StatefulParameter<HomebrewConfig, string[
1010
return {
1111
type: 'array',
1212
isElementEqual(desired, current) {
13+
if (desired === current) {
14+
return true;
15+
}
16+
1317
// Handle the case where the name is fully qualified (tap + name)
1418
if (desired.includes('/')) {
1519
const formulaName = desired.split('/').at(-1);
1620
return formulaName === current;
1721
}
1822

19-
return desired === current;
23+
return false;
2024
},
2125
}
2226
}
2327

2428
override async refresh(desired: unknown, config: Partial<HomebrewConfig>): Promise<null | string[]> {
2529
const $ = getPty();
26-
const formulaeQuery = await $.spawnSafe(`brew list --formula -1 ${config.onlyPlanUserInstalled ? '--installed-on-request' : ''}`)
30+
const formulaeQuery = await $.spawnSafe(`brew list --formula -1 --full-name ${config.onlyPlanUserInstalled ? '--installed-on-request' : ''}`)
2731

2832
if (formulaeQuery.status === SpawnStatus.SUCCESS && formulaeQuery.data !== null && formulaeQuery.data !== undefined) {
2933
return formulaeQuery.data
@@ -42,8 +46,8 @@ export class FormulaeParameter extends StatefulParameter<HomebrewConfig, string[
4246
const formulaeToInstall = newValue.filter((x: string) => !previousValue.includes(x));
4347
const formulaeToUninstall = previousValue.filter((x: string) => !newValue.includes(x));
4448

45-
await this.installFormulae(formulaeToInstall);
4649
await this.uninstallFormulae(formulaeToUninstall);
50+
await this.installFormulae(formulaeToInstall);
4751
}
4852

4953
async remove(valueToRemove: string[]): Promise<void> {

src/resources/homebrew/tap-parameter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class TapsParameter extends StatefulParameter<HomebrewConfig, string[]> {
4545
return;
4646
}
4747

48-
await codifySpawn(`brew tap ${taps.join(' ')}`)
48+
await codifySpawn(`HOMEBREW_NO_AUTO_UPDATE=1 brew tap ${taps.join(' ')}`)
4949
}
5050

5151
private async uninstallTaps(taps: string[]): Promise<void> {

src/resources/python/pip-sync/pip-sync.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,19 @@ export class PipSync extends Resource<PipSyncConfig> {
4141
}
4242

4343
async create(plan: CreatePlan<PipSyncConfig>): Promise<void> {
44-
await codifySpawn(PipSync.withVirtualEnv('pip install pip-tools'), { cwd: plan.desiredConfig.cwd ?? undefined })
44+
await codifySpawn(PipSync.withVirtualEnv('pip install pip-tools', plan.desiredConfig.virtualEnv), { cwd: plan.desiredConfig.cwd ?? undefined })
4545
}
4646

4747
async destroy(plan: DestroyPlan<PipSyncConfig>): Promise<void> {
48-
await codifySpawn(PipSync.withVirtualEnv('pip uninstall -y pip-tools'), { cwd: plan.currentConfig.cwd ?? undefined })
48+
await codifySpawn(PipSync.withVirtualEnv('pip uninstall -y pip-tools', plan.currentConfig.virtualEnv), { cwd: plan.currentConfig.cwd ?? undefined })
4949
}
5050

5151
static withVirtualEnv(command: string, virtualEnv?: string, ): string {
5252
if (!virtualEnv) {
5353
return command;
5454
}
5555

56-
return `source ${virtualEnv}/bin/activate; ${command}; deactivate`;
56+
return `( set -e; source ${virtualEnv}/bin/activate; ${command}; deactivate )`;
5757
}
5858

5959
}

src/resources/python/pip-sync/requirement-files-parameter.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ArrayParameterSetting, Plan, StatefulParameter, getPty } from 'codify-plugin-lib';
22

33
import { codifySpawn } from '../../../utils/codify-spawn.js';
4-
import { PipSync, PipSyncConfig } from './pip-sync.js';
4+
import { PipSyncConfig } from './pip-sync.js';
55

66
export class RequirementFilesParameter extends StatefulParameter<PipSyncConfig, string[]> {
77
getSettings(): ArrayParameterSetting {
@@ -18,19 +18,39 @@ export class RequirementFilesParameter extends StatefulParameter<PipSyncConfig,
1818
}
1919

2020
const pty = getPty();
21-
const { status } = await pty.spawnSafe(PipSync.withVirtualEnv(`pip-sync -n ${desired?.join(' ')}`), { cwd: config.cwd ?? undefined })
21+
const { status } = await pty.spawnSafe(
22+
this.appendVirtualEnv(`pip-sync -n ${desired?.join(' ')}`, config.virtualEnv),
23+
{ cwd: config.cwd ?? undefined }
24+
)
2225
return status === 'error' ? null : desired;
2326
}
2427

2528
async add(valueToAdd: string[], plan: Plan<PipSyncConfig>): Promise<void> {
26-
await codifySpawn(PipSync.withVirtualEnv(`pip-sync ${valueToAdd.join(' ')}`), { cwd: plan.desiredConfig?.cwd ?? undefined })
29+
await codifySpawn(
30+
this.appendVirtualEnv(`pip-sync ${valueToAdd.join(' ')}`, plan.desiredConfig?.virtualEnv),
31+
{ cwd: plan.desiredConfig?.cwd ?? undefined }
32+
)
2733
}
2834

2935
async modify(newValue: string[], _: string[], plan: Plan<PipSyncConfig>): Promise<void> {
30-
await codifySpawn(PipSync.withVirtualEnv(`pip-sync ${newValue.join(' ')}`), { cwd: plan.desiredConfig?.cwd ?? undefined })
36+
await codifySpawn(
37+
this.appendVirtualEnv(`pip-sync ${newValue.join(' ')}`, plan.desiredConfig?.virtualEnv),
38+
{ cwd: plan.desiredConfig?.cwd ?? undefined }
39+
)
3140
}
3241

3342
async remove(valueToRemove: string[], plan: Plan<PipSyncConfig>): Promise<void> {
34-
await codifySpawn(PipSync.withVirtualEnv(`pip-sync ${valueToRemove.join(' ')}`), { cwd: plan.currentConfig?.cwd ?? undefined })
43+
await codifySpawn(
44+
this.appendVirtualEnv(`pip-sync ${valueToRemove.join(' ')}`, plan.currentConfig?.virtualEnv),
45+
{ cwd: plan.currentConfig?.cwd ?? undefined }
46+
)
47+
}
48+
49+
private appendVirtualEnv(command: string, virtualEnv?: string): string {
50+
if (!virtualEnv) {
51+
return command;
52+
}
53+
54+
return `( set -e; source ${virtualEnv}/bin/activate; ${command} --python-executable ${virtualEnv}/bin/python; deactivate )`
3555
}
3656
}

src/resources/python/pip/pip.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ export class Pip extends Resource<PipResourceConfig> {
5555
}
5656

5757
const { status: pipListStatus, data: installedPackages } = await pty.spawnSafe(
58-
(parameters.virtualEnv ? `source ${parameters.virtualEnv}/bin/activate; ` : '')
59-
+ 'pip list --format=json --disable-pip-version-check'
60-
+ (parameters.virtualEnv ? '; deactivate' : ''))
58+
Pip.withVirtualEnv('pip list --format=json --disable-pip-version-check', parameters.virtualEnv)
59+
)
6160

6261
if (pipListStatus === 'error') {
6362
return null;
@@ -136,8 +135,7 @@ export class Pip extends Resource<PipResourceConfig> {
136135
});
137136

138137
await codifySpawn(
139-
(virtualEnv ? `source ${virtualEnv}/bin/activate; ` : '')
140-
+ `pip install ${packagesToInstall.join(' ')}`
138+
Pip.withVirtualEnv(`pip install ${packagesToInstall.join(' ')}`, virtualEnv)
141139
)
142140
}
143141

@@ -151,8 +149,7 @@ export class Pip extends Resource<PipResourceConfig> {
151149
});
152150

153151
await codifySpawn(
154-
(virtualEnv ? `source ${virtualEnv}/bin/activate; ` : '')
155-
+ `pip uninstall -y ${packagesToUninstall.join(' ')}`
152+
Pip.withVirtualEnv(`pip uninstall -y ${packagesToUninstall.join(' ')}`, virtualEnv)
156153
)
157154
}
158155

@@ -191,4 +188,12 @@ export class Pip extends Resource<PipResourceConfig> {
191188

192189
return a.name === b.name;
193190
}
191+
192+
static withVirtualEnv(command: string, virtualEnv?: string, ): string {
193+
if (!virtualEnv) {
194+
return command;
195+
}
196+
197+
return `( set -e; source ${virtualEnv}/bin/activate; ${command}; deactivate )`;
198+
}
194199
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,18 @@ export class AliasResource extends Resource<AliasConfig> {
4848

4949
const matchedAlias = data.split(/\n/g)
5050
.find((l) => {
51-
const [name] = l.split('=');
51+
const firstEqualIndex = l.indexOf('=');
52+
const name = l.slice(0, firstEqualIndex);
5253
return name === desired;
5354
});
5455

5556
if (!matchedAlias) {
5657
return null;
5758
}
5859

59-
const [name, value] = matchedAlias.split('=');
60+
const firstEqualIndex = matchedAlias.indexOf('=');
61+
const name = matchedAlias.slice(0, firstEqualIndex);
62+
const value = matchedAlias.slice(firstEqualIndex + 1);
6063

6164
let processedValue = value.trim()
6265
if ((processedValue.startsWith('\'') && processedValue.endsWith('\'')) || (processedValue.startsWith('"') && processedValue.endsWith('"'))) {

src/resources/xcode-tools/xcode-tools.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { getPty, Resource, ResourceSettings } from 'codify-plugin-lib';
1+
import { Resource, ResourceSettings, getPty } from 'codify-plugin-lib';
22
import { StringIndexedObject } from 'codify-schemas';
3+
import fs from 'node:fs/promises';
34
import path from 'node:path';
5+
import { compare, coerce } from 'semver';
46

57
import { SpawnStatus, codifySpawn } from '../../utils/codify-spawn.js';
6-
import fs from 'node:fs/promises';
78

89
interface XCodeToolsConfig extends StringIndexedObject {}
910

@@ -51,7 +52,22 @@ export class XcodeToolsResource extends Resource<XCodeToolsConfig> {
5152
return await this.attemptGUIInstall();
5253
}
5354

54-
await codifySpawn(`softwareupdate -i "${xcodeToolsVersion[0]}" --verbose`, { requiresRoot: true });
55+
let latestVersion = '';
56+
latestVersion = xcodeToolsVersion.length > 0 ? xcodeToolsVersion.reduce((prev, current) => {
57+
if (!prev) {
58+
return current;
59+
}
60+
61+
const currentVerIndex = current.lastIndexOf('-')
62+
const prevVerIndex = prev.lastIndexOf('-')
63+
64+
const currentVer = current.slice(currentVerIndex + 1);
65+
const prevVer = prev.slice(prevVerIndex + 1);
66+
67+
return compare(coerce(currentVer)!, coerce(prevVer)!) > 0 ? current : prev;
68+
}) : xcodeToolsVersion.at(0)!;
69+
70+
await codifySpawn(`softwareupdate -i "${latestVersion}" --verbose`, { requiresRoot: true });
5571

5672
} finally {
5773
await fs.rm('/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress', { force: true, recursive: true });

0 commit comments

Comments
 (0)