Run on CI

This commit is contained in:
Akis Kesoglou 2023-12-20 10:50:32 +02:00
parent 559f2b4d68
commit a22d2468fd
9 changed files with 208 additions and 168 deletions

View File

@ -10,12 +10,12 @@ inputs:
required: true
# --- custom environment
XCODE_APP_LOADER_EMAIL:
type: string
default: "accounts+apple@balena.io"
NODE_VERSION:
type: string
default: "18.x"
# Beware that native modules will be built for this version,
# which might not be compatible with the one used by pkg (see forge.sidecar.ts)
# https://github.com/vercel/pkg-fetch/releases
default: "18.18"
VERBOSE:
type: string
default: "true"
@ -32,7 +32,7 @@ runs:
- name: Extract custom source artifact
if: runner.os != 'Windows'
shell: pwsh
shell: bash
working-directory: .
run: tar -xf ${{ runner.temp }}/custom.tgz
@ -48,22 +48,54 @@ runs:
node-version: ${{ inputs.NODE_VERSION }}
cache: npm
- name: Install yq
shell: bash --noprofile --norc -eo pipefail -x {0}
run: choco install yq
if: runner.os == 'Windows'
- name: Install host dependencies
if: runner.os == 'Linux'
shell: bash
run: sudo apt-get install -y --no-install-recommends fakeroot dpkg rpm
- name: Install host dependencies
if: runner.os == 'macOS'
# FIXME: Python 3.12 dropped distutils that node-gyp depends upon.
# This is a temporary workaround to make the job use Python 3.11 until
# we update to npm 10+.
uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4
with:
python-version: '3.11'
# https://www.electron.build/code-signing.html
# https://github.com/Apple-Actions/import-codesign-certs
# https://dev.to/rwwagner90/signing-electron-apps-with-github-actions-4cof
- name: Import Apple code signing certificate
if: runner.os == 'macOS'
uses: apple-actions/import-codesign-certs@v1
with:
p12-file-base64: ${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
p12-password: ${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}
shell: bash
run: |
KEY_CHAIN=build.keychain
CERTIFICATE_P12=certificate.p12
# Recreate the certificate from the secure environment variable
echo $CERTIFICATE_P12_B64 | base64 --decode > $CERTIFICATE_P12
# Create a keychain
security create-keychain -p actions $KEY_CHAIN
# Make the keychain the default so identities are found
security default-keychain -s $KEY_CHAIN
# Unlock the keychain
security unlock-keychain -p actions $KEY_CHAIN
security import $CERTIFICATE_P12 -k $KEY_CHAIN -P $CERTIFICATE_PASSWORD -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k actions $KEY_CHAIN
# remove certs
rm -fr *.p12
env:
CERTIFICATE_P12_B64: ${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
CERTIFICATE_PASSWORD: ${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}
- name: Import Windows code signing certificate
if: runner.os == 'Windows'
id: import_win_signing_cert
shell: powershell
run: |
Set-Content -Path ${{ runner.temp }}/certificate.base64 -Value $env:WINDOWS_CERTIFICATE
@ -75,91 +107,64 @@ runs:
-CertStoreLocation Cert:\CurrentUser\My `
-Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
Remove-Item -path ${{ runner.temp }} -include certificate.pfx
echo "certFilePath=${{ runner.temp }}/certificate.pfx" >> $GITHUB_OUTPUT
env:
WINDOWS_CERTIFICATE: ${{ fromJSON(inputs.secrets).WINDOWS_SIGNING }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ fromJSON(inputs.secrets).WINDOWS_SIGNING_PASSWORD }}
# ... or refactor (e.g.) https://github.com/samuelmeuli/action-electron-builder
# https://github.com/product-os/scripts/tree/master/electron
# https://github.com/product-os/scripts/tree/master/shared
# https://github.com/product-os/balena-concourse/blob/master/pipelines/github-events/template.yml
- name: Package release
id: package_release
shell: bash --noprofile --norc -eo pipefail -x {0}
shell: bash
# IMPORTANT: before making changes to this step please consult @engineering in balena's chat.
run: |
set -ea
[[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x
runner_os="$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')"
runner_arch="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')"
ELECTRON_BUILDER_ARCHITECTURE="${runner_arch}"
APPLICATION_VERSION="$(jq -r '.version' package.json)"
ARCHITECTURE_FLAGS="--${ELECTRON_BUILDER_ARCHITECTURE}"
if [[ $runner_os =~ linux ]]; then
ELECTRON_BUILDER_OS='--linux'
TARGETS="$(yq e .linux.target[] electron-builder.yml)"
elif [[ $runner_os =~ darwin|macos|osx ]]; then
CSC_KEY_PASSWORD=${{ fromJSON(inputs.secrets).APPLE_SIGNING_PASSWORD }}
CSC_KEYCHAIN=signing_temp
CSC_LINK=${{ fromJSON(inputs.secrets).APPLE_SIGNING }}
ELECTRON_BUILDER_OS='--mac'
TARGETS="$(yq e .mac.target[] electron-builder.yml)"
elif [[ $runner_os =~ windows|win ]]; then
ARCHITECTURE_FLAGS="--ia32 ${ARCHITECTURE_FLAGS}"
CSC_KEY_PASSWORD=${{ fromJSON(inputs.secrets).WINDOWS_SIGNING_PASSWORD }}
CSC_LINK=${{ fromJSON(inputs.secrets).WINDOWS_SIGNING }}
ELECTRON_BUILDER_OS='--win'
TARGETS="$(yq e .win.target[] electron-builder.yml)"
else
exit 1
if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then
export DEBUG='electron-forge:*,electron-packager,electron-rebuild'
fi
npm link electron-builder
APPLICATION_VERSION="$(jq -r '.version' package.json)"
for target in ${TARGETS}; do
electron-builder ${ELECTRON_BUILDER_OS} ${target} ${ARCHITECTURE_FLAGS} \
--c.extraMetadata.analytics.sentry.token='https://739bbcfc0ba4481481138d3fc831136d@o95242.ingest.sentry.io/4504451487301632' \
--c.extraMetadata.analytics.amplitude.token='balena-etcher' \
--c.extraMetadata.packageType="${target}"
if [[ "${RUNNER_OS}" == Linux ]]; then
BUILD_ARCHS="x64"
find dist -type f -maxdepth 1
done
elif [[ "${RUNNER_OS}" == macOS ]]; then
BUILD_ARCHS="x64,arm64"
elif [[ "${RUNNER_OS}" == Windows ]]; then
#BUILD_ARCHS="ia32,x64" -- distutils fails to build for ia32
BUILD_ARCHS="x64"
else
echo "ERROR: unexpected runner OS: ${RUNNER_OS}"
exit 1
fi
npx electron-forge make --arch="${BUILD_ARCHS}"
echo "version=${APPLICATION_VERSION}" >> $GITHUB_OUTPUT
# collect all artifacts from subdirectories under a common top-level directory
mkdir -p dist
find ./out/make -type f \( \
-iname "*.zip" -o \
-iname "*.dmg" -o \
-iname "*.rpm" -o \
-iname "*.deb" -o \
-iname "*.AppImage" -o \
-iname "*Setup.exe" \
\) -ls -exec cp '{}' dist/ \;
env:
# Apple notarization (afterSignHook.js)
XCODE_APP_LOADER_EMAIL: ${{ inputs.XCODE_APP_LOADER_EMAIL }}
# ensure we sign the artifacts
NODE_ENV: production
# analytics tokens
SENTRY_TOKEN: https://739bbcfc0ba4481481138d3fc831136d@o95242.ingest.sentry.io/4504451487301632
AMPLITUDE_TOKEN: 'balena-etcher'
# Apple notarization
XCODE_APP_LOADER_EMAIL: ${{ fromJSON(inputs.secrets).XCODE_APP_LOADER_EMAIL }}
XCODE_APP_LOADER_PASSWORD: ${{ fromJSON(inputs.secrets).XCODE_APP_LOADER_PASSWORD }}
# https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/#improvements-for-public-repository-forks
# https://docs.github.com/en/actions/managing-workflow-runs/approving-workflow-runs-from-public-forks#about-workflow-runs-from-public-forks
CSC_FOR_PULL_REQUEST: true
# https://www.electron.build/auto-update.html#staged-rollouts
- name: Configure staged rollout(s)
shell: bash --noprofile --norc -eo pipefail -x {0}
run: |
set -ea
[[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x
percentage="$(cat < repo.yml | yq e .triggerNotification.stagingPercentage)"
find dist -type f -maxdepth 1 \
-name "latest*.yml" \
-exec yq -i e .version=\"${{ steps.package_release.outputs.version }}\" {} \;
find dist -type f -maxdepth 1 \
-name "latest*.yml" \
-exec yq -i e .stagingPercentage=\"$percentage\" {} \;
XCODE_APP_LOADER_TEAM_ID: ${{ fromJSON(inputs.secrets).XCODE_APP_LOADER_TEAM_ID }}
# Windows signing
WINDOWS_SIGNING_CERT_PATH: ${{ steps.import_win_signing_cert.outputs.certFilePath }}
WINDOWS_SIGNING_PASSWORD: ${{ fromJSON(inputs.secrets).WINDOWS_SIGNING_PASSWORD }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
@ -167,3 +172,4 @@ runs:
name: gh-release-${{ github.event.pull_request.head.sha || github.event.head_commit.id }}-${{ runner.os }}
path: dist
retention-days: 1
if-no-files-found: error

View File

@ -12,7 +12,7 @@ inputs:
# --- custom environment
NODE_VERSION:
type: string
default: "16.x"
default: "18.x"
VERBOSE:
type: string
default: "true"
@ -28,27 +28,45 @@ runs:
node-version: ${{ inputs.NODE_VERSION }}
cache: npm
- name: Test release
shell: bash --noprofile --norc -eo pipefail -x {0}
- name: Install host dependencies
if: runner.os == 'Linux'
shell: bash
run: |
set -ea
sudo apt-get install -y --no-install-recommends xvfb libudev-dev
cat < package.json | jq -r '.hostDependencies[][]' - | \
xargs -L1 echo | sed 's/|//g' | xargs -L1 \
sudo apt-get --ignore-missing install || true
[[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x
- name: Install host dependencies
if: runner.os == 'macOS'
# FIXME: Python 3.12 dropped distutils that node-gyp depends upon.
# This is a temporary workaround to make the job use Python 3.11 until
# we update to npm 10+.
uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4
with:
python-version: '3.11'
- name: Test release
shell: bash
run: |
if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then
export DEBUG='electron-forge:*,electron-packager,electron-rebuild'
fi
runner_os="$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')"
npm run flowzone-preinstall-${runner_os}
npm ci
npm run lint
npm run package
npm run test-${runner_os}
env:
# https://www.electronjs.org/docs/latest/api/environment-variables
ELECTRON_NO_ATTACH_CONSOLE: true
ELECTRON_NO_ATTACH_CONSOLE: 'true'
- name: Compress custom source
if: runner.os != 'Windows'
shell: pwsh
shell: bash
run: tar -acf ${{ runner.temp }}/custom.tgz .
- name: Compress custom source

View File

@ -18,7 +18,7 @@ jobs:
(github.event.pull_request.head.repo.full_name != github.repository && github.event_name == 'pull_request_target')
secrets: inherit
with:
tests_run_on: '["ubuntu-20.04","macos-latest","windows-2019"]'
tests_run_on: '["ubuntu-20.04","macos-12","windows-2019"]'
restrict_custom_actions: false
github_prerelease: true
cloudflare_website: "etcher"

View File

@ -9,5 +9,6 @@ jobs:
- uses: vedantmgoyal2009/winget-releaser@v1
with:
identifier: Balena.Etcher
installers-regex: 'balenaEtcher-Setup.*.exe$'
# matches something like "balenaEtcher-1.19.0.Setup.exe"
installers-regex: 'balenaEtcher-[\d.-]+\.Setup.exe$'
token: ${{ secrets.WINGET_PAT }}

3
.gitignore vendored
View File

@ -96,6 +96,9 @@ out/
# ---- Do not modify entries above this line ----
# Build artifacts
dist/
# Certificates
*.spc
*.pvk

View File

@ -1,25 +0,0 @@
'use strict'
const { notarize } = require('electron-notarize')
const { ELECTRON_SKIP_NOTARIZATION } = process.env
async function main(context) {
const { electronPlatformName, appOutDir } = context
if (electronPlatformName !== 'darwin' || ELECTRON_SKIP_NOTARIZATION === 'true') {
return
}
const appName = context.packager.appInfo.productFilename
const appleId = process.env.XCODE_APP_LOADER_EMAIL || 'accounts+apple@balena.io'
const appleIdPassword = process.env.XCODE_APP_LOADER_PASSWORD
// https://github.com/electron/notarize/blob/main/README.md
await notarize({
appBundleId: 'io.balena.etcher',
appPath: `${appOutDir}/${appName}.app`,
appleId,
appleIdPassword
})
}
exports.default = main

View File

@ -1,4 +0,0 @@
owner: balena-io
repo: etcher
provider: github
updaterCacheDirName: balena-etcher-updater

View File

@ -10,7 +10,24 @@ import { WebpackPlugin } from '@electron-forge/plugin-webpack';
import { mainConfig, rendererConfig } from './webpack.config';
import { productDescription } from './package.json';
import { hostDependencies, productDescription } from './package.json';
const osxSigningConfig: any = {};
let winSigningConfig: any = {};
if (process.env.NODE_ENV === 'production') {
osxSigningConfig.osxNotarize = {
tool: 'notarytool',
appleId: process.env.XCODE_APP_LOADER_EMAIL,
appleIdPassword: process.env.XCODE_APP_LOADER_PASSWORD,
teamId: process.env.XCODE_APP_LOADER_TEAM_ID,
};
winSigningConfig = {
certificateFile: process.env.WINDOWS_SIGNING_CERT_PATH,
certificatePassword: process.env.WINDOWS_SIGNING_PASSWORD,
};
}
const config: ForgeConfig = {
packagerConfig: {
@ -23,20 +40,24 @@ const config: ForgeConfig = {
appCopyright: 'Copyright 2016-2023 Balena Ltd',
darwinDarkModeSupport: true,
protocols: [{ name: 'etcher', schemes: ['etcher'] }],
// osxSign: {},
// osxNotarize: {},
extraResource: [
'lib/shared/catalina-sudo/sudo-askpass.osascript-zh.js',
'lib/shared/catalina-sudo/sudo-askpass.osascript-en.js',
],
osxSign: {
optionsForFile: () => ({
entitlements: './entitlements.mac.plist',
hardenedRuntime: true,
}),
},
...osxSigningConfig,
},
rebuildConfig: {},
makers: [
new MakerZIP(),
new MakerSquirrel({
setupIcon: 'assets/icon.ico',
...winSigningConfig,
}),
new MakerDMG({
background: './assets/dmg/background.tiff',
@ -85,43 +106,7 @@ const config: ForgeConfig = {
scripts: {
postinst: './after-install.tpl',
},
depends: [
'gconf-service',
'gconf2',
'libasound2',
'libatk1.0-0',
'libc6',
'libcairo2',
'libcups2',
'libdbus-1-3',
'libexpat1',
'libfontconfig1',
'libfreetype6',
'libgbm1',
'libgcc1',
'libgconf-2-4',
'libgdk-pixbuf2.0-0',
'libglib2.0-0',
'libgtk-3-0',
'liblzma5',
'libnotify4',
'libnspr4',
'libnss3',
'libpango1.0-0 | libpango-1.0-0',
'libstdc++6',
'libx11-6',
'libxcomposite1',
'libxcursor1',
'libxdamage1',
'libxext6',
'libxfixes3',
'libxi6',
'libxrandr2',
'libxrender1',
'libxss1',
'libxtst6',
'polkit-1-auth-agent | policykit-1-gnome | polkit-kde-1',
],
depends: hostDependencies['debian'],
},
}),
],
@ -145,6 +130,27 @@ const config: ForgeConfig = {
},
}),
],
hooks: {
readPackageJson: async (_config, packageJson) => {
packageJson.analytics = {};
if (process.env.SENTRY_TOKEN) {
packageJson.analytics.sentry = {
token: process.env.SENTRY_TOKEN,
};
}
if (process.env.AMPLITUDE_TOKEN) {
packageJson.analytics.amplitude = {
token: 'balena-etcher',
};
}
// packageJson.packageType = 'dmg' | 'AppImage' | 'rpm' | 'deb' | 'zip' | 'nsis' | 'portable'
return packageJson;
},
},
};
export default config;

View File

@ -16,10 +16,6 @@
"scripts": {
"build:rebuild-mountutils": "cd node_modules/mountutils && npm rebuild",
"build:sidecar": "npm run build:rebuild-mountutils && tsc --project tsconfig.sidecar.json && pkg build/util/api.js -c pkg-sidecar.json --target node18 --output generated/etcher-util",
"flowzone-preinstall-linux": "sudo apt-get update && sudo apt-get install -y xvfb libudev-dev && cat < electron-builder.yml | yq e .deb.depends[] - | xargs -L1 echo | sed 's/|//g' | xargs -L1 sudo apt-get --ignore-missing install || true",
"flowzone-preinstall-macos": "true",
"flowzone-preinstall-windows": "npx node-gyp install",
"flowzone-preinstall": "npm run flowzone-preinstall-linux",
"lint-css": "prettier --write lib/**/*.css",
"lint-ts": "balena-lint --fix --typescript typings lib tests forge.config.ts webpack.config.ts",
"lint": "npm run lint-ts && npm run lint-css",
@ -124,6 +120,45 @@
"typescript": "4.4.4",
"url-loader": "4.1.1"
},
"hostDependencies": {
"debian": [
"gconf-service",
"gconf2",
"libasound2",
"libatk1.0-0",
"libc6",
"libcairo2",
"libcups2",
"libdbus-1-3",
"libexpat1",
"libfontconfig1",
"libfreetype6",
"libgbm1",
"libgcc1",
"libgconf-2-4",
"libgdk-pixbuf2.0-0",
"libglib2.0-0",
"libgtk-3-0",
"liblzma5",
"libnotify4",
"libnspr4",
"libnss3",
"libpango1.0-0 | libpango-1.0-0",
"libstdc++6",
"libx11-6",
"libxcomposite1",
"libxcursor1",
"libxdamage1",
"libxext6",
"libxfixes3",
"libxi6",
"libxrandr2",
"libxrender1",
"libxss1",
"libxtst6",
"polkit-1-auth-agent | policykit-1-gnome | polkit-kde-1"
]
},
"engines": {
"node": ">=18 <20"
},