build_manager 코드 추가

This commit is contained in:
leedongmyung[desktop] 2023-10-29 21:06:12 +09:00
parent d60768a63d
commit 9c92c64e0b
26 changed files with 2732 additions and 0 deletions

View file

@ -3,8 +3,10 @@ import 'package:oto_cli/cli/commands/command_start.dart';
import 'package:oto_cli/cli/cli.dart';
import 'package:oto_cli/cli/commands/command_template.dart';
import 'package:oto_cli/oto/application.dart';
void main(List<String> arguments) async {
var app = Application.to;
CLI.initialize('OTO', arguments, [
CommandTemplate(),
CommandInstall(),

151
lib/oto/application.dart Normal file
View file

@ -0,0 +1,151 @@
import 'dart:async';
import 'dart:io';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:oto_cli/oto/data/jenkins_data.dart';
import 'package:oto_cli/oto/test_data.dart';
class Application {
Application._privateConstructor();
static final Application _instance = Application._privateConstructor();
static Application get to => _instance;
Map<String, dynamic>? _property;
Map<String, dynamic>? get property {
return _property;
}
DataJenkins? _jenkinsData;
DataJenkins? get jenkinsData {
return _jenkinsData;
}
Future<void> build(List<String> arguments) async {
// await test();
// return;
Map<String, dynamic>? buildMap;
Map<String, dynamic>? jenkinsMap;
var envArgs = envArguments(arguments);
bool isTest = envArgs.containsKey('isTest'); //for Unit Test
if (isTest) {
//Test map
buildMap = TestData.buildMap;
jenkinsMap = TestData.jenkinsMap!;
print('Start test build...');
} else {
String os = Platform.operatingSystem;
var enable = true;
print('Current OS: $os');
print('Start build...');
String jsonStr = '';
if (Platform.isMacOS || Platform.isLinux) {
await Process.run('zsh', ['./lib/shell/jenkins_env_params.sh'])
.then((ProcessResult result) => jsonStr = result.stdout);
jenkinsMap = getMapFromJson(jsonStr)!;
} else if (Platform.isWindows) {
print('Win Test!!');
await Process.run('cmd', [
'/C',
'${Directory.current.path}/lib/shell/jenkins_env_params.bat'
]).then((ProcessResult result) => jsonStr = result.stdout);
jenkinsMap = getMapFromJson(jsonStr);
} else {
enable = false;
print(
'is not Mac/Windows/Linux. build must be execute in Mac/Windows os system');
}
if (enable) {
printBuildStep('Jenkins Variables');
print(jsonStr);
}
}
if (jenkinsMap != null) {
_jenkinsData = DataJenkins.fromJson(jenkinsMap);
if (!isTest) {
var buildStr = await getBuildData();
buildMap = getMapFromYaml(buildStr);
}
execute(buildMap!);
}
}
Future<void> execute(Map<String, dynamic> buildMap) async {
var c = Completer();
DataBuild? build;
build = DataBuild.fromJson(buildMap);
_property = build.common;
for (var command in build.commands) {
String commandStr =
command.command.toString().replaceAll('CommandType.', '');
printBuildStep('Phase Start: $commandStr');
if (!await Command(command.command).execute(command)) {
exit(10);
}
}
c.complete();
printBuildStep('Build Successfully Complete');
exit(0);
return c.future;
}
void printBuildStep(String name) {
print(
'************************************************************************************');
print('* $name');
print(
'************************************************************************************');
}
Future<String> getBuildData() async {
var c = Completer<String>();
var file = File('${_jenkinsData!.workspace}/build_data.conf');
var content = '';
if (Platform.isWindows) {
content = file
.readAsStringSync(encoding: SystemEncoding())
.replaceAll('BuildData=', '');
var regEx = RegExp(r'(%)([a-zA-Z0-9]+)(%)');
var list = regEx.allMatches(content);
var params = <String>[];
for (var match in list) {
var param = match.group(2);
if (!params.contains(param)) {
params.add(param!);
}
}
var temp = File('${_jenkinsData!.workspace}/param_temp.bat');
var bat = '';
for (var param in params) {
bat += '@echo %$param%\n';
}
temp.writeAsStringSync(bat);
var resultParam = '';
await Process.run('cmd', ['/C', temp.path])
.then((ProcessResult result) => resultParam = result.stdout);
var values = resultParam.split('\n');
var length = params.length;
for (var i = 0; i < length; ++i) {
var value = values[i]
.toString()
.replaceAll('\\', '/')
.replaceAll(RegExp('\r|\n'), '');
content = content.replaceAll('%${params[i]}%', value);
}
temp.deleteSync();
} else {
content = await file.readAsString();
}
print(content);
c.complete(content);
return c.future;
}
}

View file

@ -0,0 +1,26 @@
import 'dart:async';
import 'package:dart_framework/core/app_data_manager.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class CreateAppData implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var jenkins = Application.to.jenkinsData!;
var data = DataAppDataCreator.fromJson(command.param);
var workspace = data.workspace ?? jenkins.workspace;
workspace = workspace.replaceAll('\\', '/');
var appData = await AppDataManager.to
.create(data.scm, workspace, data.relativeAssetPath, data.name);
Command.setReturnValue(data.returnVersion, appData.version);
Command.setReturnValue(data.returnHash, appData.gitHash.substring(0, 7));
c.complete(true);
return c.future;
}
}

View file

@ -0,0 +1,45 @@
import 'dart:async';
import 'package:dart_framework/platform/process.dart';
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class BuildDart implements Command {
Function? get error => null;
Future<StringBuffer> appendShell(
StringBuffer shell, DataCommand command) async {
var c = Completer<StringBuffer>();
c.complete(shell);
return c.future;
}
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataBuildDart.fromJson(command.param);
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var targetPath =
Path.getNormal(Command.replaceTag(data.targetPath ?? workspace));
var shell = StringBuffer();
shell.write('cd $targetPath');
shell.write(' && flutter clean');
shell.write(' && flutter packages upgrade');
shell.write(' && flutter pub get');
if (data.executeBuildRunner == null ? true : data.executeBuildRunner!) {
shell.write(
' && flutter pub run build_runner build --delete-conflicting-outputs');
}
var process = await ProcessExecutor.run(await appendShell(shell, command),
workspace: workspace);
print('#### Exit Code: ${process.exitCode.toString()}');
var passCode = [0, 64];
//exitCode 65: package not defiend in pubspec.yaml
var success = passCode.contains(process.exitCode);
c.complete(success);
return c.future;
}
}

View file

@ -0,0 +1,63 @@
import 'dart:async';
import 'dart:io';
import 'package:oto_cli/oto/build/build_dart.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:dart_framework/utils/path.dart';
// ignore: depend_on_referenced_packages
import 'package:path/path.dart' as path;
import '../application.dart';
class BuildDartCompile extends BuildDart {
@override
Future<StringBuffer> appendShell(
StringBuffer shell, DataCommand command) async {
var c = Completer<StringBuffer>();
var data = DataBuildDartCompile.fromJson(command.param);
var extension = getExeExtension();
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var targetPath =
Path.getNormal(Command.replaceTag(data.targetPath ?? workspace));
var binPath = path.join(targetPath, 'bin');
//find build target
var targetDir = Directory(binPath);
var findedDartFile = '';
var fileList = targetDir.listSync();
for (var file in fileList) {
findedDartFile = path.basename(file.path);
break;
}
findedDartFile = path.join(targetPath, 'bin', findedDartFile);
var targetDartFile =
Command.replaceTag(data.targetDartFile ?? findedDartFile);
//clear build path
var buildFolderName = 'release';
var buildPath = path.join(targetPath, buildFolderName);
var buildDir = Directory(buildPath);
var buildFileName = data.buildFileName ?? 'release';
if (!buildDir.existsSync()) {
buildDir.createSync();
}
buildPath =
path.join(targetPath, buildFolderName, '$buildFileName$extension');
var preBuildFile = File(buildPath);
if (preBuildFile.existsSync()) {
preBuildFile.deleteSync();
}
shell.write(' && dart compile exe "$targetDartFile" -o "$buildPath"');
c.complete(shell);
return c.future;
}
String getExeExtension() {
if (Platform.isWindows) {
return '.exe';
} else {
return '';
}
}
}

View file

@ -0,0 +1,26 @@
import 'dart:async';
import 'package:dart_framework/platform/process.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class BuildDotNet implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataBuildDotNet.fromJson(command.param);
var workspace = Application.to.jenkinsData!.workspace;
var shell = StringBuffer();
// shell.writeln('C:\\Windows\\System32\\chcp.com 65001');
shell.writeln('cd $workspace');
shell.writeln(
'MSBuild ${data.projectFile} /t:build /p:Configuration=Release;Platform=x86');
var process = await ProcessExecutor.run(shell, workspace: workspace);
// var exitCode = await process.process.exitCode;
// c.complete(process.isSuccess);
c.complete(process.exitCode == 0);
return c.future;
}
}

View file

@ -0,0 +1,22 @@
import 'dart:async';
import 'package:oto_cli/oto/build/build_dart.dart';
import 'package:oto_cli/oto/data/data.dart';
// ignore: constant_identifier_names
enum BuildPlatform { Windows, Macos, Web, Apk, Appbundle, iOS, Linux }
class BuildFlutter extends BuildDart {
@override
Future<StringBuffer> appendShell(
StringBuffer shell, DataCommand command) async {
var c = Completer<StringBuffer>();
var data = DataBuildFlutter.fromJson(command.param);
var platform =
data.platform.toString().replaceAll('BuildPlatform.', '').toLowerCase();
var additional = data.additional == null ? '' : ' ${data.additional!}';
shell.write(' && flutter build $platform$additional');
c.complete(shell);
return c.future;
}
}

