Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "default",
"version": "0.13.0",
"version": "0.14.0",
"description": "",
"main": "dist/index.js",
"scripts": {
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { JenvResource } from './resources/java/jenv/jenv.js';
import { NvmResource } from './resources/node/nvm/nvm.js';
import { Pnpm } from './resources/node/pnpm/pnpm.js';
import { PgcliResource } from './resources/pgcli/pgcli.js';
import { Pip } from './resources/python/pip/pip.js';
import { PipSync } from './resources/python/pip-sync/pip-sync.js';
import { PyenvResource } from './resources/python/pyenv/pyenv.js';
import { VenvProject } from './resources/python/venv/venv-project.js';
import { Virtualenv } from './resources/python/virtualenv/virtualenv.js';
import { VirtualenvProject } from './resources/python/virtualenv/virtualenv-project.js';
import { ActionResource } from './resources/scripting/action.js';
Expand Down Expand Up @@ -63,6 +66,9 @@ runPlugin(Plugin.create(
new Virtualenv(),
new VirtualenvProject(),
new Pnpm(),
new WaitGithubSshKey()
new WaitGithubSshKey(),
new VenvProject(),
new Pip(),
new PipSync()
])
)
13 changes: 9 additions & 4 deletions src/resources/homebrew/casks-parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,25 @@ export class CasksParameter extends StatefulParameter<HomebrewConfig, string[]>
return {
type: 'array',
isElementEqual(desired, current) {
if (desired === current) {
return true;
}

// Handle the case where the name is fully qualified (tap + name)
if (desired.includes('/')) {
const formulaName = desired.split('/').at(-1);
return formulaName === current;
}

return desired === current;
return false;
},
}
}

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

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

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

const skipAlreadyInstalledCasks = plan.desiredConfig?.skipAlreadyInstalledCasks ?? plan.currentConfig?.skipAlreadyInstalledCasks;
await this.installCasks(casksToInstall, skipAlreadyInstalledCasks!);

await this.uninstallCasks(casksToUninstall);
await this.installCasks(casksToInstall, skipAlreadyInstalledCasks!);
}

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

const result = await codifySpawn(`SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew install --casks ${casksToInstall.join(' ')}`, { throws: false })
const result = await codifySpawn(`HOMEBREW_NO_AUTO_UPDATE=1 SUDO_ASKPASS=${SUDO_ASKPASS_PATH} brew install --casks ${casksToInstall.join(' ')}`, { throws: false })
if (result.status === SpawnStatus.SUCCESS) {
// Casks can't detect if a program was installed by other means. If it returns this message, throw an error
if (result.data.includes('It seems there is already an App at')) {
Expand Down
10 changes: 7 additions & 3 deletions src/resources/homebrew/formulae-parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ export class FormulaeParameter extends StatefulParameter<HomebrewConfig, string[
return {
type: 'array',
isElementEqual(desired, current) {
if (desired === current) {
return true;
}

// Handle the case where the name is fully qualified (tap + name)
if (desired.includes('/')) {
const formulaName = desired.split('/').at(-1);
return formulaName === current;
}

return desired === current;
return false;
},
}
}

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

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

await this.installFormulae(formulaeToInstall);
await this.uninstallFormulae(formulaeToUninstall);
await this.installFormulae(formulaeToInstall);
}

async remove(valueToRemove: string[]): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/resources/homebrew/tap-parameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class TapsParameter extends StatefulParameter<HomebrewConfig, string[]> {
return;
}

await codifySpawn(`brew tap ${taps.join(' ')}`)
await codifySpawn(`HOMEBREW_NO_AUTO_UPDATE=1 brew tap ${taps.join(' ')}`)
}

private async uninstallTaps(taps: string[]): Promise<void> {
Expand Down
24 changes: 24 additions & 0 deletions src/resources/python/pip-sync/pip-sync-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://www.codifycli.com/pip-sync.json",
"title": "Pip-sync resource",
"type": "object",
"description": "Install and manage though pip-tools by installing + uninstalling packages using pip-sync",
"properties": {
"virtualEnv": {
"type": "string",
"description": "A virtual env to activate before issuing commands."
},
"requirementFiles": {
"type": "array",
"items": { "type": "string" },
"description": "A list of requirement files to supply pip-sync."
},
"cwd": {
"type": "string",
"description": "The working directory to run commands from. If cwd is supplied, the other parameters can be specified as relative to cwd."
}
},
"additionalProperties": false,
"required": ["requirementFiles"]
}
59 changes: 59 additions & 0 deletions src/resources/python/pip-sync/pip-sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { CreatePlan, DestroyPlan, RefreshContext, Resource, ResourceSettings, getPty } from 'codify-plugin-lib';
import { ResourceConfig } from 'codify-schemas';

