Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
»ùÓÚ git ºÍ CI/CD µÄ¼¯Öл¯ÅäÖùÜÀí·þÎñ
 
  2614  次浏览      30
 2020-12-3 
 
±à¼­ÍƼö:
±¾ÎÄÖ÷Òª½éÉÜÁ˼¯Öл¯ÅäÖùÜÀí·þÎñ¹¦Äܵ㡢 Ö÷Ҫ˼· ¡¢Ö÷Òª¹¦ÄÜÉè¼ÆºÍʵÏÖ¡¢ ÌṩͳһµÄ¿Í»§¶Ë library¡¢library Éè¼ÆµÈµÈÏà¹ØÄÚÈÝ¡£
±¾ÎÄÀ´×ÔÓÚÖªºõ£¬ÓÉ»ðÁú¹ûÈí¼þAnna±à¼­¡¢ÍƼö¡£

¹¦Äܵã

Ê×ÏÈ£¬ÎÒÃÇÏÈÕûÀíһϼ¯Öл¯ÅäÖùÜÀíµÄÖ÷Òª feature£º

¿ÉÒԼǼ¡¢ÉóºËÅäÖõÄÐÞ¸Ä

Ö§³Ö¶àÖÖ»·¾³£¨Éú²ú¡¢²âÊÔ¡¢¿ª·¢¡¢ÑÝʾµÈµÈ£©

ÐÞ¸ÄÅäÖÃÖ®ºó£¬Ó¦ÓõÄÅäÖÃÄܹ»¼°Ê±µÃµ½¸üÐÂ

Ö÷Ҫ˼·

ÎÒÃǵÄÖ÷Ҫ˼·ÊÇ£º½«ÅäÖ÷þÎñÖ±½Óд³ÉÒ»¸ö¶ÀÁ¢µÄ webserver£¬webserver ¶ÔÍâÌṩ http ½Ó¿Ú£¬ÅäÖÃÖ±½ÓдÔÚ webserver µÄ´úÂëµ±ÖУ¬Ã¿´ÎÌá½»´úÂëʱͨ¹ý CI/CD ×Ô¶¯·¢²¼¡£

ÕâÑù×öµÄºÃ´¦ÊÇ£º

¿ÉÒÔÖ±½Óͨ¹ý git À´¼Ç¼¡¢ÉóºËÅäÖÃÊý¾ÝµÄÐ޸ģ¬Ã¿´ÎÓÐÈËÒªÐÞ¸ÄÅäÖÃʱ£¬Ö±½ÓÌá PR£¬leader review ͨ¹ýÖ®ºóºÏ²¢µ½ master ·ÖÖ§

´úÂëºÏ²¢µ½ master ·ÖÖ§Ö®ºó£¬Í¨¹ý CI/CD ×Ô¶¯·¢²¼µ½ÏßÉÏ

¿ÉÄÜ´ó¼Ò»áÓÐÏÂÃæµÄһЩ¹ËÂÇ£º

²»Ó¦¸ÃÖ±½ÓÔÚ´úÂëµ±ÖÐÓ²±àÂë MySQL µÄÕ˺š¢ÃÜÂëÖ®ÀàµÄÃô¸ÐÊý¾Ý£¬ÕâÑùÊDz»°²È«µÄ

¼òµ¥Í¨¹ý http ½Ó¿ÚÀ´¶ÁÈ¡ÅäÖã¬Ð§Âʲ»¸ß

