Skip to content

Commit 1cc245b

Browse files
committed
feat: Added distro filtering
1 parent d7c9f1a commit 1cc245b

7 files changed

Lines changed: 305 additions & 18 deletions

File tree

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
},
66
"dependencies": {
77
"@codifycli/ink-form": "0.0.12",
8-
"@codifycli/schemas": "1.0.0",
8+
"@codifycli/schemas": "1.1.0-beta4",
99
"@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
1010
"@inkjs/ui": "^2",
1111
"@mischnic/json-sourcemap": "^0.1.1",

src/common/initialize-plugins.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class PluginInitOrchestrator {
4545
if (!args.noProgress) ctx.subprocessFinished(SubProcessName.INITIALIZE_PLUGINS)
4646

4747
project.removeResourcesUsingOsFilter();
48+
await project.removeResourcesUsingDistroFilter();
4849

4950
return { resourceDefinitions, pluginManager, project };
5051
}

src/entities/project.test.ts

Lines changed: 228 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1-
import { describe, expect, it } from 'vitest';
1+
import { LinuxDistro, ResourceOs } from '@codifycli/schemas';
2+
import { describe, expect, it, vi } from 'vitest';
3+
4+
import { OsUtils } from '../utils/os-utils.js';
25
import { Project } from './project.js';
36
import { ResourceConfig } from './resource-config.js';
4-
import { InMemoryFile } from '../parser/entities';
7+
8+
function makeResource(type: string, os?: ResourceOs[], distro?: LinuxDistro[]): ResourceConfig {
9+
return new ResourceConfig({ type, ...(os ? { os } : {}), ...(distro ? { distro } : {}) });
10+
}
11+
12+
function makeProject(...configs: ResourceConfig[]): Project {
13+
return new Project(null, configs, []);
14+
}
515

616
describe('Project Unit Tests', () => {
717
it('Can add unique names for duplicate resources', async () => {
@@ -26,4 +36,219 @@ describe('Project Unit Tests', () => {
2636
// expect(project.resourceConfigs[3].id).to.eq('other')
2737
})
2838

29-
})
39+
describe('removeResourcesUsingOsFilter', () => {
40+
it('keeps resources with no os filter', () => {
41+
vi.spyOn(OsUtils, 'getOs').mockReturnValue(ResourceOs.MACOS);
42+
43+
const project = makeProject(
44+
makeResource('tool-a'),
45+
makeResource('tool-b'),
46+
);
47+
48+
project.removeResourcesUsingOsFilter();
49+
50+
expect(project.resourceConfigs).toHaveLength(2);
51+
});
52+
53+
it('keeps resources that match the current os', () => {
54+
vi.spyOn(OsUtils, 'getOs').mockReturnValue(ResourceOs.MACOS);
55+
56+
const project = makeProject(
57+
makeResource('mac-tool', [ResourceOs.MACOS]),
58+
makeResource('linux-tool', [ResourceOs.LINUX]),
59+
);
60+
61+
project.removeResourcesUsingOsFilter();
62+
63+
expect(project.resourceConfigs).toHaveLength(1);
64+
expect(project.resourceConfigs[0].type).toBe('mac-tool');
65+
});
66+
67+
it('keeps resources that list multiple os including the current one', () => {
68+
vi.spyOn(OsUtils, 'getOs').mockReturnValue(ResourceOs.LINUX);
69+
70+
const project = makeProject(
71+
makeResource('cross-platform', [ResourceOs.MACOS, ResourceOs.LINUX]),
72+
);
73+
74+
project.removeResourcesUsingOsFilter();
75+
76+
expect(project.resourceConfigs).toHaveLength(1);
77+
});
78+
79+
it('removes resources whose os does not match the current os', () => {
80+
vi.spyOn(OsUtils, 'getOs').mockReturnValue(ResourceOs.WINDOWS);
81+
82+
const project = makeProject(
83+
makeResource('mac-only', [ResourceOs.MACOS]),
84+
makeResource('linux-only', [ResourceOs.LINUX]),
85+
);
86+
87+
project.removeResourcesUsingOsFilter();
88+
89+
expect(project.resourceConfigs).toHaveLength(0);
90+
});
91+
});
92+
93+
describe('removeResourcesUsingDistroFilter', () => {
94+
it('does nothing on macOS (not Linux)', async () => {
95+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(false);
96+
97+
const project = makeProject(
98+
makeResource('tool', undefined, [LinuxDistro.UBUNTU]),
99+
);
100+
101+
await project.removeResourcesUsingDistroFilter();
102+
103+
expect(project.resourceConfigs).toHaveLength(1);
104+
});
105+
106+
it('does nothing when distro cannot be determined', async () => {
107+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
108+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(undefined);
109+
110+
const project = makeProject(
111+
makeResource('tool', undefined, [LinuxDistro.UBUNTU]),
112+
);
113+
114+
await project.removeResourcesUsingDistroFilter();
115+
116+
expect(project.resourceConfigs).toHaveLength(1);
117+
});
118+
119+
it('keeps resources with no distro filter', async () => {
120+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
121+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.UBUNTU);
122+
123+
const project = makeProject(
124+
makeResource('tool-a'),
125+
makeResource('tool-b'),
126+
);
127+
128+
await project.removeResourcesUsingDistroFilter();
129+
130+
expect(project.resourceConfigs).toHaveLength(2);
131+
});
132+
133+
it('keeps resources that match the current distro exactly', async () => {
134+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
135+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.UBUNTU);
136+
137+
const project = makeProject(
138+
makeResource('ubuntu-tool', undefined, [LinuxDistro.UBUNTU]),
139+
makeResource('arch-tool', undefined, [LinuxDistro.ARCH]),
140+
);
141+
142+
await project.removeResourcesUsingDistroFilter();
143+
144+
expect(project.resourceConfigs).toHaveLength(1);
145+
expect(project.resourceConfigs[0].type).toBe('ubuntu-tool');
146+
});
147+
148+
it('keeps resources when current distro matches debian-based group', async () => {
149+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
150+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.UBUNTU);
151+
152+
const project = makeProject(
153+
makeResource('debian-tool', undefined, [LinuxDistro.DEBIAN_BASED]),
154+
makeResource('rpm-tool', undefined, [LinuxDistro.RPM_BASED]),
155+
);
156+
157+
await project.removeResourcesUsingDistroFilter();
158+
159+
expect(project.resourceConfigs).toHaveLength(1);
160+
expect(project.resourceConfigs[0].type).toBe('debian-tool');
161+
});
162+
163+
it('keeps resources when current distro matches rpm-based group', async () => {
164+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
165+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.FEDORA);
166+
167+
const project = makeProject(
168+
makeResource('debian-tool', undefined, [LinuxDistro.DEBIAN_BASED]),
169+
makeResource('rpm-tool', undefined, [LinuxDistro.RPM_BASED]),
170+
);
171+
172+
await project.removeResourcesUsingDistroFilter();
173+
174+
expect(project.resourceConfigs).toHaveLength(1);
175+
expect(project.resourceConfigs[0].type).toBe('rpm-tool');
176+
});
177+
178+
it('debian-based group covers all expected distros', async () => {
179+
const debianDistros = [
180+
LinuxDistro.DEBIAN,
181+
LinuxDistro.UBUNTU,
182+
LinuxDistro.MINT,
183+
LinuxDistro.POP_OS,
184+
LinuxDistro.ELEMENTARY_OS,
185+
LinuxDistro.KALI,
186+
];
187+
188+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
189+
190+
for (const distro of debianDistros) {
191+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(distro);
192+
193+
const project = makeProject(
194+
makeResource('tool', undefined, [LinuxDistro.DEBIAN_BASED]),
195+
);
196+
197+
await project.removeResourcesUsingDistroFilter();
198+
199+
expect(project.resourceConfigs).toHaveLength(1);
200+
}
201+
});
202+
203+
it('rpm-based group covers all expected distros', async () => {
204+
const rpmDistros = [
205+
LinuxDistro.FEDORA,
206+
LinuxDistro.CENTOS,
207+
LinuxDistro.RHEL,
208+
LinuxDistro.AMAZON_LINUX,
209+
LinuxDistro.OPENSUSE,
210+
LinuxDistro.SUSE,
211+
];
212+
213+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
214+
215+
for (const distro of rpmDistros) {
216+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(distro);
217+
218+
const project = makeProject(
219+
makeResource('tool', undefined, [LinuxDistro.RPM_BASED]),
220+
);
221+
222+
await project.removeResourcesUsingDistroFilter();
223+
224+
expect(project.resourceConfigs).toHaveLength(1);
225+
}
226+
});
227+
228+
it('removes resources when no distro in filter matches the current distro', async () => {
229+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
230+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.ARCH);
231+
232+
const project = makeProject(
233+
makeResource('tool', undefined, [LinuxDistro.UBUNTU, LinuxDistro.DEBIAN]),
234+
);
235+
236+
await project.removeResourcesUsingDistroFilter();
237+
238+
expect(project.resourceConfigs).toHaveLength(0);
239+
});
240+
241+
it('keeps resources that list multiple distros including the current one', async () => {
242+
vi.spyOn(OsUtils, 'isLinux').mockReturnValue(true);
243+
vi.spyOn(OsUtils, 'getLinuxDistro').mockResolvedValue(LinuxDistro.ARCH);
244+
245+
const project = makeProject(
246+
makeResource('tool', undefined, [LinuxDistro.UBUNTU, LinuxDistro.ARCH]),
247+
);
248+
249+
await project.removeResourcesUsingDistroFilter();
250+
251+
expect(project.resourceConfigs).toHaveLength(1);
252+
});
253+
});
254+
});

