Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: github action을 활용한 release 준비 #46

Merged
merged 27 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
53624ec
윈도우 최소 창 사이즈
young-do Aug 20, 2024
9f45d51
시간 끝났을때 창 focus 될수있도록 준비
young-do Aug 20, 2024
5f6e5c5
트레이 적용
young-do Aug 20, 2024
344b310
알림 타이틀은 서비스 이름이 나오도록
young-do Aug 20, 2024
8964cf5
Merge remote-tracking branch 'origin/main' into feature/setup
young-do Aug 20, 2024
e48ed04
버전 및 앱 이름 수정
young-do Aug 20, 2024
0588ce0
icon들 적용
young-do Aug 20, 2024
e8c211c
앱 빌드시 한국어 패키지명으로 보이도록
young-do Aug 20, 2024
bc44c37
창에 앱 이름 안보이게
young-do Aug 20, 2024
ea8270d
Revert "앱 빌드시 한국어 패키지명으로 보이도록"
young-do Aug 20, 2024
13442b9
tray 아이콘은 dataurl 로 전달..
young-do Aug 20, 2024
1d6fb2a
빌드 후에도 devtool 열려서 비활성화
young-do Aug 21, 2024
20d6a40
창이 닫혔을때는 다시 창 만들도록
young-do Aug 21, 2024
19763d6
권한 묻는 팝업을 알림을 생성해서 뜨도록
young-do Aug 21, 2024
9a5a08d
고양이가 없을때만 다음 페이지가 선택페이지가 되도록
young-do Aug 21, 2024
2741241
Merge remote-tracking branch 'origin/main' into feature/setup
young-do Aug 21, 2024
803c0c5
일단 signing만 적용
young-do Aug 21, 2024
169e745
release를 위한 준비
young-do Aug 21, 2024
a6b427d
license 다시 복구
young-do Aug 21, 2024
24da8da
자동 업데이트 추가
young-do Aug 21, 2024
d76593c
tag 푸시될때 draft로 배포되도록
young-do Aug 21, 2024
6a29929
auto update dependencies로 이동
young-do Aug 21, 2024
bacb40c
host 값이 기본값으로 전달되지 않아 명시
young-do Aug 21, 2024
f71e2ad
서버 url 환경변수가 빌드시에 반영되도록
young-do Aug 21, 2024
cbe5ae2
0.0.1-alpha.4
young-do Aug 21, 2024
9114cbf
0.0.1-alpha.5
young-do Aug 21, 2024
cea68e4
Merge remote-tracking branch 'origin/main' into feature/ready-to-release
young-do Aug 21, 2024
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
67 changes: 67 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Release
# @see: https://dev.to/erikhofer/build-and-publish-a-multi-platform-electron-app-on-github-3lnd
on:
push:
tags:
- 'v*'

jobs:
publish_on_linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'yarn'
- name: install dependencies
run: yarn install
- name: publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_API_SERVER_URL: ${{ secrets.VITE_API_SERVER_URL }}
run: yarn run publish

