前言
終於完成了 android-x86-6.0 源碼的編譯工作,經過簡單的測試,系統正常工作,接下來就是閱讀和修改源碼了。爲了方便修改、提交、測試源碼,想着應該將源碼上傳到 git,但是源碼過於龐大,如果將整個源碼作爲單個項目上傳 git 的話,必將造成每次同步都非常慢。想了下,覺得應該跟官方一樣採用 repo 管理源碼,將源碼拆成多個項目去維護。
搭建 GitLab 服務
想了下,還是先在服務器上搭建 GitLab 來管理源碼,如果以後有必要的話再遷移到公司的 GitLab 上去,現在就當練手吧。
更新軟件源
supo apt-get update
安裝 openssl
sudo apt-get install -y curl openssh-server ca-certificates
安裝 postfix
這裏是配置郵件服務,我跳過了
sudo apt-get install -y postfix
添加 GitLab 包
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
安裝 GitLab
sudo EXTERNAL_URL="http://gitlab.example.com" apt-get install gitlab-ee
注意 將http://gitlab.example.com
更改爲要訪問 GitLab 實例的 URL,這裏我更改爲http://192.168.1.52
關於如何卸載 GitLab
# 停止gitlab
sudo gitlab-ctl stop
# 查看進程
ps -e | grep gitlab
# 刪除所有包含gitlab的文件及目錄
find / -name gitlab | xargs rm -rf
# 卸載
sudo apt-get remove gitlab-ee
# 檢查還有沒有卸載的gitlab相關軟件
dpkg --get-selections | grep gitlab
gitlab-ee deinstall
# 再執行
sudo apt-get --purge remove gitlab-ee
登錄 GitLab
在瀏覽器訪問http://192.168.1.52
,首次訪問是以 root 用戶登錄需要設置用戶密碼。
這樣 GitLab 的服務,基本就搭建完成了。
修改 GitLab 配置
如果需要修改有關 GitLab 服務的相關配置,比如說修改訪問 GitLab 域名等
執行vi /etc/gitlab/gitlab.rb
,編譯配置文件
編輯完成後,執行sudo gitlab-ctl reconfigure
,讓配置生效
添加賬號
因爲使用郵件註冊賬號太過繁瑣,直接切換到 Root 用戶創建賬號。先創建兩個賬號,一個給我的 Mac Pro 使用,另一個 給 Ubuntu 服務器使用,並且都賦予管理員的權限。創建完成後,登錄並且設置 SSH Keys
上傳源碼至 GitLab
參考文檔
創建 manifest.git
manifest.git 倉庫是一個項目清單庫,用來管理 android-x86-6.0 源碼中的 git 倉庫,裏面標明瞭遠程倉庫地址、 項目倉庫的名稱和本地路徑等信息
1.在 GitLab 上創建名爲 Android-x86-6.0 的項目組, 創建名爲 manifest 的空倉庫
2.執行cd ~/android-x86-6.0
指令,切換到源碼文件夾
3.執行[email protected]:android-x86-6.0/manifest.git
,克隆倉庫
4.執行cd manfiest
指令切換到 manfiest 文件夾,創建 default.xml
文件,並且編寫使用 repo 管理的 git 倉庫的名稱、倉庫在本地的路徑和倉庫的 url 地址
下面是我的default.xml
內容,僅供參考:
<?xml version="1.0" encoding="UTF-8" ?>
<manifest>
<remote name="origin" fetch="." /> <!-- ".", 表示使用repo init -u url裏的url,這裏指 [email protected]:android-x86-6.0/ -->
<default remote="origin" revision="master"/>
<project path="abi" name="abi"/>
<project path="art" name="art"/>
<project path="bionic" name="bionic"/>
<project path="bootable" name="bootable"/>
<project path="build" name="build">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project path="dalvik" name="dalvik"/>
<project path="development" name="development"/>
<project path="device" name="device"/>
<project path="external" name="external"/>
<project path="frameworks" name="frameworks"/>
<project path="hardware" name="hardware"/>
<project path="kernel" name="kernel"/>
<project path="libcore" name="libcore"/>
<project path="libnativehelper" name="libnativehelper"/>
<project path="ndk" name="ndk"/>
<project path="packages" name="packages" />
<project path="platform_testing" name="platform_testing"/>
<project path="prebuilts" name="prebuilts"/>
<project path="sdk" name="sdk"/>
<project path="system" name="system"/>
</manifest>
remote 節點中 fetch: 表示遠程倉庫的 url,這裏的 url 是指整個源碼項目的名稱,像 [email protected]:android-x86-6.0/
這樣
project 節點中 path: 表示倉庫在本地的相對路徑,是以 manifest 文件夾位置爲準的
project 節點中 name: 表示倉庫的名稱,這個名稱爲創建倉庫時的名稱。這裏將 name 與 remote 節點中 fetch 拼接就是倉庫的 url,像[email protected]:android-x86-6.0/abi.git
這樣
default: 表示默認的遠程鏈接爲 origin,默認的同步的分支爲 master
5.執行git add . && git commit -m 'Initial commit' && git push
指令,將default.xml
文件推送到服務器上
這樣,manifest.git 倉庫的創建就算基本完成了
批量上傳項目
前面創建default.xml
時已經將源碼工程拆分成 20 個子項目,現在就是怎樣批量去上傳這 20 個項目了,而且上傳項目前,還要在 GitLab 上創建 20 個空的倉庫。如果這些工作手動來操作的話,那就太 Low 了,得寫腳本來自動執行。
下面是我用 Node.js 編寫的腳本,主要是用來自動遷移源碼工程至 GitLab 服務器
GitLabApi
GitLabApi 類,主要調用 Api 創建項目、創建用戶等
/**
* GitLab Api simple example
* doc: https://gitlab.com/help/api/README.md
*/
class GitLabApi {
constructor(baseUrl, token) {
this.baseUrl = baseUrl; // GitLab Server Address
this.headers = {
'PRIVATE-TOKEN': token // GitLab Account Token
};
}
[validhttpstatuscode](code) {
return code >= 200 && code < 300;
}
/**
* @returns Promise
*/
groupList() {
const url = `${this.baseUrl}/groups`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.get(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log('--- group list query success ---');
resolve(body);
} else {
console.log('--- group list query fail ---');
reject(err);
}
});
});
}
/**
*
* @param {*} group_name
* @returns Promise
*/
newGroup(group_name) {
const group_path = group_name.charAt(0).toLowerCase() + group_name.slice(1);
const url = `${this.baseUrl}/groups?name=${group_name}&path=${group_path}&visibility=private`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.post(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log(`--- group: ${group_name}, create success ---`);
resolve(body);
} else {
console.log(`--- group: ${group_name}, create fail ---`);
reject(err);
}
});
});
}
/**
*
* @param {*} id
* @returns Promise
*/
delGroup(id) {
const url = `${this.baseUrl}/groups/${id}`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.delete(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log(`group: ${id}, delete success`);
resolve(body);
} else {
console.log(`group: ${id}, delete fail`);
reject(err);
}
});
});
}
/**
* @returns Promise
*/
projectList() {
const url = `${this.baseUrl}/projects`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.get(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log('--- project list query success ---');
resolve(body);
} else {
console.log('--- project list query fail ---');
reject(err);
}
});
});
}
/**
*
* @param {*} group_id
* @param {*} name
* @returns Promise
*/
newProject(group_id, name) {
const url = group_id
? `${this.baseUrl}/projects?namespace_id=${group_id}&name=${name}`
: `${this.baseUrl}/projects?name=${name}`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.post(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log(`project: ${name}, create success`);
resolve(body);
} else {
console.log(`project: ${name}, create fail`);
reject(err);
}
});
});
}
/**
*
* @param {*} id
* @returns Promise
*/
delProject(id) {
const url = `${this.baseUrl}/projects/${id}`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.delete(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log(`project: ${id}, delete success`);
resolve(body);
} else {
console.log(`project: ${id}, delete fail`);
reject(err);
}
});
});
}
/**
* @returns Promise
*/
userList() {
const url = `${this.baseUrl}/users`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.get(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log('--- user list query success ---');
resolve(body);
} else {
console.log('--- user list query fail ---');
reject(err);
}
});
});
}
/**
*
* @param {*} username
* @returns Promise
*/
userInfo(username) {
const url = `${this.baseUrl}/users?$search=${username}`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.get(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log('--- userinfo query success ---');
resolve(body);
} else {
console.log('--- userinfo query fail ---');
reject(err);
}
});
});
}
/**
*
* @param {*} password
* @param {*} email
* @param {*} username
* @param {*} name
* @returns Promise
*/
newUser(password, email, username, name) {
const url = `${baseUrl}/users?password=${password}&email=${email}&username=${username}&name=${name}`;
const options = {
headers: this.headers
};
request.post(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log('--- add user success ---');
resolve(body);
} else {
console.log('--- add user fail ---');
reject(err);
}
});
}
/**
*
* @param {*} id
* @returns Promise
*/
delUser(id) {
const url = `${this.baseUrl}/users/${id}`;
const options = {
headers: this.headers
};
return new Promise((resolve, reject) => {
request.delete(url, options, (err, res, body) => {
if (this[validhttpstatuscode](res.statusCode)) {
console.log(`--- user: ${id}, delete success ---`);
resolve(body);
} else {
console.log(`--- user: ${id}, delete fail ---`);
reject(err);
}
});
});
}
}
Utils
Utils 類主要是查詢項目列表、執行 Linux 指令等
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
class Utils {
static input(question, isClose = false) {
if (!rl) rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve, reject) => {
rl.question(question, answer => {
if (isClose) rl.close();
resolve(answer);
});
});
// // --- simple example ---
// if (!rl) rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: '請輸入' });
// rl.prompt();
// rl.on('line', line => {
// console.log(line);
// resolve(line);
// rl.close();
// }).on('close', () => {
// console.log('close');
// process.exit();
// });
}
/**
* get projectname
* @returns Promise
*/
static getProjectNameList() {
const path = `${__dirname}/manifest.xml`;
const xml = fs.readFileSync(path).toString();
let list = [];
const names = xml.match(/<project.+/g);
for (let i = 0; i < names.length; i++) {
list.push(names[i].match(/name="\w+"/g)[0].replace(/name="|"/g, ''));
}
console.log(`--- repo project list: total ${list.length} ---\n`, list);
return list;
// // --- async ---
// const path = `${__dirname}/manifest.xml`;
// const rl = readline.createInterface({ input: fs.createReadStream(path) });
// const list = [];
// return new Promise((resolve, reject) => {
// rl.on('line', line => {
// try {
// if (line.includes('<project')) {
// const name = line.match(/name="\w+"/g)[0].replace(/name="|"/g, '');
// list.push(name);
// }
// } catch (error) {
// reject(error);
// }
// });
// rl.on('close', () => {
// console.log(`--- repo project list: total ${list.length} ---\n`, list);
// resolve(list);
// });
// });
}
/**
* run cmd
* @param {*} cmd
* @returns Promise
*/
static execCmd(cmd) {
// --- sync ---
try {
const result = childprocess.execSync(cmd).toString();
console.log(`--- "${cmd}", exec success ---`);
return result;
} catch (error) {
console.log(`--- "${cmd}", exec fail ---`);
console.log(error);
return null;
}
// // --- async ---
// return new Promise((resolve, reject) => {
// childprocess.exec(cmd, (error, stdout, stderr) => {
// if (!error) {
// console.log(`"${cmd}", exec success`);
// resolve(stdout);
// } else {
// console.log(`--- "${cmd}", exec fail ---`);
// reject(stderr);
// }
// });
// });
}
}
start
start 方法就是腳本的入口,爲了人性化,腳本編寫爲終端可交互型的,通過問答式接收參數。
async function start() {
// 1. init args
let gitlab_api_url = await Utils.input(
'please input your gitlab api address ? default: http://192.168.1.52/api/v4 \n'
);
if (!gitlab_api_url) gitlab_api_url = 'http://192.168.1.52/api/v4';
let gitlab_account_private_token = await Utils.input(
'please input your gitlab account private-token ? default: R1G9zxaXG-a9rbrp-ZZA \n'
);
if (!gitlab_account_private_token) gitlab_account_private_token = 'R1G9zxaXG-a9rbrp-ZZA';
let gitlab_group_name = await Utils.input('please input your gitlab group name ? default: Android-x86-6.0 \n');
if (!gitlab_group_name) gitlab_group_name = 'Android-x86-6.0';
let gitlab_group_workspace_url = await Utils.input(
'please input your gitlab group workspace url ? default: [email protected]:android-x86-6.0 \n'
);
if (!gitlab_group_workspace_url) gitlab_group_workspace_url = '[email protected]:android-x86-6.0';
local_workspace_url = await Utils.input(
'please input your local workspace url ? default: /Users/barry/Android/SourceCode/android-x86-6.0 \n',
true
);
if (!local_workspace_url) local_workspace_url = '/Users/barry/Android/SourceCode/android-x86-6.0';
// 2. batch create project
const api = new GitLabApi(gitlab_api_url, gitlab_account_private_token);
const res = await api.newGroup(gitlab_group_name);
const { id } = JSON.parse(res);
const names = Utils.getProjectNameList();
names.push('manifest'); // remeber create manifest project
const result = await Promise.all(names.map(name => api.newProject(id, name)));
if (result) {
// 3. create manifest
Utils.execCmd(`cd ${local_workspace_url} && git clone ${gitlab_group_workspace_url}/manifest.git`);
const manifestData = fs.readFileSync(`${__dirname}/manifest.xml`).toString();
fs.writeFileSync(`${local_workspace_url}/manifest/default.xml`, manifestData);
const migrateData = fs.readFileSync(`${__dirname}/migrate.js`).toString();
fs.writeFileSync(`${local_workspace_url}/manifest/migrate.js`, migrateData);
fs.writeFileSync(`${local_workspace_url}/manifest/.gitignore`, '.DS_Store');
Utils.execCmd(`cd ${local_workspace_url}/manifest && git add . && git commit -m 'Initial commit' && git push`);
names.pop(); // create manifest success, remeber remove manifest project from names
// 4. del local .repo folder
if (fs.existsSync(`${local_workspace_url}/.repo`)) {
Utils.execCmd(`rm -rf ${local_workspace_url}/.repo`);
}
// 5. del local git info
let result = Utils.execCmd(`find ${local_workspace_url} -name '.gitignore' -or -name '.git'`);
result = result.split('\n'); // string transform arry
result.forEach(path => {
if (path.replace(/(^\s*)|(\s*$)/g, '') && !path.startsWith('./manifest')) {
const stat = fs.statSync(path);
if (stat.isDirectory()) {
Utils.execCmd(`rm -rf ${path}`);
} else if (stat.isFile()) {
Utils.execCmd(`mv ${path} ${path}.bak`);
}
}
});
// 6. push soruce code
names.forEach(async name => {
const dirs = fs.readdirSync(`${local_workspace_url}/${name}`);
if (dirs.includes('.git')) {
console.log(`--- del default .git folder in ${name} ---`);
Utils.execCmd(`rm -rf ${local_workspace_url}/${name}/.git`);
}
if (!dirs.includes('.gitignore')) {
console.log(`--- create .gitignore file in ${name} ---`);
fs.writeFileSync(`${local_workspace_url}/${name}/.gitignore`, '.DS_Store');
}
// const cmd = `cd ${local_workspace_url}/${name} && git init && git remote add origin ${gitlab_group_workspace_url}/${name}.git && git add . && git commit -m 'Initial commit' && git push -u origin master`;
Utils.execCmd(`cd ${local_workspace_url}/${name} && git init`);
Utils.execCmd(
`cd ${local_workspace_url}/${name} && git remote add origin ${gitlab_group_workspace_url}/${name}.git`
);
Utils.execCmd(`cd ${local_workspace_url}/${name} && git add .`);
Utils.execCmd(`cd ${local_workspace_url}/${name} && git commit -m 'Initial commit'`);
Utils.execCmd(`cd ${local_workspace_url}/${name} && git push -u origin master`);
});
// 7. repo init from your gitlab
Utils.execCmd(
`cd ${local_workspace_url} && repo init -u ${gitlab_group_workspace_url}/manifest.git && repo sync --force-sync`
);
console.log('--- congratulations,migrate success ----');
}
}
說明 此腳本需要在 node.js 的環境運行,終端下執行node migrate.js
即可
大概就這麼多,此腳本經個人測試過,完美的將整個 Android-x86-6.0 的源碼上傳至 GitLab 服務器。
如果需要完整的腳本,請前往下載
從 GitLab 下載源碼
前面終於把 Android-x86-6.0 的源碼上傳到 GitLab,現在需要在 Ubuntu 服務器上下載 Android-x86-6.0 的源碼
1.安裝 git 工具
2.安裝 repo 工具
3.GitLab 配置 SSH Keys
4.執行repo init -u [email protected]:android-x86-6.0/manifest.git
初始化初始化倉庫
5.執行repo sync
指令同步源碼
總結
這樣整個 Android-x86-6.0 的源碼遷移工作就完成了,當中學習了 GitLab 服務的搭建和 NodeJS 的知識,特別是加強了編寫腳本的能力,算是點小收穫。同時,以後也要多寫腳本,拒絕做重複的工作。