Skip to content
Open
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
34 changes: 34 additions & 0 deletions packages/targets/pkg-fdroid/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import adapter from './index.js';
smokeTest(adapter, { idPrefix: 'pkg', requireKind: true });

const tempDirs: string[] = [];
const invalidPackageNames = ['../escape', 'com.acme/app', 'com..acme', '1com.acme.app', 'com.int.app'];

afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
Expand Down Expand Up @@ -79,6 +80,25 @@ describe('F-Droid target', () => {
});
});

it('rejects invalid package names before writing metadata artifacts', async () => {
const outDir = await mkdtemp(join(tmpdir(), 'sh1pt-fdroid-'));
tempDirs.push(outDir);
const ctx = fakeBuildContext({ outDir }) as any;
const metadata = {
categories: ['Development'],
license: 'MIT',
sourceRepo: 'https://github.com/acme/app',
};

for (const packageName of invalidPackageNames) {
await expect(adapter.build(ctx, {
packageName,
mode: 'main-repo',
metadata,
})).rejects.toThrow('packageName');
}
});

it('keeps dry-run shipping side-effect free for main repo submissions', async () => {
await expect(adapter.ship(fakeShipContext({ dryRun: true }) as any, {
packageName: 'com.acme.app',
Expand All @@ -96,4 +116,18 @@ describe('F-Droid target', () => {
},
});
});

it('rejects invalid package names before shipping', async () => {
for (const packageName of invalidPackageNames) {
await expect(adapter.ship(fakeShipContext({ dryRun: true }) as any, {
packageName,
mode: 'main-repo',
metadata: {
categories: ['Development'],
license: 'Apache-2.0',
sourceRepo: 'https://github.com/acme/app',
},
})).rejects.toThrow('packageName');
}
});
});
21 changes: 21 additions & 0 deletions packages/targets/pkg-fdroid/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,31 @@ function resolveRepoDir(ctx: { projectDir: string }, config: Config): string {
return isAbsolute(dir) ? dir : join(ctx.projectDir, dir);
}

const JAVA_RESERVED_WORDS = new Set([
'abstract', 'assert', 'boolean', 'break', 'byte', 'case', 'catch', 'char',
'class', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum',
'extends', 'final', 'finally', 'float', 'for', 'goto', 'if', 'implements',
'import', 'instanceof', 'int', 'interface', 'long', 'native', 'new',
'package', 'private', 'protected', 'public', 'return', 'short', 'static',
'strictfp', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws',
'transient', 'try', 'void', 'volatile', 'while', 'true', 'false', 'null',
]);

function assertPackageName(packageName: string): void {
const segment = '[A-Za-z][A-Za-z0-9_]*';
const pattern = new RegExp(`^${segment}(\\.${segment})+$`);
const segments = packageName.split('.');
if (!pattern.test(packageName) || segments.some((part) => JAVA_RESERVED_WORDS.has(part))) {
throw new Error(`packageName must be a valid Android package name, got "${packageName}"`);
}
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

export default defineTarget<Config>({
id: 'pkg-fdroid',
kind: 'package-manager',
label: 'F-Droid (Android FOSS repo)',
async build(ctx, config) {
assertPackageName(config.packageName);
if (config.mode === 'main-repo') {
ctx.log(`render fdroiddata metadata for ${config.packageName}`);
const metadataPath = join(ctx.outDir, `${config.packageName}.yml`);
Expand Down Expand Up @@ -109,6 +129,7 @@ export default defineTarget<Config>({
return { artifact: cwd };
},
async ship(ctx, config) {
assertPackageName(config.packageName);
if (config.mode === 'main-repo') {
ctx.log(`open PR against fdroiddata for ${config.packageName}`);
if (ctx.dryRun) {
Expand Down
Loading