publish_on_mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: install dependencies
run: yarn install
# see: https://dev.to/rwwagner90/signing-electron-apps-with-github-actions-4cof
- name: Add MacOS certs
run: chmod +x add-osx-cert.sh && ./add-osx-cert.sh
env:
CERTIFICATE_OSX_APPLICATION: ${{ secrets.CERTIFICATE_OSX_APPLICATION }}
CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
- name: Add api key file
run: |
echo "${{ secrets.APPLE_API_KEY_CONTENT }}" > authKey.p8
APPLE_API_KEY=$(realpath authKey.p8)
echo "APPLE_API_KEY=$APPLE_API_KEY" >> $GITHUB_ENV
- name: publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_API_SERVER_URL: ${{ secrets.VITE_API_SERVER_URL }}
APPLE_API_KEY: ${{ env.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: DEBUG=* yarn run publish

publish_on_win:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: install dependencies
run: yarn install
- name: publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_API_SERVER_URL: ${{ secrets.VITE_API_SERVER_URL }}
run: yarn run publish
24 changes: 24 additions & 0 deletions add-osx-cert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env sh

# see: https://dev.to/rwwagner90/signing-electron-apps-with-github-actions-4cof
KEY_CHAIN=build.keychain
CERTIFICATE_P12=certificate.p12

# Recreate the certificate from the secure environment variable
echo $CERTIFICATE_OSX_APPLICATION | 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
23 changes: 23 additions & 0 deletions forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ import { MakerZIP } from '@electron-forge/maker-zip';
import { FusesPlugin } from '@electron-forge/plugin-fuses';
import { VitePlugin } from '@electron-forge/plugin-vite';
import type { ForgeConfig } from '@electron-forge/shared-types';
import dotenv from 'dotenv';

dotenv.config();

const config: ForgeConfig = {
packagerConfig: {
asar: true,
icon: 'src/shared/assets/icons/icon',
osxSign: {},
osxNotarize: {
// @see: https://www.electronforge.io/guides/code-signing/code-signing-macos#option-2-using-an-app-store-connect-api-key
appleApiKey: process.env.APPLE_API_KEY as string,
appleApiKeyId: process.env.APPLE_API_KEY_ID as string,
appleApiIssuer: process.env.APPLE_API_ISSUER as string,
Comment on lines +20 to +22
Copy link
Member

Choose a reason for hiding this comment

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

요거 저도 알아야 할려나영 👀

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

실제 키값??? 일단 값은 github action에 저장되어있긴 함!
자세한지는 모르겠지만 아래 참고용!
https://www.electronforge.io/guides/code-signing/code-signing-macos#option-2-using-an-app-store-connect-api-key

},
},
rebuildConfig: {},
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
Expand Down Expand Up @@ -47,6 +58,18 @@ const config: ForgeConfig = {
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
publishers: [
{
name: '@electron-forge/publisher-github',
config: {
repository: {
owner: 'Nexters',
name: 'PomoNyang-fe',
},
draft: true,
},
},
],
};

export default config;
22 changes: 17 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "my-app",
"productName": "my-app",
"version": "1.0.0",
"description": "My Electron application description",
"name": "mohanyang",
"productName": "mohanyang",
"version": "0.0.1-alpha.5",
"description": "Pomodoro timer desktop app with cat",
"main": ".vite/build/main.js",
"scripts": {
"start": "electron-forge start",
Expand All @@ -20,13 +20,15 @@
"@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
"@electron-forge/plugin-fuses": "^7.4.0",
"@electron-forge/plugin-vite": "^7.4.0",
"@electron-forge/publisher-github": "^7.4.0",
"@electron/fuses": "^1.8.0",
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.16.1",
"autoprefixer": "^10.4.19",
"dotenv": "^16.4.5",
"electron": "31.2.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^9.1.0",
Expand All @@ -41,11 +43,20 @@
"vite": "^5.0.12",
"vite-plugin-svgr": "^4.2.0"
},
"keywords": [],
"author": {
"name": "leeharyung",
"email": "[email protected]"
},
"contributors": [
{
"name": "leeharyung",
"email": "[email protected]"
},
{
"name": "young-do cho",
"email": "[email protected]"
}
],
"license": "MIT",
"dependencies": {
"@lottiefiles/dotlottie-react": "^0.8.8",
Expand All @@ -72,6 +83,7 @@
"react-router-dom": "^6.25.0",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"update-electron-app": "^3.0.0",
"usehooks-ts": "^3.1.0",
"vaul": "^0.9.1"
}
Expand Down
69 changes: 64 additions & 5 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import { app, BrowserWindow, ipcMain, Menu, NativeImage, nativeImage, shell, Tray } from 'electron';
import path from 'path';

import { machineId } from 'node-machine-id';
import { updateElectronApp, UpdateSourceType } from 'update-electron-app';

updateElectronApp({
updateSource: {
type: UpdateSourceType.ElectronPublicUpdateService,
repo: 'Nexters/PomoNyang-fe',
host: 'https://update.electronjs.org',
},
});

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
Expand All @@ -15,6 +24,11 @@ const createWindow = () => {
preload: path.join(__dirname, 'preload.js'),
webSecurity: false,
},
width: 400,
minWidth: 400,
height: 800,
minHeight: 800,
title: '',
});

// and load the index.html of the app.
Expand All @@ -24,8 +38,8 @@ const createWindow = () => {
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}

// Open the DevTools.
mainWindow.webContents.openDevTools();
// // Open the DevTools.
// mainWindow.webContents.openDevTools();

// @note: 외부 링크 클릭 시 별도 브라우저로 열도록 설정함
// 외부 링크 여부는 url이 https://로 시작하는지로 판단함
Expand All @@ -36,12 +50,47 @@ const createWindow = () => {
}
return { action: 'allow' };
});

