From 355905df40285855f144cf66f95ff092abe45903 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Fri, 15 Nov 2024 14:46:40 -0800 Subject: [PATCH] Generate windows installer. (#1726) --- .github/workflows/binaries.yml | 71 ++++++++++++++++++++++++- installer.iss | 94 +++++++++++++++++++++++++++++++++ stellar.ico | Bin 0 -> 11166 bytes 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 installer.iss create mode 100644 stellar.ico diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 25cea7366..985f91fa5 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -34,7 +34,7 @@ jobs: target: aarch64-unknown-linux-gnu - os: macos-14 target: aarch64-apple-darwin - - os: macos-12 + - os: macos-14 target: x86_64-apple-darwin - os: windows-latest target: x86_64-pc-windows-msvc @@ -44,41 +44,50 @@ jobs: - uses: actions/checkout@v4 - run: rustup update - run: rustup target add ${{ matrix.sys.target }} + - if: matrix.sys.target == 'aarch64-unknown-linux-gnu' run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + - name: Setup vars run: | version="$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "stellar-cli") | .version')" echo "VERSION=${version}" >> $GITHUB_ENV echo "NAME=${{ matrix.crate.name }}-${version}-${{ matrix.sys.target }}" >> $GITHUB_ENV + - name: Package (release only) if: github.event_name == 'release' run: cargo package --no-verify --package ${{ matrix.crate.name }} + - name: Package Extract (release only) if: github.event_name == 'release' run: | cd target/package tar xvfz ${{ matrix.crate.name }}-$VERSION.crate echo "BUILD_WORKING_DIR=target/package/${{ matrix.crate.name }}-$VERSION" >> $GITHUB_ENV + - name: Build env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc working-directory: ${{ env.BUILD_WORKING_DIR }} run: cargo build --target-dir="$GITHUB_WORKSPACE/target" --package ${{ matrix.crate.name }} --features opt --release --target ${{ matrix.sys.target }} + - name: Build provenance for attestation (release only) if: github.event_name == 'release' uses: actions/attest-build-provenance@v1 with: subject-path: target/${{ matrix.sys.target }}/release/${{ matrix.crate.binary }}${{ matrix.sys.ext }} + - name: Compress run: | cd target/${{ matrix.sys.target }}/release tar czvf $NAME.tar.gz ${{ matrix.crate.binary }}${{ matrix.sys.ext }} + - name: Upload to Artifacts uses: actions/upload-artifact@v4 with: - name: ${{ env.NAME }} + name: ${{ env.NAME }}.tar.gz path: 'target/${{ matrix.sys.target }}/release/${{ env.NAME }}.tar.gz' + - name: Upload to Release (release only) if: github.event_name == 'release' uses: actions/github-script@v7 @@ -92,3 +101,61 @@ jobs: name: '${{ env.NAME }}.tar.gz', data: fs.readFileSync('target/${{ matrix.sys.target }}/release/${{ env.NAME }}.tar.gz'), }); + + installer: + needs: build + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup vars + run: | + version="$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "stellar-cli") | .version')" + installer_basename="stellar-cli-installer-${version}-x86_64-pc-windows-msvc" + echo "VERSION=${version}" >> $GITHUB_ENV + echo "STELLAR_CLI_INSTALLER_BASENAME=${installer_basename}" >> $GITHUB_ENV + echo "STELLAR_CLI_INSTALLER=${installer_basename}.exe" >> $GITHUB_ENV + echo "ARTIFACT_NAME=stellar-cli-${version}-x86_64-pc-windows-msvc.tar.gz" >> $GITHUB_ENV + + - name: Download Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }} + + - name: Uncompress Artifact + run: tar xvf ${{ env.ARTIFACT_NAME }} + + - name: Build Installer + shell: powershell + run: | + $Env:Path += ";C:\Users\$Env:UserName\AppData\Local\Programs\Inno Setup 6" + $Env:STELLAR_CLI_VERSION = "${{ env.VERSION }}" + ISCC.exe installer.iss + + - name: Build provenance for attestation (release only) + if: github.event_name == 'release' + uses: actions/attest-build-provenance@v1 + with: + subject-path: ${{ env.STELLAR_CLI_INSTALLER }} + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.STELLAR_CLI_INSTALLER }} + path: Output/stellar-installer.exe + + - name: Upload to Release (release only) + if: github.event_name == 'release' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + await github.rest.repos.uploadReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: ${{ github.event.release.id }}, + name: '${{ env.STELLAR_CLI_INSTALLER }}', + data: fs.readFileSync('Output/stellar-installer.exe'), + }); + diff --git a/installer.iss b/installer.iss new file mode 100644 index 000000000..895740ccb --- /dev/null +++ b/installer.iss @@ -0,0 +1,94 @@ +#define STELLAR_CLI_VERSION GetEnv("STELLAR_CLI_VERSION") +#define STELLAR_CLI_INSTALLER GetEnv("STELLAR_CLI_INSTALLER") + +[Setup] +AppName=Stellar CLI +AppVersion={#STELLAR_CLI_VERSION} +DefaultDirName={commonpf}\Stellar CLI +DefaultGroupName=Stellar CLI +OutputBaseFilename=stellar-installer +PrivilegesRequired=admin +LicenseFile=LICENSE +UninstallDisplayIcon={app}\stellar.ico +Compression=lzma +SolidCompression=yes +ChangesEnvironment=yes + +[Files] +Source: "stellar.exe"; DestDir: "{app}" +Source: "stellar.ico"; DestDir: "{app}" +Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +; Windows optimizes start menu, and removes the uninstall entry. Unless we +; specify it twice. 🫠 +Name: "{group}\Uninstall Stellar CLI"; Filename: "{uninstallexe}" +Name: "{group}\Uninstall Stellar CLI"; Filename: "{uninstallexe}" +Name: "{group}\Stellar Developer Docs"; Filename: "https://stellar.org/docs" + +[Code] +const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + +procedure EnvAddPath(Path: string); +var + Paths: string; +begin + { Retrieve current path (use empty string if entry not exists) } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Paths := ''; + + { Skip if string already found in path } + if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit; + + { App string to the end of the path variable } + Paths := Paths + ';'+ Path +';' + + { Overwrite (or create if missing) path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths])) + else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths])); +end; + +procedure EnvRemovePath(Path: string); +var + Paths: string; + P: Integer; +begin + { Skip if registry entry not exists } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then + exit; + + { Skip if string not found in path } + P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';'); + if P = 0 then exit; + + { Update path variable } + Delete(Paths, P - 1, Length(Path) + 1); + + { Overwrite path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths])) + else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths])); +end; + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall + then EnvAddPath(ExpandConstant('{app}')); +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + if CurUninstallStep = usPostUninstall + then EnvRemovePath(ExpandConstant('{app}')); +end; + +[UninstallDelete] +; Remove the Start Menu group +Type: filesandordirs; Name: "{group}" + +; Remove installed files and directory +Type: files; Name: "{app}\stellar.exe" +Type: files; Name: "{uninstallexe}" +Type: files; Name: "{app}\stellar.ico" +Type: dirifempty; Name: "{app}" diff --git a/stellar.ico b/stellar.ico new file mode 100644 index 0000000000000000000000000000000000000000..4bbfa5f04cc650d18b34a9d03d6c5c6193db2213 GIT binary patch literal 11166 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

sPeuj{E;befu1(kX?|w$Sf2{n6epS5qL+0h> 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