Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/release-notes/desktop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Grida Desktop — Technical Preview

This is a nightly release of Grida `{{version}}`.

- macOS (signed, notarized)
- darwin arm64 — Apple Silicon
- dmg · zip
- linux
- deb · rpm
- arm64 · x64
- windows (not signed)
- x64

[Join Slack](https://grida.co/join-slack) to learn more.

## Supported builds

| Name | Platform | x64 | arm64 | makers | signed | notes |
| ------- | -------- | --- | ----- | ---------------- | ------ | ------------------------------------------------------------------ |
| `Grida` | `darwin` | | ✓ | `zip`, `dmg` | ✓ | Apple Silicon only. Rosetta 2 cannot run arm64 binaries on Intel. |
| `Grida` | `win32` | ✓ | | `exe (squirrel)` | | x64 only / not signed |
| `Grida` | `linux` | ✓ | ✓ | `deb`, `rpm` | | |

## Auto-update

macOS installs receive updates automatically via [`update.electronjs.org`](https://github.com/electron/update.electronjs.org). The running app polls every 6 hours; when a newer release is available, a "Restart to update" prompt appears.

Releases marked **pre-release** on this page are *not* served to existing installs — they're for manual download / testing only.
13 changes: 13 additions & 0 deletions .github/workflows/realease-desktop-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,16 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: pnpm run publish:prerelease --arch="arm64"

- name: Update release notes from template
# Runs once (only on the macOS row) after the release tag has been
# created by the publisher. Template at .github/release-notes/desktop.md.
if: ${{ matrix.os.name == 'macos-apple-silicon' && env.HAS_SIGNING == 'true' && env.HAS_NOTARIZE == 'true' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run release-notes update even when mac signing is unavailable

The new release-notes step is conditioned on env.HAS_SIGNING == 'true' && env.HAS_NOTARIZE == 'true', so if Apple signing/notarization secrets are missing or temporarily unavailable, the Windows/Linux publish steps can still run and publish a release while this templating step is skipped. In that case the workflow reverts to empty/manual release bodies, which defeats the automation introduced here; this step should be gated only to a single matrix row (or another always-running path), not to mac signing availability.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — applied in e207677. Re-gated the step to matrix.os.name == 'linux' (no signing-secret dependency, always runs). The release tag exists by the time linux's publish step finishes, so the body edit is safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run release-notes update even when mac signing is unavailable

The new release-notes step is conditioned on env.HAS_SIGNING == 'true' && env.HAS_NOTARIZE == 'true', so if Apple signing/notarization secrets are missing or temporarily unavailable, the Windows/Linux publish steps can still run and publish a release while this templating step is skipped. In that case the workflow reverts to empty/manual release bodies, which defeats the automation introduced here; this step should be gated only to a single matrix row (or another always-running path), not to mac signing availability.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — applied in e207677. Re-gated the step to matrix.os.name == 'linux' (no signing-secret dependency, always runs). The release tag exists by the time linux's publish step finishes, so the body edit is safe. (duplicate thread — same fix applies)

working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=$(node -p "require('./desktop/package.json').version")
TAG="v${VERSION}"
BODY=$(sed "s|{{version}}|${VERSION}|g" .github/release-notes/desktop.md)
gh release edit "$TAG" --notes "$BODY" --repo gridaco/grida
95 changes: 74 additions & 21 deletions editor/app/(www)/(downloads)/downloads/downloads.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Octokit, type RestEndpointMethodTypes } from "@octokit/rest";
import { unstable_cache } from "next/cache";
import assert from "assert";

export namespace downloads {
Expand All @@ -7,9 +8,7 @@ export namespace downloads {
type GithubReleaseAsset = GithubReleaseAssets[number];

export interface DownloadLinks {
mac_dmg_x64: string;
mac_dmg_arm64: string;
mac_dmg_universal: string;
linux_deb_x64: string;
linux_rpm_x64: string;
linux_deb_arm64: string;
Expand All @@ -18,7 +17,7 @@ export namespace downloads {
}

export type Platform = "mac" | "windows" | "linux";
export type Arch = "x64" | "arm64" | "universal";
export type Arch = "x64" | "arm64";
export type Maker = "dmg" | "squirrel.windows" | "deb" | "rpm";

type Distro = {
Expand All @@ -27,7 +26,6 @@ export namespace downloads {
ext: "dmg" | "exe" | "deb" | "rpm";
arch: {
arm64?: string | undefined;
universal?: string | undefined;
x64?: string | undefined;
};
};
Expand All @@ -45,8 +43,6 @@ export namespace downloads {
ext: "dmg",
arch: {
arm64: "arm64",
universal: "universal",
x64: "x64",
},
},
],
Expand Down Expand Up @@ -97,19 +93,15 @@ export namespace downloads {
export async function getLinks(): Promise<DownloadLinks> {
const f = new Fetcher();

const mac_dmg_x64 = await f.getAsset("mac", "dmg", "x64");
const mac_dmg_arm64 = await f.getAsset("mac", "dmg", "arm64");
const mac_dmg_universal = await f.getAsset("mac", "dmg", "universal");
const linux_deb_x64 = await f.getAsset("linux", "deb", "x64");
const linux_rpm_x64 = await f.getAsset("linux", "rpm", "x64");
const linux_deb_arm64 = await f.getAsset("linux", "deb", "arm64");
const linux_rpm_arm64 = await f.getAsset("linux", "rpm", "arm64");
const windows_x64 = await f.getAsset("windows", "squirrel.windows", "x64");

return {
mac_dmg_x64: mac_dmg_x64.browser_download_url,
mac_dmg_arm64: mac_dmg_arm64.browser_download_url,
mac_dmg_universal: mac_dmg_universal.browser_download_url,
linux_deb_x64: linux_deb_x64.browser_download_url,
linux_rpm_x64: linux_rpm_x64.browser_download_url,
linux_deb_arm64: linux_deb_arm64.browser_download_url,
Expand All @@ -118,9 +110,78 @@ export namespace downloads {
};
}

export interface DownloadLinksForPage extends DownloadLinks {
default: {
platform: Platform;
maker: Maker;
arch: Arch;
url: string;
} | null;
}

function pickDefault(
os: Platform | null,
links: DownloadLinks
): DownloadLinksForPage["default"] {
switch (os) {
case "mac":
return {
platform: "mac",
maker: "dmg",
arch: "arm64",
url: links.mac_dmg_arm64,
};
case "windows":
return {
platform: "windows",
maker: "squirrel.windows",
arch: "x64",
url: links.windows_exe_x64,
};
case "linux":
return {
platform: "linux",
maker: "deb",
arch: "x64",
url: links.linux_deb_x64,
};
default:
return null;
}
}

// Cached wrapper around getLinks() — hits GitHub's unauthenticated
// releases endpoint at most ~once per hour per region, keeping the
// page out of trouble with the 60 req/hr/IP rate limit.
// `unstable_cache` only memoizes successful resolutions; on throw,
// the next render re-attempts (so transient API blips self-heal).
const getCachedLinks = unstable_cache(
() => getLinks(),
["downloads:getLinks"],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{
revalidate: 3600,
}
);

/**
* Page-bound helper: latest-release links + OS-aware default, cached.
* Falls back to the static v0.0.1 URLs if the GitHub API is unreachable.
*/
export async function getLinksForPage(
os: Platform | null
): Promise<DownloadLinksForPage> {
try {
const links = await getCachedLinks();
return { ...links, default: pickDefault(os, links) };
} catch {
return getLinks_v001(os);
}
}

/**
* @deprecated
* temporary static version of getLinks, until we find a reliable way to fetch release without rate limiting
* Static fallback used only when the GitHub API is unreachable.
* Prefer `getLinksForPage()`, which calls this internally on failure.
*/
export function getLinks_v001(
platform: Platform | null,
Expand All @@ -134,12 +195,8 @@ export namespace downloads {
} | null;
} {
const links: DownloadLinks = {
mac_dmg_x64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-x64.dmg",
mac_dmg_arm64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-arm64.dmg",
mac_dmg_universal:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-universal.dmg",
linux_deb_x64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/grida_0.0.1_amd64.deb",
linux_rpm_x64:
Expand All @@ -163,8 +220,8 @@ export namespace downloads {
d = {
platform: "mac",
maker: "dmg",
arch: "universal",
url: links.mac_dmg_universal,
arch: "arm64",
url: links.mac_dmg_arm64,
};
break;
}
Expand All @@ -189,12 +246,8 @@ export namespace downloads {

return {
default: d,
mac_dmg_x64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-x64.dmg",
mac_dmg_arm64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-arm64.dmg",
mac_dmg_universal:
"https://github.com/gridaco/grida/releases/download/v0.0.1/Grida-0.0.1-universal.dmg",
linux_deb_x64:
"https://github.com/gridaco/grida/releases/download/v0.0.1/grida_0.0.1_amd64.deb",
linux_rpm_x64:
Expand Down
16 changes: 1 addition & 15 deletions editor/app/(www)/(downloads)/downloads/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default async function DownloadsPage() {
const userAgent = headersList.get("user-agent");

const os = downloads.getDesktopOS(userAgent || "");
const links = await downloads.getLinks_v001(os);
const links = await downloads.getLinksForPage(os);

return (
<main className="min-h-screen flex flex-col">
Expand Down Expand Up @@ -96,27 +96,13 @@ export default async function DownloadsPage() {
function DownloadButtons({ links }: { links: downloads.DownloadLinks }) {
return (
<div className="container max-w-3xl flex flex-wrap justify-center gap-3 mx-auto">
{/* macOS Universal */}
<Link href={links.mac_dmg_universal} download>
<Button size="lg" variant="outline">
<Apple className="size-4" /> Download for macOS (Universal)
</Button>
</Link>

{/* macOS Apple Silicon */}
<Link href={links.mac_dmg_arm64} download>
<Button size="lg" variant="outline">
<Apple className="size-4" /> Download for macOS (Apple Silicon)
</Button>
</Link>

{/* macOS Intel */}
<Link href={links.mac_dmg_x64} download>
<Button size="lg" variant="outline">
<Apple className="size-4" /> Download for macOS (Intel-based Macs)
</Button>
</Link>

{/* Windows x64 */}
<Link href={links.windows_exe_x64} download>
<Button size="lg" variant="outline">
Expand Down
Loading