View file

@ -0,0 +1,102 @@
import 'dart:async';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:dart_framework/platform/process.dart';
import 'package:dart_framework/utils/path.dart';
class BuildiOS implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataBuildiOS.fromJson(command.param);
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var workspacePath = Path.getNormal(Command.replaceTag(data.workspacePath));
var destination = data.destination ?? 'generic/platform=iOS build';
var cleanPod = data.cleanPod ?? false;
var shell = StringBuffer();
shell.write('security unlock-keychain -pr0bins');
shell.write(' && cd $workspace/ios');
shell.write(' && export LANG=en_US.UTF-8');
shell.write(' && rm -rf Pods');
shell.write(' && rm -rf Podfile.lock');
if (cleanPod) {
shell.write(' && pod cache clean --all');
shell.write(' && pod repo update');
shell.write(' && pod install --repo-update');
} else {
shell.write(' && pod install');
}
shell.write(
' && xcodebuild -workspace $workspacePath -scheme ${data.scheme} -destination $destination');
var process = await ProcessExecutor.run(shell, workspace: workspace);
print('#### Exit Code: ${process.exitCode.toString()}');
var success = process.exitCode == 0;
if (success) {
c.complete(success);
return c.future;
} else {
if (process.exitCode == 31) {
data.cleanPod = true;
command.param = data.toJson();
return await execute(command);
} else {
c.complete(success);
return c.future;
}
}
}
}
class ArchiveiOS implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataArchiveiOS.fromJson(command.param);
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var workspacePath = Path.getNormal(Command.replaceTag(data.workspacePath));
var archivePath = Path.getNormal(Command.replaceTag(data.archivePath));
var shell = StringBuffer();
shell.write(
'xcodebuild -workspace $workspacePath -scheme ${data.scheme} -archivePath $archivePath archive');
var process = await ProcessExecutor.run(shell, workspace: workspace);
print('#### Exit Code: ${process.exitCode.toString()}');
var success = process.exitCode == 0;
c.complete(success);
return c.future;
}
}
class ExportiOS implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataExportiOS.fromJson(command.param);
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var archivePath = Path.getNormal(Command.replaceTag(data.archivePath));
var exportPath = Path.getNormal(Command.replaceTag(data.exportPath));
var exportOptionPlistPath =
Path.getNormal(Command.replaceTag(data.exportOptionPlistPath));
var shell = StringBuffer();
shell.write(
'xcodebuild -exportArchive -archivePath $archivePath -exportPath $exportPath -exportOptionsPlist $exportOptionPlistPath');
var process = await ProcessExecutor.run(shell, workspace: workspace);
print('#### Exit Code: ${process.exitCode.toString()}');
var success = process.exitCode == 0;
c.complete(success);
return c.future;
}
}

137
lib/oto/command.dart Normal file
View file

@ -0,0 +1,137 @@
import 'dart:async';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/build/app_data_creator.dart';
import 'package:oto_cli/oto/build/build_dart.dart';
import 'package:oto_cli/oto/build/build_dart_compile.dart';
import 'package:oto_cli/oto/build/build_dot_net.dart';
import 'package:oto_cli/oto/build/build_flutter.dart';
import 'package:oto_cli/oto/build/build_ios.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:oto_cli/oto/file/delete.dart';
import 'package:oto_cli/oto/file/rename.dart';
import 'package:oto_cli/oto/shell/shell.dart';
import 'package:oto_cli/oto/file/copy.dart';
import 'package:oto_cli/oto/util/execute_app.dart';
import 'package:oto_cli/oto/util/publish_ios.dart';
import 'package:oto_cli/oto/util/slack.dart';
import 'package:oto_cli/oto/util/zip.dart';
import 'package:oto_cli/oto/uploader/uploader.dart';
enum CommandType {
SimpleCommand,
CreateAppData,
BuildDart,
BuildFlutter,
BuildDartCompile,
BuildDotNet,
BuildiOS,
ArchiveiOS,
ExportiOS,
PublishiOS,
ExecuteApp,
Upload,
Copy,
Rename,
Delete,
Slack,
SlackBuild,
Zip,
Shell
}
abstract class Command {
static final Map<CommandType, Command> _commandMap = {
CommandType.SimpleCommand: SimpleCommand(),
CommandType.CreateAppData: CreateAppData(),
CommandType.BuildDart: BuildDart(),
CommandType.BuildFlutter: BuildFlutter(),
CommandType.BuildDartCompile: BuildDartCompile(),
CommandType.BuildDotNet: BuildDotNet(),
CommandType.BuildiOS: BuildiOS(),
CommandType.ArchiveiOS: ArchiveiOS(),
CommandType.ExportiOS: ExportiOS(),
CommandType.PublishiOS: PublishiOS(),
CommandType.ExecuteApp: ExecuteApp(),
CommandType.Upload: Uploader(),
CommandType.Copy: Copy(),
CommandType.Rename: Rename(),
CommandType.Delete: Delete(),
CommandType.Slack: Slack(),
CommandType.SlackBuild: SlackBuild(),
CommandType.Zip: Zip(),
CommandType.Shell: Shell()
};
factory Command(CommandType type) {
return _commandMap[type]!;
}
static final Map<String, dynamic> _cache = <String, dynamic>{};
static String replaceTag(String raw) {
var regEx = RegExp(r'(<!)([a-zA-Z0-9.]+)(>)');
var list = regEx.allMatches(raw);
for (var match in list) {
var param = match.group(2);
if (!_cache.containsKey(param)) {
_cache[param!] = _getTagValue(param);
}
var target = '<!${param!}>';
raw = raw.replaceAll(target, _cache[param]);
}
return raw;
}
static void setReturnValue(String tag, dynamic value) {
var regEx = RegExp(r'(<!)([a-zA-Z0-9.]+)(>)');
var match = regEx.firstMatch(tag);
tag = match!.group(2)!;
if (tag.startsWith('common')) {
var key = tag.replaceFirst('common.', '');
Application.to.property![key] = value;
}
}
static dynamic _getTagValue(String value) {
var setted = false;
dynamic data;
if (value.startsWith('common.')) {
var map = Application.to.property;
var param = value.replaceFirst('common.', '');
if (map!.containsKey(param)) {
data = map[param];
setted = true;
} else {
print('"$value" is not exist in common');
data = '';
setted = true;
}
}
if (!setted) {
throw Exception(
'Command.getTagValue: "$value" is not defined in common.');
}
return data;
}
// ignore: missing_return
Future<bool> execute(DataCommand command) async {
var c = Completer<bool>();
c.complete(true);
return c.future;
}
}
//Templete
class SimpleCommand implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
print(command.param);
//Start here
c.complete(true);
return c.future;
}
}

300
lib/oto/data/data.dart Normal file
View file