Õë¶ÔµÚÒ»¸öÎÊÌ⣬ÎÒÃÇËãÊÇʹÓÃÁ˵㠡°·´Ä£Ê½¡± °É¡£´úÂë¿Ï¶¨ÊÇҪȷ±£ÔÚ˽ÓдúÂë¿âµ±Öеģ¬ÄãÐèÒªÊÚȨ²ÅÄܹ»·ÃÎÊ´úÂë¿â£¬´ÓÕâ¸ö½Ç¶ÈÀ´Ëµ£¬Ö±½ÓÔÚ´úÂëÀïÃæÐ´ÅäÖÃÊý¾ÝÆäʵҲ²»ÊÇ´óÎÊÌâ£¬ÌØ±ðÊÇÔÚ²úÆ·Ñз¢³õÆÚ£¬Õâ¸öʱºòÍŶӹæÄ£Ò²²»´ó¡£¶øÇÒ£¬Ïñ gitlab¡¢github ÕâÀàµÄ·þÎñ£¬±¾Éí¾ÍÓкܺõÄȨÏÞ¹ÜÀí»úÖÆ£¬¼ÓÉÏ git ±¾Éí¾ÍÊǰ汾¹ÜÀí¹¤¾ß£¬ÎªÊ²Ã´²»³ä·ÖʹÓÃÒ»ÏÂÄØ¡£

µÚ¶þ¸öÎÊÌâÄØ£¬ÆäʵºÍµÚÒ»¸öÒ»Ñù£º³õÆÚ£¬·þÎñѹÁ¦½ÏС£¬ÅäÖÃÊý¾Ý²»¸´ÔÓ£¬Í¨¹ý http ½Ó¿ÚÀ´¶ÁÈ¡ÅäÖã¬ÐÔÄÜÆäʵûÓдóÎÊÌâ¡£

ÕâÖÖ·½°¸µÄÒâÒå¾ÍÔÚÓÚ°ÑÕâ¸öÅäÖùÜÀíµÄ¿Ó¶ùÏÈÕ¼ÉÏ£¬È·±£¸÷¸ö·þÎñÊÇͨ¹ýͳһµÄ½Ó¿ÚÀ´¶ÁÈ¡ÅäÖõģ¬ÈÕºó¿ÉÒÔÂýÂýÓÅ»¯¡£ ʵ¼ù·¢ÏÖ£¬Ëæ×ŲúÆ·µü´ú£¬ÕâÖÖ·½°¸Äܹ»³ÖÐøµÄʱ¼ä»¹ÊÇͦ³¤µÄ£¬Í¶Èë³É±¾»¹ºÜС¡£

Ö÷Òª¹¦ÄÜÉè¼ÆºÍʵÏÖ

ÎÒÃÇ×Ô¼ºµÄÏîĿʹÓà Node.js ¿ª·¢µÄ£¬ËùÒÔÏÂÃæÒÔ Node.js ΪÀý£¬À´ËµÒ»Ï¾ßÌåÉè¼Æ¡£

Ê×ÏÈ£¬ËµÒ»Ï webserver µÄ½Ó¿ÚÉè¼Æ£¬½Ó¿ÚÒª¾¡¿ÉÄܼò»¯£¬ÎÒÃÇÖ»ÌṩÁËÒ»¸ö½Ó¿Ú£º

GET /api/profiles/:profile HTTP/1.1

profile ²ÎÊý±íʾÄãÏëÒªµÄ»·¾³£¬±ÈÈ磺

ÄãÏëÒª²âÊÔ»·¾³µÄÅäÖã¬Ó¦¸Ã·¢ËÍ GET /api/profiles/dev

Èç¹ûÏëҪͬÊ Jack µÄ±¾µØ¿ª·¢»·¾³ÅäÖã¬ÄãÓ¦¸Ã·¢ËÍ GET /api/profiles/jack-local-dev

·µ»ØµÄÊý¾Ý×ÔȻӦ¸ÃÊÇ json Êý¾Ý£¬±ÈÈçÏñÏÂÃæÕâÖÖ£º

{
"revision": "5d41402abc4b2a76b9719d911017c592",
"config": {
"debug": true,
"wechat": {
"appId": "xxxx",
"secret": "xxxxx"
},
"mysql": {
"host": "localhost",
"port": 3306
}
}
}

revision ±íʾÅäÖõİ汾£¬config ¾ÍÊÇʵ¼ÊµÄÅäÖÃÊý¾ÝÀ²¡£

¸ù¾ÝÉÏÃæµÄÉè¼Æ£¬ÎÒÃÇµÄ webserver ·þÎñµÄ´úÂë¿â´ó¸ÅÊÇÏÂÃæÕâÑùµÄ£º

