getNextBuildNumber method

Future<String> getNextBuildNumber(
  1. String environment, {
  2. String? baseDir,
})

Generates the next build number for the given environment.

The returned number is 3-digit zero-padded (e.g., '001'). This operation is thread/process-safe via file locking.

Implementation

Future<String> getNextBuildNumber(String environment, {String? baseDir}) async {
  final dirPath = baseDir != null ? '$baseDir/$_stateDir' : _stateDir;
  final filePath = '$dirPath/$_stateFile';

  final dir = Directory(dirPath);
  if (!dir.existsSync()) {
    dir.createSync(recursive: true);
  }

  final file = File(filePath);
  if (!file.existsSync()) {
    file.writeAsStringSync('{}');
  }

  RandomAccessFile? raf;
  try {
    raf = file.openSync(mode: FileMode.append);
    raf.lockSync(FileLock.exclusive);

    final contentBytes = file.readAsBytesSync();
    String contentString = contentBytes.isEmpty ? '{}' : utf8.decode(contentBytes);

    Map<String, dynamic> jsonMap;
    try {
      jsonMap = json.decode(contentString);
    } catch (_) {
      jsonMap = {};
    }

    var state = BuildStateModel.fromJson(jsonMap);

    final today = DateFormat('yyyyMMdd').format(DateTime.now());
    int newCounter = 1;

    if (state.lastDate == today) {
      final currentCounter = state.counters[environment] ?? 0;
      newCounter = currentCounter + 1;

      final newCounters = Map<String, int>.from(state.counters);
      newCounters[environment] = newCounter;

      state = BuildStateModel(lastDate: today, counters: newCounters);
    } else {
      state = BuildStateModel(
        lastDate: today,
        counters: {environment: 1},
      );
    }

    final newContent = json.encode(state.toJson());
    raf.setPositionSync(0);
    raf.truncateSync(0);
    raf.writeStringSync(newContent);

    return newCounter.toString().padLeft(3, '0');
  } catch (e) {
    throw ReleaseManagerException('Failed to manage build counters safely.', details: e);
  } finally {
    try {
      raf?.unlockSync();
      raf?.closeSync();
    } catch (_) {}
  }
}