@ -0,0 +1,300 @@
import 'package:dart_framework/utils/slack/slack_sender.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:dart_framework/core/app_data_manager.dart';
import 'package:oto_cli/oto/command.dart';
part 'data.g.dart';
/// ***************************** Base Data ****************************///
@JsonSerializable()
class DataBuild {
late Map<String, dynamic> common;
late List<DataCommand> commands;
DataBuild();
factory DataBuild.fromJson(Map<String, dynamic> json) =>
_$DataBuildFromJson(json);
Map<String, dynamic> toJson() => _$DataBuildToJson(this);
}
@JsonSerializable()
class DataCommand {
DataCommand();
late CommandType command;
late dynamic param;
factory DataCommand.fromJson(Map<String, dynamic> json) =>
_$DataCommandFromJson(json);
Map<String, dynamic> toJson() => _$DataCommandToJson(this);
}
/// ***************************** Command List ****************************///
//SimpleCommand
@JsonSerializable()
class DataSimpleCommand {
DataSimpleCommand();
late String message;
factory DataSimpleCommand.fromJson(Map<String, dynamic> json) =>
_$DataSimpleCommandFromJson(json);
Map<String, dynamic> toJson() => _$DataSimpleCommandToJson(this);
}
//AppDataCreator
@JsonSerializable()
class DataAppDataCreator {
DataAppDataCreator();
late SCMType scm;
late String? workspace;
late String relativeAssetPath;
late String returnVersion;
late String returnHash;
late String name;
factory DataAppDataCreator.fromJson(Map<String, dynamic> json) =>
_$DataAppDataCreatorFromJson(json);
Map<String, dynamic> toJson() => _$DataAppDataCreatorToJson(this);
}
//BuildDart
@JsonSerializable()
class DataBuildDart {
DataBuildDart();
late String? targetPath;
late bool? executeBuildRunner;
factory DataBuildDart.fromJson(Map<String, dynamic> json) =>
_$DataBuildDartFromJson(json);
Map<String, dynamic> toJson() => _$DataBuildDartToJson(this);
}
//BuildFlutter
@JsonSerializable()
class DataBuildFlutter extends DataBuildDart {
DataBuildFlutter();
late String platform;
late String? additional;
factory DataBuildFlutter.fromJson(Map<String, dynamic> json) =>
_$DataBuildFlutterFromJson(json);
@override
Map<String, dynamic> toJson() => _$DataBuildFlutterToJson(this);
}
//BuildDartCompile
@JsonSerializable()
class DataBuildDartCompile extends DataBuildDart {
DataBuildDartCompile();
late String? targetDartFile;
late String? buildFileName;
factory DataBuildDartCompile.fromJson(Map<String, dynamic> json) =>
_$DataBuildDartCompileFromJson(json);
@override
Map<String, dynamic> toJson() => _$DataBuildDartCompileToJson(this);
}
//BuildDotNet
@JsonSerializable()
class DataBuildDotNet {
DataBuildDotNet();
late String projectFile;
factory DataBuildDotNet.fromJson(Map<String, dynamic> json) =>
_$DataBuildDotNetFromJson(json);
Map<String, dynamic> toJson() => _$DataBuildDotNetToJson(this);
}
//BuildiOS
@JsonSerializable()
class DataBuildiOS {
DataBuildiOS();
late String workspacePath;
late String scheme;
late String? destination;
late bool? cleanPod;
factory DataBuildiOS.fromJson(Map<String, dynamic> json) =>
_$DataBuildiOSFromJson(json);
Map<String, dynamic> toJson() => _$DataBuildiOSToJson(this);
}
//ArchiveiOS
@JsonSerializable()
class DataArchiveiOS {
DataArchiveiOS();
late String workspacePath;
late String scheme;
late String archivePath;
factory DataArchiveiOS.fromJson(Map<String, dynamic> json) =>
_$DataArchiveiOSFromJson(json);
Map<String, dynamic> toJson() => _$DataArchiveiOSToJson(this);
}
//ExportiOS
@JsonSerializable()
class DataExportiOS {
DataExportiOS();
late String archivePath;
late String exportOptionPlistPath;
late String exportPath;
factory DataExportiOS.fromJson(Map<String, dynamic> json) =>
_$DataExportiOSFromJson(json);
Map<String, dynamic> toJson() => _$DataExportiOSToJson(this);
}
//PublishiOS
@JsonSerializable()
class DataPublishiOS {
DataPublishiOS();
late String bundleID;
late String version;
late String appName;
late String ipaURL;
late String menifestURL;
late String displayImageURL;
late String fullSizeImageURL;
late String destinationPath;
factory DataPublishiOS.fromJson(Map<String, dynamic> json) =>
_$DataPublishiOSFromJson(json);
Map<String, dynamic> toJson() => _$DataPublishiOSToJson(this);
}
//ExecuteApp
@JsonSerializable()
class DataExecuteApp {
DataExecuteApp();
late String executable;
late String desktopServicePath;
factory DataExecuteApp.fromJson(Map<String, dynamic> json) =>
_$DataExecuteAppFromJson(json);
Map<String, dynamic> toJson() => _$DataExecuteAppToJson(this);
}
//Uploader
@JsonSerializable()
class DataUploader {
DataUploader();
late bool sftp;
late int port;
late String host;
late String user;
late String password;
late Map<String, String> uploadMap;
factory DataUploader.fromJson(Map<String, dynamic> json) =>
_$DataUploaderFromJson(json);
Map<String, dynamic> toJson() => _$DataUploaderToJson(this);
}
class DataFile {
DataFile();
late List<String>? ignorePattern;
}
//Copy & Move
@JsonSerializable()
class DataCopy extends DataFile {
DataCopy();
late bool? isMove;
late Map<String, String> copyMap;
factory DataCopy.fromJson(Map<String, dynamic> json) =>
_$DataCopyFromJson(json);
Map<String, dynamic> toJson() => _$DataCopyToJson(this);
}
//Delete
@JsonSerializable()
class DataDelete extends DataFile {
DataDelete();
late List<String> list;
factory DataDelete.fromJson(Map<String, dynamic> json) =>
_$DataDeleteFromJson(json);
Map<String, dynamic> toJson() => _$DataDeleteToJson(this);
}
//Rename
@JsonSerializable()
class DataRename extends DataFile {
DataRename();
late Map<String, String> renameMap;
factory DataRename.fromJson(Map<String, dynamic> json) =>
_$DataRenameFromJson(json);
Map<String, dynamic> toJson() => _$DataRenameToJson(this);
}
//Slack
@JsonSerializable()
class DataSlack {
DataSlack();
factory DataSlack.fromJson(Map<String, dynamic> json) =>
_$DataSlackFromJson(json);
Map<String, dynamic> toJson() => _$DataSlackToJson(this);
}
//Slack Build
@JsonSerializable()
class DataSlackBuild {
DataSlackBuild();
late String appName;
late SlackBuildType type;
late String color;
late String version;
late String downloadURL;
late String? channel;
factory DataSlackBuild.fromJson(Map<String, dynamic> json) =>
_$DataSlackBuildFromJson(json);
Map<String, dynamic> toJson() => _$DataSlackBuildToJson(this);
}
//Zip
@JsonSerializable()
class DataZip {
DataZip();
late String zipFile;
late List<String> zipList;
factory DataZip.fromJson(Map<String, dynamic> json) =>
_$DataZipFromJson(json);
Map<String, dynamic> toJson() => _$DataZipToJson(this);
}
//Shell
@JsonSerializable()
class DataShell {
DataShell();
late String? workspace;
late String path;
factory DataShell.fromJson(Map<String, dynamic> json) =>
_$DataShellFromJson(json);
Map<String, dynamic> toJson() => _$DataShellToJson(this);
}

313
lib/oto/data/data.g.dart Normal file
View file