©À©¤©¤ Dockerfile
©À©¤©¤ README.md
©À©¤©¤ app.js
©¸©¤©¤ config
©À©¤©¤ dev.yml
©À©¤©¤ prod.yml
©¸©¤©¤ jack-local-dev.yml

ÆäÖУº

ÓÐÒ»¸ö app.js £¬ÀïÃæ·â×°ÁË http ½Ó¿Ú

ÓÐÒ»¸ö config Îļþ¼Ð£¬ÀïÃæ·ÅÖò»Í¬»·¾³µÄÅäÖÃÎļþ¡£ÎÒÃÇÍÆ¼öʹÓà .yml Îļþ£¬.yml ÎļþдÆðÅäÖÃÆäʵ¸üÇåˬ£¬µ±È» json Ò²¿ÉÒÔ

ÔÙÓÐÒ»¸ö Dockerfile ÓÃÓÚÅäÖþµÏñ´ò°üºÍ×Ô¶¯·¢²¼

ʵÏÖÕâÑùÒ»¸ö½Ó¿Ú£¬app.js µÄ´úÂëÒ²±È½Ï¼òµ¥£¬´ó¸Å¾ÍÏñÏÂÃæÕâÑù£º

const fs = require('fs');
const yaml = require('js-yaml');
const hash = require('object-hash');
const express = require('express');
const app = express();
app.get('/api/profiles/:profile', (req, res) => {
const path = `${__dirname}/config/$ {req.params.profile}.yml`
fs.readFile(path, {
"ecoding": "utf-8"
}, (e, content) => {
if (e) {
return res.status(500).json({
errorId: 'internal-server-error',
errorMsg: e.message
});
}

const config = yaml.safeLoad(content);
const revision = hash(config);
res.json({
config,
revision
});
});
});
app.get('/ping', (req, res) => res.send('pong'));
const PORT = 8080;
app.listen(PORT, () => {
console.log('listening on port', PORT);
});

µ±È»ÄãÒ²¿ÉÒÔÔÚÉÏÃæ¼ÓһЩÐÔÄÜÉϵÄÓÅ»¯¹þ£¬ÌرðÊǼÓÔØ yaml ÎļþµÄ²¿·Ö¡£³ýÁ˶ÁÈ¡ yaml ÅäÖÃÎļþµÄÄÚÈÝÍ⣬ÀïÃæ»¹Í¨¹ý object-hash À´¼ÆËãÁËÅäÖÃµÄ revision£¬·½±ã¿Í»§¶ËÀ´¼ì²éÅäÖÃÊý¾ÝµÄ°æ±¾¸üС£

ÌṩͳһµÄ¿Í»§¶Ë library

Ö÷ÌåÉè¼ÆºÍʵÏÖ¾ÍÊÇÉÏÃæËµµÄÕâЩÄÚÈÝÁË¡£²»¹ý£¬»¹ÓÐÒ»Ï×÷ºÜÖØÒª£¬¾ÍÊÇÌṩͳһµÄ¿Í»§¶Ë library¡£µ±´ó¼ÒʹÓÃͬÑùµÄ¿Í»§¶Ë library À´¶ÁÈ¡ÅäÖõÄʱºò£¬ÅäÖùÜÀíµÄ¿Ó¶ù²ÅÄÜËãÕæÕýÕ¼ºÃ£¬ºóÃæ²Å·½±ãÌæ»»ÅäÖùÜÀí·þÎñµÄ¼¼Êõ·½°¸¡£

library Éè¼Æ

Ê×ÏÈ˵һÏÂÕâ¸ö library µÄ½Ó¿ÚÉè¼Æ°É

config.get(path)

Ìṩһ¸ö get ·½·¨£¬×¢Ò⣺

²ÎÊýÀïÃæÓ¦¸ÃÊÇÒ»¸ö path£¬×¼È·µÄ˵Ӧ¸ÃÊÇÒ»¸ö property path