import { codifySpawn } from '../../../utils/codify-spawn.js';
import schema from './pip-sync-schema.json'
import { RequirementFilesParameter } from './requirement-files-parameter.js';

export interface PipSyncConfig extends ResourceConfig {
requirementFiles: string[];
virtualEnv?: string;
cwd?: string;
}

export class PipSync extends Resource<PipSyncConfig> {
getSettings(): ResourceSettings<PipSyncConfig> {
return {
id: 'pip-sync',
schema,
parameterSettings: {
requirementFiles: { type: 'stateful', definition: new RequirementFilesParameter() },
virtualEnv: { type: 'directory', setting: true },
cwd: { type: 'directory', setting: true }
},
dependencies: ['pyenv', 'pip', 'venv-project', 'virtualenv-project', 'virtualenv'],
allowMultiple: {
identifyingParameters: ['virtualEnv'],
}
};
}

async refresh(parameters: Partial<PipSyncConfig>, context: RefreshContext<PipSyncConfig>): Promise<Partial<PipSyncConfig> | Partial<PipSyncConfig>[] | null> {
const pty = getPty()

const { status: pipStatus } = await pty.spawnSafe(PipSync.withVirtualEnv('which pip', parameters.virtualEnv), { cwd: parameters.cwd ?? undefined });
if (pipStatus === 'error') {
return null;
}

const { status: pipSyncStatus } = await pty.spawnSafe(PipSync.withVirtualEnv('which pip-sync', parameters.virtualEnv), { cwd: parameters.cwd ?? undefined })
return pipSyncStatus === 'error' ? null : parameters;
}

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

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

static withVirtualEnv(command: string, virtualEnv?: string, ): string {
if (!virtualEnv) {
return command;
}

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

}
56 changes: 56 additions & 0 deletions src/resources/python/pip-sync/requirement-files-parameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ArrayParameterSetting, Plan, StatefulParameter, getPty } from 'codify-plugin-lib';

import { codifySpawn } from '../../../utils/codify-spawn.js';
import { PipSyncConfig } from './pip-sync.js';

export class RequirementFilesParameter extends StatefulParameter<PipSyncConfig, string[]> {
getSettings(): ArrayParameterSetting {
return {
type: 'array',
itemType: 'directory',
canModify: true,
}
}

async refresh(desired: null | string[], config: Partial<PipSyncConfig>): Promise<null | string[]> {
if (!desired || desired?.length === 0) {
return null;
}

const pty = getPty();
const { status } = await pty.spawnSafe(
this.appendVirtualEnv(`pip-sync -n ${desired?.join(' ')}`, config.virtualEnv),
{ cwd: config.cwd ?? undefined }
)
return status === 'error' ? null : desired;
}

async add(valueToAdd: string[], plan: Plan<PipSyncConfig>): Promise<void> {
await codifySpawn(
this.appendVirtualEnv(`pip-sync ${valueToAdd.join(' ')}`, plan.desiredConfig?.virtualEnv),
{ cwd: plan.desiredConfig?.cwd ?? undefined }
)
}

async modify(newValue: string[], _: string[], plan: Plan<PipSyncConfig>): Promise<void> {
await codifySpawn(
this.appendVirtualEnv(`pip-sync ${newValue.join(' ')}`, plan.desiredConfig?.virtualEnv),
{ cwd: plan.desiredConfig?.cwd ?? undefined }
)
}

async remove(valueToRemove: string[], plan: Plan<PipSyncConfig>): Promise<void> {
await codifySpawn(
this.appendVirtualEnv(`pip-sync ${valueToRemove.join(' ')}`, plan.currentConfig?.virtualEnv),
{ cwd: plan.currentConfig?.cwd ?? undefined }
)
}

private appendVirtualEnv(command: string, virtualEnv?: string): string {
if (!virtualEnv) {
return command;
}

return `( set -e; source ${virtualEnv}/bin/activate; ${command} --python-executable ${virtualEnv}/bin/python; deactivate )`
}
}
38 changes: 38 additions & 0 deletions src/resources/python/pip/pip-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://www.codifycli.com/pip.json",
"title": "Pip resource",
"type": "object",
"description": "Install and manage packages using pip",
"properties": {
"virtualEnv": {
"type": "string",
"description": "A virtual env to activate before issuing pip commands."
},
"install": {
"type": "array",
"description": "Packages to install.",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
}
},
"required": ["name"]
}
]
}
}
},
"additionalProperties": false,
"required": ["install"]
}
Loading
Loading