Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
287 lines
7.2 KiB
TypeScript
287 lines
7.2 KiB
TypeScript
// credits: migrated from https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/providers/GitHubProvider.ts
|
|
|
|
import {
|
|
CancellationToken,
|
|
type CustomPublishOptions,
|
|
newError,
|
|
type UpdateInfo,
|
|
} from 'builder-util-runtime';
|
|
import type { AppUpdater, ResolvedUpdateFileInfo } from 'electron-updater';
|
|
import { Provider } from 'electron-updater';
|
|
import type { ProviderRuntimeOptions } from 'electron-updater/out/providers/Provider';
|
|
import {
|
|
getFileList,
|
|
parseUpdateInfo,
|
|
} from 'electron-updater/out/providers/Provider';
|
|
|
|
import type { buildType } from '../config';
|
|
import { isSquirrelBuild } from './utils';
|
|
|
|
interface GithubUpdateInfo extends UpdateInfo {
|
|
tag: string;
|
|
}
|
|
|
|
interface GithubRelease {
|
|
url: string;
|
|
name: string;
|
|
tag_name: string;
|
|
body: string;
|
|
draft: boolean;
|
|
prerelease: boolean;
|
|
created_at: string;
|
|
published_at: string;
|
|
assets: Array<{
|
|
name: string;
|
|
url: string;
|
|
size: number;
|
|
}>;
|
|
}
|
|
|
|
interface UpdateProviderOptions {
|
|
feedUrl?: string;
|
|
channel: typeof buildType;
|
|
}
|
|
|
|
export class AFFiNEUpdateProvider extends Provider<GithubUpdateInfo> {
|
|
static configFeed(options: UpdateProviderOptions): CustomPublishOptions {
|
|
return {
|
|
provider: 'custom',
|
|
feedUrl: 'https://affine.pro/api/worker/releases',
|
|
updateProvider: AFFiNEUpdateProvider,
|
|
...options,
|
|
};
|
|
}
|
|
|
|
constructor(
|
|
private readonly options: CustomPublishOptions,
|
|
_updater: AppUpdater,
|
|
runtimeOptions: ProviderRuntimeOptions
|
|
) {
|
|
super(runtimeOptions);
|
|
}
|
|
|
|
get feedUrl(): URL {
|
|
const url = new URL(this.options.feedUrl);
|
|
url.searchParams.set('channel', this.options.channel);
|
|
url.searchParams.set('minimal', 'true');
|
|
|
|
return url;
|
|
}
|
|
|
|
async getLatestVersion(): Promise<GithubUpdateInfo> {
|
|
const cancellationToken = new CancellationToken();
|
|
|
|
const releasesJsonStr = await this.httpRequest(
|
|
this.feedUrl,
|
|
{
|
|
accept: 'application/json',
|
|
'cache-control': 'no-cache',
|
|
},
|
|
cancellationToken
|
|
);
|
|
|
|
if (!releasesJsonStr) {
|
|
throw new Error(
|
|
`Failed to get releases from ${this.feedUrl.toString()}, response is empty`
|
|
);
|
|
}
|
|
|
|
const releases = JSON.parse(releasesJsonStr);
|
|
|
|
if (releases.length === 0) {
|
|
throw new Error(
|
|
`No published versions in channel ${this.options.channel}`
|
|
);
|
|
}
|
|
|
|
const latestRelease = releases[0] as GithubRelease;
|
|
const tag = latestRelease.tag_name;
|
|
|
|
const channelFileName = 'latest.yml';
|
|
const channelFileAsset = latestRelease.assets.find(
|
|
({ name }) => name === channelFileName
|
|
);
|
|
|
|
// TODO(@forehalo):
|
|
// we need a way to let UI thread prompt user to manually install the latest version,
|
|
// if we introduce breaking changes on auto updater in the future.
|
|
// for example we rename the release file from `latest.yml` to `release.yml`
|
|
if (!channelFileAsset) {
|
|
throw newError(
|
|
`Cannot find ${channelFileName} in the latest release artifacts.`,
|
|
'ERR_UPDATER_CHANNEL_FILE_NOT_FOUND'
|
|
);
|
|
}
|
|
|
|
const channelFileUrl = new URL(channelFileAsset.url);
|
|
const channelFileContent = await this.httpRequest(channelFileUrl);
|
|
|
|
const result = parseUpdateInfo(
|
|
channelFileContent,
|
|
channelFileName,
|
|
channelFileUrl
|
|
);
|
|
|
|
result.files
|
|
.filter(({ url }) =>
|
|
availableForMyPlatformAndInstaller(
|
|
url,
|
|
process.platform,
|
|
process.arch,
|
|
isSquirrelBuild()
|
|
)
|
|
)
|
|
.forEach(file => {
|
|
const asset = latestRelease.assets.find(
|
|
({ name }) => name === file.url
|
|
);
|
|
if (asset) {
|
|
file.url = asset.url;
|
|
}
|
|
});
|
|
|
|
if (result.releaseName == null) {
|
|
result.releaseName = latestRelease.name;
|
|
}
|
|
|
|
if (result.releaseNotes == null) {
|
|
result.releaseNotes = latestRelease.body;
|
|
}
|
|
|
|
return {
|
|
tag: tag,
|
|
...result,
|
|
};
|
|
}
|
|
|
|
resolveFiles(updateInfo: GithubUpdateInfo): Array<ResolvedUpdateFileInfo> {
|
|
const files = getFileList(updateInfo);
|
|
|
|
return files
|
|
.filter(({ url }) =>
|
|
availableForMyPlatformAndInstaller(
|
|
url,
|
|
process.platform,
|
|
process.arch,
|
|
isSquirrelBuild()
|
|
)
|
|
)
|
|
.map(file => ({
|
|
url: new URL(file.url),
|
|
info: file,
|
|
}));
|
|
}
|
|
}
|
|
|
|
type VersionDistribution = 'canary' | 'beta' | 'stable';
|
|
type VersionPlatform = 'windows' | 'macos' | 'linux';
|
|
type VersionArch = 'x64' | 'arm64';
|
|
type FileParts =
|
|
| ['affine', string, VersionDistribution, VersionPlatform, VersionArch]
|
|
| [
|
|
'affine',
|
|
string,
|
|
`${'canary' | 'beta'}.${number}`,
|
|
VersionDistribution,
|
|
VersionPlatform,
|
|
VersionArch,
|
|
];
|
|
|
|
export function availableForMyPlatformAndInstaller(
|
|
file: string,
|
|
platform: NodeJS.Platform,
|
|
arch: NodeJS.Architecture,
|
|
// moved to parameter to make test coverage easier
|
|
imWindowsSquirrelPkg: boolean
|
|
): boolean {
|
|
const imArm64 = arch === 'arm64';
|
|
const imX64 = arch === 'x64';
|
|
const imMacos = platform === 'darwin';
|
|
const imWindows = platform === 'win32';
|
|
const imLinux = platform === 'linux';
|
|
|
|
// in form of:
|
|
// affine-${build}-${buildSuffix}-${distribution}-${platform}-${arch}.${installer}
|
|
// ^ 1.0.0 ^canary.1 ^ canary ^windows ^ x64 ^.nsis.exe
|
|
const filename = file.split('/').pop();
|
|
|
|
if (!filename) {
|
|
return false;
|
|
}
|
|
|
|
const parts = filename.split('-') as FileParts;
|
|
|
|
// fix -${arch}.${installer}
|
|
const archDotInstaller = parts[parts.length - 1];
|
|
const installerIdx = archDotInstaller.indexOf('.');
|
|
if (installerIdx === -1) {
|
|
return false;
|
|
}
|
|
const installer = archDotInstaller.substring(installerIdx + 1);
|
|
parts[parts.length - 1] = archDotInstaller.substring(0, installerIdx);
|
|
|
|
let version: {
|
|
build: string;
|
|
suffix?: string;
|
|
distribution: VersionDistribution;
|
|
platform: VersionPlatform;
|
|
arch: VersionArch;
|
|
installer: string;
|
|
};
|
|
|
|
if (parts.length === 5) {
|
|
version = {
|
|
build: parts[1],
|
|
distribution: parts[2],
|
|
platform: parts[3],
|
|
arch: parts[4],
|
|
installer,
|
|
};
|
|
} else if (parts.length === 6) {
|
|
version = {
|
|
build: parts[1],
|
|
suffix: parts[2],
|
|
distribution: parts[3],
|
|
platform: parts[4],
|
|
arch: parts[5],
|
|
installer,
|
|
};
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
function matchPlatform(platform: VersionPlatform) {
|
|
return (
|
|
(platform === 'windows' && imWindows) ||
|
|
(platform === 'macos' && imMacos) ||
|
|
(platform === 'linux' && imLinux)
|
|
);
|
|
}
|
|
|
|
function matchArch(arch: VersionArch) {
|
|
return (
|
|
// off course we can install x64 on x64
|
|
(imX64 && arch === 'x64') ||
|
|
// arm64 macos can install arm64 or x64 in rosetta2
|
|
(imArm64 && (arch === 'arm64' || imMacos))
|
|
);
|
|
}
|
|
|
|
function matchInstaller(installer: string) {
|
|
// do not allow squirrel or nsis installer to cross download each other on windows
|
|
if (!imWindows) {
|
|
return true;
|
|
}
|
|
|
|
return imWindowsSquirrelPkg
|
|
? installer === 'exe'
|
|
: installer === 'nsis.exe';
|
|
}
|
|
|
|
return (
|
|
matchPlatform(version.platform) &&
|
|
matchArch(version.arch) &&
|
|
matchInstaller(version.installer)
|
|
);
|
|
}
|