@ -0,0 +1,313 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DataBuild _$DataBuildFromJson(Map<String, dynamic> json) => DataBuild()
..common = json['common'] as Map<String, dynamic>
..commands = (json['commands'] as List<dynamic>)
.map((e) => DataCommand.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$DataBuildToJson(DataBuild instance) => <String, dynamic>{
'common': instance.common,
'commands': instance.commands,
};
DataCommand _$DataCommandFromJson(Map<String, dynamic> json) => DataCommand()
..command = $enumDecode(_$CommandTypeEnumMap, json['command'])
..param = json['param'];
Map<String, dynamic> _$DataCommandToJson(DataCommand instance) =>
<String, dynamic>{
'command': _$CommandTypeEnumMap[instance.command]!,
'param': instance.param,
};
const _$CommandTypeEnumMap = {
CommandType.SimpleCommand: 'SimpleCommand',
CommandType.CreateAppData: 'CreateAppData',
CommandType.BuildDart: 'BuildDart',
CommandType.BuildFlutter: 'BuildFlutter',
CommandType.BuildDartCompile: 'BuildDartCompile',
CommandType.BuildDotNet: 'BuildDotNet',
CommandType.BuildiOS: 'BuildiOS',
CommandType.ArchiveiOS: 'ArchiveiOS',
CommandType.ExportiOS: 'ExportiOS',
CommandType.PublishiOS: 'PublishiOS',
CommandType.ExecuteApp: 'ExecuteApp',
CommandType.Upload: 'Upload',
CommandType.Copy: 'Copy',
CommandType.Rename: 'Rename',
CommandType.Delete: 'Delete',
CommandType.Slack: 'Slack',
CommandType.SlackBuild: 'SlackBuild',
CommandType.Zip: 'Zip',
CommandType.Shell: 'Shell',
};
DataSimpleCommand _$DataSimpleCommandFromJson(Map<String, dynamic> json) =>
DataSimpleCommand()..message = json['message'] as String;
Map<String, dynamic> _$DataSimpleCommandToJson(DataSimpleCommand instance) =>
<String, dynamic>{
'message': instance.message,
};
DataAppDataCreator _$DataAppDataCreatorFromJson(Map<String, dynamic> json) =>
DataAppDataCreator()
..scm = $enumDecode(_$SCMTypeEnumMap, json['scm'])
..workspace = json['workspace'] as String?
..relativeAssetPath = json['relativeAssetPath'] as String
..returnVersion = json['returnVersion'] as String
..returnHash = json['returnHash'] as String
..name = json['name'] as String;
Map<String, dynamic> _$DataAppDataCreatorToJson(DataAppDataCreator instance) =>
<String, dynamic>{
'scm': _$SCMTypeEnumMap[instance.scm]!,
'workspace': instance.workspace,
'relativeAssetPath': instance.relativeAssetPath,
'returnVersion': instance.returnVersion,
'returnHash': instance.returnHash,
'name': instance.name,
};
const _$SCMTypeEnumMap = {
SCMType.Svn: 'Svn',
SCMType.Git: 'Git',
};
DataBuildDart _$DataBuildDartFromJson(Map<String, dynamic> json) =>
DataBuildDart()
..targetPath = json['targetPath'] as String?
..executeBuildRunner = json['executeBuildRunner'] as bool?;
Map<String, dynamic> _$DataBuildDartToJson(DataBuildDart instance) =>
<String, dynamic>{
'targetPath': instance.targetPath,
'executeBuildRunner': instance.executeBuildRunner,
};
DataBuildFlutter _$DataBuildFlutterFromJson(Map<String, dynamic> json) =>
DataBuildFlutter()
..targetPath = json['targetPath'] as String?
..executeBuildRunner = json['executeBuildRunner'] as bool?
..platform = json['platform'] as String
..additional = json['additional'] as String?;
Map<String, dynamic> _$DataBuildFlutterToJson(DataBuildFlutter instance) =>
<String, dynamic>{
'targetPath': instance.targetPath,
'executeBuildRunner': instance.executeBuildRunner,
'platform': instance.platform,
'additional': instance.additional,
};
DataBuildDartCompile _$DataBuildDartCompileFromJson(
Map<String, dynamic> json) =>
DataBuildDartCompile()
..targetPath = json['targetPath'] as String?
..executeBuildRunner = json['executeBuildRunner'] as bool?
..targetDartFile = json['targetDartFile'] as String?
..buildFileName = json['buildFileName'] as String?;
Map<String, dynamic> _$DataBuildDartCompileToJson(
DataBuildDartCompile instance) =>
<String, dynamic>{
'targetPath': instance.targetPath,
'executeBuildRunner': instance.executeBuildRunner,
'targetDartFile': instance.targetDartFile,
'buildFileName': instance.buildFileName,
};
DataBuildDotNet _$DataBuildDotNetFromJson(Map<String, dynamic> json) =>
DataBuildDotNet()..projectFile = json['projectFile'] as String;
Map<String, dynamic> _$DataBuildDotNetToJson(DataBuildDotNet instance) =>
<String, dynamic>{
'projectFile': instance.projectFile,
};
DataBuildiOS _$DataBuildiOSFromJson(Map<String, dynamic> json) => DataBuildiOS()
..workspacePath = json['workspacePath'] as String
..scheme = json['scheme'] as String
..destination = json['destination'] as String?
..cleanPod = json['cleanPod'] as bool?;
Map<String, dynamic> _$DataBuildiOSToJson(DataBuildiOS instance) =>
<String, dynamic>{
'workspacePath': instance.workspacePath,
'scheme': instance.scheme,
'destination': instance.destination,
'cleanPod': instance.cleanPod,
};
DataArchiveiOS _$DataArchiveiOSFromJson(Map<String, dynamic> json) =>
DataArchiveiOS()
..workspacePath = json['workspacePath'] as String
..scheme = json['scheme'] as String
..archivePath = json['archivePath'] as String;
Map<String, dynamic> _$DataArchiveiOSToJson(DataArchiveiOS instance) =>
<String, dynamic>{
'workspacePath': instance.workspacePath,
'scheme': instance.scheme,
'archivePath': instance.archivePath,
};
DataExportiOS _$DataExportiOSFromJson(Map<String, dynamic> json) =>
DataExportiOS()
..archivePath = json['archivePath'] as String
..exportOptionPlistPath = json['exportOptionPlistPath'] as String
..exportPath = json['exportPath'] as String;
Map<String, dynamic> _$DataExportiOSToJson(DataExportiOS instance) =>
<String, dynamic>{
'archivePath': instance.archivePath,
'exportOptionPlistPath': instance.exportOptionPlistPath,
'exportPath': instance.exportPath,
};
DataPublishiOS _$DataPublishiOSFromJson(Map<String, dynamic> json) =>
DataPublishiOS()
..bundleID = json['bundleID'] as String
..version = json['version'] as String
..appName = json['appName'] as String
..ipaURL = json['ipaURL'] as String
..menifestURL = json['menifestURL'] as String
..displayImageURL = json['displayImageURL'] as String
..fullSizeImageURL = json['fullSizeImageURL'] as String
..destinationPath = json['destinationPath'] as String;
Map<String, dynamic> _$DataPublishiOSToJson(DataPublishiOS instance) =>
<String, dynamic>{
'bundleID': instance.bundleID,
'version': instance.version,
'appName': instance.appName,
'ipaURL': instance.ipaURL,
'menifestURL': instance.menifestURL,
'displayImageURL': instance.displayImageURL,
'fullSizeImageURL': instance.fullSizeImageURL,
'destinationPath': instance.destinationPath,
};
DataExecuteApp _$DataExecuteAppFromJson(Map<String, dynamic> json) =>
DataExecuteApp()
..executable = json['executable'] as String
..desktopServicePath = json['desktopServicePath'] as String;
Map<String, dynamic> _$DataExecuteAppToJson(DataExecuteApp instance) =>
<String, dynamic>{
'executable': instance.executable,
'desktopServicePath': instance.desktopServicePath,
};
DataUploader _$DataUploaderFromJson(Map<String, dynamic> json) => DataUploader()
..sftp = json['sftp'] as bool
..port = json['port'] as int
..host = json['host'] as String
..user = json['user'] as String
..password = json['password'] as String
..uploadMap = Map<String, String>.from(json['uploadMap'] as Map);
Map<String, dynamic> _$DataUploaderToJson(DataUploader instance) =>
<String, dynamic>{
'sftp': instance.sftp,
'port': instance.port,
'host': instance.host,
'user': instance.user,
'password': instance.password,
'uploadMap': instance.uploadMap,
};
DataCopy _$DataCopyFromJson(Map<String, dynamic> json) => DataCopy()
..ignorePattern = (json['ignorePattern'] as List<dynamic>?)
?.map((e) => e as String)
.toList()
..isMove = json['isMove'] as bool?
..copyMap = Map<String, String>.from(json['copyMap'] as Map);
Map<String, dynamic> _$DataCopyToJson(DataCopy instance) => <String, dynamic>{
'ignorePattern': instance.ignorePattern,
'isMove': instance.isMove,
'copyMap': instance.copyMap,
};
DataDelete _$DataDeleteFromJson(Map<String, dynamic> json) => DataDelete()
..ignorePattern = (json['ignorePattern'] as List<dynamic>?)
?.map((e) => e as String)
.toList()
..list = (json['list'] as List<dynamic>).map((e) => e as String).toList();
Map<String, dynamic> _$DataDeleteToJson(DataDelete instance) =>
<String, dynamic>{
'ignorePattern': instance.ignorePattern,
'list': instance.list,
};
DataRename _$DataRenameFromJson(Map<String, dynamic> json) => DataRename()
..ignorePattern = (json['ignorePattern'] as List<dynamic>?)
?.map((e) => e as String)
.toList()
..renameMap = Map<String, String>.from(json['renameMap'] as Map);
Map<String, dynamic> _$DataRenameToJson(DataRename instance) =>
<String, dynamic>{
'ignorePattern': instance.ignorePattern,
'renameMap': instance.renameMap,
};
DataSlack _$DataSlackFromJson(Map<String, dynamic> json) => DataSlack();
Map<String, dynamic> _$DataSlackToJson(DataSlack instance) =>
<String, dynamic>{};
DataSlackBuild _$DataSlackBuildFromJson(Map<String, dynamic> json) =>
DataSlackBuild()
..appName = json['appName'] as String
..type = $enumDecode(_$SlackBuildTypeEnumMap, json['type'])
..color = json['color'] as String
..version = json['version'] as String
..downloadURL = json['downloadURL'] as String
..channel = json['channel'] as String?;
Map<String, dynamic> _$DataSlackBuildToJson(DataSlackBuild instance) =>
<String, dynamic>{
'appName': instance.appName,
'type': _$SlackBuildTypeEnumMap[instance.type]!,
'color': instance.color,
'version': instance.version,
'downloadURL': instance.downloadURL,
'channel': instance.channel,
};
const _$SlackBuildTypeEnumMap = {
SlackBuildType.android: 'android',
SlackBuildType.ios: 'ios',
SlackBuildType.windows: 'windows',
SlackBuildType.mac: 'mac',
SlackBuildType.web: 'web',
};
DataZip _$DataZipFromJson(Map<String, dynamic> json) => DataZip()
..zipFile = json['zipFile'] as String
..zipList =
(json['zipList'] as List<dynamic>).map((e) => e as String).toList();
Map<String, dynamic> _$DataZipToJson(DataZip instance) => <String, dynamic>{
'zipFile': instance.zipFile,
'zipList': instance.zipList,
};
DataShell _$DataShellFromJson(Map<String, dynamic> json) => DataShell()
..workspace = json['workspace'] as String?
..path = json['path'] as String;
Map<String, dynamic> _$DataShellToJson(DataShell instance) => <String, dynamic>{
'workspace': instance.workspace,
'path': instance.path,
};

View file

@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
part 'jenkins_data.g.dart';
@JsonSerializable()
class DataJenkins {
late String workspace;
late String jenkinsHome;
late String jenkinsUrl;
late String buildUrl;
late String jobUrl;
late String jobName;
late String gitCommit;
late int buildNumber;
late int buildID;
late String buildDisplayName;
late String gitBranch;
late String buildTag;
DataJenkins();
factory DataJenkins.fromJson(Map<String, dynamic> json) =>
_$DataJenkinsFromJson(json);
Map<String, dynamic> toJson() => _$DataJenkinsToJson(this);
}

View file

@ -0,0 +1,37 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'jenkins_data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DataJenkins _$DataJenkinsFromJson(Map<String, dynamic> json) => DataJenkins()
..workspace = json['workspace'] as String
..jenkinsHome = json['jenkinsHome'] as String
..jenkinsUrl = json['jenkinsUrl'] as String
..buildUrl = json['buildUrl'] as String
..jobUrl = json['jobUrl'] as String
..jobName = json['jobName'] as String
..gitCommit = json['gitCommit'] as String
..buildNumber = json['buildNumber'] as int
..buildID = json['buildID'] as int
..buildDisplayName = json['buildDisplayName'] as String
..gitBranch = json['gitBranch'] as String
..buildTag = json['buildTag'] as String;
Map<String, dynamic> _$DataJenkinsToJson(DataJenkins instance) =>
<String, dynamic>{
'workspace': instance.workspace,
'jenkinsHome': instance.jenkinsHome,
'jenkinsUrl': instance.jenkinsUrl,
'buildUrl': instance.buildUrl,
'jobUrl': instance.jobUrl,
'jobName': instance.jobName,
'gitCommit': instance.gitCommit,
'buildNumber': instance.buildNumber,
'buildID': instance.buildID,
'buildDisplayName': instance.buildDisplayName,
'gitBranch': instance.gitBranch,
'buildTag': instance.buildTag,
};

81
lib/oto/file/copy.dart Normal file
View file

@ -0,0 +1,81 @@
import 'dart:async';
import 'dart:io';
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class Copy implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataCopy.fromJson(command.param);
var result = true;
var isMove = data.isMove == null ? false : data.isMove!;
var entries = data.copyMap.entries;
try {
var ignoredList = <String>[];
for (var entry in entries) {
var start = Path.getNormal(Command.replaceTag(entry.key));
var dest = Path.getNormal(Command.replaceTag(entry.value));
var destDir = Directory(dest);
var name = Path.getName(start);
if (name == "*") {
//Delete & recreate dest folder
if (destDir.existsSync()) destDir.deleteSync(recursive: true);
destDir.createSync(recursive: true);
var startParentStr = Path.getParent(start);
if (startParentStr != null) {
var startParent = Directory(startParentStr);
if (startParent.existsSync()) {
var files = startParent.listSync();
for (var file in files) {
if (file is File) {
await Path.copy(file.path, dest, false,
ignorePattern: data.ignorePattern,
ignoredList: ignoredList,
isMove: isMove);
} else if (file is Directory) {
await Path.copy(file.path, dest, true,
ignorePattern: data.ignorePattern,
ignoredList: ignoredList,
isMove: isMove);
}
}
} else {
throw ArgumentError('Start - $start is not exist');
}
}
} else {
if (!destDir.existsSync()) destDir.createSync(recursive: true);
var type = Path.getType(start);
if (FileType.none == type) {
print('[FileNotFound] - $start is not exist');
} else {
await Path.copy(start, dest, FileType.directory == type,
ignorePattern: data.ignorePattern,
ignoredList: ignoredList,
isMove: isMove);
}
}
}
if (ignoredList.isNotEmpty) {
print('==================== Ignored list ====================');
for (var element in ignoredList) {
print(element);
}
print('======================================================');
}
} on Exception catch (e) {
print('Copy General Exception: $e');
result = false;
}
c.complete(result);
return c.future;
}
}

