Skip to content

Commit

Permalink
feat(update_version): set the file to update
Browse files Browse the repository at this point in the history
  • Loading branch information
asartalo committed Feb 11, 2022
1 parent 083c048 commit 104e677
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 11 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,40 @@ The following command will update the version on `pubspec.yaml` on the current d
$ release_tools update_version 1.0.1
```

If you want to update the version on a text file other than `pubspec.yaml`, use the `--file` option:

```sh
$ release_tools update_version --file="README.md" 1.0.1
```

By default, it will look for strings that look like semver-flavored version strings. If you want to be specific, you can specify a template for replacement. For example, if the `README.md` file contains the following text:

```markdown
# My Project

Current Version: 1.0.0

Starting from version 1.0.0, all alert buttons will be red.

```

Running the following command...

```sh
$ release_tools update_version --file="README.md" --template="Current Version: [VERSION]" 1.0.1
```

...will change the contents of `README.md` to:

```markdown
# My Project

Current Version: 1.0.1

Starting from version 1.0.0, all alert buttons will be red.

```

### next_version

```sh
Expand Down
2 changes: 1 addition & 1 deletion lib/prepare_release_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class PrepareReleaseCommand extends ReleaseToolsCommand {
version: nextVersion,
);

await updateVersionCommand.updateVersionOnPubspecFile(nextVersion);
await updateVersionCommand.updateVersionOnFile(nextVersion);
if (summary is ChangeSummary) {
if (args['writeSummary'] as bool) {
final project = changelogCommand.project;
Expand Down
4 changes: 4 additions & 0 deletions lib/project.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class Project {
return pubspec().exists();
}

Future<bool> fileExists(String fileName) {
return getFile(fileName).exists();
}

Future<void> writeToChangelog(String contents) async {
await changelog().writeAsString(contents);
}
Expand Down
109 changes: 103 additions & 6 deletions lib/update_version_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import 'printer.dart';
import 'project.dart';
import 'release_tools_command.dart';

// Taken from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
const validSemverRegex =
r'\b(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?\b';

const pubspecFile = 'pubspec.yaml';

class UpdateVersionCommand extends ReleaseToolsCommand {
final Project project;

Expand All @@ -26,25 +32,54 @@ class UpdateVersionCommand extends ReleaseToolsCommand {
final takesArguments = true;

@override
final usageFooter = helpFooter('release_tools update_version 2.0.1');
final usageFooter = helpFooter('''
release_tools update_version 2.0.1
release_tools udpate_version --file="README.md" --template="Current Version: [VERSION]"''');

UpdateVersionCommand({
required this.project,
required this.printer,
}) : super();
}) {
argParser.addOption(
'template',
abbr: 't',
help: 'Replace version on file using string template and "[VERSION]"',
defaultsTo: '',
);
argParser.addOption(
'file',
abbr: 'f',
help: 'Set the file to update',
defaultsTo: pubspecFile,
);
}

@override
Future<void> run() async {
if (argResults is ArgResults) {
final template = _getTemplate();
final newVersion = _getNewVersion(argResults!.rest);
await updateVersionOnPubspecFile(newVersion);
await updateVersionOnFile(
newVersion,
file: await _getFile(),
template: template,
);
printer.printSuccess('Updated version to "$newVersion".');
}
}

Future<void> updateVersionOnPubspecFile(String newVersion) async {
final pubspecFile = await _getPubspecFile();
await _updateVersionOnFile(pubspecFile, newVersion);
await _updateVersionOnPubspecFile(await _getPubspecFile(), newVersion);
}

Future<void> updateVersionOnFile(String newVersion,
{File? file, String template = ''}) async {
final theFile = file ?? await _getPubspecFile();
if (theFile.basename == pubspecFile) {
await _updateVersionOnPubspecFile(theFile, newVersion);
} else {
await _updateVersionOnFileWithTemplate(theFile, template, newVersion);
}
}

String _getNewVersion(List<String> arguments) {
Expand All @@ -55,14 +90,39 @@ class UpdateVersionCommand extends ReleaseToolsCommand {
return arguments.first;
}

String _getTemplate() {
final template = argResults?['template'];
if (template is String) {
return template;
}
return '';
}

String _getFilePath() {
final fileArgument = argResults?['file'];
if (fileArgument is String) {
return fileArgument;
}
return '';
}

Future<File> _getFile() async {
final fileArgument = _getFilePath();
if (fileArgument != pubspecFile) {
return project.getFile(fileArgument);
}
return _getPubspecFile();
}

Future<File> _getPubspecFile() async {
if (!await project.pubspecExists()) {
throw MissingPubspecError(project.workingDir);
}
return project.pubspec();
}

Future<void> _updateVersionOnFile(File pubspecFile, String newVersion) async {
Future<void> _updateVersionOnPubspecFile(
File pubspecFile, String newVersion) async {
final contents = await pubspecFile.readAsString();
final doc = loadYamlNode(contents) as YamlMap;
final versionNode = doc.nodes['version'];
Expand All @@ -78,11 +138,48 @@ class UpdateVersionCommand extends ReleaseToolsCommand {
}
}

Future<void> _updateVersionOnFileWithTemplate(
File file, String template, String newVersion) async {
final contents = await file.readAsString();
await file.writeAsString(
replaceVersionWithTemplate(contents, template, newVersion),
);
}

String replaceVersionWithTemplate(
String contents,
String template,
String version,
) {
if (template == '') {
final regex = RegExp(validSemverRegex);
return contents.replaceAll(regex, version);
}

final versionPosition = template.indexOf('[VERSION]');
if (versionPosition == -1) {
return contents;
}
final prefix = template.substring(0, versionPosition);
final suffix = template.substring(versionPosition + 9);
final regex = RegExp(
"(${RegExp.escape(prefix)})($validSemverRegex)(${RegExp.escape(suffix)})");

return contents.replaceAllMapped(regex, (match) {
return '${match.group(1)}$version${match.group(8)}';
});
}

class MissingPubspecError extends StateError {
MissingPubspecError(String workingDir)
: super('There is no pubspec.yaml in $workingDir');
}

class MissingFileError extends StateError {
MissingFileError(String workingDir, String filePath)
: super('There is no file "$filePath" in $workingDir');
}

class InvalidPubspecError extends StateError {
InvalidPubspecError(String message) : super(message);
}
106 changes: 102 additions & 4 deletions test/update_version_command_test.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:release_tools/release_tools_runner.dart';
import 'package:release_tools/printer.dart';
import 'package:release_tools/release_tools_runner.dart';
import 'package:release_tools/update_version_command.dart';
import 'package:test/test.dart';
import 'runner_setup.dart';
Expand All @@ -22,13 +22,27 @@ void main() {
printer = context.printer;
});

Future<void> writeFile(String fileName, String contents) async {
final file = fs.directory(workingDir).childFile(fileName);
await file.writeAsString(contents);
}

Future<void> validYamlFile() async {
final file = fs.directory(workingDir).childFile('pubspec.yaml');
await file.writeAsString(kValidPubspecFile);
await writeFile('pubspec.yaml', kValidPubspecFile);
}

Future<String> getFileContents(String fileName) async {
return fs
.file(fs.directory(workingDir).childFile(fileName))
.readAsString();
}

Future<File> getFile(String fileName) async {
return fs.file(fs.directory(workingDir).childFile(fileName));
}

Future<File> getPubFile() async {
return fs.file(fs.directory(workingDir).childFile('pubspec.yaml'));
return getFile('pubspec.yaml');
}

group('errors', () {
Expand Down Expand Up @@ -83,6 +97,78 @@ dev_dependencies:
});
});

group('updates a version to a specified file', () {
setUp(() async {
await writeFile('README.md', '''
Hello.
This is version 1.0.0. Everything is awesome.
''');
await runner.run([
'update_version',
"--template=This is version [VERSION].",
'--file=README.md',
'2.0.0',
]);
});

test('it updates the version in file', () async {
final contents = await getFileContents('README.md');
expect(contents, equals('''
Hello.
This is version 2.0.0. Everything is awesome.
'''));
});
});

group('when template and file are set', () {
const newVersion = '2.0.0';
final testData = {
'with just prefix': TemplateTest(
contents: 'Hello.\nThis is version 1.0.0.\nEverything is awesome.',
template: 'version [VERSION]',
expectedResults:
'Hello.\nThis is version $newVersion.\nEverything is awesome.',
),
'when template just [VERSION]': TemplateTest(
contents: 'Hello.\nThis is version 1.0.0.\nEverything is awesome.',
template: '[VERSION]',
expectedResults:
'Hello.\nThis is version $newVersion.\nEverything is awesome.',
),
'When template is empty': TemplateTest(
contents: 'Hello.\nThis is version 1.0.0.\nEverything is awesome.',
template: '',
expectedResults:
'Hello.\nThis is version $newVersion.\nEverything is awesome.',
),
'When version is not specified': TemplateTest(
contents: 'Hello.\nThis is version 1.0.0.\nEverything is awesome.',
template: 'This is versino .',
expectedResults:
'Hello.\nThis is version 1.0.0.\nEverything is awesome.',
),
};

testData.forEach((description, data) {
group(description, () {
setUp(() async {
await writeFile('README.md', data.contents);
await runner.run([
'update_version',
"--template=${data.template}",
'--file=README.md',
newVersion,
]);
});

test('it updates the version in file correctly', () async {
final contents = await getFileContents('README.md');
expect(contents, equals(data.expectedResults));
});
});
});
});

test('it prints help text', () async {
await runner.run(['update_version', '--help']);
final helpText = printer.prints.join('\n');
Expand All @@ -96,6 +182,18 @@ dev_dependencies:
});
}

class TemplateTest {
final String template;
final String contents;
final String expectedResults;

TemplateTest({
required this.template,
required this.contents,
required this.expectedResults,
});
}

const kValidPubspecFile = '''
name: foo_bar
description: A sample pubspec file._file
Expand Down

0 comments on commit 104e677

Please sign in to comment.