Õâ¸ö·½·¨Ó¦¸ÃÊÇ Í¬²½Ö´ÐÐ µÄ£¬ËùÒÔÏÂÃæÎÒÌṩÁËÒ»¸ö sync ·½·¨£¬×¨ÃÅÓÃÀ´Í¬²½ÅäÖÃÊý¾Ý

¼ÙÉèÍêÕûµÄÅäÖÃÊý¾ÝÊÇÕâÑùµÄ£º

{
"mysql": {
"host": "111.111.11.11",
"port": 3306,
"username": "root",
"password": "123456"
},
"redis": {
"host": "111.111.11.12",
"port": 6379
},
"wechat": {
"appId": "wx888888888"
},
"secret": "foobar"
}

ÄÇôͨ¹ý get ·½·¨Ó¦¸ÃÄܹ»×öµ½ÏÂÃæÕâЩÊÂÇ飺

config.get("mysql")
// => {"host": "111.111.11.11", "port": 3306, ...}
config.get("wechat.appId")
// => "wx888888888"
config.get()
// => {"mysql": {...}, "wechat": {...}, ...}

Ò²¾ÍÊÇ˵£¬´ó¼Ò¿ÉÒÔͨ¹ý get ·½·¨Áé»îµÄ»ñÈ¡µ½ÅäÖÃÊý¾ÝµÄijһ²¿·Ö¡£Õâ¿éÎÒÃÇʹÓÃÁË object-path Õâ¸öÄ£¿é¡£

config.sync(host, profile, token)

Ìṩһ¸ö sync ·½·¨£¬ÓÃÀ´³õʼ»°ºÍÂÖѵͬ²½ÅäÖÃÊý¾Ý

config.on(event, listener)

Ó¦¸ÃÌṩʼþ»Øµ÷½Ó¿Ú£¬ÓÃÀ´¼ì²âÊÇ·ñÓÐÊý¾Ý·¢Éú±ä»¯£¬Õâ¸ö½Ó¿ÚÔÚ Node.js ·þÎñÖÐÓÐÒ»¶¨Óô¦£¬ÆäËûµÄͬ²½µÄ¼¼Êõ¿ò¼ÜÓ¦¸Ã¾Í²»ÐèÒªÁË¡£

config.mock(object)

×îºó£¬Ó¦¸ÃÓÐÒ»¸ö mock ·½·¨£¬·½±ãÖ§³Ö×Ô¶¯»¯²âÊÔ

һЩ²¹³äÄÚÈÝ

ÕâÀïÏë²¹³ä˵Ã÷µÄÊÇ£¬¹ØÓÚ sync ·½·¨µÄһЩСÎÊÌâ¡£ÉÏÃæËµµ½ get ·½·¨Ó¦¸ÃÊÇÒ»¸öͬ²½·½·¨£¬±Ï¾¹Èç¹û¶ÁÈ¡ÅäÖÃÐÅÏ¢Ò²ÒªÒì²½µÄ»°£¬ÄǶԹ¤³ÌµÄÀ´Ëµ¸´ÔÓ¶È·´¶øÔö¼ÓÁË¡£

ËùÒÔÎÒ¶àÉè¼ÆÁËÒ»¸ö sync ·½·¨¡£ÔÚ Node.js ÏîÄ¿ÖУ¬Ó¦ÓÃÆô¶¯Ö®Ç°£¬Ó¦¸ÃÏȵ÷Óà sync ·½·¨£¬ÂÖѵͬ²½ÅäÖÃÊý¾Ý¡£ÕâÑù±£Ö¤ get ·½·¨±»µ÷ÓõÄʱºò£¬Ê¼ÖÕÊÇÄܹ»·µ»ØÊý¾ÝµÄ¡£

»¹ÓÐÒ»µã¾ÍÊÇ£¬sync ·½·¨±»µ÷ÓõÄʱºò£¬Ó¦¸ÃÏÈ·¢Ò»¸ö ͬ²½µÄ http ·½·¨À´»ñÈ¡Êý¾Ý£¬Õâ¿éÎÒÃÇʹÓÃÁË sync-request À´ÊµÏÖ¡£

