Merge branch 'release/1.0'
This commit is contained in:
commit
14539d89fa
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@ -4,23 +4,34 @@
|
|||||||
"Версионирование",
|
"Версионирование",
|
||||||
"версионируется",
|
"версионируется",
|
||||||
"Alek",
|
"Alek",
|
||||||
|
"Aleksei",
|
||||||
|
"Badiaev",
|
||||||
"Childs",
|
"Childs",
|
||||||
|
"choco",
|
||||||
"drawio",
|
"drawio",
|
||||||
"errcheck",
|
"errcheck",
|
||||||
"gocov",
|
"gocov",
|
||||||
"GOPATH",
|
"GOPATH",
|
||||||
"honnef",
|
"honnef",
|
||||||
|
"INSTALLDIR",
|
||||||
"jstemmer",
|
"jstemmer",
|
||||||
"kisielk",
|
"kisielk",
|
||||||
"matm",
|
"matm",
|
||||||
"mousesoft",
|
"mousesoft",
|
||||||
"mxfile",
|
"mxfile",
|
||||||
"nxfile",
|
"nxfile",
|
||||||
|
"outdir",
|
||||||
|
"outext",
|
||||||
"sashamelentyev",
|
"sashamelentyev",
|
||||||
|
"shlibs",
|
||||||
|
"STARTMENUSHORTCUT",
|
||||||
"staticcheck",
|
"staticcheck",
|
||||||
"stretchr",
|
"stretchr",
|
||||||
|
"subdir",
|
||||||
|
"svvg",
|
||||||
"tamerh",
|
"tamerh",
|
||||||
"usestdlibvars",
|
"usestdlibvars",
|
||||||
|
"wdir",
|
||||||
"xmlparser"
|
"xmlparser"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
|
||||||
и этот проект придерживается [Semantic Versioning](https://semver.org/lang/ru/).
|
и этот проект придерживается [Semantic Versioning](https://semver.org/lang/ru/).
|
||||||
|
|
||||||
## [0.1] - Не опубликовано
|
## [1.0] - Не опубликовано
|
||||||
|
|
||||||
- **Добавлено**
|
- **Добавлено**
|
||||||
- Все параметры передаются программе `drawio` из пакета `drawio-desktop` без
|
- Все параметры передаются программе `drawio` из пакета `drawio-desktop` без
|
||||||
|
75
build/ci/Jenkinsfile
vendored
75
build/ci/Jenkinsfile
vendored
@ -30,35 +30,35 @@ pipeline {
|
|||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// stage('Build Windows') {
|
stage('Build Windows') {
|
||||||
// agent{ label 'windows' }
|
agent{ label 'windows' }
|
||||||
// environment {
|
environment {
|
||||||
// GO_OPT = ' '
|
GO_OPT = ' '
|
||||||
// CGO_ENABLED = 0
|
CGO_ENABLED = 0
|
||||||
// }
|
}
|
||||||
// steps {
|
steps {
|
||||||
// echo "***** BUILD ${PROJECT_NAME} on Windows *****"
|
echo "***** BUILD ${PROJECT_NAME} on Windows *****"
|
||||||
// cleanWs(disableDeferredWipeout: true, deleteDirs: true)
|
cleanWs(disableDeferredWipeout: true, deleteDirs: true)
|
||||||
// catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||||
// checkout scm
|
checkout scm
|
||||||
// bat 'make clean vendor build dist pkg-msi'
|
bat 'make clean vendor build dist'
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// post {
|
post {
|
||||||
// always {
|
always {
|
||||||
// script {
|
script {
|
||||||
// if (getContext(hudson.FilePath)) {
|
if (getContext(hudson.FilePath)) {
|
||||||
// archiveArtifacts (
|
archiveArtifacts (
|
||||||
// artifacts: 'out/*.zip,out/*.msi',
|
artifacts: 'out/*.zip,out/*.msi',
|
||||||
// allowEmptyArchive: true,
|
allowEmptyArchive: true,
|
||||||
// fingerprint: true,
|
fingerprint: true,
|
||||||
// onlyIfSuccessful: true,
|
onlyIfSuccessful: true,
|
||||||
// )
|
)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Test') {
|
stage('Test') {
|
||||||
@ -117,15 +117,16 @@ pipeline {
|
|||||||
steps {
|
steps {
|
||||||
echo "***** RELEASE ${PROJECT_NAME} for Linux *****"
|
echo "***** RELEASE ${PROJECT_NAME} for Linux *****"
|
||||||
sh '''#!/bin/bash
|
sh '''#!/bin/bash
|
||||||
make build dist
|
make build package
|
||||||
|
find -O1 dist/ -name '*.changes' -exec dput mousesoft \\{\\} \\;
|
||||||
'''
|
'''
|
||||||
script {
|
script {
|
||||||
msUploadFilesToRelease(
|
// msUploadFilesToRelease(
|
||||||
"${PROJECT_OWNER}",
|
// "${PROJECT_OWNER}",
|
||||||
"${PROJECT_ID}",
|
// "${PROJECT_ID}",
|
||||||
RELEASE_ID,
|
// RELEASE_ID,
|
||||||
['dist/*.tar.gz', 'out/doc/*.pdf'],
|
// ['dist/*.deb'],
|
||||||
)
|
// )
|
||||||
if( currentBuild.currentResult == 'SUCCESS' ) {
|
if( currentBuild.currentResult == 'SUCCESS' ) {
|
||||||
currentBuild.keepLog = true
|
currentBuild.keepLog = true
|
||||||
}
|
}
|
||||||
@ -143,7 +144,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
echo "***** RELEASE ${PROJECT_NAME} for Windows *****"
|
echo "***** RELEASE ${PROJECT_NAME} for Windows *****"
|
||||||
bat 'make build dist pkg-msi'
|
bat 'make build package'
|
||||||
script {
|
script {
|
||||||
msUploadFilesToRelease(
|
msUploadFilesToRelease(
|
||||||
"${PROJECT_OWNER}",
|
"${PROJECT_OWNER}",
|
||||||
|
5
build/package/debian/changelog
Normal file
5
build/package/debian/changelog
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
drawio-export (0.1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* Initial Release.
|
||||||
|
|
||||||
|
-- Aleksei Badiaev <aleksei.badiaev@mousesoft.ru> Sun, 9 Apr 2023 21:45:22 +0700
|
8
build/package/debian/control
Normal file
8
build/package/debian/control
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Source: drawio-export
|
||||||
|
Maintainer: Aleksei Badiaev <aleksei.badiaev@mousesoft.ru>
|
||||||
|
|
||||||
|
Package: drawio-export
|
||||||
|
Section: graphics
|
||||||
|
Priority: optional
|
||||||
|
Architecture: amd64
|
||||||
|
Description: Export Draw.io diagrams using drawio-desktop.
|
36
build/package/msi/templates/LicenseAgreementDlg_HK.wxs
Normal file
36
build/package/msi/templates/LicenseAgreementDlg_HK.wxs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Fragment>
|
||||||
|
<UI>
|
||||||
|
<Dialog Id="LicenseAgreementDlg_HK" Width="370" Height="270" Title="!(loc.LicenseAgreementDlg_Title)">
|
||||||
|
<Control Id="LicenseAcceptedCheckBox" Type="CheckBox" X="20" Y="207" Width="330" Height="18" CheckBoxValue="1" Property="LicenseAccepted"
|
||||||
|
Text="!(loc.LicenseAgreementDlgLicenseAcceptedCheckBox)" />
|
||||||
|
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
|
||||||
|
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
|
||||||
|
<Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">CostingComplete = 1</Publish>
|
||||||
|
<Condition Action="disable"><![CDATA[LicenseAccepted <> "1"]]></Condition>
|
||||||
|
<Condition Action="enable">LicenseAccepted = "1"</Condition>
|
||||||
|
</Control>
|
||||||
|
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
|
||||||
|
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
|
||||||
|
</Control>
|
||||||
|
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.LicenseAgreementDlgBannerBitmap)" />
|
||||||
|
<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="140" Sunken="yes" TabSkip="no">
|
||||||
|
|
||||||
|
{{if gt (.License | len) 0}}
|
||||||
|
<Text SourceFile="{{.License}}" />
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</Control>
|
||||||
|
<Control Id="Print" Type="PushButton" X="112" Y="243" Width="56" Height="17" Text="!(loc.WixUIPrint)">
|
||||||
|
<Publish Event="DoAction" Value="WixUIPrintEula">1</Publish>
|
||||||
|
</Control>
|
||||||
|
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
|
||||||
|
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||||
|
<Control Id="Description" Type="Text" X="25" Y="23" Width="340" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.LicenseAgreementDlgDescription)" />
|
||||||
|
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.LicenseAgreementDlgTitle)" />
|
||||||
|
</Dialog>
|
||||||
|
</UI>
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
60
build/package/msi/templates/WixUI_HK.wxs
Normal file
60
build/package/msi/templates/WixUI_HK.wxs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Fragment>
|
||||||
|
|
||||||
|
<UI Id="WixUI_HK">
|
||||||
|
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||||
|
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||||
|
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||||
|
|
||||||
|
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||||
|
<Property Id="WixUI_Mode" Value="InstallDir" />
|
||||||
|
|
||||||
|
<DialogRef Id="BrowseDlg" />
|
||||||
|
<DialogRef Id="DiskCostDlg" />
|
||||||
|
<DialogRef Id="ErrorDlg" />
|
||||||
|
<DialogRef Id="FatalError" />
|
||||||
|
<DialogRef Id="FilesInUse" />
|
||||||
|
<DialogRef Id="MsiRMFilesInUse" />
|
||||||
|
<DialogRef Id="PrepareDlg" />
|
||||||
|
<DialogRef Id="ProgressDlg" />
|
||||||
|
<DialogRef Id="ResumeDlg" />
|
||||||
|
<DialogRef Id="UserExit" />
|
||||||
|
|
||||||
|
<!-- Make sure to include custom dialogs in the installer database via a DialogRef command,
|
||||||
|
especially if they are not included explicitly in the publish chain below -->
|
||||||
|
<DialogRef Id="LicenseAgreementDlg_HK"/>
|
||||||
|
|
||||||
|
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
|
||||||
|
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="{{if gt (.License | len) 0}}LicenseAgreementDlg_HK{{else}}InstallDirDlg{{end}}">NOT Installed</Publish>
|
||||||
|
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="LicenseAgreementDlg_HK" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
|
||||||
|
<Publish Dialog="LicenseAgreementDlg_HK" Control="Next" Event="NewDialog" Value="InstallDirDlg">LicenseAccepted = "1"</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="{{if gt (.License | len) 0}}LicenseAgreementDlg_HK{{else}}WelcomeDlg{{end}}">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||||
|
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg">NOT Installed</Publish>
|
||||||
|
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg">Installed</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
|
||||||
|
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="WixUI_Common" />
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
9
build/package/msi/templates/choco/LICENSE.txt
Normal file
9
build/package/msi/templates/choco/LICENSE.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
From: {{.Choco.LicenseURL}}
|
||||||
|
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
{{if gt (.License | len) 0}}
|
||||||
|
{{.License | cat}}
|
||||||
|
{{else if gt (.Choco.LicenseURL | len) 0}}
|
||||||
|
{{.Choco.LicenseURL | download}}
|
||||||
|
{{end}}
|
10
build/package/msi/templates/choco/VERIFICATION.txt
Normal file
10
build/package/msi/templates/choco/VERIFICATION.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
VERIFICATION
|
||||||
|
|
||||||
|
To check the checksum of this package, extract the msi file contained into it,
|
||||||
|
then run
|
||||||
|
|
||||||
|
checksum.exe {{.Choco.MsiFile}} -t=sha256
|
||||||
|
|
||||||
|
The result must match
|
||||||
|
|
||||||
|
{{.Choco.MsiSum | upper}}
|
7
build/package/msi/templates/choco/chocolateyInstall.ps1
Normal file
7
build/package/msi/templates/choco/chocolateyInstall.ps1
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
$packageName = '{{.Choco.ID}}'
|
||||||
|
$fileType = 'msi'
|
||||||
|
$silentArgs = '/quiet';
|
||||||
|
$scriptPath = $(Split-Path $MyInvocation.MyCommand.Path);
|
||||||
|
$fileFullPath = Join-Path $scriptPath '{{.Choco.MsiFile}}';
|
||||||
|
|
||||||
|
Install-ChocolateyInstallPackage $packageName $fileType $silentArgs $fileFullPath -checksum '{{.Choco.MsiSum}}' -checksumType = 'sha256'
|
@ -0,0 +1,6 @@
|
|||||||
|
$packageName = "{{.Choco.ID}}";
|
||||||
|
$fileType = 'msi';
|
||||||
|
$scriptPath = $(Split-Path $MyInvocation.MyCommand.Path);
|
||||||
|
$fileFullPath = Join-Path $scriptPath '{{.Choco.MsiFile}}';
|
||||||
|
|
||||||
|
Uninstall-ChocolateyPackage $packageName $fileType "$fileFullPath /q"
|
38
build/package/msi/templates/choco/pkg.nuspec
Normal file
38
build/package/msi/templates/choco/pkg.nuspec
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<package xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<metadata>
|
||||||
|
<id>{{.Choco.ID}}</id>
|
||||||
|
<title>{{.Choco.Title}}</title>
|
||||||
|
<version>{{.Version.MSI}}</version>
|
||||||
|
<authors>{{.Choco.Authors}}</authors>
|
||||||
|
<owners>{{.Choco.Owners}}</owners>
|
||||||
|
<description>{{.Choco.Description}}</description>
|
||||||
|
{{if gt (.Choco.ProjectURL | len) 0}}
|
||||||
|
<projectUrl>{{.Choco.ProjectURL}}</projectUrl>
|
||||||
|
{{end}}
|
||||||
|
{{if gt (.Choco.Tags | len) 0}}
|
||||||
|
<tags>{{.Choco.Tags}}</tags>
|
||||||
|
{{end}}
|
||||||
|
{{if gt (.Choco.LicenseURL | len) 0}}
|
||||||
|
<licenseUrl>{{.Choco.LicenseURL}}</licenseUrl>
|
||||||
|
{{end}}
|
||||||
|
{{if gt (.Choco.IconURL | len) 0}}
|
||||||
|
<iconUrl>{{.Choco.IconURL}}</iconUrl>
|
||||||
|
{{end}}
|
||||||
|
{{if gt (.Choco.ChangeLog | len) 0}}
|
||||||
|
<releaseNotes>{{.Choco.ChangeLog}}</releaseNotes>
|
||||||
|
{{end}}
|
||||||
|
{{if .Choco.RequireLicense}}
|
||||||
|
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||||
|
{{else}}
|
||||||
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
|
{{end}}
|
||||||
|
</metadata>
|
||||||
|
<files>
|
||||||
|
<file src="{{.Choco.BuildDir}}\chocolateyInstall.ps1" target="tools" />
|
||||||
|
<file src="{{.Choco.BuildDir}}\chocolateyUninstall.ps1" target="tools" />
|
||||||
|
<file src="{{.Choco.BuildDir}}\{{.Choco.MsiFile}}" target="tools" />
|
||||||
|
<file src="{{.Choco.BuildDir}}\LICENSE.txt" target="tools" />
|
||||||
|
<file src="{{.Choco.BuildDir}}\VERIFICATION.txt" target="tools" />
|
||||||
|
</files>
|
||||||
|
</package>
|
212
build/package/msi/templates/product.wxs
Normal file
212
build/package/msi/templates/product.wxs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<?if $(sys.BUILDARCH)="x86"?>
|
||||||
|
<?define Program_Files="ProgramFilesFolder"?>
|
||||||
|
<?elseif $(sys.BUILDARCH)="x64"?>
|
||||||
|
<?define Program_Files="ProgramFiles64Folder"?>
|
||||||
|
<?else?>
|
||||||
|
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||||
|
<?endif?>
|
||||||
|
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
|
||||||
|
<Product Id="*" UpgradeCode="{{.UpgradeCode}}"
|
||||||
|
Name="{{.Product}}"
|
||||||
|
Version="{{.Version.MSI}}"
|
||||||
|
Manufacturer="{{.Company}}"
|
||||||
|
Language="1033">
|
||||||
|
|
||||||
|
<Package InstallerVersion="200" Compressed="yes" Description="{{.Product}} {{.Version.Display}}"
|
||||||
|
Comments="This installs {{.Product}} {{.Version.Display}}" InstallScope="perMachine"/>
|
||||||
|
|
||||||
|
<MediaTemplate EmbedCab="yes" {{if gt (.Compression | len) 0}}CompressionLevel="{{.Compression}}"{{end}}/>
|
||||||
|
|
||||||
|
<MajorUpgrade DowngradeErrorMessage="A newer version of this software is already installed."/>
|
||||||
|
|
||||||
|
{{if gt (.Banner | len) 0 }} <WixVariable Id="WixUIBannerBmp" Value="{{.Banner}}"/> {{end}}
|
||||||
|
{{if gt (.Dialog | len) 0 }} <WixVariable Id="WixUIDialogBmp" Value="{{.Dialog}}"/> {{end}}
|
||||||
|
|
||||||
|
{{if gt (.Icon | len) 0 }}
|
||||||
|
<Icon Id="Installer.Ico" SourceFile="{{.Icon}}"/>
|
||||||
|
<Property Id="ARPPRODUCTICON" Value="Installer.Ico"/>
|
||||||
|
{{end}}
|
||||||
|
<!-- Need to customize the Add/remove program list entry, set the automatically created one to SystemComponent to hide it then create another one. -->
|
||||||
|
<Property Id="ARPSYSTEMCOMPONENT" Value="1"/>
|
||||||
|
|
||||||
|
{{range $i, $p := .Properties}}
|
||||||
|
<Property Id="{{$p.ID}}" {{if $p.Value}}Value="{{$p.Value}}"{{end}} {{if not $p.Registry}}Secure="yes"{{end}}>
|
||||||
|
{{if $p.Registry}}
|
||||||
|
<RegistrySearch Id="{{$p.ID}}Search" Root="{{$p.Registry.Root}}" Key="{{$p.Registry.Key}}"
|
||||||
|
{{if gt ($p.Registry.Name | len) 0}} Name="{{$p.Registry.Name}}" {{end}} Type="raw"/>
|
||||||
|
{{end}}
|
||||||
|
</Property>
|
||||||
|
{{end}}
|
||||||
|
{{range $i, $c := .Conditions}}
|
||||||
|
<Condition Message="{{$c.Message}}"><![CDATA[{{$c.Condition}}]]></Condition>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
|
||||||
|
<Directory Id="$(var.Program_Files)">
|
||||||
|
<Directory Id="INSTALLDIR" Name="{{.Product}}">
|
||||||
|
{{define "FILES"}}
|
||||||
|
{{range $f := .}}
|
||||||
|
<Component
|
||||||
|
Id="ApplicationFiles{{$f.ID}}"
|
||||||
|
Guid="*"
|
||||||
|
Permanent="{{if $f.Permanent}}yes{{else}}no{{end}}"
|
||||||
|
NeverOverwrite="{{if $f.NeverOverwrite}}yes{{else}}no{{end}}">
|
||||||
|
|
||||||
|
<File Id="ApplicationFile{{$f.ID}}" Source="{{$f.Path}}"/>
|
||||||
|
{{if $f.Service}}
|
||||||
|
<ServiceInstall Id="ServiceInstall{{$f.ID}}" Type="ownProcess" Name="{{$f.Service.Name}}" Start="{{$f.Service.Start}}" Account="LocalSystem" ErrorControl="normal"
|
||||||
|
{{if gt ($f.Service.DisplayName | len) 0}} DisplayName="{{$f.Service.DisplayName}}" {{end}}
|
||||||
|
{{if gt ($f.Service.Description | len) 0}} Description="{{$f.Service.Description}}" {{end}}
|
||||||
|
{{if gt ($f.Service.Arguments | len) 0}} Arguments="{{$f.Service.Arguments}}" {{end}}>
|
||||||
|
{{range $d := $f.Service.Dependencies}}
|
||||||
|
<ServiceDependency Id="{{$d}}"/>
|
||||||
|
{{end}}
|
||||||
|
{{if $f.Service.Delayed}}
|
||||||
|
<ServiceConfig DelayedAutoStart="yes" OnInstall="yes" OnReinstall ="yes"/>
|
||||||
|
{{end}}
|
||||||
|
</ServiceInstall>
|
||||||
|
<ServiceControl Id="ServiceControl{{$f.ID}}" Name="{{$f.Service.Name}}" Start="install" Stop="both" Remove="uninstall"/>
|
||||||
|
{{end}}
|
||||||
|
</Component>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{template "FILES" .Directory.Files}}
|
||||||
|
{{define "DIRECTORIES"}}
|
||||||
|
{{range $d := .}}
|
||||||
|
<Directory Id="ApplicationDirectory{{$d.ID}}" Name="{{$d.Name}}">
|
||||||
|
{{template "FILES" $d.Files}}
|
||||||
|
{{template "DIRECTORIES" $d.Directories}}
|
||||||
|
</Directory>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{template "DIRECTORIES" .Directory.Directories}}
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
{{range $i, $e := .Environments}}
|
||||||
|
<Component Id="Environments{{$i}}" Guid="*">
|
||||||
|
<Environment Id="Environment{{$i}}" Name="{{$e.Name}}" Value="{{$e.Value}}" Permanent="{{$e.Permanent}}" Part="{{$e.Part}}" Action="{{$e.Action}}" System="{{$e.System}}"/>
|
||||||
|
<RegistryValue Root="HKLM" Key="Software\[Manufacturer]\[ProductName]" Name="envvar{{$i}}" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
{{if gt ($e.Condition | len) 0}}<Condition><![CDATA[{{$e.Condition}}]]></Condition>{{end}}
|
||||||
|
</Component>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $i, $r := .Registries}}
|
||||||
|
<Component Id="RegistryEntries{{$i}}" Guid="*">
|
||||||
|
<RegistryKey Root="{{$r.Root}}" Key="{{$r.Key}}">
|
||||||
|
{{range $j, $v := $r.Values}}
|
||||||
|
<RegistryValue Type="{{$v.Type}}" {{if gt ($v.Name | len) 0}} Name="{{$v.Name}}" {{end}} Value="{{$v.Value}}" {{if eq $i 0}}{{if eq $j 0}} KeyPath="yes" {{end}}{{end}}/>
|
||||||
|
{{end}}
|
||||||
|
</RegistryKey>
|
||||||
|
{{if gt ($r.Condition | len) 0}}<Condition><![CDATA[{{$r.Condition}}]]></Condition>{{end}}
|
||||||
|
</Component>
|
||||||
|
{{end}}
|
||||||
|
<Component Id="RegistryEntriesARP" Guid="*">
|
||||||
|
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName]">
|
||||||
|
<RegistryValue Type="string" Name="AuthorizedCDFPrefix" Value=""/>
|
||||||
|
<RegistryValue Type="string" Name="Comments" Value="{{.Info.Comments}}"/>
|
||||||
|
<RegistryValue Type="string" Name="Contact" Value="{{.Info.Contact}}"/>
|
||||||
|
{{if gt (.Icon | len) 0 }}
|
||||||
|
<RegistryValue Type="string" Name="DisplayIcon" Value="%SystemRoot%\Installer\[ProductCode]\Installer.Ico"/>
|
||||||
|
{{end}}
|
||||||
|
<RegistryValue Type="string" Name="DisplayName" Value="[ProductName]" KeyPath="yes"/>
|
||||||
|
<RegistryValue Type="string" Name="DisplayVersion" Value="{{.Version.Display}}"/>
|
||||||
|
<RegistryValue Type="integer" Name="EstimatedSize" Value="{{.Info.Size}}"/>
|
||||||
|
<RegistryValue Type="string" Name="HelpLink" Value="{{.Info.HelpLink}}"/>
|
||||||
|
<RegistryValue Type="string" Name="HelpTelephone" Value="{{.Info.SupportTelephone}}"/>
|
||||||
|
<RegistryValue Type="string" Name="InstallDate" Value="[Date]"/>
|
||||||
|
<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLDIR]"/>
|
||||||
|
<RegistryValue Type="string" Name="InstallSource" Value="[SourceDir]"/>
|
||||||
|
<RegistryValue Type="integer" Name="Language" Value="[ProductLanguage]"/>
|
||||||
|
<RegistryValue Type="expandable" Name="ModifyPath" Value="MsiExec.exe /I[ProductCode]"/>
|
||||||
|
<RegistryValue Type="string" Name="Publisher" Value="{{.Company}}"/>
|
||||||
|
<RegistryValue Type="string" Name="Readme" Value="{{.Info.Readme}}"/>
|
||||||
|
<RegistryValue Type="expandable" Name="UninstallString" Value="MsiExec.exe /I[ProductCode]"/>
|
||||||
|
<RegistryValue Type="string" Name="URLInfoAbout" Value="{{.Info.SupportLink}}"/>
|
||||||
|
<RegistryValue Type="string" Name="URLUpdateInfo" Value="{{.Info.UpdateInfoLink}}"/>
|
||||||
|
<RegistryValue Type="integer" Name="Version" Value="{{.Version.Hex}}"/>
|
||||||
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
|
|
||||||
|
<Directory Id="ProgramMenuFolder"/>
|
||||||
|
<Directory Id="DesktopFolder"/>
|
||||||
|
|
||||||
|
{{range $i, $s := .Shortcuts}}
|
||||||
|
<Component Id="ApplicationShortcuts{{$i}}" Guid="*">
|
||||||
|
<Shortcut Id="ApplicationShortcut{{$i}}" Name="{{$s.Name}}" Description="{{$s.Description}}" Target="{{$s.Target}}" WorkingDirectory="{{$s.WDir}}"
|
||||||
|
Directory={{if eq $s.Location "program"}}"ProgramMenuFolder"{{else}}"DesktopFolder"{{end}}
|
||||||
|
{{if gt ($s.Arguments | len) 0}}Arguments="{{$s.Arguments}}"{{end}}>
|
||||||
|
{{if gt ($s.Icon | len) 0}}<Icon Id="Icon{{$i}}" SourceFile="{{$s.Icon}}"/>{{end}}
|
||||||
|
{{range $j, $p := $s.Properties}}<ShortcutProperty Key="{{$p.Key}}" Value="{{$p.Value}}"/>{{end}}
|
||||||
|
</Shortcut>
|
||||||
|
{{if gt ($s.Condition | len) 0}}<Condition><![CDATA[{{$s.Condition}}]]></Condition>{{end}}
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Name="shortcut{{$i}}" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
{{range $i, $h := .Hooks}}
|
||||||
|
<SetProperty Action="SetCustomExec{{$i}}" {{if eq $h.Execute "immediate"}} Id="WixQuietExecCmdLine" {{else}} Id="CustomExec{{$i}}" {{end}} Value="{{$h.CookedCommand}}" Before="CustomExec{{$i}}" Sequence="execute"/>
|
||||||
|
<CustomAction Id="CustomExec{{$i}}" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="{{$h.Execute}}" Impersonate="{{$h.Impersonate}}" {{if gt ($h.Return | len) 0}} Return="{{$h.Return}}" {{end}}/>
|
||||||
|
{{end}}
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
{{range $i, $h := .Hooks}}
|
||||||
|
<Custom Action="CustomExec{{$i}}" {{if eq $h.When "install"}} After="InstallFiles" {{else if eq $h.Execute "immediate"}} Before="InstallValidate" {{else}} After="InstallInitialize" {{end}}>
|
||||||
|
{{if eq $h.When "install"}}
|
||||||
|
<![CDATA[NOT Installed AND NOT REMOVE{{if gt ($h.Condition | len) 0}} AND ({{$h.Condition}}){{end}}]]>
|
||||||
|
{{else if eq $h.When "uninstall"}}
|
||||||
|
<![CDATA[REMOVE{{if gt ($h.Condition | len) 0}} AND ({{$h.Condition}}){{end}}]]>
|
||||||
|
{{else if gt ($h.Condition | len) 0 }}
|
||||||
|
<![CDATA[{{$h.Condition}}]]>
|
||||||
|
{{end}}
|
||||||
|
</Custom>
|
||||||
|
{{end}}
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<Feature Id="DefaultFeature" Level="1">
|
||||||
|
{{range $i, $e := .Environments}}
|
||||||
|
<ComponentRef Id="Environments{{$i}}"/>
|
||||||
|
{{end}}
|
||||||
|
{{$id := 0}}
|
||||||
|
{{define "FILESREF"}}
|
||||||
|
{{range $f := .}}
|
||||||
|
<ComponentRef Id="ApplicationFiles{{$f.ID}}"/>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{template "FILESREF" .Directory.Files}}
|
||||||
|
{{define "DIRECTORIESREF"}}
|
||||||
|
{{range $d := .}}
|
||||||
|
{{template "FILESREF" $d.Files}}
|
||||||
|
{{template "DIRECTORIESREF" $d.Directories}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{template "DIRECTORIESREF" .Directory.Directories}}
|
||||||
|
{{range $i, $r := .Registries}}
|
||||||
|
<ComponentRef Id="RegistryEntries{{$i}}"/>
|
||||||
|
{{end}}
|
||||||
|
<ComponentRef Id="RegistryEntriesARP"/>
|
||||||
|
{{range $i, $e := .Shortcuts}}
|
||||||
|
<ComponentRef Id="ApplicationShortcuts{{$i}}"/>
|
||||||
|
{{end}}
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<UI>
|
||||||
|
<UIRef Id="WixUI_ErrorProgressText"/>
|
||||||
|
<!-- Define the installer UI -->
|
||||||
|
<UIRef Id="WixUI_HK"/>
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
|
||||||
|
|
||||||
|
<!-- this should help to propagate env var changes -->
|
||||||
|
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
|
||||||
|
|
||||||
|
</Product>
|
||||||
|
|
||||||
|
</Wix>
|
37
build/package/msi/wix.json
Normal file
37
build/package/msi/wix.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"product": "Draw.io Export",
|
||||||
|
"company": "MouseSoft",
|
||||||
|
"license": "tmp\\LICENSE.rtf",
|
||||||
|
"info": {
|
||||||
|
"comments": "",
|
||||||
|
"contact": "Aleksei Badiaev <aleksei.badiaev@mousesoft.ru>",
|
||||||
|
"help-link": "http://mousesoft.ru/help",
|
||||||
|
"support-telephone": "555-123456789",
|
||||||
|
"support-link": "http://mousesoft.ru/support",
|
||||||
|
"update-info-link": "http://mousesoft.ru/update",
|
||||||
|
"readme": "[INSTALLDIR]readme.txt"
|
||||||
|
},
|
||||||
|
"upgrade-code": "a2313bf4-d6ef-11ed-9abd-071668ee52fd",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "out/bin/drawio-export.exe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "MouseSoft Draw.io Export",
|
||||||
|
"description": "Export Draw.io diagrams using drawio-desktop",
|
||||||
|
"location": "program",
|
||||||
|
"target": "[INSTALLDIR]drawio-export.exe",
|
||||||
|
"wdir": "INSTALLDIR",
|
||||||
|
"condition": "STARTMENUSHORTCUT ~= \"yes\""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"choco": {},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "STARTMENUSHORTCUT",
|
||||||
|
"value": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,7 +1,31 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.mousesoft.ru/ms/drawio-exporter/pkg/drawio"
|
||||||
|
)
|
||||||
|
|
||||||
|
var version string // Версия приложения
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Draw.io Exporter")
|
flag.Parse()
|
||||||
|
if flagHelp {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if flagVersion {
|
||||||
|
fmt.Println("Draw.io Export cli util", version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if flag.NArg() < 1 {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
exporter := drawio.NewWithOptions(&opts)
|
||||||
|
if err := exporter.Export(flag.Args()...); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
75
cmd/drawio-export/options.go
Normal file
75
cmd/drawio-export/options.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"git.mousesoft.ru/ms/drawio-exporter/pkg/drawio"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flagHelp bool // Вывести справку о приложении и выйти
|
||||||
|
flagVersion bool // Вывести информацию о версии приложения и выйти
|
||||||
|
opts = drawio.Options{} // Аргументы командной строки приложения
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// version
|
||||||
|
flag.BoolVar(&flagVersion, "V", false, "Prints version information")
|
||||||
|
flag.BoolVar(&flagVersion, "version", false, "Prints version information")
|
||||||
|
// Application
|
||||||
|
flag.StringVar(&opts.Application, "A", "", "Draw.io Desktop Application")
|
||||||
|
flag.StringVar(&opts.Application, "application", "", "Draw.io Desktop Application")
|
||||||
|
// Output
|
||||||
|
flag.StringVar(&opts.Output, "o", "", "Exported folder name [default: export]")
|
||||||
|
flag.StringVar(&opts.Output, "output", "", "Exported folder name [default: export]")
|
||||||
|
// Format
|
||||||
|
flag.Var(&opts.Format, "f",
|
||||||
|
"Exported format [default: pdf] [possible values: pdf, png, jpg, svg, vsdx, xml]")
|
||||||
|
flag.Var(&opts.Format, "format",
|
||||||
|
"Exported format [default: pdf] [possible values: pdf, png, jpg, svg, vsdx, xml]")
|
||||||
|
// Recursive
|
||||||
|
flag.BoolVar(&opts.Recursive, "r", false,
|
||||||
|
"For a folder input, recursively convert all files in sub-folders also")
|
||||||
|
flag.BoolVar(&opts.Recursive, "recursive", false,
|
||||||
|
"For a folder input, recursively convert all files in sub-folders also")
|
||||||
|
// RemovePageSuffix
|
||||||
|
flag.BoolVar(&opts.RemovePageSuffix, "remove-page-suffix", false,
|
||||||
|
"Remove page suffix when possible (in case of single page file")
|
||||||
|
// Quality
|
||||||
|
flag.UintVar(&opts.Quality, "q", 0, "Output image quality for JPEG [default: 90]")
|
||||||
|
flag.UintVar(&opts.Quality, "quality", 0, "Output image quality for JPEG [default: 90]")
|
||||||
|
// Transparent
|
||||||
|
flag.BoolVar(&opts.Transparent, "t", false, "Set transparent background for PNG")
|
||||||
|
flag.BoolVar(&opts.Transparent, "transparent", false, "Set transparent background for PNG")
|
||||||
|
// EmbedDiagram
|
||||||
|
flag.BoolVar(&opts.EmbedDiagram, "e", false,
|
||||||
|
"Includes a copy of the diagram for PDF, PNG, or SVG")
|
||||||
|
flag.BoolVar(&opts.EmbedDiagram, "embed-diagram", false,
|
||||||
|
"Includes a copy of the diagram for PDF, PNG, or SVG")
|
||||||
|
// EmbedSvgImages
|
||||||
|
flag.BoolVar(&opts.EmbedSvgImages, "embed-svg-images", false, "Embed Images in SVG file")
|
||||||
|
// Border
|
||||||
|
flag.UintVar(&opts.Border, "b", 0,
|
||||||
|
"Sets the border width around the diagram [default: 0]")
|
||||||
|
flag.UintVar(&opts.Border, "border", 0,
|
||||||
|
"Sets the border width around the diagram [default: 0]")
|
||||||
|
// Scale
|
||||||
|
flag.UintVar(&opts.Scale, "s", 0, "Scales the diagram size")
|
||||||
|
flag.UintVar(&opts.Scale, "scale", 0, "Scales the diagram size")
|
||||||
|
// Width
|
||||||
|
flag.UintVar(&opts.Width, "width", 0,
|
||||||
|
"Fits the generated image/pdf into the specified width, preserves aspect ratio")
|
||||||
|
// Height
|
||||||
|
flag.UintVar(&opts.Height, "height", 0,
|
||||||
|
"Fits the generated image/pdf into the specified height, preserves aspect ratio")
|
||||||
|
// Crop
|
||||||
|
flag.BoolVar(&opts.Crop, "crop", false, "Crops PDF to diagram size")
|
||||||
|
// Uncompressed
|
||||||
|
flag.BoolVar(&opts.Uncompressed, "u", false, "Uncompressed XML output")
|
||||||
|
flag.BoolVar(&opts.Uncompressed, "uncompressed", false, "Uncompressed XML output")
|
||||||
|
// EnablePlugins
|
||||||
|
flag.BoolVar(&opts.EnablePlugins, "enable-plugins", false, "Enable Plugins")
|
||||||
|
// help
|
||||||
|
flag.BoolVar(&flagHelp, "h", false, "Prints help information")
|
||||||
|
flag.BoolVar(&flagHelp, "help", false, "Prints help information")
|
||||||
|
}
|
58
makefile
58
makefile
@ -1,7 +1,8 @@
|
|||||||
# drawio-exporter makefile
|
# drawio-exporter makefile
|
||||||
# ========================
|
# ========================
|
||||||
|
|
||||||
PROJECT_NAME := drawio-exporter
|
PROJECT_ID := drawio-export
|
||||||
|
BIN_SUFFIX :=
|
||||||
|
|
||||||
TMPDIR ?= $(CURDIR)/tmp
|
TMPDIR ?= $(CURDIR)/tmp
|
||||||
OUTDIR ?= $(CURDIR)/out
|
OUTDIR ?= $(CURDIR)/out
|
||||||
@ -28,6 +29,8 @@ MSI_FILE := $(PROJECT_NAME)_$(VERSION)_$(DIST_KIND).msi
|
|||||||
DIST_EXT := .zip
|
DIST_EXT := .zip
|
||||||
DIST_OPTS := -a -cf
|
DIST_OPTS := -a -cf
|
||||||
ECHO_CMD := echo -e
|
ECHO_CMD := echo -e
|
||||||
|
MSI_VERSION := $(shell echo $(VERSION_NUMBER) | sed -e 's/-.*//')
|
||||||
|
BIN_SUFFIX := .exe
|
||||||
else
|
else
|
||||||
PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
DIST_SUFFIX := $(PLATFORM)-$(DIST_KIND)
|
DIST_SUFFIX := $(PLATFORM)-$(DIST_KIND)
|
||||||
@ -44,7 +47,7 @@ WHITE := $(shell tput -Txterm setaf 7)
|
|||||||
CYAN := $(shell tput -Txterm setaf 6)
|
CYAN := $(shell tput -Txterm setaf 6)
|
||||||
RESET := $(shell tput -Txterm sgr0)
|
RESET := $(shell tput -Txterm sgr0)
|
||||||
|
|
||||||
.PHONY: all version version-number test build dist vendor
|
.PHONY: all version version-number test build dist vendor package pkg-deb
|
||||||
|
|
||||||
version: ## Version of the project to be built
|
version: ## Version of the project to be built
|
||||||
@echo $(VERSION)
|
@echo $(VERSION)
|
||||||
@ -72,7 +75,7 @@ dist: ## Create all distro packages
|
|||||||
tar $(DIST_OPTS) "$(OUTDIR)/$(DIST_FILE)" -C "$(OUTDIR)" bin
|
tar $(DIST_OPTS) "$(OUTDIR)/$(DIST_FILE)" -C "$(OUTDIR)" bin
|
||||||
@$(ECHO_CMD) "Dist\t\t${GREEN}[OK]${RESET}"
|
@$(ECHO_CMD) "Dist\t\t${GREEN}[OK]${RESET}"
|
||||||
|
|
||||||
vendor: ## Copy of all packages needed to support builds and tests in the vendor directory.
|
vendor: ## Copy of all packages needed to support builds and tests in the vendor directory
|
||||||
$(GOCMD) mod vendor
|
$(GOCMD) mod vendor
|
||||||
@echo "Vendor\t\t${GREEN}[OK]${RESET}"
|
@echo "Vendor\t\t${GREEN}[OK]${RESET}"
|
||||||
|
|
||||||
@ -82,7 +85,56 @@ clean: ## Remove build related files
|
|||||||
@rm -fr dist/
|
@rm -fr dist/
|
||||||
@$(ECHO_CMD) "Clean\t\t${GREEN}[OK]${RESET}"
|
@$(ECHO_CMD) "Clean\t\t${GREEN}[OK]${RESET}"
|
||||||
|
|
||||||
|
## Package:
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
PACKAGE_TARGETS := pkg-msi
|
||||||
|
else
|
||||||
|
PACKAGE_TARGETS := pkg-deb
|
||||||
|
endif
|
||||||
|
|
||||||
|
package: $(PACKAGE_TARGETS) ## Build all available packages
|
||||||
|
@$(ECHO_CMD) "Package\t\t${GREEN}[OK]${RESET}"
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
pkg-msi: ## Create MSI package
|
||||||
|
@rm -f $(OUTDIR)/$(MSI_FILE)
|
||||||
|
@mkdir -p $(TMPDIR)
|
||||||
|
go-msi to-rtf --src LICENSE.txt --out $(TMPDIR)/LICENSE.rtf -e
|
||||||
|
go-msi generate-templates --path build/package/msi/wix.json \
|
||||||
|
--src build/package/msi/templates --out $(TMPDIR) \
|
||||||
|
--version $(MSI_VERSION) --display $(VERSION) --license tmp/LICENSE.rtf
|
||||||
|
go-msi gen-wix-cmd --path build/package/msi/wix.json --src tmp \
|
||||||
|
--out $(TMPDIR) --arch amd64 --msi $(TMPDIR)/$(MSI_FILE) \
|
||||||
|
--version $(MSI_VERSION) --display $(VERSION)
|
||||||
|
@rm -f wix.dynamic.json
|
||||||
|
cd $(TMPDIR) && ./build.bat
|
||||||
|
mv $(TMPDIR)/$(MSI_FILE) $(OUTDIR)/
|
||||||
|
@$(ECHO_CMD) "MSI package\t${GREEN}[OK]${RESET}"
|
||||||
|
else
|
||||||
|
DEB_NAME := $(PROJECT_ID)_$(VERSION_NUMBER)-1_amd64
|
||||||
|
|
||||||
|
pkg-deb: ## Build debian package
|
||||||
|
@mkdir -p $(TMPDIR)/$(DEB_NAME)/usr/bin
|
||||||
|
@mkdir -p $(TMPDIR)/$(DEB_NAME)/debian
|
||||||
|
@mkdir -p $(TMPDIR)/$(DEB_NAME)/DEBIAN
|
||||||
|
@cp -a $(BINDIR)/* $(TMPDIR)/$(DEB_NAME)/usr/bin/
|
||||||
|
dpkg-gencontrol -v1.0-1 \
|
||||||
|
-c$(CURDIR)/build/package/debian/control \
|
||||||
|
-lbuild/package/debian/changelog \
|
||||||
|
-f$(TMPDIR)/$(DEB_NAME)/debian/files -Ptmp/$(DEB_NAME)
|
||||||
|
dpkg-deb --build --root-owner-group $(TMPDIR)/$(DEB_NAME)
|
||||||
|
dpkg-genchanges --build=binary \
|
||||||
|
-c$(CURDIR)/build/package/debian/control \
|
||||||
|
-lbuild/package/debian/changelog \
|
||||||
|
-f$(TMPDIR)/$(DEB_NAME)/debian/files \
|
||||||
|
-u$(TMPDIR) -O$(OUTDIR)/$(DEB_NAME).changes
|
||||||
|
@mv $(TMPDIR)/*.deb $(OUTDIR)/
|
||||||
|
@$(ECHO_CMD) "pkg-deb\t\t${GREEN}[OK]${RESET}"
|
||||||
|
endif
|
||||||
|
|
||||||
## Test:
|
## Test:
|
||||||
|
|
||||||
test: ## Run the tests of the project
|
test: ## Run the tests of the project
|
||||||
ifeq ($(EXPORT_RESULT), true)
|
ifeq ($(EXPORT_RESULT), true)
|
||||||
@mkdir -p $(OUTDIR)
|
@mkdir -p $(OUTDIR)
|
||||||
|
20
pkg/drawio/execution.go
Normal file
20
pkg/drawio/execution.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package drawio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Последовательный запуск команд в ОС
|
||||||
|
func RunSequence(command ...*exec.Cmd) error {
|
||||||
|
var (
|
||||||
|
errs = []error{}
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for _, cmd := range command {
|
||||||
|
if err = cmd.Run(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
@ -2,12 +2,168 @@ package drawio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
xmlparser "github.com/tamerh/xml-stream-parser"
|
xmlparser "github.com/tamerh/xml-stream-parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Создать нового экспортёра диаграмм
|
||||||
|
func New(opt ...Option) Exporter {
|
||||||
|
options := &Options{
|
||||||
|
Application: "drawio",
|
||||||
|
Output: "export",
|
||||||
|
Format: Format("pdf"),
|
||||||
|
}
|
||||||
|
for _, opt := range opt {
|
||||||
|
opt.apply(options)
|
||||||
|
}
|
||||||
|
return NewWithOptions(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создать нового экспортёра с параметрами
|
||||||
|
func NewWithOptions(opts *Options) Exporter {
|
||||||
|
exp := Exporter{opts: opts}
|
||||||
|
if opts.openFile == nil {
|
||||||
|
exp.openFile = func(name string) (io.ReadCloser, error) {
|
||||||
|
return os.Open(name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exp.openFile = opts.openFile
|
||||||
|
}
|
||||||
|
if opts.readDir == nil {
|
||||||
|
exp.readDir = func(name string) ([]os.DirEntry, error) {
|
||||||
|
return os.ReadDir(name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exp.readDir = opts.readDir
|
||||||
|
}
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспортёр диаграмм
|
||||||
|
type Exporter struct {
|
||||||
|
opts *Options // Параметры экспортёра диаграмм
|
||||||
|
openFile OpenFileFunc // Открыть файл
|
||||||
|
readDir ReadDirFunc // Прочитать папку на диске
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспорт диаграмм из указанный файлов или папок
|
||||||
|
func (exp Exporter) Export(fileOrDir ...string) error {
|
||||||
|
var (
|
||||||
|
commands = []*exec.Cmd{}
|
||||||
|
err error
|
||||||
|
errs = []error{}
|
||||||
|
args = exp.opts.Args()
|
||||||
|
info fs.FileInfo
|
||||||
|
)
|
||||||
|
for _, item := range fileOrDir {
|
||||||
|
if info, err = os.Stat(item); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var addCommands []*exec.Cmd
|
||||||
|
if info.IsDir() {
|
||||||
|
addCommands, err = exp.ExportDir(item, args)
|
||||||
|
} else {
|
||||||
|
addCommands, err = exp.ExportFile(item, args)
|
||||||
|
}
|
||||||
|
commands = append(commands, addCommands...)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = RunSequence(commands...); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспорт диаграмм из всех файлов в папке
|
||||||
|
func (exp Exporter) ExportDir(
|
||||||
|
dirPath string, args []string, subDir ...string,
|
||||||
|
) ([]*exec.Cmd, error) {
|
||||||
|
var (
|
||||||
|
commands = []*exec.Cmd{}
|
||||||
|
err error
|
||||||
|
entries []fs.DirEntry
|
||||||
|
errs []error
|
||||||
|
)
|
||||||
|
if entries, err = exp.readDir(dirPath); err != nil {
|
||||||
|
return commands, err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
name := entry.Name()
|
||||||
|
var addCommands []*exec.Cmd
|
||||||
|
if entry.IsDir() {
|
||||||
|
if exp.opts.Recursive {
|
||||||
|
deep := len(subDir)
|
||||||
|
outDir := make([]string, deep+1)
|
||||||
|
copy(outDir, subDir)
|
||||||
|
outDir[deep] = name
|
||||||
|
addCommands, err = exp.ExportDir(path.Join(dirPath, name), args, outDir...)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addCommands, err = exp.ExportFile(path.Join(dirPath, name), args, subDir...)
|
||||||
|
}
|
||||||
|
commands = append(commands, addCommands...)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commands, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспорт файла с диаграммами
|
||||||
|
func (exp Exporter) ExportFile(
|
||||||
|
filePath string, args []string, subDir ...string,
|
||||||
|
) ([]*exec.Cmd, error) {
|
||||||
|
var (
|
||||||
|
commands = []*exec.Cmd{}
|
||||||
|
err error
|
||||||
|
file io.ReadCloser
|
||||||
|
diagrams []string
|
||||||
|
)
|
||||||
|
if file, err = exp.openFile(filePath); err != nil {
|
||||||
|
return commands, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
if diagrams, err = Diagrams(file); err != nil {
|
||||||
|
return commands, err
|
||||||
|
}
|
||||||
|
outDirs := make([]string, 1, len(subDir)+1)
|
||||||
|
outDirs[0] = exp.opts.OutDir()
|
||||||
|
outDirs = append(outDirs, subDir...)
|
||||||
|
output := path.Join(outDirs...)
|
||||||
|
for i, name := range diagrams {
|
||||||
|
outName := strings.TrimSuffix(path.Base(filePath), path.Ext(filePath))
|
||||||
|
if !exp.opts.RemovePageSuffix || len(diagrams) > 1 {
|
||||||
|
outName += "-" + name
|
||||||
|
}
|
||||||
|
outName += exp.opts.OutExt()
|
||||||
|
drawioArgs := make([]string, len(args), len(args)+4)
|
||||||
|
copy(drawioArgs, args)
|
||||||
|
drawioArgs = append(drawioArgs,
|
||||||
|
"--page-index", strconv.Itoa(i),
|
||||||
|
"--output", path.Join(output, outName),
|
||||||
|
"--export", filePath,
|
||||||
|
)
|
||||||
|
cmd := exec.Command(exp.opts.App(), drawioArgs...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
commands = append(commands, cmd)
|
||||||
|
}
|
||||||
|
return commands, err
|
||||||
|
}
|
||||||
|
|
||||||
// Разбирает данные и возвращает срез имён диаграмм в них
|
// Разбирает данные и возвращает срез имён диаграмм в них
|
||||||
func Diagrams(reader io.Reader) ([]string, error) {
|
func Diagrams(reader io.Reader) ([]string, error) {
|
||||||
var (
|
var (
|
||||||
@ -21,7 +177,6 @@ func Diagrams(reader io.Reader) ([]string, error) {
|
|||||||
if xml.Err != nil {
|
if xml.Err != nil {
|
||||||
return result, xml.Err
|
return result, xml.Err
|
||||||
}
|
}
|
||||||
fmt.Println(xml.Name)
|
|
||||||
if items, ok := xml.Childs["diagram"]; ok {
|
if items, ok := xml.Childs["diagram"]; ok {
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
result = append(result, item.Attrs["name"])
|
result = append(result, item.Attrs["name"])
|
||||||
|
@ -1,22 +1,126 @@
|
|||||||
package drawio_test
|
package drawio_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.mousesoft.ru/ms/drawio-exporter/pkg/drawio"
|
"git.mousesoft.ru/ms/drawio-exporter/pkg/drawio"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Команда оболочки ОС
|
||||||
|
type command struct {
|
||||||
|
cmd string // Команда
|
||||||
|
args []string // Аргументы команды
|
||||||
|
}
|
||||||
|
|
||||||
|
// Файл с диаграммами
|
||||||
|
type source struct {
|
||||||
|
output string // Папка вывода для ExportFile
|
||||||
|
data string // Данные файла с диаграммами
|
||||||
|
diagrams []string // Срез имён диаграмм
|
||||||
|
commands []command // Ожидаемые команды экспорта диаграмм
|
||||||
|
}
|
||||||
|
|
||||||
|
// Элемент папки с файлами
|
||||||
|
type dirEntry struct {
|
||||||
|
name string // Имя элемента
|
||||||
|
isDir bool // Истина, если элемент является папкой
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ os.DirEntry = (*dirEntry)(nil)
|
||||||
|
_ os.FileInfo = (*dirEntry)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (entry dirEntry) Name() string {
|
||||||
|
return entry.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) IsDir() bool {
|
||||||
|
return entry.isDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) Type() fs.FileMode {
|
||||||
|
if entry.isDir {
|
||||||
|
return fs.ModeDir
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) Info() (fs.FileInfo, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) Size() int64 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) Mode() fs.FileMode {
|
||||||
|
return entry.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) ModTime() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry dirEntry) Sys() any {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Конвертация команд в информацию о командах
|
||||||
|
func cmd2info(commands []*exec.Cmd) []command {
|
||||||
|
cmdInfo := make([]command, len(commands))
|
||||||
|
for i, cmd := range commands {
|
||||||
|
cmdInfo[i] = command{
|
||||||
|
cmd: cmd.Path,
|
||||||
|
args: make([]string, len(cmd.Args)),
|
||||||
|
}
|
||||||
|
copy(cmdInfo[i].args, cmd.Args)
|
||||||
|
}
|
||||||
|
return cmdInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportTest struct {
|
||||||
|
name string // Наименование теста
|
||||||
|
dirs map[string][]os.DirEntry // Папки файлов с диаграммами
|
||||||
|
files map[string]source // Файлы с диаграммами
|
||||||
|
}
|
||||||
|
|
||||||
|
// Информация об ожидаемых командах при экспорте папки
|
||||||
|
func (test exportTest) dirCommands(dir string, recursive bool) []command {
|
||||||
|
commands := make([]command, 0, 4)
|
||||||
|
if tc, ok := test.dirs[dir]; ok {
|
||||||
|
for _, entry := range tc {
|
||||||
|
path := path.Join(dir, entry.Name())
|
||||||
|
if entry.IsDir() && recursive {
|
||||||
|
commands = append(commands, test.dirCommands(path, true)...)
|
||||||
|
} else {
|
||||||
|
commands = append(commands, test.files[path].commands...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
|
||||||
// Тестовые данные
|
// Тестовые данные
|
||||||
var testData = []struct {
|
var testData = []exportTest{
|
||||||
name string // Наименование теста
|
|
||||||
source string // Данные файла с диаграммами
|
|
||||||
diagrams []string // Срез имён диаграмм
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
name: "positive case",
|
name: "positive case",
|
||||||
source: `<mxfile host="test">
|
dirs: map[string][]os.DirEntry{
|
||||||
|
"source": {
|
||||||
|
dirEntry{name: "diagrams.drawio", isDir: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: map[string]source{
|
||||||
|
"source/diagrams.drawio": {
|
||||||
|
data: `<mxfile host="test">
|
||||||
<diagram name="1" id="01">
|
<diagram name="1" id="01">
|
||||||
<mxGraphModel page="1">
|
<mxGraphModel page="1">
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
@ -30,11 +134,78 @@ var testData = []struct {
|
|||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>`,
|
</mxfile>`,
|
||||||
diagrams: []string{"1", "2", "3"},
|
diagrams: []string{"1", "2", "3"},
|
||||||
|
commands: []command{
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "0",
|
||||||
|
"--output", "export/diagrams-1.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "1",
|
||||||
|
"--output", "export/diagrams-2.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "2",
|
||||||
|
"--output", "export/diagrams-3.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid source",
|
name: "invalid source",
|
||||||
source: `<nxfile host="test">
|
dirs: map[string][]os.DirEntry{
|
||||||
|
"source": {
|
||||||
|
dirEntry{name: "diagrams.drawio", isDir: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: map[string]source{
|
||||||
|
"source/diagrams.drawio": {
|
||||||
|
data: `<nxfile host="test">
|
||||||
|
<diagram name="1" id="01">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
<diagram name="2" id="02">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
<diagram name="3" id="03">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</nxfile>`,
|
||||||
|
diagrams: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested dirs",
|
||||||
|
dirs: map[string][]os.DirEntry{
|
||||||
|
"source": {
|
||||||
|
dirEntry{name: "diagrams.drawio", isDir: false},
|
||||||
|
dirEntry{name: "subdir", isDir: true},
|
||||||
|
dirEntry{name: "additional.xml", isDir: false},
|
||||||
|
},
|
||||||
|
"source/subdir": {
|
||||||
|
dirEntry{name: "Вложенные диаграммы.drawio", isDir: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: map[string]source{
|
||||||
|
"source/diagrams.drawio": {
|
||||||
|
data: `<mxfile host="test">
|
||||||
<diagram name="1" id="01">
|
<diagram name="1" id="01">
|
||||||
<mxGraphModel page="1">
|
<mxGraphModel page="1">
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
@ -47,17 +218,193 @@ var testData = []struct {
|
|||||||
<mxGraphModel page="1">
|
<mxGraphModel page="1">
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</nxfile>`,
|
</mxfile>`,
|
||||||
diagrams: []string{},
|
diagrams: []string{"1", "2", "3"},
|
||||||
|
commands: []command{
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "0",
|
||||||
|
"--output", "export/diagrams-1.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "1",
|
||||||
|
"--output", "export/diagrams-2.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "2",
|
||||||
|
"--output", "export/diagrams-3.pdf",
|
||||||
|
"--export", "source/diagrams.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"source/additional.xml": {
|
||||||
|
data: `<mxfile host="test">
|
||||||
|
<diagram name="Один" id="01">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
<diagram name="Два" id="02">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>`,
|
||||||
|
diagrams: []string{"Один", "Два"},
|
||||||
|
commands: []command{
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "0",
|
||||||
|
"--output", "export/additional-Один.pdf",
|
||||||
|
"--export", "source/additional.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "1",
|
||||||
|
"--output", "export/additional-Два.pdf",
|
||||||
|
"--export", "source/additional.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"source/subdir/Вложенные диаграммы.drawio": {
|
||||||
|
output: "export/subdir",
|
||||||
|
data: `<mxfile host="test">
|
||||||
|
<diagram name="Первая диаграмма" id="01">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
<diagram name="Вторая диаграмма" id="02">
|
||||||
|
<mxGraphModel page="1">
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>`,
|
||||||
|
diagrams: []string{"Первая диаграмма", "Вторая диаграмма"},
|
||||||
|
commands: []command{
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "0",
|
||||||
|
"--output", "export/subdir/Вложенные диаграммы-Первая диаграмма.pdf",
|
||||||
|
"--export", "source/subdir/Вложенные диаграммы.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cmd: "/usr/bin/drawio",
|
||||||
|
args: []string{
|
||||||
|
"drawio", "--page-index", "1",
|
||||||
|
"--output", "export/subdir/Вложенные диаграммы-Вторая диаграмма.pdf",
|
||||||
|
"--export", "source/subdir/Вложенные диаграммы.drawio",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiagrams(t *testing.T) {
|
func TestDiagrams(t *testing.T) {
|
||||||
for _, test := range testData {
|
for _, test := range testData {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
diagrams, err := drawio.Diagrams(strings.NewReader(test.source))
|
for filePath, source := range test.files {
|
||||||
assert.NoError(t, err)
|
t.Run(filePath, func(t *testing.T) {
|
||||||
assert.ElementsMatch(t, test.diagrams, diagrams)
|
diagrams, err := drawio.Diagrams(strings.NewReader(source.data))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.ElementsMatch(t, source.diagrams, diagrams)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExportFile(t *testing.T) {
|
||||||
|
for _, test := range testData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
for filePath, source := range test.files {
|
||||||
|
var openOpt = drawio.WithNop()
|
||||||
|
if len(source.output) > 0 {
|
||||||
|
openOpt = drawio.WithOutput(source.output)
|
||||||
|
}
|
||||||
|
t.Run(filePath, func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
openFileCalls = []string{}
|
||||||
|
exp = drawio.New(
|
||||||
|
drawio.WithOpenFile(func(s string) (io.ReadCloser, error) {
|
||||||
|
openFileCalls = append(openFileCalls, s)
|
||||||
|
return io.NopCloser(strings.NewReader(source.data)), nil
|
||||||
|
}),
|
||||||
|
openOpt,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
commands, err := exp.ExportFile(filePath, []string{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.Equal(t, 1, len(openFileCalls)) {
|
||||||
|
assert.Equal(t, filePath, openFileCalls[0])
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, source.commands, cmd2info(commands))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExportDir(t *testing.T) {
|
||||||
|
for _, recursive := range []bool{false, true} {
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
recursiveOption drawio.Option
|
||||||
|
)
|
||||||
|
if recursive {
|
||||||
|
name = "recursive"
|
||||||
|
recursiveOption = drawio.WithRecursive()
|
||||||
|
} else {
|
||||||
|
name = "non-recursive"
|
||||||
|
recursiveOption = drawio.WithNop()
|
||||||
|
}
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
for _, test := range testData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
openFileCalls = []string{}
|
||||||
|
exp = drawio.New(
|
||||||
|
drawio.WithOpenFile(func(s string) (io.ReadCloser, error) {
|
||||||
|
openFileCalls = append(openFileCalls, s)
|
||||||
|
return io.NopCloser(
|
||||||
|
strings.NewReader(test.files[s].data),
|
||||||
|
), nil
|
||||||
|
}),
|
||||||
|
drawio.WithReadDir(func(s string) ([]os.DirEntry, error) {
|
||||||
|
return test.dirs[s], nil
|
||||||
|
}),
|
||||||
|
recursiveOption,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for dir := range test.dirs {
|
||||||
|
pathDir, _ := path.Split(dir)
|
||||||
|
if len(pathDir) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Run(dir, func(t *testing.T) {
|
||||||
|
openFileCalls = openFileCalls[:0]
|
||||||
|
commands, err := exp.ExportDir(dir, []string{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.ElementsMatch(t,
|
||||||
|
test.dirCommands(dir, recursive), cmd2info(commands))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
421
pkg/drawio/options.go
Normal file
421
pkg/drawio/options.go
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
package drawio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Интерфейс параметра экспортёра диаграмм
|
||||||
|
type Option interface {
|
||||||
|
apply(opts *Options) // Применить параметр к хранилищу параметров
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ошибка "неподдерживаемый формат"
|
||||||
|
type ErrUnsupportedFormat struct {
|
||||||
|
Format string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Описание ошибки
|
||||||
|
func (e ErrUnsupportedFormat) Error() string {
|
||||||
|
return fmt.Sprintf("unsupported format: '%s'", e.Format)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка эквивалентности ошибок
|
||||||
|
func (e ErrUnsupportedFormat) Is(target error) bool {
|
||||||
|
switch t := target.(type) {
|
||||||
|
case ErrUnsupportedFormat:
|
||||||
|
return t.Format == e.Format
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = (*ErrUnsupportedFormat)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
type Format string // Формат экспортированного файла
|
||||||
|
|
||||||
|
const (
|
||||||
|
PDF Format = "pdf"
|
||||||
|
PNG Format = "png"
|
||||||
|
JPG Format = "jpg"
|
||||||
|
SVG Format = "svg"
|
||||||
|
VSDX Format = "vsdx"
|
||||||
|
XML Format = "xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Строковое представление формата
|
||||||
|
func (f Format) String() string {
|
||||||
|
return string(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка формата из строки
|
||||||
|
func (f *Format) Set(str string) error {
|
||||||
|
for _, fmt := range SupportedFormats() {
|
||||||
|
if str == string(fmt) {
|
||||||
|
*f = Format(str)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ErrUnsupportedFormat{str}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Список всех поддерживаемых форматов экспорта
|
||||||
|
func SupportedFormats() []Format {
|
||||||
|
return []Format{"pdf", "png", "jpg", "svg", "vsdx", "xml"}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ flag.Value = (*Format)(nil) // Поверка реализации интерфейса
|
||||||
|
|
||||||
|
// Расширение файла заданного формата
|
||||||
|
func (f Format) ext() string {
|
||||||
|
return "." + string(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Параметры экспортёра диаграмм
|
||||||
|
type Options struct {
|
||||||
|
Application string // Путь к приложению drawio
|
||||||
|
Output string // Путь к папке с экспортированными файлами
|
||||||
|
Format Format // Формат экспортированных файлов
|
||||||
|
Recursive bool // Рекурсивно сканировать вложенные папки с файлами
|
||||||
|
RemovePageSuffix bool // Удалять суффикс страницы, если это возможно
|
||||||
|
Quality uint // Качество экспортированного изображения (только для JPEG)
|
||||||
|
Transparent bool // Прозрачный фона для PNG
|
||||||
|
// Включать копию диаграммы в экспортированный файл для PDF, PNG и SVG
|
||||||
|
EmbedDiagram bool
|
||||||
|
EmbedSvgImages bool // Встраивать изображения в файл формата SVG
|
||||||
|
Border uint // Ширина рамки вокруг диаграмм
|
||||||
|
Scale uint // Масштаб в процентах размера экспортированных диаграмм
|
||||||
|
Width uint // Ширина экспортированной диаграммы с сохранением масштаба
|
||||||
|
Height uint // Высота экспортированной диаграммы с сохранением масштаба
|
||||||
|
Crop bool // Обрезать результирующий PDF до размера диаграммы
|
||||||
|
Uncompressed bool // Выводить несжатый XML
|
||||||
|
EnablePlugins bool // Включить подключаемые модули
|
||||||
|
openFile OpenFileFunc // Открыть файл
|
||||||
|
readDir ReadDirFunc // Прочитать папку на диске
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция открытия файла
|
||||||
|
type OpenFileFunc func(string) (io.ReadCloser, error)
|
||||||
|
|
||||||
|
// Функция чтения папки на диске
|
||||||
|
type ReadDirFunc func(string) ([]os.DirEntry, error)
|
||||||
|
|
||||||
|
// Путь к приложению drawio
|
||||||
|
func (opts Options) App() string {
|
||||||
|
app := opts.Application
|
||||||
|
if len(app) == 0 {
|
||||||
|
app = "drawio"
|
||||||
|
}
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// Путь к папке с экспортированными файлами
|
||||||
|
func (opts Options) OutDir() string {
|
||||||
|
out := opts.Output
|
||||||
|
if len(out) == 0 {
|
||||||
|
out = "export"
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Расширение экспортированных файлов
|
||||||
|
func (opts Options) OutExt() string {
|
||||||
|
fmt := opts.Format
|
||||||
|
if len(fmt) == 0 {
|
||||||
|
fmt = Format("pdf")
|
||||||
|
}
|
||||||
|
return fmt.ext()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Аргументы командной строки drawio
|
||||||
|
func (opts Options) Args() []string {
|
||||||
|
args := []string{}
|
||||||
|
if len(opts.Format) > 0 {
|
||||||
|
args = append(args, "--format", string(opts.Format))
|
||||||
|
}
|
||||||
|
if opts.Quality != 0 {
|
||||||
|
args = append(args, "--quality", strconv.Itoa(int(opts.Quality)))
|
||||||
|
}
|
||||||
|
if opts.Transparent {
|
||||||
|
args = append(args, "--transparent")
|
||||||
|
}
|
||||||
|
if opts.EmbedDiagram {
|
||||||
|
args = append(args, "--embed-diagram")
|
||||||
|
}
|
||||||
|
if opts.EmbedSvgImages {
|
||||||
|
args = append(args, "--embed-svg-images")
|
||||||
|
}
|
||||||
|
if opts.Border != 0 {
|
||||||
|
args = append(args, "--border", strconv.Itoa(int(opts.Border)))
|
||||||
|
}
|
||||||
|
if opts.Scale != 0 {
|
||||||
|
args = append(args, "--scale", strconv.Itoa(int(opts.Scale)))
|
||||||
|
}
|
||||||
|
if opts.Width != 0 {
|
||||||
|
args = append(args, "--width", strconv.Itoa(int(opts.Width)))
|
||||||
|
}
|
||||||
|
if opts.Height != 0 {
|
||||||
|
args = append(args, "--height", strconv.Itoa(int(opts.Height)))
|
||||||
|
}
|
||||||
|
if opts.Crop {
|
||||||
|
args = append(args, "--crop")
|
||||||
|
}
|
||||||
|
if opts.Uncompressed {
|
||||||
|
args = append(args, "--uncompressed")
|
||||||
|
}
|
||||||
|
if opts.EnablePlugins {
|
||||||
|
args = append(args, "--enable-plugins")
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
// Путь к приложению drawio
|
||||||
|
func WithAppPath(path string) Option {
|
||||||
|
return optionApplication(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionApplication string
|
||||||
|
|
||||||
|
var _ Option = (*optionApplication)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionApplication) apply(opts *Options) {
|
||||||
|
opts.Application = string(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Путь к папке с экспортированными файлами
|
||||||
|
func WithOutput(path string) Option {
|
||||||
|
return optionOutput(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionOutput string
|
||||||
|
|
||||||
|
var _ Option = (*optionOutput)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionOutput) apply(opts *Options) {
|
||||||
|
opts.Output = string(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Формат экспортированных файлов
|
||||||
|
func WithFormat(format Format) Option {
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Option = (*Format)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt Format) apply(opts *Options) {
|
||||||
|
opts.Format = opt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рекурсивно сканировать вложенные папки с файлами
|
||||||
|
func WithRecursive() Option {
|
||||||
|
return optionRecursive{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionRecursive struct{}
|
||||||
|
|
||||||
|
var _ Option = optionRecursive{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionRecursive) apply(opts *Options) {
|
||||||
|
opts.Recursive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удалять суффикс страницы, если это возможно
|
||||||
|
func WithRemovePageSuffix() Option {
|
||||||
|
return optionRemovePageSuffix{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionRemovePageSuffix struct{}
|
||||||
|
|
||||||
|
var _ Option = optionRemovePageSuffix{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionRemovePageSuffix) apply(opts *Options) {
|
||||||
|
opts.RemovePageSuffix = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Качество экспортированного изображения (только для JPEG)
|
||||||
|
func WithQuality(q uint) Option {
|
||||||
|
return optionQuality(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionQuality uint
|
||||||
|
|
||||||
|
var _ Option = (*optionQuality)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionQuality) apply(opts *Options) {
|
||||||
|
opts.Quality = uint(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Прозрачный фона для PNG
|
||||||
|
func WithTransparent() Option {
|
||||||
|
return optionTransparent{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionTransparent struct{}
|
||||||
|
|
||||||
|
var _ Option = optionTransparent{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionTransparent) apply(opts *Options) {
|
||||||
|
opts.Transparent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Включать копию диаграммы в экспортированный файл для PDF, PNG и SVG
|
||||||
|
func WithEmbedDiagram() Option {
|
||||||
|
return optionEmbedDiagram{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionEmbedDiagram struct{}
|
||||||
|
|
||||||
|
var _ Option = optionEmbedDiagram{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionEmbedDiagram) apply(opts *Options) {
|
||||||
|
opts.EmbedDiagram = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Встраивать изображения в файл формата SVG
|
||||||
|
func WithEmbedSvgImages() Option {
|
||||||
|
return optionEmbedSvgImages{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionEmbedSvgImages struct{}
|
||||||
|
|
||||||
|
var _ Option = optionEmbedSvgImages{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionEmbedSvgImages) apply(opts *Options) {
|
||||||
|
opts.EmbedSvgImages = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ширина рамки вокруг диаграмм
|
||||||
|
func WithBorder(border uint) Option {
|
||||||
|
return optionBorder(border)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionBorder uint
|
||||||
|
|
||||||
|
var _ Option = (*optionBorder)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionBorder) apply(opts *Options) {
|
||||||
|
opts.Border = uint(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Масштаб в процентах размера экспортированных диаграмм
|
||||||
|
func WithScale(scale uint) Option {
|
||||||
|
return optionScale(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionScale uint
|
||||||
|
|
||||||
|
var _ Option = (*optionScale)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionScale) apply(opts *Options) {
|
||||||
|
opts.Scale = uint(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ширина экспортированной диаграммы с сохранением масштаба
|
||||||
|
func WithWidth(width uint) Option {
|
||||||
|
return optionWidth(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionWidth uint
|
||||||
|
|
||||||
|
var _ Option = (*optionWidth)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionWidth) apply(opts *Options) {
|
||||||
|
opts.Width = uint(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Высота экспортированной диаграммы с сохранением масштаба
|
||||||
|
func WithHeight(height uint) Option {
|
||||||
|
return optionHeight(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionHeight uint
|
||||||
|
|
||||||
|
var _ Option = (*optionHeight)(nil) // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionHeight) apply(opts *Options) {
|
||||||
|
opts.Height = uint(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обрезать результирующий PDF до размера диаграммы
|
||||||
|
func WithCrop() Option {
|
||||||
|
return optionCrop{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionCrop struct{}
|
||||||
|
|
||||||
|
var _ Option = optionCrop{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionCrop) apply(opts *Options) {
|
||||||
|
opts.Crop = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выводить несжатый XML
|
||||||
|
func WithUncompressed() Option {
|
||||||
|
return optionUncompressed{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionUncompressed struct{}
|
||||||
|
|
||||||
|
var _ Option = optionUncompressed{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionUncompressed) apply(opts *Options) {
|
||||||
|
opts.Uncompressed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Включить подключаемые модули
|
||||||
|
func WithEnablePlugins() Option {
|
||||||
|
return optionEnablePlugins{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionEnablePlugins struct{}
|
||||||
|
|
||||||
|
var _ Option = optionEnablePlugins{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionEnablePlugins) apply(opts *Options) {
|
||||||
|
opts.EnablePlugins = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Открыть файл
|
||||||
|
func WithOpenFile(openFile OpenFileFunc) Option {
|
||||||
|
return optionOpenFile{openFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionOpenFile struct {
|
||||||
|
openFile OpenFileFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Option = optionOpenFile{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionOpenFile) apply(opts *Options) {
|
||||||
|
opts.openFile = opt.openFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// Прочитать папку на диске
|
||||||
|
func WithReadDir(readDir ReadDirFunc) Option {
|
||||||
|
return optionReadDir{readDir}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionReadDir struct {
|
||||||
|
readDir ReadDirFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Option = optionReadDir{} // Проверка реализации интерфейса
|
||||||
|
|
||||||
|
func (opt optionReadDir) apply(opts *Options) {
|
||||||
|
opts.readDir = opt.readDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пустой параметр
|
||||||
|
func WithNop() Option {
|
||||||
|
return optionNop{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type optionNop struct{}
|
||||||
|
|
||||||
|
var _ Option = optionNop{}
|
||||||
|
|
||||||
|
func (opt optionNop) apply(opts *Options) {
|
||||||
|
// Не требуется действий, так как это пустой параметр
|
||||||
|
}
|
105
pkg/drawio/options_internal_test.go
Normal file
105
pkg/drawio/options_internal_test.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package drawio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptions(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
name string // Наименование теста
|
||||||
|
opts []Option // Параметры
|
||||||
|
app string // Путь к приложению drawio
|
||||||
|
outdir string // Путь к папке с экспортированными файлами
|
||||||
|
outext string // Расширение экспортированных файлов
|
||||||
|
args []string // Аргументы командной строки drawio
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
opts: []Option{},
|
||||||
|
app: "drawio",
|
||||||
|
outdir: "export",
|
||||||
|
outext: ".pdf",
|
||||||
|
args: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "svg with all options",
|
||||||
|
opts: []Option{
|
||||||
|
WithAppPath("/usr/local/bin/drawio"),
|
||||||
|
WithOutput("/tmp/images/"),
|
||||||
|
WithFormat(SVG),
|
||||||
|
WithRecursive(),
|
||||||
|
WithRemovePageSuffix(),
|
||||||
|
WithQuality(100),
|
||||||
|
WithTransparent(),
|
||||||
|
WithEmbedDiagram(),
|
||||||
|
WithEmbedSvgImages(),
|
||||||
|
WithBorder(1),
|
||||||
|
WithScale(120),
|
||||||
|
WithWidth(800),
|
||||||
|
WithHeight(600),
|
||||||
|
WithCrop(),
|
||||||
|
WithUncompressed(),
|
||||||
|
WithEnablePlugins(),
|
||||||
|
},
|
||||||
|
app: "/usr/local/bin/drawio",
|
||||||
|
outdir: "/tmp/images/",
|
||||||
|
outext: ".svg",
|
||||||
|
args: []string{
|
||||||
|
"--format", "svg",
|
||||||
|
"--quality", "100",
|
||||||
|
"--transparent",
|
||||||
|
"--embed-diagram",
|
||||||
|
"--embed-svg-images",
|
||||||
|
"--border", "1",
|
||||||
|
"--scale", "120",
|
||||||
|
"--width", "800",
|
||||||
|
"--height", "600",
|
||||||
|
"--crop",
|
||||||
|
"--uncompressed",
|
||||||
|
"--enable-plugins",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
options := Options{}
|
||||||
|
for _, opt := range test.opts {
|
||||||
|
opt.apply(&options)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.app, options.App())
|
||||||
|
assert.Equal(t, test.outdir, options.OutDir())
|
||||||
|
assert.Equal(t, test.outext, options.OutExt())
|
||||||
|
assert.ElementsMatch(t, test.args, options.Args())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Тестовая функция открытия файла
|
||||||
|
func openFile(name string) (io.ReadCloser, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionsOpenFileFunc(t *testing.T) {
|
||||||
|
opts := New(WithOpenFile(openFile))
|
||||||
|
assert.Equal(t,
|
||||||
|
reflect.ValueOf(openFile).Pointer(),
|
||||||
|
reflect.ValueOf(opts.openFile).Pointer(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readDir(name string) ([]os.DirEntry, error) {
|
||||||
|
return []os.DirEntry{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOptionsReadDirFunc(t *testing.T) {
|
||||||
|
opts := New(WithReadDir(readDir))
|
||||||
|
assert.Equal(t,
|
||||||
|
reflect.ValueOf(readDir).Pointer(),
|
||||||
|
reflect.ValueOf(opts.readDir).Pointer(),
|
||||||
|
)
|
||||||
|
}
|
76
pkg/drawio/options_test.go
Normal file
76
pkg/drawio/options_test.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package drawio_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.mousesoft.ru/ms/drawio-exporter/pkg/drawio"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormat(t *testing.T) {
|
||||||
|
testData := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
format drawio.Format
|
||||||
|
ext string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "pdf",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.PDF,
|
||||||
|
ext: ".pdf",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "png",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.PNG,
|
||||||
|
ext: ".png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jpg",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.JPG,
|
||||||
|
ext: ".jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "svg",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.SVG,
|
||||||
|
ext: ".svg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vsdx",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.VSDX,
|
||||||
|
ext: ".vsdx",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "xml",
|
||||||
|
err: nil,
|
||||||
|
format: drawio.XML,
|
||||||
|
ext: ".xml",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "svvg",
|
||||||
|
err: drawio.ErrUnsupportedFormat{"svvg"},
|
||||||
|
format: drawio.Format(""),
|
||||||
|
ext: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testData {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
v drawio.Format
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
err = (&v).Set(test.name)
|
||||||
|
if test.err == nil {
|
||||||
|
assert.Equal(t, test.name, test.format.String())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else {
|
||||||
|
assert.ErrorIs(t, err, test.err)
|
||||||
|
assert.ErrorContains(t, err, test.err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user