Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
40 changes: 40 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,43 @@ jobs:
args: ${{ matrix.args }}
updaterJsonPath: latest.json
updaterJsonKeepUniversal: true

# The source repo is private, so its release assets 404 for the public.
# Mirror the built artifacts to a PUBLIC releases repo that the updater
# endpoint and the website download button point at.
- name: Mirror release to public repo
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # read the private release
RELEASES_TOKEN: ${{ secrets.RELEASES_TOKEN }} # write to the public repo
PUBLIC_REPO: mydevtools-tech/mydevtools-releases
run: |
set -euo pipefail
VERSION="$(node -p "require('./apps/desktop/src-tauri/tauri.conf.json').version")"
TAG="v$VERSION"
BUNDLE="apps/desktop/src-tauri/target/universal-apple-darwin/release/bundle"
DMG="$(ls "$BUNDLE"/dmg/*.dmg)"
TARGZ="$(ls "$BUNDLE"/macos/*.app.tar.gz)"
SIG="$(ls "$BUNDLE"/macos/*.app.tar.gz.sig)"

# Pull tauri's generated latest.json from the private release.
GH_TOKEN="$GITHUB_TOKEN" gh release download "$TAG" \
--repo "$GITHUB_REPOSITORY" --pattern latest.json --dir . --clobber

# Repoint its asset URLs at the public repo. The minisign signature
# signs the file bytes, not the URL, so it stays valid.
node -e '
const fs = require("fs");
const src = process.env.GITHUB_REPOSITORY, dst = process.env.PUBLIC_REPO;
const j = JSON.parse(fs.readFileSync("latest.json", "utf8"));
for (const k of Object.keys(j.platforms || {}))
j.platforms[k].url = j.platforms[k].url.split(src).join(dst);
fs.writeFileSync("latest.json", JSON.stringify(j, null, 2));
'

# Create the public release (once) and upload all four assets.
if ! GH_TOKEN="$RELEASES_TOKEN" gh release view "$TAG" --repo "$PUBLIC_REPO" >/dev/null 2>&1; then
GH_TOKEN="$RELEASES_TOKEN" gh release create "$TAG" --repo "$PUBLIC_REPO" \
--title "MyDevTools $TAG" --notes "MyDevTools desktop $TAG"
fi
GH_TOKEN="$RELEASES_TOKEN" gh release upload "$TAG" --repo "$PUBLIC_REPO" \
--clobber "$DMG" "$TARGZ" "$SIG" latest.json
33 changes: 33 additions & 0 deletions apps/desktop-ui/src/components/desktop/app-version-label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use client";

import { useEffect, useState } from "react";

import { isDesktop } from "@/lib/desktop/is-desktop";

/**
* Subtle running-app-version line for the sidebar footer. Reads the version
* baked into the bundle (tauri.conf.json) via the updater lib. Desktop-only —
* renders nothing on web.
*/
export function AppVersionLabel() {
const [version, setVersion] = useState("");

useEffect(() => {
if (!isDesktop()) return;
void import("@/lib/desktop/updater").then(async (m) => {
try {
setVersion(await m.currentAppVersion());
} catch {
// version API unavailable — just don't show it
}
});
}, []);

if (!isDesktop() || !version) return null;

return (
<p className="px-3 pb-1 pt-0.5 text-[11px] leading-none text-muted-foreground/60">
MyDevTools v{version}
</p>
);
}
2 changes: 2 additions & 0 deletions apps/desktop-ui/src/components/sidebar/app-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { NavGroup } from './nav-group'
import { NavUser } from './nav-user'
import { FeedbackDialog } from '@/components/feedback-dialog'
import { AppVersionLabel } from '@/components/desktop/app-version-label'
import { Logo } from '../logo'
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar'
import { LayoutDashboard, Sparkles } from 'lucide-react'
Expand Down Expand Up @@ -191,6 +192,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<SidebarFooter className="hidden md:block border-t border-border/30 dark:border-white/5">
<FeedbackDialog variant="sidebar" />
<NavUser user={user} onSignout={handleSignOut} />
<AppVersionLabel />
</SidebarFooter>
<SidebarRail />
</Sidebar>
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
},
"updater": {
"endpoints": ["https://github.com/itsmeakhil/mydevtools/releases/latest/download/latest.json"],
"endpoints": ["https://github.com/mydevtools-tech/mydevtools-releases/releases/latest/download/latest.json"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEIwQTI5NjQwRjg5MjgwNjQKUldSa2dKTDRRSmFpc0Rqc21XWGdNSGVPNlp4YVlVV0JWdFpJVk9kYzBIdkx2MUFHNHZWc1VKUmsK"
}
},
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/components/download-desktop-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { Button } from "@/components/ui/button";
* across releases without editing the site. Falls back to the releases page if
* the API is unreachable or no asset is found yet.
*/
const REPO = "itsmeakhil/mydevtools";
// Public releases-mirror repo (source repo is private, so its release assets
// aren't publicly downloadable). CI mirrors the DMG + latest.json here.
const REPO = "mydevtools-tech/mydevtools-releases";
const RELEASES_PAGE = `https://github.com/${REPO}/releases/latest`;

type Asset = { name: string; browser_download_url: string };
Expand Down