feat: integrate automatic update checks and enhance settings UI

- Added functionality for automatic update checks in the settings page, allowing users to enable or disable this feature.
- Integrated app version display and update availability notifications.
- Updated settings store to manage auto-check updates state and handle version migration.
- Enhanced layout with a new card for update settings, improving user experience and accessibility.
This commit is contained in:
Evandro Fonseca Junior committed 2025-11-27 22:47:48 -03:00
1 parent 6cdc8d4294
commit 6c366eecb0
20 files changed
+1266 -6

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const versionType = process.argv[2] || 'patch'; // patch, minor, major
const shouldCommit = process.argv.includes('--commit');
// Validar tipo de versão
if (!['patch', 'minor', 'major'].includes(versionType)) {
console.error('❌ Tipo de versão inválido. Use: patch, minor ou major');
process.exit(1);
}
// Ler package.json
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
// Bump version
const [major, minor, patch] = packageJson.version.split('.').map(Number);
let newVersion;
switch (versionType) {
case 'major':
newVersion = `${major + 1}.0.0`;
break;
case 'minor':
newVersion = `${major}.${minor + 1}.0`;
break;
case 'patch':
default:
newVersion = `${major}.${minor}.${patch + 1}`;
break;
}
console.log(`📦 Atualizando versão de ${packageJson.version} para ${newVersion}...`);
// Atualizar package.json
packageJson.version = newVersion;
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
console.log('✅ package.json atualizado');
// Atualizar tauri.conf.json
const tauriConfPath = path.join(__dirname, '..', 'src-tauri', 'tauri.conf.json');
const tauriConf = JSON.parse(fs.readFileSync(tauriConfPath, 'utf-8'));
tauriConf.version = newVersion;
fs.writeFileSync(tauriConfPath, JSON.stringify(tauriConf, null, 2) + '\n');
console.log('✅ tauri.conf.json atualizado');
// Atualizar Cargo.toml
const cargoTomlPath = path.join(__dirname, '..', 'src-tauri', 'Cargo.toml');
let cargoToml = fs.readFileSync(cargoTomlPath, 'utf-8');
cargoToml = cargoToml.replace(/^version = ".*"$/m, `version = "${newVersion}"`);
fs.writeFileSync(cargoTomlPath, cargoToml);
console.log('✅ Cargo.toml atualizado');
// Criar commit e tag (opcional)
if (shouldCommit) {
try {
execSync(`git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml`, { stdio: 'inherit' });
execSync(`git commit -m "chore: bump version to ${newVersion}"`, { stdio: 'inherit' });
execSync(`git tag v${newVersion}`, { stdio: 'inherit' });
console.log(`✅ Commit e tag v${newVersion} criados`);
console.log(`\n📝 Próximo passo: git push && git push --tags`);
} catch (error) {
console.error('❌ Erro ao criar commit/tag:', error.message);
process.exit(1);
}
}
console.log(`\n✅ Versão atualizada para ${newVersion}`);
+205
View File
@@ -0,0 +1,205 @@
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
// Ler versão do package.json
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const currentVersion = packageJson.version;
// Verificar se estamos em um repositório Git
let isGitRepo = false;
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore', cwd: path.join(__dirname, '..') });
isGitRepo = true;
} catch (error) {
console.warn('⚠️ Não é um repositório Git. Gerando changelog vazio.');
}
// Função para sanitizar mensagens de commit
function sanitizeMessage(message) {
return message
.replace(/\n/g, ' ')
.replace(/\r/g, '')
.replace(/"/g, '\\"')
.trim();
}
// Função para extrair tipo e mensagem do commit
function parseCommitMessage(fullMessage) {
const featMatch = fullMessage.match(/^feat(\(.+?\))?:\s*(.+)$/i);
const fixMatch = fullMessage.match(/^fix(\(.+?\))?:\s*(.+)$/i);
if (featMatch) {
return { type: 'feat', message: featMatch[2] || featMatch[0] };
}
if (fixMatch) {
return { type: 'fix', message: fixMatch[2] || fixMatch[0] };
}
return null;
}
// Função para obter tags de versão
function getVersionTags() {
try {
const tags = execSync('git tag --sort=-version:refname', {
encoding: 'utf-8',
cwd: path.join(__dirname, '..'),
})
.trim()
.split('\n')
.filter(Boolean)
.filter(tag => /^v?\d+\.\d+\.\d+/.test(tag));
return tags;
} catch (error) {
return [];
}
}
// Função para obter commits entre duas tags ou desde o início
function getCommitsBetweenTags(fromTag, toTag) {
try {
let range = '';
if (fromTag && toTag) {
range = `${fromTag}..${toTag}`;
} else if (toTag) {
range = toTag;
} else {
range = 'HEAD';
}
const logFormat = '%H|%an|%ad|%s';
const dateFormat = '--date=iso-strict';
const output = execSync(
`git log ${range} --pretty=format:"${logFormat}" --date=${dateFormat}`,
{
encoding: 'utf-8',
cwd: path.join(__dirname, '..'),
}
);
return output
.trim()
.split('\n')
.filter(Boolean)
.map(line => {
const [hash, author, date, message] = line.split('|');
const parsed = parseCommitMessage(message);
if (!parsed) return null;
return {
hash: hash.substring(0, 7),
type: parsed.type,
message: sanitizeMessage(parsed.message),
author: author.trim(),
date: date.trim(),
};
})
.filter(Boolean);
} catch (error) {
return [];
}
}
// Função principal para gerar changelog
function generateChangelog() {
const changelogPath = path.join(__dirname, '..', 'data', 'changelog.json');
const dataDir = path.dirname(changelogPath);
// Criar diretório data se não existir
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
if (!isGitRepo) {
// Gerar changelog vazio se não for repositório Git
const emptyChangelog = {
version: currentVersion,
generatedAt: new Date().toISOString(),
releases: [],
};
fs.writeFileSync(changelogPath, JSON.stringify(emptyChangelog, null, 2), 'utf-8');
console.log('✅ Changelog vazio gerado (não é repositório Git)');
return;
}
const releases = [];
const versionTags = getVersionTags();
if (versionTags.length > 0) {
// Agrupar por tags de versão
for (let i = 0; i < versionTags.length; i++) {
const currentTag = versionTags[i];
const nextTag = i < versionTags.length - 1 ? versionTags[i + 1] : null;
const commits = getCommitsBetweenTags(nextTag, currentTag);
if (commits.length > 0) {
// Extrair versão da tag (remover 'v' prefix se existir)
const version = currentTag.replace(/^v/, '');
const releaseDate = commits[0]?.date || new Date().toISOString();
releases.push({
version,
date: releaseDate.split('T')[0],
commits,
});
}
}
// Adicionar commits não versionados (após a última tag)
const unreleasedCommits = getCommitsBetweenTags(versionTags[0], 'HEAD');
if (unreleasedCommits.length > 0) {
releases.unshift({
version: 'unreleased',
date: unreleasedCommits[0]?.date.split('T')[0] || new Date().toISOString().split('T')[0],
commits: unreleasedCommits,
});
}
} else {
// Sem tags, agrupar por data (últimos 30 dias ou todos se menos)
const allCommits = getCommitsBetweenTags(null, 'HEAD');
if (allCommits.length > 0) {
// Agrupar por data
const commitsByDate = {};
allCommits.forEach(commit => {
const date = commit.date.split('T')[0];
if (!commitsByDate[date]) {
commitsByDate[date] = [];
}
commitsByDate[date].push(commit);
});
// Criar releases por data (ordenado por data decrescente)
Object.keys(commitsByDate)
.sort((a, b) => b.localeCompare(a))
.forEach(date => {
releases.push({
version: 'unreleased',
date,
commits: commitsByDate[date],
});
});
}
}
const changelog = {
version: currentVersion,
generatedAt: new Date().toISOString(),
releases,
};
fs.writeFileSync(changelogPath, JSON.stringify(changelog, null, 2), 'utf-8');
console.log(`✅ Changelog gerado: ${releases.length} release(s) encontrada(s)`);
console.log(` Total de commits: ${releases.reduce((sum, r) => sum + r.commits.length, 0)}`);
}
// Executar
try {
generateChangelog();
} catch (error) {
console.error('❌ Erro ao gerar changelog:', error.message);
process.exit(1);
}