40
lib/oto/file/delete.dart Normal file
View file

@ -0,0 +1,40 @@
import 'dart:async';
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class Delete implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataDelete.fromJson(command.param);
var result = true;
try {
var ignoredList = <String>[];
for (var item in data.list) {
var path = Path.getNormal(Command.replaceTag(item));
var name = Path.getName(path);
if (name == "*") {
//Delete & recreate dest folder
} else {}
}
if (ignoredList.isNotEmpty) {
print('==================== Ignored list ====================');
for (var element in ignoredList) {
print(element);
}
print('======================================================');
}
} on Exception catch (e) {
print('Copy General Exception: $e');
result = false;
}
c.complete(result);
return c.future;
}
}

41
lib/oto/file/rename.dart Normal file
View file

@ -0,0 +1,41 @@
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class Rename implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataRename.fromJson(command.param);
var result = true;
var entries = data.renameMap.entries;
try {
for (var entry in entries) {
var start = Path.getNormal(Command.replaceTag(entry.key));
var targetName = Command.replaceTag(entry.value);
var type = Path.getType(start);
var parentPath = Path.getParent(start)!;
var newPath = path.join(parentPath, targetName);
if (type == FileType.directory) {
await Directory(start).rename(newPath);
} else {
await File(start).rename(newPath);
}
print('Rename: $start --> ${path.basename(newPath)}');
}
} on Exception catch (e) {
print('Copy General Exception: $e');
result = false;
}
c.complete(result);
return c.future;
}
}

View file

@ -0,0 +1,14 @@
@echo {
@echo "workspace":"%WORKSPACE%",
@echo "jenkinsHome":"%JENKINS_HOME%",
@echo "jenkinsUrl":"%JENKINS_URL%",
@echo "buildUrl":"%BUILD_URL%",
@echo "jobUrl":"%JOB_URL%",
@echo "jobName":"%JOB_NAME%",
@echo "gitCommit":"%GIT_COMMIT%",
@echo "buildNumber":%BUILD_NUMBER%,
@echo "buildID":%BUILD_ID%,
@echo "buildDisplayName":"%BUILD_DISPLAY_NAME%",
@echo "gitBranch":"%GIT_BRANCH%",
@echo "buildTag":"%BUILD_TAG%"
@echo }

View file

@ -0,0 +1,17 @@
# shellcheck disable=SC2016
echo -e '
{
"workspace":"'${WORKSPACE}'",
"jenkinsHome":"'${JENKINS_HOME}'",
"jenkinsUrl":"'${JENKINS_URL}'",
"buildUrl":"'${BUILD_URL}'",
"jobUrl":"'${JOB_URL}'",
"jobName":"'${JOB_NAME}'",
"gitCommit":"'${GIT_COMMIT}'",
"buildNumber":'${BUILD_NUMBER}',
"buildID":'${BUILD_ID}',
"buildDisplayName":"'${BUILD_DISPLAY_NAME}'",
"gitBranch":"'${GIT_BRANCH}'",
"buildTag":"'${BUILD_TAG}'"
}'
echo ${BuildData} > "${WORKSPACE}/build_data.conf"

37
lib/oto/shell/shell.dart Normal file
View file

@ -0,0 +1,37 @@
import 'dart:async';
import 'dart:io';
import 'package:dart_framework/platform/process.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class Shell implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData!;
var data = DataShell.fromJson(command.param);
print(command.param);
//Start here
var workspace = Command.replaceTag(data.workspace ?? jenkins.workspace);
var path = Command.replaceTag(data.path);
var shell = StringBuffer();
if (Platform.isWindows) {
shell.writeln('call $path');
} else {
shell.writeln('source $path');
}
var process = await ProcessExecutor.run(shell, workspace: workspace);
c.complete(isSuccess(process.exitCode));
return c.future;
}
bool isSuccess(int exitCode) {
var success = false;
if (exitCode == 0 || exitCode == 128) {
success = true;
}
return success;
}
}

535
lib/oto/test_data.dart Normal file
View file