×îºó£¬²¹³äÒ»ÏÂÖ÷ÒªµÄʵÏÖ´úÂ룬¹©´ó¼Ò²Î¿¼£º

const EventEmitter = require('events').EventEmitter;
const objectPath = require('object-path');
class Config {
constructor(interval) {
this.interval = interval || 5000;
this.emitter = new EventEmitter();
}

sync(host, profile, token) {
this.host = host;
this.profile = profile;
this.token = token;
this.data = loadConfigSync(); // Ê×ÏÈͬ²½»ñÈ¡ÅäÖÃÊý¾Ý
setTimeOut(() => this.watch(), this.interval); // Ö®ºó£¬¶¨Ê±ÂÖѵÊý¾Ý
}

get(path) {
return objectPath.get(this.data.config, path);
}

loadConfigSync() {
// Õⲿ·Ö´úÂë¾ÍÏÈÊ¡ÂÔÁË~
}

async loadConfigAsync() {
// Õⲿ·Ö´úÂë¾ÍÏÈÊ¡ÂÔÁË~
}

async watch() {
const result = await this.loadConfigAsync();
if (result.revision !== this.data.revision) {
this.data = result;
this.emitter.emit('update', this.data.config);
}

setTimeout(() => this.watch(), this.interval);
}
}
module.exports = new Config();

ʹÓÿͻ§¶Ë library µÄÒ»°ãÌ×·£º

// server.js
const config = require('config-module-name');
// 1. µ÷Óà sync ·½·¨¼ÓÔØÅäÖÃ
config.sync(process.env.CONFIG_HOST, process.env.CONFIG_PROFILE, process.env.CONFIG_TOKEN);
// 2. Æô¶¯Êµ¼ÊÏîÄ¿µÄ WebServer
const server = new WebServer();
server.serve();

Ôö¼ÓÅäÖø²¸Ç¹¦ÄÜ

ÉÏÃæµÄ webserver Éè¼Æ»¹ÊǼòµ¥ÁËһЩ£¬ÒòΪƽʱÎÒÃÇÅäÖ÷þÎñµÄʱºò£¬¾­³£»áÓÐһϵÁÐͨÓõÄÅäÖ㬶øÃ¿¸ö»·¾³ÀïÃæ¿ÉÄܸ÷ÓÐһЩÉÙÁ¿ÌØÊâµÄÅäÖá£

ΪÁ˽â¾öÕâ¸öÎÊÌ⣬ÎÒÃÇÔÚÇ°ÃæµÄ·½°¸»ù´¡Ö®ÉÏ£¬¿ª·¢ÁËÒ»¸ö¼òµ¥µÄÅäÖø²¸Ç¹¦ÄÜ¡£ÎÒÃÇÊÇÕâô×öµÄ£º

ÔÚ config Îļþ¼Ðµ±ÖÐÌṩһ¸ö defaul.yml ÅäÖÃÎļþ£¬ÔÚÕâ¸öÎļþµ±ÖÐÈ¥±£´æÍ¨ÓõÄÅäÖÃÊý¾Ý

¼ÙÉ裬ÏÖÔÚÒª·ÃÎÊ dev »·¾³µÄÅäÖã¬webserver ¾Í°Ñ dev.yml ºÍ default.yml ÅäÖÃÎļþ¶¼¶ÁÈ¡³öÀ´£¬½« dev.yml ºÍ default.yml ÖØºÏµÄ²¿·Ö merge µ½Ò»Æð£¬Õâ¿éÎÒÃÇʹÓõÄÒ»¸ö½Ð×ö deepmerge µÄÄ£¿éÀ´ÊµÏÖµÄ

ÏÖÔÚ¾ÙÒ»¸öʵ¼ÊµÄÀý×Ó£¬¼ÙÉèÉú²ú£¨prod£©ºÍ¿ª·¢»·¾³£¨dev£©¾ÍÊý¾Ý¿âµÄÃû³Æ²»Í¬£¬Ã»ÓÐÔö¼ÓÅäÖø²¸Ç¹¦ÄÜ֮ǰ£¬ÅäÖÃÎļþÊÇÕâÑùµÄ£º