return mainWindow;
};

const trayIconMap: Record<string, string> = {
cat: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEuSURBVHgB1ZRNTsMwEIXfmBCxzBF8A1hCYeFIlC1HCNyAI7QnQNygR8i+IFcCdd0bEG7QJVRNhiENKM4PoemGvoXljD3fOOMZA/9dVDXw9MIAmYHnTyicJU1ObE811uoGjJiu5otWID+d34H5vlhJoPywCs1hmbIC0xuDCmn4Mvte99zQuC7NxXFl2ZoQ74XtSCZlWB6YIxlbgMRaNsOBpqtXHBbfqUKXund0S7cDGQF2VPWEPYAcNALZDk7QT1ouLqgBsWaDfgqQfpg6ELRz/lygkkLuq4ySBqAfy7jEtpKOKrffD1BabCll84DtNXb5FfHj2UTMEf4ixpiG89GvwHzfdDCSs0dOz7pe8jcHt3T5HNeX2oJvXhUjUKlPOhbLV37f5HWJ4XmLPEV7qU80zGLwaU45uQAAAABJRU5ErkJggg',
focus:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAF5SURBVHgBxZRBTsJAGIXfTEkJEWNJ3LiyG1dugJWCizbGvTcQb4AnQE+AnoB7iKRdaHRXbiBx44qAG40F+jttLdgMbYGNL5nm78ybN99kJgP8l+i+VibL0LN8HCuIevW2cDqYTTpZXpYaZBmaCLFEVV50cpOdPdrYiNCbtGJhAQK1sAlhSOeOls9it+z06QprEU6n5cQxoiZ1j5vrBeamA6SJsTZ1TwypW1rc3yqgMdMe0EPN37KGZI2hqBXfu5SQrCMdnuuIZoWrsz7SpYUHl0BIvVoHhAbWlaKWBOU4RhjQbRLmy3PPo3KxZY8biRMKe8D2AZIDoUdlDlkqVUSr/rqLwMiRPYz6MiH3bMnoU0VhQXh1OamSlwOZ+TIQXztm3DmUJxelwLvEawNldinOfT4IpkISzy9q36uo17Hhvz8BJffMeejnmxzoDiO3DR5c6nEsAwkSd7IBXrjAbt3A1n7Y+fUODJ9tfH/cpD1hmSKnqdNrR1/F+wOB4H1F8tsNAAAAAABJRU5ErkJggg==',
};
const getTrayIcon = (icon: string): NativeImage => {
return nativeImage.createFromDataURL(trayIconMap[icon] ?? trayIconMap['cat']);
};

const createTray = (mainWindow: BrowserWindow) => {
const tray = new Tray(getTrayIcon('cat'));
const contextMenu = Menu.buildFromTemplate([
{
label: '모하냥 열기',
click: () => {
if (BrowserWindow.getAllWindows().length === 0) {
mainWindow = createWindow();
}
mainWindow?.show();
},
},
{ type: 'separator' },
{ label: '종료', role: 'quit' },
]);
tray.setContextMenu(contextMenu);
return tray;
};

let mainWindow: BrowserWindow | null = null;
let tray: Tray | null = null;

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
app.on('ready', () => {
mainWindow = createWindow();
});

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
Expand All @@ -56,15 +105,25 @@ app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
mainWindow = createWindow();
}
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
app.whenReady().then(() => {
if (mainWindow) {
tray = createTray(mainWindow);
}

// event handling
ipcMain.handle('get-machine-id', async () => {
return await machineId(true);
});
ipcMain.on('show-window', () => {
mainWindow?.show();
});
ipcMain.handle('change-tray-icon', (event, icon: string) => {
tray?.setImage(getTrayIcon(icon));
});
});
2 changes: 2 additions & 0 deletions src/preload/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { contextBridge, ipcRenderer } from 'electron';
import { IElectronAPI } from '../shared/type';