@ -0,0 +1,535 @@
import 'dart:convert';
import 'dart:io';
import 'package:yaml/yaml.dart' as yaml;
import 'package:oto_cli/oto/build/build_flutter.dart';
class TestData {
static Map<String, dynamic> get buildMap {
Map<String, dynamic> map = {
"common": {"workspace": ""},
"commands": []
};
if (Platform.isMacOS) {
map = {
"common": {"workspace": Directory.current.path},
"commands": [
// {
// "command": "BuildDart",
// "param": {"targetPath": "./"}
// },
// {
// "command": "BuildFlutter",
// "param": {
// "targetPath": "../desktop_service_frontend",
// "platform": BuildPlatform.Macos.toString()
// }
// },
// {
// "command": "BuildFlutter",
// "param": {
// "targetPath": "../desktop_service_frontend",
// "platform": BuildPlatform.Web.toString()
// }
// },
{
"command": "BuildDartCompile",
"param": {
"targetPath": "/Users/toki/works/dart/desktop_service_cli",
"targetDartFile": "./bin/desktop_service_cli.dart",
"buildFileName": "desktop_service_cli"
}
},
// {
// "command": "Copy",
// "param": {
// "ignorePattern": [
// ".git",
// ".dart_tool",
// ".gitignore",
// ".idea",
// "example"
// ],
// "isMove": false,
// "copyMap": {
// "<!common.workspace>/*": "/Users/toki/server/temp",
// // "../toki_flutter_framework/*":
// // "/Users/toki/server/toki_flutter_framework"
// }
// }
// },
// {
// "command": "Zip",
// "param": {
// "zipFile":
// "<!common.workspace>/test_v<!common.version>_<!common.hash>.zip",
// "zipList": [
// "<!common.workspace>/bin/*",
// "<!common.workspace>/README.md"
// ]
// }
// },
// {
// "command": "Upload",
// "param": {
// "sftp": false,
// "host": "toki-labs.com",
// "port": 215,
// "user": "spritex",
// "password": "Na2674126",
// "uploadMap": {
// "README.md": "/HDD1/WEB/_tmp",
// "bin/*": "/HDD1/WEB/_tmp/bin"
// }
// }
// },
// {
// "command": "Shell",
// "param": {
// "workspace": "<!common.workspace>",
// "path": "<!common.workspace>/lib/shell/jenkins_env_params.sh"
// }
// }
]
};
var json = jsonEncode(yaml.loadYaml('''
---
common:
workspace: ${Directory.current.path}
commands:
- command: CreateAppData
param:
scm: Git
relativeAssetPath: assets/data
returnVersion: "<!common.version>"
returnHash: "<!common.hash>"
#- command: BuildDart
# param:
# targetPath: "./"
#- command: BuildFlutter
# param:
# targetPath: "../tplayer"
# executeBuildRunner: false
# platform: ${BuildPlatform.Apk.toString()}
# additional: "--release --target-platform=android-arm64"
#- command: BuildiOS
# param:
# workspacePath: "/Users/toki/jenkins/workspace/tplayer_ios/ios/Runner.xcworkspace"
# scheme: Runner
# destination: "generic/platform=iOS build"
#- command: PublishiOS
# param:
# bundleID: com.tokilabs.tokiplayer
# version: <!common.version>
# appName: TPlayer
# ipaURL: https://toki-labs.com/build/tplayer/ios/tplayer.ipa
# menifestURL: https://toki-labs.com/build/tplayer/ios/menifest.plist
# displayImageURL: https://toki-labs.com/build/tplayer/icon.png
# fullSizeImageURL: https://toki-labs.com/build/tplayer/icon.png
# destinationPath: d:/build
#- command: ArchiveiOS
# param:
# workspacePath: "<!common.workspace>/ios/Runner.xcworkspace"
# scheme: Runner
# archivePath: "<!common.workspace>/build/ios/TPlayer"
#- command: ExportiOS
# param:
# archivePath: "<!common.workspace>/build/ios/TPlayer"
# exportOptionPlistPath: "<!common.workspace>/ExportOptions.plist"
# exportPath: "<!common.workspace>/build/ios"
#- command: BuildDartCompile
# param:
# targetPath: d:/work/dart/desktop_service_cli
# buildFileName: desktop_service_cli
#- command: Copy
# param:
# ignorePattern: []
# isMove: false
# copyMap:
# "<!common.workspace>/README.md": "<!common.workspace>/../temp"
# "<!common.workspace>/bin/*": "<!common.workspace>/../temp/bin"
#- command: Rename
# param:
# renameMap:
# "<!common.workspace>/../temp/README.md": "README_<!common.version>.md.back"
# "<!common.workspace>/../temp/bin/main.dart": "main.dart.back"
# "<!common.workspace>/../temp/test": "test2"
#- command: Copy
# param:
# isMove: true
# copyMap:
# "<!common.workspace>/../temp/README.md": "<!common.workspace>/../temp/bin"
#- command: Zip
# param:
# zipFile: "<!common.workspace>/test_v<!common.version>_<!common.hash>.zip"
# zipList:
# - "<!common.workspace>/bin/*"
# - "<!common.workspace>/README.md"
#- command: Upload
# param:
# sftp: false
# host: toki-labs.com
# port: 215
# user: spritex
# password: Na2674126
# uploadMap:
# README.md: "/HDD1/WEB/_tmp"
# bin/*: "/HDD1/WEB/_tmp/bin"
#- command: Shell
# param:
# workspace: "<!common.workspace>"
# path: "<!common.workspace>/lib/shell/jenkins_env_params.bat"
#- command: ExecuteApp
# param:
# executable: D:/Program Files/TokiSystemTrading/SystemTradingAPIApp.exe
# desktopServicePath: D:/work/dart/build_manager
#- command: SlackBuild
# param:
# appName: tplayer
# type: android
# color: ED1458
# version: <!common.version>
# downloadURL: http://toki-labs.com/build/tplayer/android/tplayer_arm64_<!common.version>.apk
# channel: release
'''));
map = jsonDecode(json);
} else if (Platform.isWindows) {
var json = jsonEncode(yaml.loadYaml('''
---
common:
workspace: ${Directory.current.path}
commands:
- command: CreateAppData
param:
scm: Git
name: Build Manager
relativeAssetPath: assets/data
returnVersion: "<!common.version>"
returnHash: "<!common.hash>"
#- command: BuildDart
# param:
# targetPath: "./"
#- command: BuildFlutter
# param:
# targetPath: "../tplayer"
# executeBuildRunner: false
# platform: ${BuildPlatform.Apk.toString()}
# additional: "--release --target-platform=android-arm64"
#- command: BuildiOS
# param:
# workspacePath: "<!common.workspace>/ios/Runner.xcworkspace"
# scheme: Runner
# destination: "generic/platform=iOS build"
#- command: ArchiveiOS
# param:
# workspacePath: "<!common.workspace>/ios/Runner.xcworkspace"
# scheme: Runner
# archivePath: "<!common.workspace>/build/ios/TPlayer"
#- command: ExportiOS
# param:
# archivePath: "<!common.workspace>/build/ios/TPlayer"
# exportOptionPlistPath: "<!common.workspace>/ExportOptions.plist"
# exportPath: "<!common.workspace>/build/ios"
#- command: PublishiOS
# param:
# bundleID: com.tokilabs.tokiplayer
# version: <!common.version>
# appName: TPlayer
# ipaURL: https://toki-labs.com/build/tplayer/ios/tplayer.ipa
# menifestURL: https://toki-labs.com/build/tplayer/ios/manifest.plist
# displayImageURL: https://toki-labs.com/build/tplayer/icon.png
# fullSizeImageURL: https://toki-labs.com/build/tplayer/icon.png
# destinationPath: d:/build
#- command: BuildDartCompile
# param:
# targetPath: d:/work/dart/desktop_service_cli
# buildFileName: desktop_service_cli
#- command: Copy
# param:
# ignorePattern: []
# isMove: false
# copyMap:
# "<!common.workspace>/README.md": "<!common.workspace>/../temp"
# "<!common.workspace>/bin/*": "<!common.workspace>/../temp/bin"
#- command: Rename
# param:
# renameMap:
# "<!common.workspace>/../temp/README.md": "README_<!common.version>.md.back"
# "<!common.workspace>/../temp/bin/main.dart": "main.dart.back"
# "<!common.workspace>/../temp/test": "test2"
#- command: Copy
# param:
# isMove: true
# copyMap:
# "<!common.workspace>/../temp/README.md": "<!common.workspace>/../temp/bin"
#- command: Zip
# param:
# zipFile: "<!common.workspace>/test_v<!common.version>_<!common.hash>.zip"
# zipList:
# - "<!common.workspace>/bin/*"
# - "<!common.workspace>/README.md"
#- command: Upload
# param:
# sftp: false
# host: toki-labs.com
# port: 215
# user: spritex
# password: Na2674126
# uploadMap:
# README.md: "/HDD1/WEB/_tmp"
# bin/*: "/HDD1/WEB/_tmp/bin"
#- command: Shell
# param:
# workspace: "<!common.workspace>"
# path: "<!common.workspace>/lib/shell/jenkins_env_params.bat"
#- command: ExecuteApp
# param:
# executable: D:/Program Files/TokiSystemTrading/SystemTradingAPIApp.exe
# desktopServicePath: D:/work/dart/build_manager
#- command: SlackBuild
# param:
# appName: tplayer
# type: android
# color: ED1458
# version: <!common.version>
# downloadURL: http://toki-labs.com/build/tplayer/android/tplayer_arm64_<!common.version>.apk
# channel: release
'''));
map = jsonDecode(json);
// map = {
// "common": {"workspace": Directory.current.path},
// "commands": [
// {
// "command": "CreateAppData",
// "param": {
// "scm": "Git",
// "relativeAssetPath": "assets/data",
// "returnVersion": "<!common.version>",
// "returnHash": "<!common.hash>"
// }
// },
// {
// "command": "BuildDart",
// "param": {"targetPath": "./"}
// },
// {
// "command": "BuildFlutter",
// "param": {
// "targetPath": "../desktop_service_frontend",
// "platform": BuildPlatform.Windows.toString()
// }
// },
// {
// "command": "BuildDartCompile",
// "param": {
// "targetPath": "d:\\work\\dart\\desktop_service_cli",
// "buildFileName": "desktop_service_cli"
// }
// },
// {
// "command": "Copy",
// "param": {
// "ignorePattern": [],
// "isMove": false,
// "copyMap": {
// "<!common.workspace>/README.md": "<!common.workspace>/../temp",
// "<!common.workspace>/bin/*": "<!common.workspace>/../temp/bin"
// }
// }
// },
// {
// "command": "Zip",
// "param": {
// "zipFile":
// "<!common.workspace>/test_v<!common.version>_<!common.hash>.zip",
// "zipList": [
// "<!common.workspace>/bin/*",
// "<!common.workspace>/README.md"
// ]
// }
// },
// {
// "command": "Upload",
// "param": {
// "sftp": false,
// "host": "toki-labs.com",
// "port": 215,
// "user": "spritex",
// "password": "Na2674126",
// "uploadMap": {
// "README.md": "/HDD1/WEB/_tmp",
// "bin/*": "/HDD1/WEB/_tmp/bin"
// }
// }
// },
// {
// "command": "Shell",
// "param": {
// "workspace": "<!common.workspace>",
// "path": "<!common.workspace>/lib/shell/jenkins_env_params.bat"
// }
// },
// {
// "command": "ExecuteApp",
// "param": {
// "executable":
// "D:/Program Files/TokiSystemTrading/SystemTradingAPIApp.exe",
// "desktopServicePath": "D:/work/dart/toki_desktop_service"
// }
// }
// ]
// };
} else if (Platform.isLinux) {
map = {
"common": {"workspace": Directory.current.path},
"commands": [
// {
// "command": "BuildDart",
// "param": {"targetPath": "<!common.workspace>"}
// },
{
"command": "BuildDartCompile",
"param": {
"targetPath": "/home/deck/work/desktop_service_cli",
"targetDartFile": "./bin/desktop_service_cli.dart",
"buildFileName": "desktop_service_cli"
}
},
// {
// "command": "BuildFlutter",
// "param": {
// "targetPath": "../desktop_service_frontend",
// "platform": BuildPlatform.Linux.toString()
// }
// },
// {
// "command": "Copy",
// "param": {
// "ignorePattern": [
// ".git",
// ".dart_tool",
// ".gitignore",
// ".idea",
// "example"
// ],
// "isMove": false,
// "copyMap": {
// "<!common.workspace>/*": "/home/deck/temp",
// // "../toki_flutter_framework/*":
// // "/Users/toki/server/toki_flutter_framework"
// }
// }
// },
// {
// "command": "Zip",
// "param": {
// "zipFile":
// "<!common.workspace>/test_v<!common.version>_<!common.hash>.zip",
// "zipList": [
// "<!common.workspace>/bin/*",
// "<!common.workspace>/README.md"
// ]
// }
// },
// {
// "command": "Upload",
// "param": {
// "sftp": false,
// "host": "toki-labs.com",
// "port": 215,
// "user": "spritex",
// "password": "Na2674126",
// "uploadMap": {
// "README.md": "/HDD1/WEB/_tmp",
// "bin/*": "/HDD1/WEB/_tmp/bin"
// }
// }
// },
// {
// "command": "Shell",
// "param": {
// "workspace": "<!common.workspace>",
// "path": "<!common.workspace>/lib/shell/jenkins_env_params.sh"
// }
// }
]
};
}
return map;
}
static Map<String, dynamic>? get jenkinsMap {
Map<String, dynamic>? map;
if (Platform.isMacOS || Platform.isLinux) {
map = {
"workspace": Directory.current.path,
"jenkinsHome": "/var/lib/jenkins",
"jenkinsUrl": "http://toki-labs.com:8083/",
"buildUrl": "http://toki-labs.com:8083/job/toki_os/54/",
"jobUrl": "http://toki-labs.com:8083/job/toki_os/",
"jobName": "toki_os",
"gitCommit": "6087aadcf9f8dbe0d2f024ae32dbcf0f6955d45b",
"buildNumber": 54,
"buildID": 54,
"buildDisplayName": "#54",
"gitBranch": "origin/master",
"buildTag": "jenkins-toki_os-54",
"buildData": buildMap
};
} else if (Platform.isWindows) {
map = {
"workspace": Directory.current.path,
"jenkinsHome": "/var/lib/jenkins",
"jenkinsUrl": "http://toki-labs.com:8083/",
"buildUrl": "http://toki-labs.com:8083/job/toki_system_trading/18/",
"jobUrl": "http://toki-labs.com:8083/job/toki_system_trading/",
"jobName": "toki_system_trading",
"gitCommit": "abef2bc09c1d82e69ebe897ee079135aac9e797a",
"buildNumber": 18,
"buildID": 18,
"buildDisplayName": "#18",
"gitBranch": "origin/master",
"buildTag": "jenkins-toki_system_trading-18",
"buildData": buildMap
};
}
return map;
}
}