# prod.yml
mysql:
host: localhost
port: 3306
username: root
password: root
database: prod
# dev.yml
mysql:
host: localhost
port: 3306
username: root
password: root
database: de

Ôö¼ÓÁËÅäÖø²¸ÇµÄ¹¦ÄÜÖ®ºó£¬ÅäÖÃÎļþ±ä³ÉÁËÏÂÃæÕâ¸öÑù×Ó£º

# default.yml
mysql:
host: localhost
port: 3306
usrename: root
password: root
# prod.yml
mysql:
database: prod
# dev.yml
mysql:
database: dev

ÔÚʵ¼ÊµÄÏîÄ¿µ±ÖУ¬Ôö¼ÓÅäÖø²¸ÇµÄÒ»¸ö×î´óºÃ´¦ÊÇ£¬ÓÐеÄͬʼÓÈëÏîĿʱ£¬ËûÐèÒªÔö¼ÓµÄÅäÖÃÄÚÈݾͻáÉٺܶ࣬¶ø²»ÐèҪȫÁ¿µÄ copy Ò»·Ý±ðÈ˵ÄÅäÖÃÎļþ£¬Ö÷ÌåµÄÅäÖö¼¿ÉÒԷŵ½ default.yml ÎļþÖС£

°²È«ÎÊÌâ

Õâ¸ö·½°¸ÏÖÔÚ»¹ÓÐһЩÃ÷ÏԵݲȫÎÊÌ⣺

½Ó¿Ú·ÃÎÊûÓÐÔö¼Ó¼øÈ¨

ÓÐЩÊý¾Ý¾ÍÊDz»Ï£Íûдµ½´úÂëµ±ÖÐÈ¥£¬¸ÃÔõô°ì

¹ØÓÚ½Ó¿Ú¼øÈ¨£¬ÎÒÃǵĽâ¾ö·½°¸ÊÇÌṩһ¸ö token ÁÐ±í£¬token Êdz£Á¿µÄ UUID »òÕßËæ»ú×Ö·û´®¼´¿É¡£ÁíÍâÇ¿ÖÆÒªÇóʹÓà https À´·ÃÎʽӿڣ¬²»ÒªÖ±½ÓÔÚǰ¶Ë¶ÁÈ¡ÅäÖá£

Èç¹ûÓÐЩÊý¾Ý¾ÍÊDz»Ï£Íûдµ½´úÂëµ±ÖÐÈ¥£¬¸ÄÔõô°ì£¿

ÎÒÃǽ¨ÒéÔö¼ÓÒ»¸ö»·¾³±äÁ¿×¢ÈëµÄ feature£¬±ÈÈçÅäÖÃÎļþ¸Äд³ÉÕâÑù£º

mysql:
password: ${MYSQL_PASSWORD}

½Ó¿ÚÔÚ·µ»ØÊý¾Ý֮ǰ£¬Ôö¼ÓÒ»µÀ¹¤Ðò£¬½«ÉÏÃæµÄ ${MYSQL_PASSWORD} ÕâÀàµÄ±í´ïʽ½âÎö³öÀ´£¬È»ºó½«»·¾³±äÁ¿×¢Èë½øÈ¥¡£ÎÒÃÇĿǰÊÇʹÓÃÕýÔò±í´ïʽ¼òµ¥´Ö±©µÄ´¦ÀíµÄ£¬´ó¸Å¾ÍÊÇÕâÑù£º

const traverse = require('traverse');
const delimeter = /\$\{(.+?)\}/g;
function enjectEnv(config) {
return traverse(config).map(value => {
value.replace(delimeter, (match, p1) => {
return process.env[p1] || "";
})
})
}

ͨ¹ýÕâÖÖ·½Ê½£¬Äã¾Í¿ÉÒÔͨ¹ý»·¾³±äÁ¿È¥ÅäÖÃһЩÃô¸ÐÐÅÏ¢ÁË¡£

