Compare commits
3 Commits
fc33a9c051
...
0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4b627b6bb | ||
|
|
921c4a29a7 | ||
|
|
5e633d9dd6 |
140
bin/uploadArtifacts.js
Normal file
140
bin/uploadArtifacts.js
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { execSync } from 'child_process'
|
||||||
|
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
|
dotenv.config({ quiet: true })
|
||||||
|
|
||||||
|
const DIST_DIR = path.resolve('dist')
|
||||||
|
const BUCKET = 'dist'
|
||||||
|
|
||||||
|
const client = new S3Client({
|
||||||
|
region: 'us-east-1',
|
||||||
|
endpoint: 'https://s3.takerofnotes.com',
|
||||||
|
forcePathStyle: true,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function getContentType(file) {
|
||||||
|
if (file.endsWith('.yml')) return 'text/yaml'
|
||||||
|
if (file.endsWith('.json')) return 'application/json'
|
||||||
|
if (file.endsWith('.AppImage')) return 'application/octet-stream'
|
||||||
|
if (file.endsWith('.deb')) return 'application/vnd.debian.binary-package'
|
||||||
|
if (file.endsWith('.exe'))
|
||||||
|
return 'application/vnd.microsoft.portable-executable'
|
||||||
|
if (file.endsWith('.dmg')) return 'application/x-apple-diskimage'
|
||||||
|
return 'application/octet-stream'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentTag() {
|
||||||
|
try {
|
||||||
|
const tag = execSync('git describe --tags --exact-match 2>/dev/null', {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
}).trim()
|
||||||
|
return tag
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGitTagDirty() {
|
||||||
|
try {
|
||||||
|
const status = execSync('git status --porcelain', {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
}).trim()
|
||||||
|
return status.length > 0
|
||||||
|
} catch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadFile(filePath, platform, version) {
|
||||||
|
const fileStream = fs.createReadStream(filePath)
|
||||||
|
const fileName = path.basename(filePath)
|
||||||
|
const key = `${platform}/${version}/${fileName}`
|
||||||
|
|
||||||
|
console.log(`Uploading ${fileName} → ${key}`)
|
||||||
|
|
||||||
|
await client.send(
|
||||||
|
new PutObjectCommand({
|
||||||
|
Bucket: BUCKET,
|
||||||
|
Key: key,
|
||||||
|
Body: fileStream,
|
||||||
|
ContentType: getContentType(fileName),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const platform = process.argv[2]
|
||||||
|
const version = process.argv[3]
|
||||||
|
|
||||||
|
if (!platform || !version) {
|
||||||
|
console.error('Usage: node uploadArtifacts.js <platform> <version>')
|
||||||
|
console.error('Example: node uploadArtifacts.js win 1.0.0')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTag = getCurrentTag()
|
||||||
|
if (!currentTag) {
|
||||||
|
console.log(
|
||||||
|
'No git tag found. Skipping upload (artifacts are only uploaded on tagged commits).',
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTag !== version && !currentTag.startsWith(version)) {
|
||||||
|
console.log(
|
||||||
|
`Git tag (${currentTag}) does not match version (${version}). Skipping upload.`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (isGitTagDirty()) {
|
||||||
|
// console.log('Git working directory is dirty. Skipping upload.')
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Uploading artifacts for ${platform} v${version} (tag: ${currentTag})`,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!fs.existsSync(DIST_DIR)) {
|
||||||
|
throw new Error('dist directory not found. Run build first.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(DIST_DIR)
|
||||||
|
|
||||||
|
const uploadTargets = files.filter((file) => {
|
||||||
|
return (
|
||||||
|
file.endsWith('.yml') ||
|
||||||
|
file.endsWith('.AppImage') ||
|
||||||
|
file.endsWith('.deb') ||
|
||||||
|
file.endsWith('.exe') ||
|
||||||
|
file.endsWith('.dmg')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (uploadTargets.length === 0) {
|
||||||
|
console.log('No artifacts found to upload.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of uploadTargets) {
|
||||||
|
const fullPath = path.join(DIST_DIR, file)
|
||||||
|
await uploadFile(fullPath, platform, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Upload complete.')
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
@@ -30,14 +30,11 @@ dmg:
|
|||||||
linux:
|
linux:
|
||||||
target:
|
target:
|
||||||
- AppImage
|
- AppImage
|
||||||
- snap
|
# - snap
|
||||||
- deb
|
- deb
|
||||||
maintainer: electronjs.org
|
maintainer: electronjs.org
|
||||||
category: Utility
|
category: Utility
|
||||||
appImage:
|
appImage:
|
||||||
artifactName: ${name}-${version}.${ext}
|
artifactName: ${name}-${version}.${ext}
|
||||||
npmRebuild: false
|
npmRebuild: false
|
||||||
publish:
|
publish: null
|
||||||
provider: s3
|
|
||||||
bucket: dist
|
|
||||||
endpoint: https://s3.takerofnotes.com
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
|
||||||
import { app, ipcMain, BrowserWindow, shell } from "electron";
|
import { app, ipcMain, BrowserWindow, dialog, shell } from "electron";
|
||||||
import filesystemPlugin from "@takerofnotes/plugin-filesystem";
|
import filesystemPlugin from "@takerofnotes/plugin-filesystem";
|
||||||
import supabasePlugin from "@takerofnotes/plugin-supabase";
|
import supabasePlugin from "@takerofnotes/plugin-supabase";
|
||||||
import s3Plugin from "@takerofnotes/plugin-s3";
|
import s3Plugin from "@takerofnotes/plugin-s3";
|
||||||
@@ -577,6 +577,12 @@ app.whenReady().then(async () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ipcMain.handle("open-directory-dialog", async () => {
|
||||||
|
const result = await dialog.showOpenDialog({
|
||||||
|
properties: ["openDirectory"]
|
||||||
|
});
|
||||||
|
return result.canceled ? null : result.filePaths[0];
|
||||||
|
});
|
||||||
electronApp.setAppUserModelId("com.electron");
|
electronApp.setAppUserModelId("com.electron");
|
||||||
app.on("browser-window-created", (_, window) => {
|
app.on("browser-window-created", (_, window) => {
|
||||||
optimizer.watchWindowShortcuts(window);
|
optimizer.watchWindowShortcuts(window);
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ const api = {
|
|||||||
},
|
},
|
||||||
moveClosed: () => {
|
moveClosed: () => {
|
||||||
ipcRenderer.invoke("move-closed");
|
ipcRenderer.invoke("move-closed");
|
||||||
|
},
|
||||||
|
openDirectoryDialog: () => {
|
||||||
|
return ipcRenderer.invoke("open-directory-dialog");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (process.contextIsolated) {
|
if (process.contextIsolated) {
|
||||||
|
|||||||
635
package-lock.json
generated
635
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,12 @@
|
|||||||
"build": "electron-vite build",
|
"build": "electron-vite build",
|
||||||
"build:web": "vite build",
|
"build:web": "vite build",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"build:unpack": "npm run build && electron-builder --dir",
|
|
||||||
"build:win": "npm run build && electron-builder --win",
|
"build:win": "npm run build && electron-builder --win",
|
||||||
"build:mac": "npm run build && electron-builder --mac",
|
"build:mac": "npm run build && electron-builder --mac",
|
||||||
"build:linux": "npm run build && electron-builder --linux"
|
"build:linux": "npm run build && electron-builder --linux",
|
||||||
|
"release:win": "node bin/uploadArtifacts.js win $npm_package_version",
|
||||||
|
"release:mac": "node bin/uploadArtifacts.js mac $npm_package_version",
|
||||||
|
"release:linux": "node bin/uploadArtifacts.js linux $npm_package_version"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^8.3.0",
|
"@capacitor/android": "^8.3.0",
|
||||||
@@ -64,6 +66,7 @@
|
|||||||
"vue-router": "^5.0.3"
|
"vue-router": "^5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.1025.0",
|
||||||
"@capacitor/cli": "^8.3.0",
|
"@capacitor/cli": "^8.3.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.2",
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
"electron": "^39.2.6",
|
"electron": "^39.2.6",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config'
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron'
|
||||||
import filesystemPlugin from '@takerofnotes/plugin-filesystem'
|
import filesystemPlugin from '@takerofnotes/plugin-filesystem'
|
||||||
import supabasePlugin from '@takerofnotes/plugin-supabase'
|
import supabasePlugin from '@takerofnotes/plugin-supabase'
|
||||||
import s3Plugin from '@takerofnotes/plugin-s3'
|
import s3Plugin from '@takerofnotes/plugin-s3'
|
||||||
@@ -137,6 +137,13 @@ app.whenReady().then(async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('open-directory-dialog', async () => {
|
||||||
|
const result = await dialog.showOpenDialog({
|
||||||
|
properties: ['openDirectory'],
|
||||||
|
})
|
||||||
|
return result.canceled ? null : result.filePaths[0]
|
||||||
|
})
|
||||||
|
|
||||||
electronApp.setAppUserModelId('com.electron')
|
electronApp.setAppUserModelId('com.electron')
|
||||||
|
|
||||||
app.on('browser-window-created', (_, window) => {
|
app.on('browser-window-created', (_, window) => {
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ const api = {
|
|||||||
moveClosed: () => {
|
moveClosed: () => {
|
||||||
ipcRenderer.invoke('move-closed')
|
ipcRenderer.invoke('move-closed')
|
||||||
},
|
},
|
||||||
|
openDirectoryDialog: () => {
|
||||||
|
return ipcRenderer.invoke('open-directory-dialog')
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.contextIsolated) {
|
if (process.contextIsolated) {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="menu">
|
<transition name="menu">
|
||||||
<div v-if="menuOpen" class="menu" ref="container">
|
<div
|
||||||
<Nav />
|
v-if="menuOpen || lockOpen"
|
||||||
|
:class="['menu', { locked: lockOpen }]"
|
||||||
|
ref="container"
|
||||||
|
>
|
||||||
|
<Nav v-if="!lockOpen" />
|
||||||
|
|
||||||
<div class="menu-wrap layout-block">
|
<div class="menu-wrap layout-block">
|
||||||
<new-note class="menu-item" @noteOpened="closeMenu" />
|
<new-note class="menu-item" @noteOpened="closeMenu" />
|
||||||
@@ -32,6 +36,10 @@ import useMenu from '@/composables/useMenu'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import useExport from '@/composables/useExport'
|
import useExport from '@/composables/useExport'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
lockOpen: { type: Boolean, default: () => false },
|
||||||
|
})
|
||||||
|
|
||||||
const container = ref()
|
const container = ref()
|
||||||
|
|
||||||
const { menuOpen, closeMenu } = useMenu()
|
const { menuOpen, closeMenu } = useMenu()
|
||||||
@@ -85,6 +93,10 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.locked .menu-wrap {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.menu-enter-active,
|
&.menu-enter-active,
|
||||||
&.menu-leave-active {
|
&.menu-leave-active {
|
||||||
transition: transform 300ms var(--ease-out-expo);
|
transition: transform 300ms var(--ease-out-expo);
|
||||||
|
|||||||
@@ -1,40 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="preferences-directory-input">
|
<div class="preferences-directory-input">
|
||||||
<input v-model="model" type="text" />
|
<label :for="key"> {{ label }}{{ required ? '*' : '' }} </label>
|
||||||
|
|
||||||
<button @click="openDirectoryPicker">Browse</button>
|
<div class="input-row">
|
||||||
|
<input
|
||||||
|
v-model="model"
|
||||||
|
:id="key"
|
||||||
|
type="text"
|
||||||
|
:placeholder="default"
|
||||||
|
:required="required"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
|
||||||
<input
|
<button @click="openDirectoryPicker" type="button">Browse</button>
|
||||||
ref="fileInput"
|
</div>
|
||||||
type="file"
|
|
||||||
webkitdirectory
|
|
||||||
style="display: none"
|
|
||||||
@change="handleDirectoryChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
const props = defineProps({
|
||||||
|
label: String,
|
||||||
|
key: String,
|
||||||
|
required: Boolean,
|
||||||
|
default: String,
|
||||||
|
})
|
||||||
|
|
||||||
const model = defineModel()
|
const model = defineModel()
|
||||||
const fileInput = ref(null)
|
|
||||||
|
|
||||||
function openDirectoryPicker() {
|
const openDirectoryPicker = async () => {
|
||||||
fileInput.value?.click()
|
const path = await window.api.openDirectoryDialog()
|
||||||
}
|
if (path) {
|
||||||
|
model.value = path
|
||||||
function handleDirectoryChange(event) {
|
|
||||||
const files = event.target.files
|
|
||||||
if (files.length > 0) {
|
|
||||||
const path = files[0].webkitRelativePath || files[0].path
|
|
||||||
const directoryPath = path.split('/').slice(0, -1).join('/')
|
|
||||||
model.value = directoryPath || files[0].path
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.preferences-directory-input {
|
.preferences-directory-input {
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.input-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid var(--grey-100);
|
||||||
|
border-radius: 0.2em;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<preferences-text-input
|
<preferences-text-input
|
||||||
v-model.trim="config.encryptionKey"
|
v-model.trim="config.encryptionKey"
|
||||||
type="text"
|
type="password"
|
||||||
label="Encryption Key"
|
label="Encryption Key"
|
||||||
key="encryptionKey"
|
key="encryptionKey"
|
||||||
required
|
required
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<main v-if="loaded" class="directory layout-block">
|
<main
|
||||||
|
v-if="loaded && (categories?.length || notes?.length)"
|
||||||
|
class="directory layout-block"
|
||||||
|
>
|
||||||
<category-row
|
<category-row
|
||||||
v-for="(category, i) in categories"
|
v-for="(category, i) in categories"
|
||||||
:index="i"
|
:index="i"
|
||||||
@@ -18,6 +21,10 @@
|
|||||||
<decryption-warning :failedIds="decryptionFailures" />
|
<decryption-warning :failedIds="decryptionFailures" />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<main v-else-if="loaded">
|
||||||
|
<Menu lockOpen />
|
||||||
|
</main>
|
||||||
|
|
||||||
<page-loading v-else />
|
<page-loading v-else />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,6 +37,7 @@ import NoteRow from '@/components/NoteRow.vue'
|
|||||||
import useNotes from '@/composables/useNotes'
|
import useNotes from '@/composables/useNotes'
|
||||||
import NewNote from '@/components/NewNote.vue'
|
import NewNote from '@/components/NewNote.vue'
|
||||||
import { useMagicKeys } from '@vueuse/core'
|
import { useMagicKeys } from '@vueuse/core'
|
||||||
|
import Menu from '@/components/Menu.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|||||||
Reference in New Issue
Block a user