438
lib/oto/uploader/ftp.dart Normal file
View file

@ -0,0 +1,438 @@
import 'dart:async';
import 'dart:io';
import 'package:ftpconnect/ftpconnect.dart';
// ignore: implementation_imports
import 'package:ftpconnect/src/ftp_entry.dart';
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/command.dart';
class FTP {
final String _host;
final int _port;
final int _timeout;
final String _user;
final String _pass;
bool _isProgress = false;
String? _currentRemoteDir;
FTPConnect? _ftp;
FTP(String host,
{int port = 21,
String user = 'anonymous',
String password = '',
int timeout = 30})
: _host = host,
_port = port,
_user = user,
_pass = password,
_timeout = timeout {
_ftp = FTPConnect(host,
port: port, user: user, pass: password, timeout: timeout);
}
Future<void> connect() async {
int tryCount = 0;
bool connected = false;
while (!connected && tryCount < 10) {
try {
await _ftp!.connect();
connected = true;
} on Exception catch (e) {
var code = e.hashCode;
var type = e.runtimeType;
print('========================= Connect Failed!');
print('$code = $type');
await Future.delayed(Duration(seconds: 1));
}
}
}
Future<void> disconnect() async {
await _ftp!.disconnect();
}
//Remote target must be folder
Future<void> upload(Map<String, String> localRemoteMap) async {
var cache = <String, Map<String, FileType>>{};
Map<String, FileType> map = {};
_currentRemoteDir = null;
try {
_isProgress = true;
var entries = localRemoteMap.entries;
for (var entry in entries) {
var local = Path.getNormalizePath(entry.key);
var remote = Path.getNormalizePath(entry.value);
var parent = Path.getParent(remote);
if (_currentRemoteDir != parent) {
await _ftp!.changeDirectory(parent!);
_currentRemoteDir = parent;
if (cache.containsKey(_currentRemoteDir)) {
map = cache[_currentRemoteDir]!;
} else {
map = await list(parent);
cache[_currentRemoteDir!] = map;
}
}
if (map.containsKey(remote) && map[remote] == FileType.directory) {
await _upload(local, remote);
}
}
} finally {
_isProgress = false;
}
}
Future<void> _upload(String local, String remote) async {
local = Command.replaceTag(local);
remote = Command.replaceTag(remote);
if (await Directory(local).exists()) {
var name = Path.getName(local);
var childRemoteFolder = '$remote/$name';
await createFolder(childRemoteFolder);
var list = Directory(local).listSync();
for (var item in list) {
if (await Directory(item.path).exists()) {
await _upload(item.path, childRemoteFolder);
} else {
await _ftp!.changeDirectory(childRemoteFolder);
await _ftp!.uploadFile(File(item.path));
}
}
} else {
await _ftp!.changeDirectory(remote);
await _ftp!.uploadFile(File(local));
}
}
Map<String, Map<String, FileType>> _cache = <String, Map<String, FileType>>{};
//Local target must be folder
Future<void> download(Map<String, String> remoteLocalMap) async {
var c = Completer();
_cache = <String, Map<String, FileType>>{};
try {
_isProgress = true;
var entries = remoteLocalMap.entries;
for (var entry in entries) {
await _download(entry.key, entry.value);
}
} finally {
_isProgress = false;
c.complete();
}
return c.future;
}
Future<void> _download(String remote, String local) async {
Map<String, FileType>? map;
remote = Path.getNormalizePath(remote);
local = Path.getNormalizePath(local);
var parent = Path.getParent(remote);
var name = Path.getName(remote);
if (_cache.containsKey(parent)) {
map = _cache[parent]!;
} else {
map ??= await list(parent!);
}
if (map.containsKey(remote)) {
if (map[remote] == FileType.directory) {
var childLocalFolder = '$local/$name';
var dir = Directory(childLocalFolder);
if (!await dir.exists()) dir.createSync();
Map<String, FileType> childList;
if (_cache.containsKey(remote)) {
childList = _cache[remote]!;
} else {
childList = await list(remote);
_cache[remote] = childList;
}
var entries = childList.entries;
for (var entry in entries) {
await _download(entry.key, childLocalFolder);
}
} else {
await _ftp!.changeDirectory(parent!);
await _ftp!.downloadFile(name, File('$local/$name'));
}
}
}
Future<Map<String, FileType>> list(String path) async {
var c = Completer<Map<String, FileType>>();
var map = <String, FileType>{};
try {
await _ftp!.changeDirectory(path);
var list = await _ftp!.listDirectoryContent();
var count = 0;
for (var item in list) {
FileType? type;
switch (item.type) {
case FTPEntryType.DIR:
type = FileType.directory;
break;
case FTPEntryType.FILE:
type = FileType.file;
break;
case FTPEntryType.LINK:
// TODO: Handle this case.
break;
case FTPEntryType.UNKNOWN:
// TODO: Handle this case.
break;
}
var name = item.name;
if (name != '.' && name != '..') {
count++;
var remotePath = '$path/$name';
map[remotePath] = type!;
// print('$count = $type = $remotePath');
}
}
} finally {}
c.complete(map);
return c.future;
}
Future<void> delete(List<String> remoteList) async {
var cache = <String, Map<String, FileType>>{};
Map<String, FileType>? map;
_currentRemoteDir = null;
try {
_isProgress = true;
for (var item in remoteList) {
var parent = Path.getParent(item);
var name = Path.getName(item);
if (_currentRemoteDir != parent) {
await _ftp!.changeDirectory(parent!);
_currentRemoteDir = parent;
if (cache.containsKey(_currentRemoteDir)) {
map = cache[_currentRemoteDir!]!;
} else {
map = await list(parent);
cache[_currentRemoteDir!] = map;
}
}
if (map!.containsKey(item)) {
if (map[item] == FileType.directory) {
await _ftp!.deleteDirectory(name);
} else {
await _ftp!.deleteFile(name);
}
print('Deleted: $item');
}
}
} finally {
_isProgress = false;
}
}
Future<bool> createFolder(String path) async {
var c = Completer<bool>();
try {
var parent = Path.getParent(path);
var name = Path.getName(path);
await _ftp!.changeDirectory(parent!);
if (await _ftp!.checkFolderExistence(name)) {
c.complete(false);
print('Create failed: Already exist file or folder');
} else {
await _ftp!.makeDirectory(name);
c.complete(true);
print('Created Folder: $path');
}
} finally {}
return c.future;
}
}
// ignore: slash_for_doc_comments
/********************************* SFTP **********************************
Need sshpass
//Brew install - https://brew.sh/index_ko
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
//sshpass install - https://gist.github.com/arunoda/7790979
brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb
*************************************************************************/
class SFTP extends FTP {
SFTP(String host,
{int port = 22,
String user = 'anonymous',
String password = '',
int timeout = 30})
: super(host,
port: port, user: user, password: password, timeout: timeout);
@override
Future<void> connect() async {}
@override
Future<void> disconnect() async {}
//Remote target must be folder
@override
Future<void> upload(Map<String, String> localRemoteMap) async {
Map<String, FileType>? map;
var entries = localRemoteMap.entries;
for (var entry in entries) {
var local = Path.getNormalizePath(entry.key);
var remote = Path.getNormalizePath(entry.value);
String type;
var parent = Path.getParent(remote);
if (map == null || !map.containsKey(local)) map = await list(parent!);
if (map[remote] == FileType.directory) {
if (await Directory(local).exists()) {
type = '[D]';
await Process.run('sshpass', [
'-p',
_pass,
'scp',
'-P',
'$_port',
'-r',
local,
'$_user@$_host:$remote'
]).then((ProcessResult results) => print(results.stdout));
} else {
type = '[F]';
await Process.run('sshpass', [
'-p',
_pass,
'scp',
'-P',
'$_port',
local,
'$_user@$_host:$remote'
]).then((ProcessResult results) => print(results.stdout));
}
print('Uploaded: $type$local --> $remote');
} else {
throw ArgumentError('Remote target should be "Folder"');
}
}
}
//Local target must be folder
@override
Future<void> download(Map<String, String> remoteLocalMap) async {
Map<String, FileType>? map;
var entries = remoteLocalMap.entries;
for (var entry in entries) {
var remote = Path.getNormalizePath(entry.key);
var local = Path.getNormalizePath(entry.value);
String type;
var parent = remote.substring(0, remote.lastIndexOf('/'));
if (map == null || !map.containsKey(remote)) map = await list(parent);
if (map[remote] == FileType.directory) {
type = '[D]';
await Process.run('sshpass', [
'-p',
_pass,
'scp',
'-P',
'$_port',
'-r',
'$_user@$_host:$remote',
local
]).then((ProcessResult result) => print(result.stdout));
} else {
type = '[F]';
await Process.run('sshpass', [
'-p',
_pass,
'scp',
'-P',
'$_port',
'$_user@$_host:$remote',
local
]).then((ProcessResult result) => print(result.stdout));
}
print('Download: $type$remote --> $local');
}
}
@override
Future<Map<String, FileType>> list(String path) async {
var c = Completer<Map<String, FileType>>();
var map = <String, FileType>{};
var count = 0;
await Process.run('sshpass', [
'-p',
_pass,
'ssh',
'$_user@$_host',
'-p$_port',
'ls -l $path'
]).then((ProcessResult result) async {
var data = result.stdout;
var list = data.split('\n');
list.forEach((element) {
if (count != 0 && element.length > 10) {
var info = element.split(' ');
var typeStr = info[0].substring(0, 1);
FileType type;
var fileName = info[info.length - 1];
switch (typeStr) {
case 'd':
type = FileType.directory;
break;
case '-':
type = FileType.file;
break;
default:
type = FileType.link;
}
var mapKey = '$path/$fileName';
// print('$count = $type = $mapKey');
map[mapKey] = type;
}
count++;
});
});
c.complete(map);
return c.future;
}
@override
Future<void> delete(List<String> remoteList) async {
print('######################## delete ${remoteList.length}');
//check file exist
for (var item in remoteList) {
await Process.run('sshpass', [
'-p',
_pass,
'ssh',
'$_user@$_host',
'-p$_port',
'rm -rf $item'
]).then((ProcessResult results) => print(results.stdout));
print('Deleted: $item');
}
}
@override
Future<bool> createFolder(String path) async {
var c = Completer<bool>();
var parent = path.substring(0, path.lastIndexOf('/'));
var map = await list(parent);
if (map.containsKey(path)) {
c.complete(false);
print('Create failed: Already exist file or folder');
} else {
await Process.run('sshpass', [
'-p',
_pass,
'ssh',
'$_user@$_host',
'-p$_port',
'mkdir $path'
]).then((ProcessResult results) => print(results.stdout));
c.complete(true);
print('Created Folder: $path');
}
return c.future;
}
}