×ܽá

×ܽáһϣ¬ÕâÑùÒ»¸ö·½°¸£¬Ö÷ÒªµÄ¹¤×÷£º

»ùÓÚ git ºÍ CI/CD ´î½¨ÅäÖ÷þÎñ

ÌṩͳһµÄ¿Í»§¶Ë library

À©Õ¹¹¦ÄÜ£¬Ôö¼ÓÅäÖø²¸Ç»úÖÆ

Ìṩ¼òµ¥µÄ½Ó¿Ú¼øÈ¨ºÍ»·¾³±äÁ¿×¢Èë

ÕâÑùÒ»¸ö·½°¸£¬ÆäʵÔÚ²úÆ·³õÆÚ½×¶ÎÓ¦¸Ã×ã¹»ºÃÓÃÁË¡£ÕâÖÖ·½°¸µÄºÃ´¦¾ÍÊÇ¿ìËÙÕ¼¿Ó£¬½«ÅäÖùÜÀí»úÖÆ¹Ì»¯ÏÂÀ´¡£ÕûÌ×·½°¸³ä·ÖʹÓÃÁË git ºÍ CI/CD£¬Õû¸ö·þÎñÒ²ºÜÇá£¬ÍÆ¼ö´ó¼Ò³¢ÊÔÒ»ÏÂ~

 
   
2614 ´Îä¯ÀÀ       30
Ïà¹ØÎÄÕÂ

ÿÈÕ¹¹½¨½â¾ö·½°¸
ÈçºÎÖÆ¶¨ÓÐЧµÄÅäÖùÜÀíÁ÷³Ì
ÅäÖùÜÀíÖ÷Òª»î¶¯¼°ÊµÏÖ·½·¨
¹¹½¨¹ÜÀíÈëÃÅ
Ïà¹ØÎĵµ

ÅäÖùÜÀíÁ÷³Ì
ÅäÖùÜÀí°×ƤÊé
CM09_CÅäÖùÜÀí±ê×¼
ʹÓÃSVN½øÐа汾¿ØÖÆ
Ïà¹Ø¿Î³Ì

ÅäÖùÜÀíʵ¼ù
ÅäÖùÜÀí·½·¨¡¢¹¤¾ßÓëÓ¦ÓÃ
¶à²ã´Î¼¯³ÉÅäÖùÜÀí
²úÆ··¢²¼¹ÜÀí
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]
 
×îÐÂÎÄÕÂ
gitÔ­Àíͼ½â
Git·ÖÖ§¹ÜÀíʵ¼ù
GitѧϰºÍÏîĿӦÓÃʵÀý
Git ÌìÌìÓà µ«ÊÇ Git Ô­ÀíÄãÁ˽âÂð£¿
¶Ô±È Git Óë SVN£¬ÕâÆª½²µÄºÜÒ×¶®
×îпγÌ
Git°æ±¾¿ØÖÆÏµÍ³
ÅäÖùÜÀíÓë³ÖÐø¼¯³Éʵ¼ù
ÅäÖùÜÀí·½·¨¡¢Êµ¼ù¡¢¹¤¾ßÓëÓ¦ÓÃ
³ÖÐø¼¯³ÉÓëÃô½Ý¿ª·¢
ÅäÖùÜÀíʵ¼ù£¨´Ó×éÖ¯¼¶µ½ÏîÄ¿¼¶£©
³É¹¦°¸Àý
ijµ¥Î»Ñз¢ÖÐÐÄ ²úÆ·¼¯³ÉÓë·þÎñƽ̨
ijµç×ÓÖÆÔìÉÌ ÅäÖùÜÀíÓë³ÖÐø¼¯³É
±±¾© ÅäÖùÜÀíÓë³ÖÐø¼¯³Éʵ¼ù
½ðÑÅÍØ ·Ö²¼Ê½³ÖÐø¼¯³É¹¤¾ßÁ´
±±¾© ³ÖÐø¼¯³É²âÊÔ×î¼Ñʵ¼ù