src/entities/project.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { ResourceDefinitionMap } from '../plugins/plugin-manager.js';
1515
import { DependencyGraphResolver } from '../utils/dependency-graph-resolver.js';
1616
import { groupBy } from '../utils/index.js';
1717
import { OsUtils } from '../utils/os-utils.js';
18-
import { ShellUtils } from '../utils/shell.js';
1918
import { ConfigBlock, ConfigType } from './config.js';
2019
import { type Plan } from './plan.js';
2120
import { ProjectConfig } from './project-config.js';
@@ -187,12 +186,12 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
187186
}
188187

189188
if (os.type() === OS.Linux) {
190-
const currentDistro = await ShellUtils.getLinuxDistro();
189+
const currentDistro = await OsUtils.getLinuxDistro();
191190
if (!currentDistro) {
192191
throw new Error('Unable to determine Linux distribution');
193192
}
194193

195-
this.resourceConfigs.filter((c) => {
194+
const distroInvalidConfigs = this.resourceConfigs.filter((c) => {
196195
const distros = resourceDefinitions.get(c.type)?.linuxDistros;
197196
if (!distros) {
198197
return false;
@@ -201,8 +200,8 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
201200
return !distros.includes(currentDistro);
202201
});
203202

204-
if (invalidConfigs.length > 0) {
205-
throw new LinuxDistroNotSupportedError(invalidConfigs, this.sourceMaps);
203+
if (distroInvalidConfigs.length > 0) {
204+
throw new LinuxDistroNotSupportedError(distroInvalidConfigs, this.sourceMaps);
206205
}
207206
}
208207
}
@@ -217,6 +216,25 @@ ${JSON.stringify(projectConfigs, null, 2)}`);
217216
});
218217
}
219218

219+
async removeResourcesUsingDistroFilter() {
220+
if (!OsUtils.isLinux()) {
221+
return;
222+
}
223+
224+
const currentDistro = await OsUtils.getLinuxDistro();
225+
if (!currentDistro) {
226+
return;
227+
}
228+
229+
this.resourceConfigs = this.resourceConfigs.filter((r) => {
230+
if (!r.distro || r.distro.length === 0) {
231+
return true;
232+
}
233+
234+
return r.distro.some((d) => OsUtils.distroMatchesCurrent(d, currentDistro));
235+
});
236+
}
237+
220238
resolveDependenciesAndCalculateEvalOrder(resourceDefinitions?: ResourceDefinitionMap) {
221239
this.resolveResourceDependencies(resourceDefinitions);
222240
this.calculateEvaluationOrder();

0 commit comments

Comments
 (0)