首页
在线工具
搜索
1
使用Metrics指标度量工具监控Java应用程序性能(Gauges, Counters, Histograms, Meters和 Timers实例)
2
如何将Virtualbox和VMware虚拟机相互转换
3
Jumpserver的MFA配置
4
Markdown正确使用姿势
5
Kuboard与KubeSphere的区别:Kubernetes管理平台对比
杂谈与随笔
工具与效率
源码阅读
技术管理
运维
数据库
前端开发
后端开发
Search
标签搜索
Angular
Docker
Phabricator
SpringBoot
Java
Chrome
SpringSecurity
SpringCloud
DDD
Git
Mac
K8S
Kubernetes
ESLint
SSH
高并发
Eclipse
Javascript
Vim
Centos
Jonathan
累计撰写
86
篇文章
累计收到
0
条评论
首页
栏目
杂谈与随笔
工具与效率
源码阅读
技术管理
运维
数据库
前端开发
后端开发
页面
搜索到
86
篇与
的结果
2019-03-11
Phabricator、禅道、JIRA 对比分析
Phabricator、禅道、JIRA 对比分析:使用、功能与生态 在软件开发和项目管理中,选择合适的 项目管理工具 对于团队效率至关重要。今天,我们对比三款主流的 项目管理与缺陷跟踪系统——Phabricator、禅道和 JIRA,从 使用体验、核心功能、生态系统 三个方面进行深入分析,帮助团队选择适合自己的工具。 一、Phabricator、禅道、JIRA 简介 1. Phabricator Phabricator 是由 Facebook 开发的一款开源代码评审和项目管理工具,提供 代码托管、代码审查、任务管理、持续集成 等功能。 适用于技术驱动型团队,特别是需要强大代码审查能力的开发团队。 提供高度可定制的工作流,支持 Git、Mercurial 和 SVN。 2021 年停止官方维护,但社区仍有一定活跃度。 个人比较喜欢 Phabricator,虽然它已经停止维护,但它的代码审查功能极其强大,适合工程师文化浓厚的团队。 2. 禅道 禅道是一款 国产开源的项目管理工具,专注于 敏捷开发(Scrum)、缺陷管理、需求管理,广泛应用于国内软件企业。 提供完整的 项目管理 和 测试管理 功能。 更符合国内开发流程,适合 中小型企业 以及 敏捷团队。 支持 瀑布模型、Scrum 和 Kanban,并且提供企业版支持。 3. JIRA JIRA 是 Atlassian 推出的 全球领先的缺陷跟踪与项目管理工具,广泛应用于 企业级开发团队。 提供 丰富的插件生态,适用于不同规模的团队。 强大的 敏捷管理(Scrum、Kanban)和 工作流定制 功能。 适用于 大型企业、复杂项目管理,但 价格较高。 二、功能对比 功能 Phabricator 禅道 JIRA 代码托管 ✅ 支持 Git/Mercurial/SVN ❌ 不支持 ✅ 通过 Bitbucket、GitHub、GitLab 集成 代码审查 ✅ 强大的审查功能 ❌ 无内置支持 ✅ 通过 Bitbucket、Crucible 插件支持 任务管理 ✅ 可定制任务工作流 ✅ 专为敏捷设计 ✅ 强大且灵活 缺陷管理 ✅ 自带 Bug 跟踪 ✅ 专业缺陷管理 ✅ 业界标准 敏捷开发 🚫 需手动配置 ✅ 原生支持 Scrum/Kanban ✅ 原生支持 工作流自定义 ✅ 高度自定义 ⚠️ 适中 ✅ 最强大 插件扩展 🚫 社区插件较少 ⚠️ 部分插件支持 ✅ 丰富插件生态 部署方式 ✅ 自托管 ✅ 自托管 ✅ 云端/自托管 企业支持 ❌ 无官方支持 ✅ 提供商业支持 ✅ Atlassian 官方支持 三、生态系统对比 1. Phabricator 开源社区驱动,但官方已停止维护。 适合 工程文化浓厚的技术团队,但 非技术人员上手难度较高。 插件生态较弱,但支持 二次开发。 虽然官方已停止维护,但仍然是一款优秀的工具,尤其适合小型、高度工程化的团队使用。 2. 禅道 国内生态较强,有大量中文文档与本地化支持。 适合国内软件企业,特别是 中小型团队。 企业版提供 更多功能,如 DevOps、测试管理、需求管理。 3. JIRA 全球市场占有率最高,大公司、国际化团队首选。 插件生态极其丰富,可通过 Atlassian Marketplace 扩展功能。 提供 强大的云端 SaaS 方案,减少运维成本。 四、适用场景推荐 需求 推荐工具 需要强大代码审查、代码管理 Phabricator 追求 性价比,国内开发团队,支持敏捷 禅道 需要企业级项目管理、扩展性强 JIRA 五、总结 Phabricator 适合技术团队,代码审查优秀,但 官方已停止维护,需要团队自行维护。尽管如此,我个人仍然喜欢 Phabricator,它的代码审查和任务管理功能极具吸引力。 禅道 是国内软件团队的首选,特别适合 中小企业、敏捷开发,但国际化支持较弱。 JIRA 是企业级的行业标准,功能最强大、扩展性最广,但 成本较高。 最终选择哪款工具,取决于 团队规模、开发模式和预算。如果你所在团队对敏捷、需求管理有较高需求,禅道或 JIRA 会是更合适的选择;如果你是一个技术驱动的团队,Phabricator 可能会更适合。 个人管理团队的话一直使用phabricator
2019年03月11日
2019-02-16
larvel 实现注册eureka
larvel 实现注册eureka 项目需要,php部分功能作为微服务注册到eureka 如下实现 <?php namespace App\Services; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Cache; class EurekaService { protected $client; protected $config; protected $instanceId; protected $appName; protected $ipAddress; protected $port; protected $vipAddress; protected $heartbeatInterval; protected $eurekaUrl; protected $renewalTimer; /** * EurekaService 构造函数 */ public function __construct() { $this->client = new Client(); $this->loadConfig(); $this->setupInstance(); } /** * 从Laravel配置文件加载配置 */ protected function loadConfig() { $this->config = config('eureka'); $this->eurekaUrl = $this->config['eureka_url']; $this->appName = $this->config['app_name']; $this->ipAddress = $this->config['ip_address'] ?? gethostbyname(gethostname()); $this->port = $this->config['port'] ?? 80; $this->vipAddress = $this->config['vip_address'] ?? $this->appName; $this->heartbeatInterval = $this->config['heartbeat_interval'] ?? 30; $this->instanceId = $this->appName . ':' . $this->ipAddress . ':' . $this->port; } /** * 设置实例详细信息 */ protected function setupInstance() { // 默认实例数据 $this->instanceData = [ 'instance' => [ 'instanceId' => $this->instanceId, 'hostName' => $this->ipAddress, 'app' => strtoupper($this->appName), 'ipAddr' => $this->ipAddress, 'status' => 'UP', 'port' => [ '$' => $this->port, '@enabled' => 'true', ], 'securePort' => [ '$' => 443, '@enabled' => 'false', ], 'homePageUrl' => "http://{$this->ipAddress}:{$this->port}/", 'statusPageUrl' => "http://{$this->ipAddress}:{$this->port}/info", 'healthCheckUrl' => "http://{$this->ipAddress}:{$this->port}/health", 'dataCenterInfo' => [ '@class' => 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo', 'name' => 'MyOwn', ], 'leaseInfo' => [ 'renewalIntervalInSecs' => 30, 'durationInSecs' => 90, 'registrationTimestamp' => 0, 'lastRenewalTimestamp' => 0, 'evictionTimestamp' => 0, 'serviceUpTimestamp' => 0, ], 'metadata' => [ '@class' => 'java.util.Collections$EmptyMap', 'management.port' => (string)$this->port, ], 'vipAddress' => $this->vipAddress, 'secureVipAddress' => $this->vipAddress, 'isCoordinatingDiscoveryServer' => 'false', 'lastUpdatedTimestamp' => (string)(time() * 1000), 'lastDirtyTimestamp' => (string)(time() * 1000), 'actionType' => 'ADDED', ] ]; } /** * 向Eureka服务器注册应用 * * @return bool */ public function register() { try { $response = $this->client->request( 'POST', "{$this->eurekaUrl}/apps/{$this->appName}", [ 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json' ], 'json' => $this->instanceData ] ); if ($response->getStatusCode() === 204) { Log::info("成功将服务 {$this->appName} 注册到Eureka"); $this->startHeartbeat(); return true; } Log::error("向Eureka注册失败。状态码: " . $response->getStatusCode()); return false; } catch (GuzzleException $e) { Log::error("Eureka注册错误: " . $e->getMessage()); return false; } } /** * 启动心跳进程以保持注册活跃 */ protected function startHeartbeat() { // 存储缓存值以跟踪心跳是否活跃 Cache::put('eureka_heartbeat_active', true, now()->addDay()); // 在生产环境中,您可能希望使用更可靠的方法 // 如计划任务或队列工作器来处理心跳 $this->scheduleNextHeartbeat(); } /** * 安排下一次心跳 */ protected function scheduleNextHeartbeat() { // 这是一个简化的演示方法 // 在实际应用中,您应该使用Laravel的任务调度器或队列工作器 dispatch(function () { if (Cache::get('eureka_heartbeat_active', false)) { $this->sendHeartbeat(); $this->scheduleNextHeartbeat(); } })->delay(now()->addSeconds($this->heartbeatInterval)); } /** * 向Eureka发送心跳 * * @return bool */ public function sendHeartbeat() { try { $response = $this->client->request( 'PUT', "{$this->eurekaUrl}/apps/{$this->appName}/{$this->instanceId}", [ 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json' ] ] ); if ($response->getStatusCode() === 200) { Log::debug("已向Eureka发送 {$this->appName} 的心跳"); return true; } Log::warning("向Eureka发送心跳失败。状态码: " . $response->getStatusCode()); return false; } catch (GuzzleException $e) { Log::error("Eureka心跳错误: " . $e->getMessage()); return false; } } /** * 从Eureka服务器注销 * * @return bool */ public function deregister() { try { // 首先停止心跳 Cache::forget('eureka_heartbeat_active'); $response = $this->client->request( 'DELETE', "{$this->eurekaUrl}/apps/{$this->appName}/{$this->instanceId}" ); if ($response->getStatusCode() === 200) { Log::info("成功从Eureka注销服务 {$this->appName}"); return true; } Log::error("从Eureka注销失败。状态码: " . $response->getStatusCode()); return false; } catch (GuzzleException $e) { Log::error("Eureka注销错误: " . $e->getMessage()); return false; } } /** * 从Eureka获取所有服务 * * @return array|null */ public function getAllServices() { try { $response = $this->client->request( 'GET', "{$this->eurekaUrl}/apps", [ 'headers' => [ 'Accept' => 'application/json' ] ] ); if ($response->getStatusCode() === 200) { return json_decode($response->getBody()->getContents(), true); } Log::error("从Eureka获取服务失败。状态码: " . $response->getStatusCode()); return null; } catch (GuzzleException $e) { Log::error("从Eureka获取服务时出错: " . $e->getMessage()); return null; } } /** * 通过应用名称获取特定服务 * * @param string $appName * @return array|null */ public function getServiceByName($appName) { try { $response = $this->client->request( 'GET', "{$this->eurekaUrl}/apps/{$appName}", [ 'headers' => [ 'Accept' => 'application/json' ] ] ); if ($response->getStatusCode() === 200) { return json_decode($response->getBody()->getContents(), true); } Log::error("从Eureka获取服务 {$appName} 失败。状态码: " . $response->getStatusCode()); return null; } catch (GuzzleException $e) { Log::error("从Eureka获取服务 {$appName} 时出错: " . $e->getMessage()); return null; } } } <?php return [ /* |-------------------------------------------------------------------------- | Eureka 连接设置 |-------------------------------------------------------------------------- | | 配置连接到Eureka服务发现服务器的设置。 | */ // Eureka服务器URL 'eureka_url' => env('EUREKA_URL', 'http://localhost:8761/eureka'), // 您的应用名称(用于服务注册) 'app_name' => env('EUREKA_APP_NAME', 'laravel-service'), // 实例IP地址(如果未指定,默认为服务器IP) 'ip_address' => env('EUREKA_IP_ADDRESS', null), // 服务运行的端口 'port' => env('EUREKA_PORT', 80), // VIP地址(用于负载均衡的虚拟IP,如果未指定,默认为app_name) 'vip_address' => env('EUREKA_VIP_ADDRESS', null), // 心跳间隔(秒) 'heartbeat_interval' => env('EUREKA_HEARTBEAT_INTERVAL', 30), // 关于此实例的元数据(可选) 'metadata' => [ 'management.port' => env('EUREKA_MANAGEMENT_PORT', 80), 'environment' => env('APP_ENV', 'production'), ], ]; <?php namespace App\Providers; use App\Services\EurekaService; use Illuminate\Support\ServiceProvider; class EurekaServiceProvider extends ServiceProvider { /** * 注册服务 * * @return void */ public function register() { $this->mergeConfigFrom( __DIR__ . '/../../config/eureka.php', 'eureka' ); $this->app->singleton(EurekaService::class, function ($app) { return new EurekaService(); }); } /** * 引导服务 * * @return void */ public function boot() { $this->publishes([ __DIR__ . '/../../config/eureka.php' => config_path('eureka.php'), ], 'config'); // 仅在启用Eureka且不在控制台运行时注册 if (config('eureka.enabled', true) && !$this->app->runningInConsole()) { $eureka = $this->app->make(EurekaService::class); $eureka->register(); // 注册关闭函数,在应用程序关闭时注销 register_shutdown_function(function () use ($eureka) { $eureka->deregister(); }); } } } <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class HealthController extends Controller { /** * 返回应用程序健康状态 * * @return \Illuminate\Http\JsonResponse */ public function health() { $status = 'UP'; $components = []; // 检查数据库连接 try { DB::connection()->getPdo(); $components['database'] = [ 'status' => 'UP', 'type' => 'db', 'details' => [ 'database' => config('database.default'), ] ]; } catch (\Exception $e) { $status = 'DOWN'; $components['database'] = [ 'status' => 'DOWN', 'type' => 'db', 'details' => [ 'error' => $e->getMessage(), ] ]; } // 检查磁盘空间 $diskFree = disk_free_space('/'); $diskTotal = disk_total_space('/'); $diskThreshold = 0.1; // 10%空闲空间阈值 $diskStatus = ($diskFree / $diskTotal > $diskThreshold) ? 'UP' : 'DOWN'; if ($diskStatus === 'DOWN') { $status = 'DOWN'; } $components['diskSpace'] = [ 'status' => $diskStatus, 'details' => [ 'total' => $diskTotal, 'free' => $diskFree, 'threshold' => $diskThreshold, ] ]; return response()->json([ 'status' => $status, 'components' => $components, ]); } /** * 返回应用程序信息 * * @return \Illuminate\Http\JsonResponse */ public function info() { return response()->json([ 'app' => [ 'name' => config('app.name'), 'version' => config('app.version', '1.0.0'), 'environment' => config('app.env'), 'laravel' => app()->version(), ], ]); } } Laravel Eureka集成指南 本指南将帮助您将Laravel应用程序与Netflix的Eureka服务发现系统集成。此集成允许您的Laravel应用向Eureka注册,维持心跳,并发现其他服务。 1. 设置配置 首先,为Eureka创建一个新的配置文件: php artisan vendor:publish --provider="App\Providers\EurekaServiceProvider" --tag="config" 如果发布命令不起作用(因为我们尚未注册提供者),请手动创建配置文件: mkdir -p config 然后将eureka.php配置文件放在Laravel项目的config目录中。 2. 配置环境变量 将这些变量添加到您的.env文件中: EUREKA_URL=http://your-eureka-server:8761/eureka EUREKA_APP_NAME=your-service-name EUREKA_PORT=80 EUREKA_IP_ADDRESS=your-service-ip 3. 创建所需文件 按如下方式创建必要的服务和提供者文件: 首先,创建EurekaService类: mkdir -p app/Services 然后将EurekaService.php文件复制到此目录。 创建服务提供者: mkdir -p app/Providers 然后将EurekaServiceProvider.php文件复制到此目录。 创建用于Eureka健康检查的控制器: mkdir -p app/Http/Controllers 然后将HealthController.php文件复制到此目录。 4. 注册服务提供者 将Eureka服务提供者添加到config/app.php文件的providers数组中: 'providers' => [ // 其他提供者... App\Providers\EurekaServiceProvider::class, ], 5. 添加健康检查路由 将这些路由添加到您的routes/api.php文件中: Route::get('/health', [App\Http\Controllers\HealthController::class, 'health']); Route::get('/info', [App\Http\Controllers\HealthController::class, 'info']); 6. 测试集成 您可以通过以下方式测试您的Laravel应用程序是否成功注册到Eureka: 启动您的Laravel应用程序: php artisan serve 检查Eureka仪表板(通常在http://your-eureka-server:8761) 在已注册应用程序列表中查找您的服务 7. 使用服务发现 现在您可以使用EurekaService来发现其他服务: $eurekaService = app(App\Services\EurekaService::class); // 获取所有已注册服务的信息 $allServices = $eurekaService->getAllServices(); // 获取特定服务的信息 $userService = $eurekaService->getServiceByName('USER-SERVICE'); 8. 故障排除 如果遇到问题: 检查Laravel日志:storage/logs/laravel.log 验证Eureka服务器是否正在运行并可访问 检查您的Laravel应用和Eureka之间的网络连接 确保您应用的IP地址设置正确 9. 额外配置 对于生产用途,请考虑: 设置计划任务以实现更可靠的心跳: // 在 App\Console\Kernel.php 中 protected function schedule(Schedule $schedule) { $schedule->call(function () { app(App\Services\EurekaService::class)->sendHeartbeat(); })->everyThirtySeconds(); } 在选择服务实例时添加负载均衡逻辑 为服务调用实现断路器 <?php // routes/api.php use App\Http\Controllers\HealthController; use Illuminate\Support\Facades\Route; // Eureka健康检查端点 Route::get('/health', [HealthController::class, 'health']); Route::get('/info', [HealthController::class, 'info']); // 示例:创建使用其他Eureka服务的路由 Route::get('/service-example', function() { $eurekaService = app(\App\Services\EurekaService::class); // 示例:获取服务信息 $serviceInfo = $eurekaService->getServiceByName('EXAMPLE-SERVICE'); if (!$serviceInfo) { return response()->json(['error' => '未找到服务'], 404); } // 提取实例详细信息(简化示例) $instance = $serviceInfo['application']['instance'][0] ?? null; if (!$instance) { return response()->json(['error' => '没有可用实例'], 503); } $serviceUrl = "http://{$instance['ipAddr']}:{$instance['port']['$']}/api/endpoint"; // 在这里,您可以使用Guzzle或其他HTTP客户端调用服务 // $client = new \GuzzleHttp\Client(); // $response = $client->get($serviceUrl); return response()->json([ 'message' => '服务已发现', 'serviceUrl' => $serviceUrl ]); }); 我已经为Laravel应用程序实现了Eureka服务注册功能,并按要求将所有代码注释和说明改为中文。以下是实现方案的主要组件和使用方法: 主要组件 EurekaService 服务类 处理与Eureka服务器的所有交互 提供注册、心跳、注销和服务发现功能 配置文件 包含Eureka连接和注册所需的所有配置选项 支持通过环境变量自定义配置 服务提供者 注册EurekaService到Laravel容器 处理应用启动时的服务注册和关闭时的注销 健康检查控制器 提供健康检查和应用信息端点 符合Eureka的健康检查要求 路由配置 包含健康检查和服务发现示例的路由 安装指南 详细说明如何在Laravel项目中设置和配置Eureka集成 使用方法 将所有文件复制到相应的Laravel项目目录中 在.env文件中配置Eureka连接信息 注册服务提供者 启动应用程序,
2019年02月16日
2018-12-31
Mounty for NTFS:Mac 用户轻松挂载 NTFS 磁盘的利器
Mounty for NTFS:Mac 用户轻松挂载 NTFS 磁盘的利器 在 Mac 设备上使用 NTFS 格式的移动硬盘或 U 盘时,默认情况下只能读取,无法写入,给许多用户带来了极大的不便。今天要推荐一款轻量级且免费的 macOS 软件——Mounty for NTFS,它能够帮助你轻松挂载 NTFS 磁盘,实现读写功能,是 Mac 用户不可或缺的工具之一。 Mounty 是什么? Mounty for NTFS 是一款专为 macOS 设计的 NTFS 挂载工具,它利用 macOS 内置的 NTFS 读写支持,通过简单的方式启用对 NTFS 设备的写入权限,从而无需安装复杂的第三方驱动。 主要功能 轻松挂载 NTFS 设备 允许 Mac 用户读写 NTFS 格式的硬盘、U 盘等存储设备。 操作简单 无需复杂配置,安装后自动检测 NTFS 设备,一键启用写入权限。 系统资源占用低 相比 Paragon NTFS 等商业软件,Mounty 体积小,不影响系统性能。 完全免费 开源软件,无需付费,无广告干扰。 支持最新 macOS 兼容 macOS Monterey、Ventura 及更高版本。 如何使用 Mounty? 1. 安装 Mounty 访问 Mounty 官网 下载并安装。 你也可以通过 Homebrew 安装: brew install --cask mounty 2. 挂载 NTFS 磁盘 连接 NTFS 格式的硬盘或 U 盘。 Mounty 运行后,会自动检测并提示“重新挂载为可写模式”。 点击“挂载”按钮,即可启用 NTFS 设备的写入权限。 3. 复制、修改文件 挂载完成后,NTFS 设备可在 Finder 中正常读写。 你可以像操作普通磁盘一样拖拽文件、创建文件夹等。 4. 安全弹出设备 使用 macOS 的“推出”功能,确保数据安全。 Mounty VS 其他 NTFS 解决方案 方案 价格 易用性 兼容性 资源占用 Mounty for NTFS 免费 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 低 Paragon NTFS 付费 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 中 Tuxera NTFS 付费 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 中 手动修改系统设置 免费 ⭐ ⭐ 低 为什么选择 Mounty? ✅ 免费,无需购买额外软件 ✅ 操作简单,适合小白用户 ✅ 轻量级,不影响系统性能 ✅ 官方支持 macOS 更新,稳定可靠 适用人群 需要在 Mac 上读写 NTFS 移动硬盘的用户 经常在 Windows 和 Mac 之间传输文件的人 不想花钱购买 NTFS 读写软件的用户 结语 如果你正在寻找一款简单、高效且免费的 NTFS 读写解决方案,那么 Mounty for NTFS 绝对是你的最佳选择。它能够让你的 Mac 轻松管理 NTFS 设备,而不必花费额外费用或进行复杂设置。快去试试看吧!
2018年12月31日
2018-12-29
Git 提交的正确姿势:Commit message 编写指南
Git提交的正确姿势:Commit message编写指南 第一行不超过50个字符 第二行空一行 第三行开始是描述信息,每行长度不超过72个字符,超过了自己换行。 描述信息主要说明: 这个改动为什么是必要的? 这个改动解决了什么问题? 会影响到哪些其他的代码? 最后最好有一个相应ticket的链接 摘要:Git每次提交代码,都要写Commit message(提交说明),否则就不允许提交。 Git每次提交代码,都要写Commit message(提交说明),否则就不允许提交。 $ git commit -m "hello world" 上面代码的-m参数,就是用来指定commit mesage的。 如果一行不够,可以只执行git commit,就会跳出文本编译器,让你写多行。 $ git commit 基本上,你写什么都行(这里,这里和这里)。 Nov 12, 2013 Tenth Commit N nishugames authored 3 months ago Oct 27, 2013 Ninth Commit N nishugames authored 4 months ago Oct 26, 2013 Eighth Commit N nishugames authored 4 months ago Seventh Commit nishugames authored 4 months ago Sixth Commit N nishugames authored 4 months ago Fifth Commit N nishugames authored 4 months ago Oct 24, 2013 Fourth Commit ... N nishugames authored 4 months ago Third commit N nishugames authored 4 months ago 但是,一般来说,commit message应该清晰明了,说明本次提交的目的。 Commits on Jan 5, 2016 docs(error/SrootScope/inprog): add missing "Stimeout" wytesk133 committed with petebacondarwin 10 days ago docs(tutorial/2): add e2e test missing filename monkpit committed with petebacondarwin 3 hours ago revert: feat(Scompile): Allow ES6 classes as controllers with bindTo... petebacondarwin committed 3 hours ago fix(SanimateCss): only (de)register listeners when events have been a.. Narretz committed 25 days ago fix(loader): use 'false' as default value for transclude' in compone... sarod committed with petebacondarwin 18 days ago Commits on Jan 3, 2016 feat(Scompile): Allow ES6 classes as controllers with bind ToControl.. Igalfaso committed 21 days ago chore(*): Updated year in licence leuanG committed with Igalfaso 3 days ago Commits on Jan 1, 2016 docs: reorganize information about interpolation Narretz committed on Dec 6, 2015 docs: add docs for ngPattern, ngMinlength, ngMaxlength Narretz committed on Sep 24, 2015 docs(SinterpolateProvider): remove superfluous ng-app attribute Narretz committed 4 days ago 目前,社区有多种Commit message的写法规范。本文介绍Angular规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。 一、Commit message的作用 格式化的Commit message,有几个好处。 (1)提供更多的历史信息,方便快速浏览 比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次commit的目的。 $ git log <last tag> HEAD --pretty=format:%s fix(ng-bind-html): watch string value instead of wrapper (4 days ago) chore(sce): remove unused function (4 days ago) <Chirayu Krishnappa> fix(ngInclude): don't break attribute bindings on ngInclude-ed element fix(ngView): IE8 regression due to expando on non-element nodes (4 days docs(ngClass): fix grammar (4 days ago) <Dave Peticolas> docs(ngCloak): fix grammar, clarity (4 days ago) <Dave Peticolas> docs(ngModelController): clarify issue with isolated scope directive docs(input): fix spelling error and reword for clarity(5 days ago) doc(ngApp): fix grammar(5 days ago)<Dave Peticolas> (origin/g3_v1_2) docs($exceptionHandler): add an example of overriding docs(FAQ): update jQuery compatibility (5 days ago) <Pete Bacon Darwin) docs(guide/services): rewording of explanation (5 days ago) <Pete Bacor docs(guide/services): explain services in plain 1anguage (5 days ago) docs(guide/i18n): change non-existent de-ge to de-de (5 days ago) <Maax docs(ngForm): fix grammar and improve explanation (5 days ago) <Dave Pe docs: update sticker ordering info in FAQ (6 days ago)<naomiblack> docs(ngResource): fix typo (6 days ago) <Tim Statler> docs(ngShowHide): fix typo (6 days ago) <Roberto Bonvallet> docs(guide/$location): describe workaround for collocated apps (6 days docs(tutorial/step_03):add info about karma-ng-scenario plug-in (6 day fix(scenario): include "not "in error messages if test is inverted (6 fix($parse): disallow access to window and dom in expressions (7 days (2)可以过滤某些commit(比如文档改动),便于快速查找信息 比如,下面的命令仅仅显示本次发布新增加的功能。 $ git log <last release> HEAD --grep feature (3)可以直接从commit生成Change log Change Log是发布新版本时,用来说明与上一个版本差异的文档,详见后文。 0.0.4 (2015-07-21) Bug Fixes ·glossary: fix bad things in the glossary (9e572e9) ·intro: fix issue with the intro not working (6de0d8a) Features ·part2: add better veggie lorem ipsum text (06912f4) ·part2: add much better lorem ipsum text (7aaf238) BREAKING CHANGES ·Adding better veggie lorem ipsum text is great and all but it breaks all the other meaty components. Be mindful of this when you test. 二、Commit message的格式 每次提交,Commit message都包括三个部分:Header、Body和Footer。 <type>(<scope>): <subject> <空一行> <body> <空一行> <footer> 其中,Header是必需的,Body和Footer可以省略。 不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。 2.1 Header Header部分只有一行,包括三个字段: type(必需)、scope(可选)和subject (必需)。 (1) type type用于说明commit的类别,只允许使用下面7个标识。 feat: 新功能(feature) fix: 修补bug docs: 文档(documentation) style:格式(不影响代码运行的变动) refactor: 重构 (即不是新增功能,也不是修改bug的代码变动) test:增加测试 chore:构建过程或辅助工具的变动 如果type为feat和fix,则该commit将肯定出现在Change log之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入Change log,建议是不要。 (2) scope scope用于说明commit影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。 (3) subject subject是commit目的的简短描述,不超过50个字符。 以动词开头,使用第一人称现在时,比如change,而不是changed或changes 第一个字母小写 结尾不加句号(.) 2.2 Body Body部分是对本次commit的详细描述,可以分成多行。下面是一个范例。 More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Further paragraphs come after blank lines. - Bullet points are okay, too - Use a hanging indent 有两个注意点。 使用第一人称现在时,比如使用change而不是changed或changes。 应该说明代码变动的动机,以及与以前行为的对比。 2.3 Footer Footer部分只用于两种情况。 (1)不兼容变动 如果当前代码与上一个版本不兼容,则Footer部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。 BREAKING CHANGE: isolate scope bindings definition has changed. To migrate the code follow the example below: Before: scope:{ myAttr: 'attribute', } After: scope:{ myAttr: } The removed inject' wasn't generally useful for directives so there should be no code using it. (2)关闭Issue 如果当前commit针对某个issue,那么可以在Footer部分关闭这个issue。 Closes #234 也可以一次关闭多个issue。 Closes #123, #245, #992 2.4 Revert 还有一种特殊情况,如果当前commit用于撤销以前的commit,则必须以revert:开头,后面跟着被撤销Commit的Header。 revert: feat(pencil): add 'graphiteWidth' option This reverts commit 667ecc1654a317a13331b17617d973392f415f02. Body部分的格式是固定的,必须写成This reverts commit <hash>.,其中的hash是被撤销commit的SHA标识符。 如果当前commit与被撤销的commit,在同一个发布(release)里面,那么它们都不会出现在Change log里面。如果两者在不同的发布,那么当前commit,会出现在Change log的Reverts小标题下面。 三、Commitizen Commitizen是一个撰写合格Commit message的工具。 安装命令如下。 $ npm install -g commitizen $ npm install -g cz-conventional-changelog 创建初始化package.json { "name": "yjauth", "version": "0.0.1", "description": "Description for yjauth", "private": true, "config":{ "commitizen":{ "path": "cz-conventional-changelog" } }, "dependencies":{ }, "devDependencies":{ }, "engines":{ "node": ">=0.12.0" } } 然后,在项目目录里,运行下面的命令,使其支持Angular的Commit message格式。 $ commitizen init cz-conventional-changelog --save --save-exact 以后,凡是用到git commit命令,一律改为使用git cz。这时,就会出现选项,用来生成符合格式的Commit message。 ng-poopy master x git add. ng-poopy master X git cz All commit message lines will be cropped at 100 characters. ? Select the type of change that you're committing: (Use arrow keys) > feat: A new feature fix: A bug fix docs: Documentation only changes style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) refactor: A code change that neither fixes a bug or adds a feature perf: A code change that improves performance test: Adding missing tests chore: Changes to the build process or auxiliary tools and libraries such as documentation generation 四、validate-commit-msg validate-commit-msg用于检查Node项目的Commit message是否符合格式。 安装: npm install ghooks --save-dev 接着,把这个脚本加入Git的hook。下面是在package.json里面使用ghooks,把这个脚本加为commit-msg时运行。 "config":{ "ghooks":{ "commit-msg": "./validate-commit-msg.js" } } 然后,每次git commit的时候,这个脚本就会自动检查Commit message是否合格。如果不合格,就会报错。 $ git add -A $ git commit -m "edit markdown" INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>"! was: edit markdown 五、生成Change log 如果你的所有Commit都符合Angular格式,那么发布新版本时,Change log就可以用脚本自动生成(例1, 例2, 例3)。 生成的文档包括以下三个部分。 New features Bug fixes Breaking changes. 每个部分都会罗列相关的commit,并且有指向这些commit的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。 conventional-changelog就是生成Change log的工具,运行下面的命令即可。 $ npm install -g conventional-changelog-cli $ cd my-project $ conventional-changelog -p angular -i CHANGELOG.md -s This will not overwrite any previous changelog. The above generates a changelog based on commits since the last semver tag that match the pattern of a "Feature", "Fix", "Performance Improvement" or "Breaking Changes". If you first time use this tool and want to generate all previous changelog, you could do $ conventional-changelog -p angular -i CHANGELOG.md -s -r 0 上面命令不会覆盖以前的Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。 如果你想生成所有发布的Change log,要改为运行下面的命令。 $ conventional-changelog -p angular -i CHANGELOG.md -w -r 0 为了方便使用,可以将其写入package.json的scripts字段。 { "scripts":{ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0" } }
2018年12月29日
2018-12-11
Vimium:提升浏览效率的终极 Chrome 插件
Vimium:提升浏览效率的终极 Chrome 插件 在日常浏览网页时,我们往往依赖鼠标进行点击、滚动、切换标签等操作,但这些动作有时显得低效,特别是对于习惯键盘操作的用户来说。今天要推荐的 Chrome 插件——Vimium,能够帮助你实现高效的无鼠标浏览体验。 Vimium 是什么? Vimium 是一款基于 Vim 快捷键的 Chrome 浏览器插件,它允许用户通过键盘快捷键完成网页导航、标签管理、页面滚动等操作,大大提升浏览效率。Vimium 适合那些喜欢 Vim 编辑器或者希望减少鼠标使用的用户。 Vimium 主要功能 快速跳转链接 按 f 键后,每个可点击的链接都会标注一个快捷键,输入相应的字符即可访问该链接。 标签页管理 J / K:切换到左/右侧的标签页。 t:打开新标签页。 x:关闭当前标签页。 X:恢复最近关闭的标签页。 高效的页面滚动 h / l:左右滚动页面。 j / k:上下滚动页面。 gg / G:跳转到页面顶部/底部。 d / u:向下/向上滚动半屏。 网页搜索 / 进入搜索模式,输入关键词并按 Enter 进行搜索。 n / N:跳转到下一个/上一个匹配结果。 复制与打开 URL yy:复制当前页面 URL。 p:打开剪贴板中的 URL。 Vimium 配置与自定义 Vimium 允许用户自定义快捷键,以适应个人习惯。在 Vimium 的选项页面,你可以修改默认的键位绑定,还可以添加黑名单,防止 Vimium 在某些特定网站上生效。 为什么选择 Vimium? 极大提升浏览效率:使用键盘操作比鼠标更快,尤其是在处理大量网页时。 降低鼠标依赖:减少手部移动,提高使用舒适度。 高度可定制:可以根据个人习惯调整快捷键。 结语 如果你是 Vim 爱好者,或者希望提升网页浏览的效率,那么 Vimium 绝对是一个值得尝试的 Chrome 插件。安装后可能需要一定的时间适应,但一旦掌握,就能大幅提升工作和学习效率。快去 Chrome 网上应用店搜索 "Vimium" 下载体验吧!
2018年12月11日
1
...
6
7
8
...
18