View file

@ -0,0 +1,74 @@
import 'dart:async';
import 'dart:core';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:oto_cli/oto/uploader/ftp.dart';
import 'package:dart_framework/utils/path.dart';
class Uploader implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataUploader.fromJson(command.param);
//Check asterisk
var uploadMap = <String, String>{};
var map = data.uploadMap;
int port = data.port == 0 ? (data.sftp ? 22 : 21) : data.port;
var ftp = data.sftp
? SFTP(data.host, port: port, user: data.user, password: data.password)
: FTP(data.host, port: port, user: data.user, password: data.password);
print(
'========================= [Try connection] =========================');
await ftp.connect();
print(
'========================= [Connected! Start Upload] ================');
var entries = map.entries;
for (var entry in entries) {
var local = path.join(Application.to.jenkinsData!.workspace, entry.key);
var remote = entry.value;
print('[Upload]: $local ====> $remote');
var parentRemote = Path.getParent(remote);
var remoteMap = await ftp.list(parentRemote!);
var parentLocal = Path.getParent(local);
var nameLocal = Path.getName(local);
if (nameLocal == '*') {
//(*)
//Remove before folder
if (remoteMap.containsKey(remote) &&
remoteMap[remoteMap] == FileType.directory) {
await ftp.delete([remote]);
}
//Create new folder
await ftp.createFolder(remote);
var parentLocalDir = Directory(parentLocal!);
if (parentLocalDir.existsSync()) {
var localFiles = parentLocalDir.listSync();
var map = <String, String>{};
for (var file in localFiles) {
map[file.path] = remote;
}
await ftp.upload(map);
} else {
throw ArgumentError('Start - $parentLocal is not exist');
}
} else {
uploadMap[local] = remote;
}
}
if (uploadMap.isNotEmpty) await ftp.upload(uploadMap);
await ftp.disconnect();
print(
'========================= [Upload completed!] ======================');
c.complete(true);
return c.future;
}
}

View file

@ -0,0 +1,27 @@
import 'dart:async';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:dart_framework/platform/process.dart';
class ExecuteApp implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataExecuteApp.fromJson(command.param);
var exe = Command.replaceTag(data.executable);
var desktop = Command.replaceTag(data.desktopServicePath);
print('Process Start: $exe');
var content = 'start "" "$exe" & exit';
var buffer = StringBuffer(content);
var processData =
await ProcessExecutor.run(buffer, workspace: data.desktopServicePath);
c.complete(true);
return c.future;
}
}

View file

@ -0,0 +1,55 @@
import 'dart:async';
import 'dart:io';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:dart_framework/utils/path.dart';
// ignore: depend_on_referenced_packages
import 'package:path/path.dart' as path;
class PublishiOS implements Command {
Function? get error => null;
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var data = DataPublishiOS.fromJson(command.param);
var workspace = Path.getNormal(Application.to.jenkinsData!.workspace);
var menifestURL = Command.replaceTag(data.menifestURL);
var destinationPath = Command.replaceTag(data.destinationPath);
var templatePath = path.normalize(
'${path.dirname(Platform.script.toFilePath())}/../assets/template');
var indexContent = await File('$templatePath/index.html').readAsString();
indexContent = indexContent.replaceAll("MENIFEST_URL", menifestURL);
var destinationDir = Directory(destinationPath);
if (!destinationDir.existsSync()) {
await destinationDir.create(recursive: true);
}
var indexFile = File('$destinationPath/index.html');
await indexFile.writeAsString(indexContent);
print('Created "index.html" --> "$destinationPath/index.html"');
var menifestContent =
await File('$templatePath/manifest.plist').readAsString();
menifestContent = menifestContent
.replaceFirst('IPA_URL', Command.replaceTag(data.ipaURL))
.replaceFirst('BUNDLE_ID', Command.replaceTag(data.bundleID))
.replaceFirst('VERSION', Command.replaceTag(data.version))
.replaceFirst('APP_NAME', Command.replaceTag(data.appName))
.replaceFirst(
'DISPLAY_IMAGE_URL', Command.replaceTag(data.displayImageURL))
.replaceFirst(
'FULL_SIZE_IMAGE_URL', Command.replaceTag(data.fullSizeImageURL));
var menifestFile = File('$destinationPath/manifest.plist');
await menifestFile.writeAsString(menifestContent);
print('Created "manifest.plist" --> "$destinationPath/manifest.plist"');
c.complete(true);
return c.future;
}
}

45
lib/oto/util/slack.dart Normal file
View file

@ -0,0 +1,45 @@
import 'dart:async';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
import 'package:color/color.dart';
import 'package:dart_framework/utils/slack/slack_sender.dart';
class Slack implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataSlack.fromJson(command.param);
var result = true;
//To-do: compose parameters
c.complete(result);
return c.future;
}
}
class SlackBuild implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataSlackBuild.fromJson(command.param);
var result = true;
var version = Command.replaceTag(data.version);
var download = Command.replaceTag(data.downloadURL);
var channel = data.channel ?? 'build';
var color = Color.hex(data.color);
var sender = SlackSender();
await sender.sendBuild(data.appName, data.type, color, version, download,
channel: channel);
c.complete(result);
return c.future;
}
}

80
lib/oto/util/zip.dart Normal file
View file

@ -0,0 +1,80 @@
import 'dart:async';
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:dart_framework/utils/path.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/command.dart';
import 'package:oto_cli/oto/data/data.dart';
class Zip implements Command {
@override
Future<bool> execute(DataCommand command) async {
Completer<bool> c = Completer<bool>();
var common = Application.to.property;
var jenkins = Application.to.jenkinsData;
var data = DataZip.fromJson(command.param);
try {
var zipFilePath = Path.getNormal(Command.replaceTag(data.zipFile));
print('Start Compress...');
var parent = Directory(Path.getParent(zipFilePath)!);
if (!parent.existsSync()) parent.createSync(recursive: true);
var zipFile = File(zipFilePath);
if (zipFile.existsSync()) zipFile.deleteSync();
var tempDir = Directory(zipFilePath);
if (!tempDir.existsSync()) {
var encoder = ZipFileEncoder();
encoder.create(zipFilePath);
var list = data.zipList;
for (var path in list) {
path = Path.getNormal(Command.replaceTag(path));
var name = Path.getName(path);
if (name == '*') {
parent = Directory(Path.getParent(path)!);
if (parent.existsSync()) {
var list = parent.listSync();
for (var item in list) {
var dir = Directory(item.path);
var file = File(item.path);
if (dir.existsSync()) {
print('- Compress Dir --> ${dir.path}');
encoder.addDirectory(dir);
}
if (file.existsSync()) {
encoder.addFile(file);
print('- Compress File --> ${file.path}');
}
}
} else {
print('================== "$path" is not exist');
}
} else {
var dir = Directory(path);
var file = File(path);
var added = false;
if (dir.existsSync()) {
print('- Compress Dir --> ${dir.path}');
encoder.addDirectory(dir);
added = true;
}
if (file.existsSync()) {
encoder.addFile(file);
print('- Compress File --> ${file.path}');
added = true;
}
if (!added) print('================== "$path" is not exist');
}
}
encoder.close();
c.complete(true);
print('Compress success: $zipFilePath');
} else {
throw Exception('"${tempDir.path}" is Directory');
}
} on Exception catch (e) {
print('Zip General Exception: $e');
}
return c.future;
}
}