const electronAPI: IElectronAPI = {
showWindow: () => ipcRenderer.send('show-window'),
getMachineId: () => ipcRenderer.invoke('get-machine-id'),
changeTrayIcon: (icon: string) => ipcRenderer.invoke('change-tray-icon', icon),
};

contextBridge.exposeInMainWorld('electronAPI', electronAPI);
6 changes: 3 additions & 3 deletions src/renderer/pages/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { cn } from '@/shared/utils';

const Home = () => {
const navigate = useNavigate();
const [isCompleted] = useLocalStorage(LOCAL_STORAGE_KEY.ONBOARDING_COMPLETED, false);
const [isCompletedOnboarding] = useLocalStorage(LOCAL_STORAGE_KEY.ONBOARDING_COMPLETED, false);
const [isMinTimePassed, setIsMinTimePassed] = useState(false);
const { data: user } = useUser();

Expand All @@ -19,10 +19,10 @@ const Home = () => {

useEffect(() => {
if (!user) return;
if (!isCompleted) return navigate(PATH.ONBOARDING);
if (!isCompletedOnboarding) return navigate(PATH.ONBOARDING);
if (!user.cat) return navigate(PATH.SELECTION);
navigate(PATH.POMODORO);
}, [isCompleted, user]);
}, [isCompletedOnboarding, user]);

return (
<div
Expand Down
5 changes: 4 additions & 1 deletion src/renderer/pages/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Autoplay from 'embla-carousel-autoplay';
import { useNavigate } from 'react-router-dom';
import { useLocalStorage } from 'usehooks-ts';

import { useUser } from '@/features/user';
import onboardingImage1 from '@/shared/assets/images/onboarding-1.png';
import onboardingImage2 from '@/shared/assets/images/onboarding-2.png';
import onboardingImage3 from '@/shared/assets/images/onboarding-3.png';
Expand Down Expand Up @@ -48,6 +49,7 @@ const contents = [
const OnboardingContent = () => {
const { currentIndex } = useCarousel();
const navigate = useNavigate();
const { data: user } = useUser();
const [, setIsCompleted] = useLocalStorage(LOCAL_STORAGE_KEY.ONBOARDING_COMPLETED, false);

return (
Expand Down Expand Up @@ -96,7 +98,8 @@ const OnboardingContent = () => {
className="w-[200px]"
onClick={() => {
setIsCompleted(true);
navigate(PATH.SELECTION);
// @note: 고양이가 없을때만 다음 페이지가 선택페이지가 되도록
navigate(!user?.cat ? PATH.SELECTION : PATH.POMODORO);
}}
>
시작하기
Expand Down
11 changes: 8 additions & 3 deletions src/renderer/shared/hooks/use-notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ export const useNotification = () => {

const permission = await Notification.requestPermission();
setPermission(permission);
// @note: notification이 처음 생성되었을때 권한여부를 물어본다.
createNotification('반갑다냥');
return permission;
};

// TODO: 아이콘도 넣고 개선 필요
const createNotification = (title: string, options?: NotificationOptions) => {
// TODO: 아이콘도 넣고 개선 필요 -> 앱아이콘이 설정되면 알아서 보임
const createNotification = (body: string, options?: NotificationOptions) => {
if (permission === 'granted') {
return new Notification(title, options);
return new Notification('모하냥', {
body,
...options,
});
}
};

Expand Down
Binary file added src/shared/assets/icons/icon.icns
Binary file not shown.
Binary file added src/shared/assets/icons/icon.ico
Binary file not shown.
Binary file added src/shared/assets/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/shared/assets/icons/[email protected]
Binary file not shown.
Binary file added src/shared/assets/icons/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/shared/type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// @see: https://www.electronjs.org/docs/latest/tutorial/context-isolation#usage-with-typescript
export interface IElectronAPI {
showWindow: () => void;
changeTrayIcon: (icon: string) => void;
getMachineId: () => Promise<string>;
}
Loading