mirror of
https://github.com/beekeeper-studio/beekeeper-studio.git
synced 2026-03-13 10:12:54 +08:00
# Conflicts: # .github/workflows/studio-build-non-production.yml # .github/workflows/studio-publish.yml
514 lines
19 KiB
YAML
514 lines
19 KiB
YAML
name: Studio - Build & Publish
|
|
|
|
permissions:
|
|
contents: read
|
|
actions: write
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
paths-ignore:
|
|
- "apps/sqltools/**"
|
|
jobs:
|
|
create_draft_release:
|
|
runs-on: "ubuntu-22.04"
|
|
outputs:
|
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
assets_url: ${{ steps.create_release.outputs.assets_url }}
|
|
id: ${{ steps.create_release.outputs.id }}
|
|
json: ${{ steps.create_release.outputs.json }}
|
|
azure_client_id: ${{ steps.azure_creds.outputs.client_id }}
|
|
azure_tenant_id: ${{ steps.azure_creds.outputs.tenant_id }}
|
|
azure_keyvault_url: ${{ steps.azure_creds.outputs.keyvault_url }}
|
|
steps:
|
|
- name: Check out Git repository
|
|
uses: actions/checkout@v1
|
|
|
|
# Requires us to use a github token with more power (to create drafts on another repo)
|
|
- name: Create or check draft release
|
|
id: create_release
|
|
uses: actions/github-script@v7
|
|
env:
|
|
TAG_NAME: ${{ github.ref_name }}
|
|
OWNER: 'beekeeper-studio'
|
|
REPO: 'beekeeper-studio'
|
|
with:
|
|
github-token: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
script: |
|
|
const script = require('./.github/scripts/create_draft_release.js')
|
|
await script({github, context, core}, process.env.OWNER, process.env.REPO, process.env.TAG_NAME)
|
|
|
|
- name: Extract Azure credentials from existing secrets
|
|
id: azure_creds
|
|
env:
|
|
KEYVAULT_AUTH: "${{secrets.keyvault_auth}}"
|
|
run: |
|
|
CLIENT_ID=$(echo "$KEYVAULT_AUTH" | jq -r '.id')
|
|
TENANT_ID=$(echo "$KEYVAULT_AUTH" | jq -r '.tenant')
|
|
KEYVAULT_URL=$(echo "$KEYVAULT_AUTH" | jq -r '.url')
|
|
echo "client_id=$CLIENT_ID" >> $GITHUB_OUTPUT
|
|
echo "tenant_id=$TENANT_ID" >> $GITHUB_OUTPUT
|
|
echo "keyvault_url=$KEYVAULT_URL" >> $GITHUB_OUTPUT
|
|
# electron-builder comes built in with channels -- latest, beta, alpha.
|
|
# To support these for deb, rpm, and snap, we need to extract the channel from the package version
|
|
# outputs: latest, beta, alpha
|
|
# deb_codename: stable, beta
|
|
identify_channel:
|
|
runs-on: "ubuntu-22.04"
|
|
outputs:
|
|
channel: ${{steps.extract_channel.outputs.channel}}
|
|
deb_codename: ${{steps.extract_channel.outputs.deb_codename}}
|
|
steps:
|
|
- name: Check out Git repository
|
|
uses: actions/checkout@v1
|
|
- name: Extract Channel from package.json
|
|
id: extract_channel
|
|
run: bash ./.github/scripts/extract_channel.sh
|
|
|
|
release:
|
|
runs-on: ${{ matrix.os.name }}
|
|
needs: [create_draft_release, identify_channel]
|
|
outputs:
|
|
mac_x64_yml: ${{ steps.set_yaml.outputs.mac_x64_yml }}
|
|
mac_arm64_yml: ${{ steps.set_yaml.outputs.mac_arm64_yml }}
|
|
env:
|
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }}
|
|
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os:
|
|
- name: ubuntu-22.04
|
|
arch: x64
|
|
type: linux
|
|
setup_python: true
|
|
setup_ruby: true
|
|
setup_flatpak: true
|
|
- name: windows-2022
|
|
arch: x64
|
|
type: windows
|
|
setup_python: true
|
|
- name: ubuntu-22.04-arm
|
|
arch: arm64
|
|
type: linux
|
|
setup_python: true
|
|
setup_ruby: true
|
|
setup_flatpak: true
|
|
- name: macos-14-large
|
|
arch: x64
|
|
type: macos
|
|
- name: macos-14
|
|
arch: arm64
|
|
type: macos
|
|
steps:
|
|
|
|
- name: Check out Git repository
|
|
uses: actions/checkout@v1
|
|
|
|
- name: Install flatpak tools
|
|
if: matrix.os.setup_flatpak
|
|
run: bash ./.github/scripts/install-build-deps.sh
|
|
|
|
- name: Install azuresigntool
|
|
run: 'dotnet tool install --global AzureSignTool --version 6.0.1'
|
|
if: matrix.os.type == 'windows'
|
|
|
|
- name: Azure Login
|
|
if: matrix.os.type == 'windows'
|
|
uses: azure/login@v2
|
|
with:
|
|
creds: '{"clientId":"${{ needs.create_draft_release.outputs.azure_client_id }}","clientSecret":"${{ secrets.keyvault_auth_secret }}","subscriptionId":"${{ secrets.azure_subscription_id }}","tenantId":"${{ needs.create_draft_release.outputs.azure_tenant_id }}"}'
|
|
|
|
- uses: ruby/setup-ruby@v1
|
|
with:
|
|
ruby-version: 3.0.2
|
|
if: matrix.os.setup_ruby
|
|
|
|
- name: Install fpm
|
|
run: sudo gem install fpm -v 1.17.0
|
|
if: matrix.os.setup_ruby
|
|
|
|
- name: Update repository
|
|
env:
|
|
OWNER: 'beekeeper-studio'
|
|
REPO: 'beekeeper-studio'
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const fs = require('fs')
|
|
const path = `apps/studio/package.json`
|
|
const githubUrl = `git@github.com/${process.env.OWNER}/${process.env.REPO}.git`
|
|
|
|
const packageJson = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
|
|
// Update the 'repository' field
|
|
packageJson.repository = githubUrl;
|
|
|
|
// Write the updated package.json back to the file
|
|
fs.writeFileSync(path, JSON.stringify(packageJson, null, 2) + '\n');
|
|
|
|
|
|
- name: Add msbuild to PATH
|
|
uses: microsoft/setup-msbuild@v1.1
|
|
with:
|
|
vs-version: '16.11.14'
|
|
if: matrix.os.type == 'windows'
|
|
|
|
|
|
- name: "Install python 3.11 (NOT Mac)"
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: 3.11
|
|
if: matrix.os.setup_python
|
|
|
|
- name: Install python 3.11 (mac arm64)
|
|
if: matrix.os.type == 'macos'
|
|
id: mac_python
|
|
env:
|
|
ARCH: ${{ matrix.os.arch }}
|
|
run: |
|
|
rm -f /usr/local/bin/2to3-3.11
|
|
rm -f /usr/local/bin/idle3.11
|
|
rm -f /usr/local/bin/pydoc3.11
|
|
rm -f /usr/local/bin/python3.11
|
|
rm -f /usr/local/bin/python3.11-config
|
|
brew install python@3.11
|
|
if [ "$ARCH" = "arm64" ]; then
|
|
# Add Python 3.11 to PATH for arm64
|
|
echo "/opt/homebrew/opt/python@3.11/libexec/bin" >> $GITHUB_PATH
|
|
export PATH="/opt/homebrew/opt/python@3.11/libexec/bin:$PATH"
|
|
export PYTHON_PATH="/opt/homebrew/opt/python@3.11/libexec/bin/python3"
|
|
echo "python_path=$PYTHON_PATH" >> $GITHUB_OUTPUT
|
|
else
|
|
# Add Python 3.11 to PATH for other architectures
|
|
echo "/usr/local/opt/python@3.11/libexec/bin" >> $GITHUB_PATH
|
|
export PATH="/usr/local/opt/python@3.11/libexec/bin:$PATH"
|
|
export PYTHON_PATH="/usr/local/opt/python@3.11/libexec/bin/python3"
|
|
echo "python_path=$PYTHON_PATH" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Verify Python Version
|
|
if: matrix.os.type == 'windows'
|
|
run: |
|
|
$PYTHON_VERSION = python --version | Out-String
|
|
Write-Output "Installed Python version: $PYTHON_VERSION"
|
|
if (-not $PYTHON_VERSION.StartsWith("Python 3.11")) {
|
|
Write-Output "Error: Python version does not start with 3.11"
|
|
exit 1
|
|
}
|
|
shell: powershell
|
|
|
|
# Verify on everything except windows
|
|
- name: Verify Python Version
|
|
if: matrix.os.type != 'windows'
|
|
run: |
|
|
PYTHON_VERSION=$(python3 --version | cut -d " " -f 2)
|
|
echo "Installed Python version: $PYTHON_VERSION"
|
|
if [[ ! $PYTHON_VERSION == 3.11* ]]; then
|
|
echo "Error: Python version does not start with 3.11"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Install Node.js, NPM and Yarn
|
|
uses: actions/setup-node@v3
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
|
|
- name: Cache node_modules
|
|
id: cache-nm
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
node_modules
|
|
apps/studio/node_modules
|
|
apps/ui-kit/node_modules
|
|
apps/sqltools/node_modules
|
|
key: node-modules-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc') }}-${{ hashFiles('yarn.lock') }}
|
|
|
|
- name: Install Snapcraft
|
|
uses: samuelmeuli/action-snapcraft@v3
|
|
if: matrix.os.type == 'linux'
|
|
|
|
- name: Clean cache
|
|
if: steps.cache-nm.outputs.cache-hit != 'true'
|
|
run: yarn cache clean --all
|
|
|
|
# FIXME (matthew) Windows needs retries. It sometimes fails to build
|
|
# the native oracledb package.
|
|
# But only sometimes. I cannot figure out why.
|
|
# Someone should at some point.
|
|
- name: yarn install (with retry)
|
|
if: steps.cache-nm.outputs.cache-hit != 'true'
|
|
uses: nick-fields/retry@v2
|
|
with:
|
|
timeout_minutes: 20
|
|
max_attempts: 3
|
|
command: "yarn install --frozen-lockfile --network-timeout 100000"
|
|
env:
|
|
npm_config_node_gyp: ${{ github.workspace }}${{ runner.os == 'Windows' && '\node_modules\node-gyp\bin\node-gyp.js' || '/node_modules/node-gyp/bin/node-gyp.js' }}
|
|
|
|
- name: Remove dist directory
|
|
if: matrix.os.type != 'windows'
|
|
run: rm -rf ./apps/studio/dist_electron
|
|
|
|
- name: Remove dist directory windows
|
|
if: matrix.os.type == 'windows'
|
|
run: Remove-Item dist_electron -Recurse -ErrorAction Ignore
|
|
|
|
- name: Prepare for app notarization
|
|
if: matrix.os.type == 'macos'
|
|
# Import Apple API key for app notarization on macOS
|
|
run: |
|
|
mkdir -p ~/private_keys/
|
|
echo '${{ secrets.apple_key }}' > ~/private_keys/AuthKey_${{ secrets.apple_key_id }}.p8
|
|
|
|
- name: Decode macOS signing certificate
|
|
if: matrix.os.type == 'macos'
|
|
env:
|
|
DATA: ${{ secrets.mac_dev }}
|
|
run: |
|
|
echo "$DATA" | base64 --decode > ~/mac-certificate.p12
|
|
|
|
# Oh hello future Matthew, why do we split out windows build?
|
|
# well CSC_LINK gets picked up EVEN THOUGH YOU HAVE
|
|
# A CUSTOM sign.js. Obviously a bug, but this is a workaround
|
|
# Bug report: https://github.com/electron-userland/electron-builder/issues/8731
|
|
- name: Build & Publish (NT)
|
|
if: matrix.os.type == 'windows'
|
|
env:
|
|
KEYVAULT_URL: "${{ needs.create_draft_release.outputs.azure_keyvault_url }}"
|
|
KV_WIN_CERTIFICATE: "${{secrets.kv_win_certificate}}"
|
|
PYTHON_PATH: "${{'$PYTHON_PATH' }}"
|
|
PYTHONPATH: "${{ '$PYTHONPATH' }}"
|
|
GH_TOKEN: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
USE_SYSTEM_FPM: true
|
|
SNAPCRAFT_BUILD_ENVIRONMENT: 'lxd'
|
|
run: yarn run electron:build --publish always
|
|
|
|
- name: Build & Publish (*NIX)
|
|
if: matrix.os.type != 'windows'
|
|
env:
|
|
APPLE_ID: ${{ secrets.apple_id }}
|
|
APPLE_ID_PASSWORD: ${{ secrets.apple_id_password }}
|
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.apple_id_password }}
|
|
APPLE_TEAM_ID: "7KK583U8H2"
|
|
CSC_LINK: "~/mac-certificate.p12"
|
|
CSC_KEY_PASSWORD: ${{ secrets.mac_dev_pw }}
|
|
PYTHON_PATH: "${{ matrix.os.type == 'macos' && steps.mac_python.outputs.python_path || '$PYTHON_PATH' }}"
|
|
PYTHONPATH: "${{ matrix.os.type == 'macos' && steps.mac_python.outputs.python_path || '$PYTHONPATH' }}"
|
|
GH_TOKEN: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
USE_SYSTEM_FPM: true
|
|
SNAPCRAFT_BUILD_ENVIRONMENT: 'lxd'
|
|
run: yarn run electron:build --publish always
|
|
|
|
- name: Delete latest-mac.yml
|
|
if: matrix.os.type == 'macos'
|
|
uses: actions/github-script@v7
|
|
env:
|
|
RELEASE_ID: ${{needs.create_draft_release.outputs.id}}
|
|
with:
|
|
github-token: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
script: |
|
|
const script = require('./.github/scripts/delete_latest_yml.js')
|
|
const assetsUrl = '${{ needs.create_draft_release.outputs.assets_url }}'
|
|
const channel = '${{needs.identify_channel.outputs.channel}}'
|
|
await script({ github, core, context }, assetsUrl, channel)
|
|
|
|
- name: Invalidate cloudfront cache
|
|
if: matrix.os.type == 'linux' && needs.identify_channel.outputs.channel == 'latest'
|
|
run: |
|
|
aws cloudfront create-invalidation \
|
|
--distribution "$DISTRIBUTION" \
|
|
--paths "/*"
|
|
env:
|
|
AWS_ACCESS_KEY_ID: "${{secrets.aws_access_key_id}}"
|
|
AWS_SECRET_ACCESS_KEY: "${{secrets.aws_secret_access_key}}"
|
|
DISTRIBUTION: ${{secrets.cloudfront_distribution}}
|
|
AWS_DEFAULT_REGION: "us-east-1"
|
|
|
|
- name: Set yaml files
|
|
id: set_yaml
|
|
if: matrix.os.type == 'macos'
|
|
uses: actions/github-script@v7
|
|
env:
|
|
ARCH: ${{ matrix.os.arch }}
|
|
CHANNEL: ${{needs.identify_channel.outputs.channel}}
|
|
with:
|
|
github-token: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
script: |
|
|
const fs = require('fs')
|
|
const content = fs.readFileSync(`apps/studio/dist_electron/latest-mac.yml`, 'utf8')
|
|
core.setOutput(`mac_${process.env.ARCH}_yml`, content)
|
|
- name: Upload DEB/RPM artifacts
|
|
uses: actions/upload-artifact@v4
|
|
if: matrix.os.type == 'linux'
|
|
with:
|
|
name: "${{matrix.os.type}}-${{matrix.os.arch}}"
|
|
path: |
|
|
apps/studio/dist_electron/*.deb
|
|
apps/studio/dist_electron/*.rpm
|
|
|
|
- name: Cleanup artifacts
|
|
if: ${{!startsWith(matrix.os.name, 'windows')}}
|
|
run: npx rimraf "apps/studio/dist_electron/!(*.exe|*.deb|*.rpm|*.AppImage|*.dmg|*.snap|*.yml|*.flatpak|*.aur)"
|
|
|
|
- name: Cleanup artifacts Win
|
|
if: startsWith(matrix.os.name, 'windows')
|
|
shell: powershell
|
|
run: Get-ChildItem apps/studio/dist_electron -File | Where-Object { $_.Extension -notin '.exe','.yml' } | Remove-Item -Force
|
|
|
|
|
|
finalize_mac_yml:
|
|
needs: [release, create_draft_release, identify_channel]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/setup-node@v3
|
|
- run: npm install js-yaml
|
|
- name: Merge yml files
|
|
uses: actions/github-script@v7
|
|
env:
|
|
INTEL_YML: ${{ needs.release.outputs.mac_x64_yml }}
|
|
ARM_YML: ${{ needs.release.outputs.mac_arm64_yml }}
|
|
with:
|
|
github-token: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
script: |
|
|
const fs = require('fs');
|
|
const yaml = require('js-yaml');
|
|
const intelContent = process.env.INTEL_YML
|
|
const armContent = process.env.ARM_YML
|
|
|
|
const mergeFiles = () => {
|
|
const intelObject = yaml.load(intelContent);
|
|
const armObject = yaml.load(armContent);
|
|
|
|
const mergedObject = {
|
|
...intelObject,
|
|
};
|
|
|
|
mergedObject.files = [
|
|
...intelObject.files,
|
|
...armObject.files,
|
|
];
|
|
|
|
const dumpOptions = {
|
|
// avoids moving the sha512 checksum into its own line
|
|
lineWidth: -1,
|
|
};
|
|
|
|
const mergedString = yaml.dump(mergedObject, dumpOptions);
|
|
return mergedString;
|
|
};
|
|
|
|
const merge = mergeFiles();
|
|
fs.writeFileSync('mac.yml', merge, 'utf8');
|
|
- name: Upload fixed mac yml
|
|
uses: actions/github-script@v7
|
|
env:
|
|
RELEASE_ID: ${{ needs.create_draft_release.outputs.id }}
|
|
with:
|
|
github-token: ${{ secrets.GH_DEPLOY_TOKEN }}
|
|
script: |
|
|
const fs = require('fs');
|
|
const releaseId = process.env.RELEASE_ID;
|
|
|
|
// Read the merged yml file
|
|
const ymlContent = fs.readFileSync('mac.yml', 'utf8');
|
|
|
|
// Upload the asset using the release ID
|
|
await github.rest.repos.uploadReleaseAsset({
|
|
owner: 'beekeeper-studio',
|
|
repo: 'beekeeper-studio',
|
|
release_id: releaseId,
|
|
name: 'latest-mac.yml',
|
|
data: ymlContent,
|
|
headers: {
|
|
'content-type': 'application/octet-stream'
|
|
}
|
|
});
|
|
publish_snapcraft:
|
|
# The release pushes to edge. We need to promote the edge release if we have a stable release
|
|
needs: [release, identify_channel]
|
|
runs-on: ubuntu-latest
|
|
if: needs.identify_channel.outputs.channel == 'latest'
|
|
env:
|
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }}
|
|
steps:
|
|
- name: Install Snapcraft
|
|
uses: samuelmeuli/action-snapcraft@v3
|
|
- name: Move release snap from edge to stable
|
|
continue-on-error: true
|
|
run: |
|
|
snapcraft promote beekeeper-studio --from-channel "latest/edge" --to-channel "latest/stable" --yes
|
|
|
|
publish_repositories:
|
|
needs: [release, create_draft_release, identify_channel]
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Check out Git repository
|
|
uses: actions/checkout@v1
|
|
|
|
- uses: ruby/setup-ruby@v1
|
|
with:
|
|
ruby-version: 3.0.2
|
|
|
|
- name: Install dependencies for rpm deployment
|
|
run: sudo apt-get update && sudo apt-get install -y createrepo-c
|
|
|
|
- name: Install deb-s3 from GitHub
|
|
run: |
|
|
curl -sL https://github.com/deb-s3/deb-s3/releases/download/0.11.8/deb-s3-0.11.8.gem -o ./deb-s3.gem
|
|
gem install -N ./deb-s3.gem
|
|
|
|
- name: Import GPG key
|
|
id: import_gpg
|
|
uses: crazy-max/ghaction-import-gpg@v3
|
|
with:
|
|
gpg-private-key: ${{ secrets.gpg_key }}
|
|
|
|
- run: "rm -rf ./artifacts; mkdir artifacts"
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: ./artifacts
|
|
|
|
# Only publishing the DEB for the stable channel (for now)
|
|
- name: Publish DEB to R2
|
|
if: needs.identify_channel.outputs.channel == 'latest'
|
|
run: |
|
|
deb-s3 upload $(find ./artifacts -type f -name "*.deb") \
|
|
--bucket=beekeeper-deb-repo \
|
|
--codename=${{needs.identify_channel.outputs.deb_codename}} \
|
|
--endpoint=${{secrets.cloudflare_endpoint}} \
|
|
--lock \
|
|
--sign=${{steps.import_gpg.outputs.keyid}} \
|
|
--preserve-versions
|
|
env:
|
|
AWS_ACCESS_KEY_ID: "${{secrets.cloudflare_key_id}}"
|
|
AWS_SECRET_ACCESS_KEY: "${{secrets.cloudflare_secret_access_key}}"
|
|
AWS_DEFAULT_REGION: "us-east-1"
|
|
# 2025-01-15: AWS made changes to their clis that broke compatibility
|
|
# for third party services.
|
|
# This works around that.
|
|
# https://github.com/aws/aws-sdk-ruby/issues/3166
|
|
AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
|
|
AWS_RESPONSE_CHECKSUM_VALIDATION: WHEN_REQUIRED
|
|
|
|
- name: Publish RPM to R2
|
|
if: needs.identify_channel.outputs.channel == 'latest'
|
|
run: |
|
|
.github/scripts/publish_rpm.sh $(find ./artifacts -type f -name "*.rpm")
|
|
env:
|
|
GPG_KEY_ID: "${{steps.import_gpg.outputs.keyid}}"
|
|
R2_BUCKET: "beekeeper-rpm-repo/repo"
|
|
R2_ENDPOINT: ${{secrets.cloudflare_endpoint}}
|
|
AWS_ACCESS_KEY_ID: "${{secrets.cloudflare_key_id}}"
|
|
AWS_SECRET_ACCESS_KEY: "${{secrets.cloudflare_secret_access_key}}"
|
|
# 2025-01-15: AWS made changes to their clis that broke compatibility
|
|
# for third party services.
|
|
# This works around that.
|
|
# https://github.com/aws/aws-sdk-ruby/issues/3166
|
|
AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
|
|
AWS_RESPONSE_CHECKSUM_VALIDATION: WHEN_REQUIRED
|