mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
[v2] NSIS installer support for Windows (#1184)
* [v2] Add support for post build hooks Currently only supports build-level hooks * [v2] Improve build assets handling and use single source for manifest generation The manifest asset files are now a go template and data will be resolved before they are included into the build output. Breaking Change: Windows manifest file must be named “wails.exe.manifest” and doesn’t depend on the project name anymore. * [v2, windows] NSIS installer generation
This commit is contained in:
+5
-5
@@ -1,14 +1,14 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleName</key><string>{{.Name}}</string>
|
||||
<key>CFBundleName</key><string>{{.Info.ProductName}}</string>
|
||||
<key>CFBundleExecutable</key><string>{{.Name}}</string>
|
||||
<key>CFBundleIdentifier</key><string>com.wails.{{.Name}}</string>
|
||||
<key>CFBundleVersion</key><string>1.0.0</string>
|
||||
<key>CFBundleGetInfoString</key><string>Built using Wails (https://wails.app)</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
||||
<key>CFBundleVersion</key><string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleGetInfoString</key><string>{{.Info.Comments}}</string>
|
||||
<key>CFBundleShortVersionString</key><string>{{.Info.ProductVersion}}</string>
|
||||
<key>CFBundleIconFile</key><string>iconfile</string>
|
||||
<key>LSMinimumSystemVersion</key><string>10.13.0</string>
|
||||
<key>NSHighResolutionCapable</key><string>true</string>
|
||||
<key>NSHumanReadableCopyright</key><string>Copyright.........</string>
|
||||
<key>NSHumanReadableCopyright</key><string>{{.Info.Copyright}}</string>
|
||||
</dict></plist>
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"fixed": {
|
||||
"file_version": "{{.Info.ProductVersion}}"
|
||||
},
|
||||
"info": {
|
||||
"0000": {
|
||||
"ProductVersion": "{{.Info.ProductVersion}}",
|
||||
"CompanyName": "{{.Info.CompanyName}}",
|
||||
"FileDescription": "{{.Info.ProductName}}",
|
||||
"LegalCopyright": "{{.Info.Copyright}}",
|
||||
"ProductName": "{{.Info.ProductName}}",
|
||||
"Comments": "{{.Info.Comments}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"fixed": {
|
||||
"file_version": "1.0.0"
|
||||
},
|
||||
"info": {
|
||||
"0000": {
|
||||
"ProductVersion": "1.0.0",
|
||||
"CompanyName": "{{.Name}}",
|
||||
"FileDescription": "{{.Name}}",
|
||||
"LegalCopyright": "Copyright.........",
|
||||
"ProductName": "{{.Name}}",
|
||||
"Comments": "Built using Wails (https://wails.app)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
Unicode true
|
||||
|
||||
####
|
||||
## Please note: Template replacements don't work in this file. They are provided with default defines like
|
||||
## mentioned underneath.
|
||||
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
|
||||
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
|
||||
## from outside of Wails for debugging and development of the installer.
|
||||
##
|
||||
## For development first make a wails nsis build to populate the "wails_tools.nsh":
|
||||
## > wails build --target windows/amd64 --nsis
|
||||
## Then you can call makensis on this file with specifying the path to your binary:
|
||||
## For a AMD64 only installer:
|
||||
## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app.exe
|
||||
## For a ARM64 only installer:
|
||||
## > makensis -DARG_WAILS_ARM64_BINARY=../../bin/app.exe
|
||||
## For a installer with both architectures:
|
||||
## > makensis -DARG_WAILS_AMD64_BINARY=../../bin/app-amd64.exe -DARG_WAILS_ARM64_BINARY=../../bin/app-arm64.exe
|
||||
####
|
||||
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
|
||||
####
|
||||
## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}"
|
||||
## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}"
|
||||
## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}"
|
||||
## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}"
|
||||
## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}"
|
||||
###
|
||||
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
|
||||
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||
####
|
||||
## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html
|
||||
####
|
||||
## Include the wails tools
|
||||
####
|
||||
!include "wails_tools.nsh"
|
||||
|
||||
# The version information for this two must consist of 4 parts
|
||||
VIProductVersion "${INFO_PRODUCTVERSION}.0"
|
||||
VIFileVersion "${INFO_PRODUCTVERSION}.0"
|
||||
|
||||
VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
|
||||
VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
|
||||
VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
|
||||
VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
|
||||
VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
|
||||
VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
|
||||
|
||||
!include "MUI.nsh"
|
||||
|
||||
!define MUI_ICON "../icon.ico"
|
||||
!define MUI_UNICON "../icon.ico"
|
||||
# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources/leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
|
||||
!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
|
||||
# !insertmacro MUI_PAGE_LICENSE "resources/eula.txt" # Adds a EULA page to the installer
|
||||
!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
|
||||
!insertmacro MUI_PAGE_INSTFILES # Installing page.
|
||||
!insertmacro MUI_PAGE_FINISH # Finished installation page.
|
||||
|
||||
!insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
|
||||
|
||||
## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
|
||||
#!uninstfinalize 'signtool --file "%1"'
|
||||
#!finalize 'signtool --file "%1"'
|
||||
|
||||
Name "${INFO_PRODUCTNAME}"
|
||||
OutFile "../../bin/${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
|
||||
InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
|
||||
ShowInstDetails show # This will always show the installation details.
|
||||
|
||||
Function .onInit
|
||||
!insertmacro wails.checkArchitecture
|
||||
FunctionEnd
|
||||
|
||||
Section
|
||||
!insertmacro wails.webview2runtime
|
||||
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
!insertmacro wails.files
|
||||
|
||||
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
|
||||
!insertmacro wails.writeUninstaller
|
||||
SectionEnd
|
||||
|
||||
Section "uninstall"
|
||||
RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
|
||||
|
||||
RMDir /r $INSTDIR
|
||||
|
||||
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
|
||||
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
|
||||
|
||||
!insertmacro wails.deleteUninstaller
|
||||
SectionEnd
|
||||
@@ -0,0 +1,171 @@
|
||||
# DO NOT EDIT - Generated automatically by `wails build`
|
||||
|
||||
!include "x64.nsh"
|
||||
!include "WinVer.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
!ifndef INFO_PROJECTNAME
|
||||
!define INFO_PROJECTNAME "{{.Name}}"
|
||||
!endif
|
||||
!ifndef INFO_COMPANYNAME
|
||||
!define INFO_COMPANYNAME "{{.Info.CompanyName}}"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTNAME
|
||||
!define INFO_PRODUCTNAME "{{.Info.ProductName}}"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTVERSION
|
||||
!define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}"
|
||||
!endif
|
||||
!ifndef INFO_COPYRIGHT
|
||||
!define INFO_COPYRIGHT "{{.Info.Copyright}}"
|
||||
!endif
|
||||
!ifndef PRODUCT_EXECUTABLE
|
||||
!define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
|
||||
!endif
|
||||
!ifndef UNINST_KEY_NAME
|
||||
!define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
|
||||
!endif
|
||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
|
||||
|
||||
!ifndef REQUEST_EXECUTION_LEVEL
|
||||
!define REQUEST_EXECUTION_LEVEL "admin"
|
||||
!endif
|
||||
|
||||
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
||||
|
||||
!ifdef ARG_WAILS_AMD64_BINARY
|
||||
!define SUPPORTS_AMD64
|
||||
!endif
|
||||
|
||||
!ifdef ARG_WAILS_ARM64_BINARY
|
||||
!define SUPPORTS_ARM64
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_AMD64
|
||||
!ifdef SUPPORTS_ARM64
|
||||
!define ARCH "amd64_arm64"
|
||||
!else
|
||||
!define ARCH "amd64"
|
||||
!endif
|
||||
!else
|
||||
!ifdef SUPPORTS_ARM64
|
||||
!define ARCH "arm64"
|
||||
!else
|
||||
!error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!macro wails.checkArchitecture
|
||||
!ifndef WAILS_WIN10_REQUIRED
|
||||
!define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
|
||||
!endif
|
||||
|
||||
!ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
|
||||
!define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
|
||||
!endif
|
||||
|
||||
${If} ${AtLeastWin10}
|
||||
!ifdef SUPPORTS_AMD64
|
||||
${if} ${IsNativeAMD64}
|
||||
Goto ok
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_ARM64
|
||||
${if} ${IsNativeARM64}
|
||||
Goto ok
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
IfSilent silentArch notSilentArch
|
||||
silentArch:
|
||||
SetErrorLevel 65
|
||||
Abort
|
||||
notSilentArch:
|
||||
MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
|
||||
Quit
|
||||
${else}
|
||||
IfSilent silentWin notSilentWin
|
||||
silentWin:
|
||||
SetErrorLevel 64
|
||||
Abort
|
||||
notSilentWin:
|
||||
MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
|
||||
Quit
|
||||
${EndIf}
|
||||
|
||||
ok:
|
||||
!macroend
|
||||
|
||||
!macro wails.files
|
||||
!ifdef SUPPORTS_AMD64
|
||||
${if} ${IsNativeAMD64}
|
||||
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
|
||||
${EndIf}
|
||||
!endif
|
||||
|
||||
!ifdef SUPPORTS_ARM64
|
||||
${if} ${IsNativeARM64}
|
||||
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
|
||||
${EndIf}
|
||||
!endif
|
||||
!macroend
|
||||
|
||||
!macro wails.writeUninstaller
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
SetRegView 64
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
|
||||
!macroend
|
||||
|
||||
!macro wails.deleteUninstaller
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
SetRegView 64
|
||||
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||
!macroend
|
||||
|
||||
# Install webview2 by launching the bootstrapper
|
||||
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
|
||||
!macro wails.webview2runtime
|
||||
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
|
||||
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
|
||||
!endif
|
||||
|
||||
SetRegView 64
|
||||
# If the admin key exists and is not empty then webview2 is already installed
|
||||
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||
${If} $0 != ""
|
||||
Goto ok
|
||||
${EndIf}
|
||||
|
||||
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
|
||||
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
|
||||
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
|
||||
${If} $0 != ""
|
||||
Goto ok
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
SetDetailsPrint both
|
||||
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
|
||||
SetDetailsPrint listonly
|
||||
|
||||
InitPluginsDir
|
||||
CreateDirectory "$pluginsdir\webview2bootstrapper"
|
||||
SetOutPath "$pluginsdir\webview2bootstrapper"
|
||||
File "tmp/MicrosoftEdgeWebview2Setup.exe"
|
||||
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
|
||||
|
||||
SetDetailsPrint both
|
||||
ok:
|
||||
!macroend
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="amd64"/>
|
||||
<assemblyIdentity type="win32" name="com.wails.{{.Name}}" version="{{.Info.ProductVersion}}.0" processorArchitecture="*"/>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
package buildassets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
iofs "io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
)
|
||||
|
||||
//go:embed build
|
||||
var assets embed.FS
|
||||
|
||||
type assetData struct {
|
||||
Name string
|
||||
}
|
||||
const (
|
||||
rootFolder = "build"
|
||||
)
|
||||
|
||||
// Install will install all default project assets
|
||||
func Install(targetDir string, projectName string) error {
|
||||
func Install(targetDir string) error {
|
||||
templateDir := gosod.New(assets)
|
||||
err := templateDir.Extract(targetDir, &assetData{Name: projectName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename the manifest file
|
||||
windowsDir := filepath.Join(targetDir, "build", "windows")
|
||||
manifest := filepath.Join(windowsDir, "wails.exe.manifest")
|
||||
targetFile := filepath.Join(windowsDir, projectName+".exe.manifest")
|
||||
err = os.Rename(manifest, targetFile)
|
||||
err := templateDir.Extract(targetDir, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -36,32 +34,106 @@ func Install(targetDir string, projectName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RegenerateManifest(target string) error {
|
||||
a, err := debme.FS(assets, "build")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.CopyFile("windows/wails.exe.manifest", target, 0644)
|
||||
// GetLocalPath returns the local path of the requested build asset file
|
||||
func GetLocalPath(projectData *project.Project, file string) string {
|
||||
return filepath.Clean(filepath.Join(projectData.Path, rootFolder, filepath.FromSlash(file)))
|
||||
}
|
||||
|
||||
func RegenerateAppIcon(target string) error {
|
||||
a, err := debme.FS(assets, "build")
|
||||
if err != nil {
|
||||
return err
|
||||
// ReadFile reads the file from the project build folder.
|
||||
// If the file does not exist it falls back to the embedded file and the file will be written
|
||||
// to the disk for customisation.
|
||||
func ReadFile(projectData *project.Project, file string) ([]byte, error) {
|
||||
fs := os.DirFS(filepath.ToSlash(projectData.Path)) // os.DirFs always operates on "/" as separatator
|
||||
file = path.Join(rootFolder, file)
|
||||
|
||||
content, err := iofs.ReadFile(fs, file)
|
||||
if errors.Is(err, iofs.ErrNotExist) {
|
||||
// The file does not exist, let's read it from the assets FS and write it to disk
|
||||
content, err := iofs.ReadFile(assets, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := writeFileSystemFile(projectData, file, content); err != nil {
|
||||
return nil, fmt.Errorf("Unable to create file in build folder: %s", err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
return a.CopyFile("appicon.png", target, 0644)
|
||||
|
||||
return content, err
|
||||
}
|
||||
|
||||
func RegeneratePlist(targetDir string, projectName string) error {
|
||||
darwinAssets, err := debme.FS(assets, "build/darwin")
|
||||
// ReadFileWithProjectData reads the file from the project build folder and replaces ProjectInfo if necessary.
|
||||
// If the file does not exist it falls back to the embedded file and the file will be written
|
||||
// to the disk for customisation. The file written is the original unresolved one.
|
||||
func ReadFileWithProjectData(projectData *project.Project, file string) ([]byte, error) {
|
||||
content, err := ReadFile(projectData, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
templateDir := gosod.New(darwinAssets)
|
||||
err = templateDir.Extract(targetDir, &assetData{Name: projectName})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err = resolveProjectData(content, projectData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to resolve data in %s: %w", file, err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// ReadOriginalFileWithProjectDataAndSave reads the file from the embedded assets and replaces
|
||||
// ProjectInfo if necessary.
|
||||
// It will also write the resolved final file back to the project build folder.
|
||||
func ReadOriginalFileWithProjectDataAndSave(projectData *project.Project, file string) ([]byte, error) {
|
||||
file = path.Join(rootFolder, file)
|
||||
content, err := iofs.ReadFile(assets, file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to read file %s: %w", file, err)
|
||||
}
|
||||
|
||||
content, err = resolveProjectData(content, projectData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to resolve data in %s: %w", file, err)
|
||||
}
|
||||
|
||||
if err := writeFileSystemFile(projectData, file, content); err != nil {
|
||||
return nil, fmt.Errorf("Unable to create file in build folder: %w", err)
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
type assetData struct {
|
||||
Name string
|
||||
Info project.Info
|
||||
}
|
||||
|
||||
func resolveProjectData(content []byte, projectData *project.Project) ([]byte, error) {
|
||||
tmpl, err := template.New("").Parse(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &assetData{
|
||||
Name: projectData.Name,
|
||||
Info: projectData.Info,
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
if err := tmpl.Execute(&out, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func writeFileSystemFile(projectData *project.Project, file string, content []byte) error {
|
||||
path := filepath.Clean(filepath.Join(projectData.Path, filepath.FromSlash(file)))
|
||||
|
||||
if dir := filepath.Dir(path); !fs.DirExists(dir) {
|
||||
if err := fs.MkDirs(dir, 0755); err != nil {
|
||||
return fmt.Errorf("Unable to create directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, content, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user