543 lines
17 KiB
Dart
543 lines
17 KiB
Dart
// ignore_for_file: constant_identifier_names, depend_on_referenced_packages, avoid_init_to_null
|
|
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:dart_framework/platform/process.dart';
|
|
import 'package:dart_framework/utils/path.dart';
|
|
import 'package:dart_framework/utils/system_util.dart';
|
|
import 'package:oto_cli/oto/application.dart';
|
|
import 'package:oto_cli/oto/commands/build/app_data_creator.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_dart.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_dart_compile.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_dot_net.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_flutter.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_ios.dart';
|
|
import 'package:oto_cli/oto/commands/build/build_msbuild.dart';
|
|
import 'package:oto_cli/oto/commands/build/testflight_ios.dart';
|
|
import 'package:oto_cli/oto/commands/build/xcodeproj.dart';
|
|
import 'package:oto_cli/oto/commands/file/directory.dart';
|
|
import 'package:oto_cli/oto/commands/file/file.dart';
|
|
import 'package:oto_cli/oto/commands/file/file_diff_check.dart';
|
|
import 'package:oto_cli/oto/commands/ftp/download.dart';
|
|
import 'package:oto_cli/oto/commands/git/git.dart';
|
|
import 'package:oto_cli/oto/commands/aws/awscli.dart';
|
|
import 'package:oto_cli/oto/commands/docker/docker.dart';
|
|
import 'package:oto_cli/oto/commands/git/git_hub.dart';
|
|
import 'package:oto_cli/oto/commands/gradle/gradle.dart';
|
|
import 'package:oto_cli/oto/commands/jira/jira.dart';
|
|
import 'package:oto_cli/oto/commands/process/process.dart';
|
|
import 'package:oto_cli/oto/commands/proto/protobuf.dart';
|
|
import 'package:oto_cli/oto/commands/util/json.dart';
|
|
import 'package:oto_cli/oto/commands/util/print.dart';
|
|
import 'package:oto_cli/oto/commands/util/set_value.dart';
|
|
import 'package:oto_cli/oto/commands/util/smb_authorize.dart';
|
|
import 'package:oto_cli/oto/commands/util/string_util.dart';
|
|
import 'package:oto_cli/oto/commands/util/timer.dart';
|
|
import 'package:oto_cli/oto/commands/web/web.dart';
|
|
import 'package:oto_cli/oto/data/command_data.dart';
|
|
import 'package:oto_cli/oto/data/jenkins_data.dart';
|
|
import 'package:oto_cli/oto/commands/file/delete.dart';
|
|
import 'package:oto_cli/oto/commands/file/rename.dart';
|
|
import 'package:oto_cli/oto/commands/shell/shell.dart';
|
|
import 'package:oto_cli/oto/commands/file/copy.dart';
|
|
import 'package:oto_cli/oto/commands/util/publish_ios.dart';
|
|
import 'package:oto_cli/oto/commands/util/slack.dart';
|
|
import 'package:oto_cli/oto/commands/util/zip.dart';
|
|
import 'package:oto_cli/oto/commands/ftp/upload.dart';
|
|
import 'package:oto_cli/oto/pipeline/pipeline_exe_handle.dart';
|
|
|
|
enum CommandType {
|
|
SimpleCommand,
|
|
Print,
|
|
SetValue,
|
|
CreateAppData,
|
|
BuildDart,
|
|
BuildFlutter,
|
|
BuildDartCompile,
|
|
BuildDotNet,
|
|
BuildiOS,
|
|
TestflightUpload,
|
|
TestflightStatusCheck,
|
|
TestflightDistribute,
|
|
XcodeprojAddFile,
|
|
BuildMSBuild,
|
|
CodeSign,
|
|
CodeSignVerify,
|
|
ProductBuild,
|
|
Notarize,
|
|
ArchiveiOS,
|
|
ExportiOS,
|
|
PublishiOS,
|
|
Upload,
|
|
Download,
|
|
Copy,
|
|
Rename,
|
|
Delete,
|
|
FileDiffCheck,
|
|
FileRead,
|
|
FileWrite,
|
|
Files,
|
|
FileInfo,
|
|
DirectoryCreate,
|
|
Slack,
|
|
SlackBuild,
|
|
StringSub,
|
|
StringReplace,
|
|
StringReplacePattern,
|
|
StringIndex,
|
|
URLInfo,
|
|
Jira,
|
|
Zip,
|
|
Shell,
|
|
ShellFile,
|
|
Git,
|
|
GitCommit,
|
|
GitPush,
|
|
GitCheckout,
|
|
GitRev,
|
|
GitCount,
|
|
GitReset,
|
|
GitStashPush,
|
|
GitStashApply,
|
|
GitHub,
|
|
GitHubPullRequestCreate,
|
|
GitHubPullRequestList,
|
|
GitHubPullRequestClose,
|
|
Protobuf,
|
|
JsonReader,
|
|
JsonReaderFile,
|
|
JsonWriterFile,
|
|
AwsCli,
|
|
Docker,
|
|
Gradle,
|
|
WebRequest,
|
|
WebFile,
|
|
ProcessPortUse,
|
|
ProcessRun,
|
|
ProcessKill,
|
|
SMBAuth,
|
|
Delay
|
|
}
|
|
|
|
abstract class Command {
|
|
static final Map<CommandType, Command Function()> _commandMap = {
|
|
CommandType.SimpleCommand: () => SimpleCommand(),
|
|
CommandType.Print: () => PrintCommand(),
|
|
CommandType.SetValue: () => SetValueCommand(),
|
|
CommandType.CreateAppData: () => CreateAppData(),
|
|
CommandType.BuildDart: () => BuildDart(),
|
|
CommandType.BuildFlutter: () => BuildFlutter(),
|
|
CommandType.BuildDartCompile: () => BuildDartCompile(),
|
|
CommandType.BuildDotNet: () => BuildDotNet(),
|
|
CommandType.BuildiOS: () => BuildiOS(),
|
|
CommandType.TestflightUpload: () => TestflightUpload(),
|
|
CommandType.TestflightStatusCheck: () => TestflightStatusCheck(),
|
|
CommandType.TestflightDistribute: () => TestflightDistribute(),
|
|
CommandType.XcodeprojAddFile: () => XcodeprojAddFile(),
|
|
CommandType.BuildMSBuild: () => BuildMSBuild(),
|
|
CommandType.CodeSign: () => CodeSign(),
|
|
CommandType.CodeSignVerify: () => CodeSignVerify(),
|
|
CommandType.ProductBuild: () => ProductBuild(),
|
|
CommandType.Notarize: () => Notarize(),
|
|
CommandType.ArchiveiOS: () => ArchiveiOS(),
|
|
CommandType.ExportiOS: () => ExportiOS(),
|
|
CommandType.PublishiOS: () => PublishiOS(),
|
|
CommandType.Upload: () => Upload(),
|
|
CommandType.Download: () => Download(),
|
|
CommandType.Copy: () => Copy(),
|
|
CommandType.Rename: () => Rename(),
|
|
CommandType.Delete: () => Delete(),
|
|
CommandType.FileDiffCheck: () => FileDiffCheck(),
|
|
CommandType.FileRead: () => FileRead(),
|
|
CommandType.FileWrite: () => FileWrite(),
|
|
CommandType.Files: () => Files(),
|
|
CommandType.FileInfo: () => FileInfo(),
|
|
CommandType.DirectoryCreate: () => DirectoryCreate(),
|
|
CommandType.Slack: () => Slack(),
|
|
CommandType.SlackBuild: () => SlackBuild(),
|
|
CommandType.StringSub: () => StringSub(),
|
|
CommandType.StringReplace: () => StringReplace(),
|
|
CommandType.StringReplacePattern: () => StringReplacePattern(),
|
|
CommandType.StringIndex: () => StringIndex(),
|
|
CommandType.URLInfo: () => URLInfo(),
|
|
CommandType.Jira: () => Jira(),
|
|
CommandType.Zip: () => Zip(),
|
|
CommandType.Shell: () => Shell(),
|
|
CommandType.ShellFile: () => ShellFile(),
|
|
CommandType.Git: () => Git(),
|
|
CommandType.GitCommit: () => GitCommit(),
|
|
CommandType.GitPush: () => GitPush(),
|
|
CommandType.GitCheckout: () => GitCheckout(),
|
|
CommandType.GitRev: () => GitRev(),
|
|
CommandType.GitCount: () => GitCount(),
|
|
CommandType.GitReset: () => GitReset(),
|
|
CommandType.GitStashPush: () => GitStashPush(),
|
|
CommandType.GitStashApply: () => GitStashApply(),
|
|
CommandType.GitHub: () => GitHub(),
|
|
CommandType.GitHubPullRequestCreate: () => GitHubPullRequestCreate(),
|
|
CommandType.GitHubPullRequestList: () => GitHubPullRequestList(),
|
|
CommandType.GitHubPullRequestClose: () => GitHubPullRequestClose(),
|
|
CommandType.Protobuf: () => Protobuf(),
|
|
CommandType.JsonReader: () => JsonReader(),
|
|
CommandType.JsonReaderFile: () => JsonReaderFile(),
|
|
CommandType.JsonWriterFile: () => JsonWriterFile(),
|
|
CommandType.AwsCli: () => AwsCli(),
|
|
CommandType.Docker: () => Docker(),
|
|
CommandType.Gradle: () => Gradle(),
|
|
CommandType.WebRequest: () => WebRequest(),
|
|
CommandType.WebFile: () => WebFile(),
|
|
CommandType.ProcessPortUse: () => ProcessPortUse(),
|
|
CommandType.ProcessRun: () => ProcessRun(),
|
|
CommandType.ProcessKill: () => ProcessKill(),
|
|
CommandType.SMBAuth: () => SMBAuth(),
|
|
CommandType.Delay: () => Delay(),
|
|
};
|
|
|
|
factory Command.byType(CommandType type) {
|
|
return _commandMap[type]!();
|
|
}
|
|
|
|
Command();
|
|
late String id;
|
|
|
|
static RegExp regEx = RegExp(r'(<!)([_a-zA-Z0-9.-]+)(>)');
|
|
static RegExp regSetEx = RegExp(r'(<@)([_a-zA-Z0-9.-]+)(>)');
|
|
|
|
static Map<String, dynamic> replaceAllTagsMap(Map<String, dynamic> map,
|
|
{bool replace = false}) {
|
|
var newMap = <String, dynamic>{};
|
|
var deleteKeys = <String>[];
|
|
for (var pair in map.entries) {
|
|
var key = pair.key;
|
|
var keyReplacedTag = Command.replaceTagValue(key);
|
|
if (keyReplacedTag != key) {
|
|
deleteKeys.add(key);
|
|
}
|
|
if (map[key] is String) {
|
|
newMap[keyReplacedTag] = Command.replaceTagValue(map[key]);
|
|
} else if (map[key] is List) {
|
|
newMap[keyReplacedTag] = replaceAllTagsList(map[key], replace: replace);
|
|
} else if (map[key] is Map) {
|
|
newMap[keyReplacedTag] = replaceAllTagsMap(map[key], replace: replace);
|
|
} else {
|
|
newMap[keyReplacedTag] = map[key];
|
|
}
|
|
if (replace) {
|
|
map[keyReplacedTag] = newMap[keyReplacedTag];
|
|
}
|
|
}
|
|
if (replace) {
|
|
for (var key in deleteKeys) {
|
|
map.remove(key);
|
|
}
|
|
return map;
|
|
}
|
|
return newMap;
|
|
}
|
|
|
|
static List replaceAllTagsList(List list, {bool replace = false}) {
|
|
var newList = <dynamic>[];
|
|
for (var i = 0; i < list.length; ++i) {
|
|
if (list[i] is String) {
|
|
newList.add(Command.replaceTagValue(list[i]));
|
|
} else if (list[i] is List) {
|
|
newList.add(replaceAllTagsList(list[i]));
|
|
} else if (list[i] is Map) {
|
|
newList.add(replaceAllTagsMap(list[i]));
|
|
} else {
|
|
newList.add(list[i]);
|
|
}
|
|
if (replace) {
|
|
list[i] = newList[i];
|
|
}
|
|
}
|
|
if (replace) {
|
|
return newList;
|
|
}
|
|
return newList;
|
|
}
|
|
|
|
static Future setPropertyValue(String tag, dynamic value,
|
|
{bool? showPrint}) async {
|
|
var match = regSetEx.firstMatch(tag);
|
|
tag = match!.group(2)!;
|
|
if (tag.startsWith('property')) {
|
|
var key = tag.replaceFirst('property.', '');
|
|
Application.instance.property[key] = value;
|
|
// await CLI.println('[Return] $key : $value', color: Color.yellowStrong);
|
|
if (showPrint ?? true) {
|
|
if (value is List) {
|
|
Application.log('[Return] $key :\n');
|
|
var index = 0;
|
|
var length = value.length.toString().length;
|
|
for (var item in value) {
|
|
var indexStr = index.toString().padLeft(length);
|
|
Application.log('$indexStr - $item');
|
|
index++;
|
|
}
|
|
} else {
|
|
Application.log('[Return] $key : $value');
|
|
}
|
|
}
|
|
}
|
|
return simpleFuture;
|
|
}
|
|
|
|
static dynamic _getTagValue(String value) {
|
|
var setted = false;
|
|
dynamic data;
|
|
if (value.startsWith('property.')) {
|
|
var map = Application.instance.property;
|
|
var param = value.replaceFirst('property.', '');
|
|
if (map.containsKey(param)) {
|
|
data = map[param];
|
|
setted = true;
|
|
} else {
|
|
var params = param.split('.');
|
|
if (params.length > 1) {
|
|
var subMap = map;
|
|
for (var index = 0; index < params.length; index++) {
|
|
if (subMap.containsKey(params[index])) {
|
|
var value = subMap[params[index]];
|
|
if (value is Map<String, dynamic>) {
|
|
subMap = value;
|
|
} else {
|
|
data = value;
|
|
setted = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!setted) {
|
|
Application.log('"$value" is not exist in property');
|
|
data = null;
|
|
setted = true;
|
|
}
|
|
}
|
|
|
|
if (value.startsWith('state.')) {
|
|
var map = Application.instance.commandStates;
|
|
var param = value.replaceFirst('state.', '');
|
|
if (map.containsKey(param)) {
|
|
var value = map[param].toString().replaceAll('CommandState.', '');
|
|
data = value == '' ? null : value;
|
|
} else {
|
|
data = null;
|
|
}
|
|
setted = true;
|
|
}
|
|
|
|
if (!setted) {
|
|
throw Exception(
|
|
'Command.getTagValue: "$value" is not defined in property.');
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static String getAbs(String path) {
|
|
path = getPathNormal(path);
|
|
if (path == '.' || path.startsWith('./') || path.startsWith('.\\')) {
|
|
path = path.replaceFirst('.', getPathNormal(Application.current));
|
|
}
|
|
if (path == '..' || path.startsWith('../') || path.startsWith('..\\')) {
|
|
path =
|
|
path.replaceFirst('..', '${getPathNormal(Application.current)}/..');
|
|
}
|
|
return File(path).absolute.path;
|
|
}
|
|
|
|
static String getPathNormal(String path) {
|
|
return replaceTagValue(Path.getNormal(path));
|
|
}
|
|
|
|
static dynamic replaceTagValue(String raw) {
|
|
var list = regEx.allMatches(raw);
|
|
if (list.length == 1) {
|
|
var item = list.first;
|
|
var sub = list.first.input.substring(item.start, item.end);
|
|
if (sub == raw) {
|
|
return replaceSingleTagValue(raw);
|
|
}
|
|
}
|
|
|
|
for (var match in list) {
|
|
var param = match.group(2);
|
|
var target = '<!${param!}>';
|
|
raw = raw.replaceAll(target, _getTagValue(param).toString());
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
static dynamic replaceSingleTagValue(String raw) {
|
|
dynamic value;
|
|
var list = regEx.allMatches(raw);
|
|
for (var match in list) {
|
|
var param = match.group(2);
|
|
value = _getTagValue(param!);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
late ExeHandleData? exeHandle = null;
|
|
|
|
late String _workspace;
|
|
String get workspace {
|
|
return _workspace;
|
|
}
|
|
|
|
Map<String, dynamic> get property {
|
|
return Application.instance.property;
|
|
}
|
|
|
|
DataCommon? get commonData {
|
|
return Application.instance.commonData;
|
|
}
|
|
|
|
String getWorkspace(String? dataWorkspace) {
|
|
if (dataWorkspace == null) {
|
|
//모델에 정의된 workspace가 없을경우 property에 설정된 workspace 우선
|
|
if (property.containsKey('workspace')) {
|
|
return getAbs(property['workspace']);
|
|
}
|
|
//property에도 설정이 되어 있지 않으면 현재 실행 프로젝트 기준으로 workspace 설정
|
|
else {
|
|
return getAbs(commonData!.workspace);
|
|
}
|
|
} else {
|
|
return getAbs(dataWorkspace);
|
|
}
|
|
}
|
|
|
|
String getCDPath(String path) {
|
|
String drive = '';
|
|
if (Platform.isWindows) {
|
|
drive = '${path[0]}: $and ';
|
|
}
|
|
return '${drive}cd $path';
|
|
}
|
|
|
|
String replaceTag(String raw) {
|
|
return Command.replaceTagValue(raw);
|
|
}
|
|
|
|
dynamic replaceSingleTag(String raw) {
|
|
return Command.replaceSingleTagValue(raw);
|
|
}
|
|
|
|
Future setProperty(String tag, dynamic value, {bool? showPrint}) async {
|
|
await Command.setPropertyValue(tag, value, showPrint: showPrint);
|
|
}
|
|
|
|
Map<String, dynamic> getParam(DataCommand command) {
|
|
Map<String, dynamic> map;
|
|
if (command.param == null) {
|
|
map = <String, dynamic>{};
|
|
} else {
|
|
map = Command.replaceAllTagsMap(command.param);
|
|
}
|
|
map['workspace'] = _workspace = getWorkspace(map['workspace']);
|
|
return map;
|
|
}
|
|
|
|
Future execute(DataCommand command) async {
|
|
return simpleFuture;
|
|
}
|
|
|
|
Future executeHandle(DataCommand command, ExeHandleData handleData) async {
|
|
exeHandle = handleData;
|
|
try {
|
|
return await execute(command);
|
|
} on Exception {
|
|
return await exeHandle!.pipelineFail!.execute();
|
|
}
|
|
}
|
|
|
|
Future<List<int>> getMergedPassExitCode(
|
|
Map<String, dynamic>? param, List<int>? passExitCodes) async {
|
|
var c = Completer<List<int>>();
|
|
var passExitCodesMerged = [0];
|
|
if (param != null) {
|
|
var data = DataParam.fromJson(param);
|
|
if (data.passExitCodes != null) {
|
|
passExitCodesMerged.addAll(data.passExitCodes!);
|
|
}
|
|
}
|
|
if (passExitCodes != null) {
|
|
passExitCodesMerged.addAll(passExitCodes);
|
|
}
|
|
c.complete(passExitCodesMerged);
|
|
return c.future;
|
|
}
|
|
|
|
Future completeProcess(ProcessData processData, DataCommand command,
|
|
{List<int>? passExitCodes}) async {
|
|
var exitCode = await processData.process.exitCode;
|
|
var passExitCodesMerge =
|
|
await getMergedPassExitCode(command.param, passExitCodes);
|
|
String? resultMessage = null;
|
|
if (exitCode != 0 && passExitCodesMerge.contains(exitCode)) {
|
|
resultMessage = processData.stderr.toString();
|
|
}
|
|
return await complete(exitCode, command,
|
|
errorMessage: processData.stderr.toString(),
|
|
passExitCodes: passExitCodes,
|
|
resultMessage: resultMessage);
|
|
}
|
|
|
|
Future<dynamic> complete(int exitCode, DataCommand command,
|
|
{String? errorMessage,
|
|
List<int>? passExitCodes,
|
|
String? resultMessage}) async {
|
|
var passExitCodesMerge =
|
|
await getMergedPassExitCode(command.param, passExitCodes);
|
|
if (command.param != null) {
|
|
var data = DataParam.fromJson(command.param);
|
|
if (data.setExitCode != null) {
|
|
await setProperty(data.setExitCode!, exitCode);
|
|
}
|
|
if (data.setResult != null) {
|
|
await setProperty(data.setResult!, resultMessage);
|
|
}
|
|
}
|
|
if (exeHandle == null) {
|
|
if (passExitCodesMerge.contains(exitCode)) {
|
|
return simpleFuture;
|
|
} else {
|
|
Application.log('Exit Code: $exitCode');
|
|
var data = ExceptionData();
|
|
data.phase = command.name;
|
|
data.message = errorMessage;
|
|
throw Exception(data);
|
|
}
|
|
} else {
|
|
if (passExitCodesMerge.contains(exitCode)) {
|
|
return await exeHandle!.pipelineSuccess!.execute();
|
|
} else {
|
|
return await exeHandle!.pipelineFail!.execute();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Templete
|
|
class SimpleCommand extends Command {
|
|
@override
|
|
Future execute(DataCommand command) async {
|
|
Application.log(command.param);
|
|
return simpleFuture;
|
|
}
|
|
}
|
|
|
|
class ExceptionData {
|
|
String? phase;
|
|
String? message;
|
|
|
|
@override
|
|
String toString() {
|
|
var str = ' [$phase]\n\n';
|
|
str += message ??= '';
|
|
return str;
|
|
}
|
|
}
|