Skip to content

Commit

Permalink
feat(developer): verify package version number format in kmc-package
Browse files Browse the repository at this point in the history
Ensures that version number field is not blank, and is a recognized
format matching the pattern 'number[.number[.number]]'. Each number
component should be an integer, without leading zeroes.

The format chosen is more restrictive than semver, because much of our
existing infrastructure assumes this format anyway. This does lead to
a mismatch in version number format for LDML keyboards, which do support
semver according to the spec, so this may need to be revisited in the
future to allow for full semver formats.

Fixes: #13067
  • Loading branch information
mcdurdin committed Feb 3, 2025
1 parent 1dae865 commit 6dbc20f
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,5 +184,12 @@ export class PackageCompilerMessages {
static Warn_VisualKeyboardFileIsInvalid = (o:{filename: string}) => m(
this.WARN_VisualKeyboardFileIsInvalid, `Visual keyboard file '${def(o.filename)}' is invalid.`
);

static WARN_PackageVersionIsUnrecognizedFormat = SevWarn | 0x0027;
static Warn_PackageVersionIsUnrecognizedFormat = (o:{version: string}) => m(
this.WARN_PackageVersionIsUnrecognizedFormat, `Package version '${def(o.version)}' has an unrecognized format.`,
`The format for version numbers should be number[.number[.number]]. Each
number component should be an integer, without leading zeroes.`
);
}

Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export class PackageVersionValidator {
kmp.info.version = {description: kmp.keyboards[0].version};
}

if(result && !isValidVersionNumber(kmp.info.version.description)) {
this.callbacks.reportMessage(PackageCompilerMessages.Warn_PackageVersionIsUnrecognizedFormat({version: kmp.info.version.description}));
return false;
}

return result;
}

Expand All @@ -109,3 +114,11 @@ export class PackageVersionValidator {
return true;
}
}

function isValidVersionNumber(version: string) {
return /^[0-9]+(\.(0|[1-9]+[0-9]*)){0,2}$/.test(version);
}

export const unitTestEndpoints = {
isValidVersionNumber,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<Package>
<System>
<KeymanDeveloperVersion>15.0.266.0</KeymanDeveloperVersion>
<FileVersion>7.0</FileVersion>
</System>
<Info>
<!-- blank name -->
<Name URL="">warn_package_version_is_unrecognized_format</Name>
<Copyright URL="">© 2019 National Research Council Canada</Copyright>
<Author URL="mailto:[email protected]">Eddie Antonio Santos</Author>
<Version>1.0-invalid</Version>
</Info>
<Files>
<File>
<Name>basic.kmx</Name>
<Description>Keyboard Basic</Description>
<CopyLocation>0</CopyLocation>
<FileType>.kmx</FileType>
</File>
</Files>
<Keyboards>
<Keyboard>
<Name>Basic</Name>
<ID>basic</ID>
<Version>1.0</Version>
<Languages>
<Language ID="km">Central Khmer (Khmer, Cambodia)</Language>
</Languages>
</Keyboard>
</Keyboards>
</Package>
5 changes: 5 additions & 0 deletions developer/src/kmc-package/test/messages.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,9 @@ describe('PackageCompilerMessages', function () {
await testForMessage(this, ['invalid', 'error_package_file_has_empty_version.kps'],
PackageCompilerMessages.ERROR_PackageFileHasEmptyVersion);
});

it('should generate Warn_PackageVersionIsUnrecognizedFormat if package version field is not a valid format', async function() {
await testForMessage(this, ['invalid', 'warn_package_version_is_unrecognized_format.kps'],
PackageCompilerMessages.WARN_PackageVersionIsUnrecognizedFormat);
});
});
34 changes: 32 additions & 2 deletions developer/src/kmc-package/test/versioning.tests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'mocha';
import { assert } from 'chai';
import { TestCompilerCallbacks } from '@keymanapp/developer-test-helpers';
import { makePathToFixture } from './helpers/index.js';
import { KmpCompiler } from '../src/compiler/kmp-compiler.js';
import { KmpJsonFile } from '@keymanapp/common-types';
import { KmpCompiler } from '../src/compiler/kmp-compiler.js';
import { unitTestEndpoints } from '../src/compiler/package-version-validator.js';
import { makePathToFixture } from './helpers/index.js';

// This unit test was translated from a Delphi test
// Keyman.Test.System.CompilePackageVersioningTest, but note the difference in
Expand Down Expand Up @@ -41,4 +42,33 @@ describe('package versioning', function () {
assert.isTrue(kmpJson !== null);
});
}

it('should validate recognized version numbers', function() {
const goodVersions = [
'1.0',
'1',
'1.0.2',
'2.2.3',
'9',
'0',
], badVersions = [
'0.1m',
'2.3.4.5',
'1.03.4',
'test',
'1.0-beta',
'',
null,
undefined,
'xxx',
];

for(const v of goodVersions) {
assert.isTrue(unitTestEndpoints.isValidVersionNumber(v), `'${v}' should be recognized as a valid version number`);
}

for(const v of badVersions) {
assert.isFalse(unitTestEndpoints.isValidVersionNumber(v), `'${v}' should be recognized as an invalid version number`);
}
});
});

0 comments on commit 6dbc20f

Please sign in to comment.