From 1d82ec98a27959c3ea630221efb57221bf70f414 Mon Sep 17 00:00:00 2001
From: lipingEmmaSiguyi <1477412247@qq.com>
Date: Thu, 16 Jun 2022 15:18:51 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=A4=E4=B8=AA?=
=?UTF-8?q?=E8=84=9A=E6=9C=AC=E7=94=A8=E4=BA=8E=E5=B0=86=E5=85=BC=E5=AE=B9?=
=?UTF-8?q?=E6=80=A7=E6=B8=85=E5=8D=95=E7=BD=91=E9=A1=B5=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=88=B0excel=E8=A1=A8=E6=A0=BC=E4=B8=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tools/data_collection/README.md | 18 ++++++++
.../get_ecology_compatiable_list.py | 38 +++++++++++++++++
.../get_kylinos_compatiable_list.py | 41 +++++++++++++++++++
3 files changed, 97 insertions(+)
create mode 100644 tools/data_collection/get_ecology_compatiable_list.py
create mode 100644 tools/data_collection/get_kylinos_compatiable_list.py
diff --git a/tools/data_collection/README.md b/tools/data_collection/README.md
index a95084c..71bfa42 100644
--- a/tools/data_collection/README.md
+++ b/tools/data_collection/README.md
@@ -83,3 +83,21 @@ dnf install
...
}
```
+
+## get_ecology_compatiable_list.py
+
+### 用法
+
+```
+python3 get_ecology_compatiable_list.py
+ 将https://ecology.chinauos.com上的兼容性清单导出到excel表格: ecology_uos_compatiable_list.xls
+```
+
+## get_kylinos_compatiable_list.py
+
+### 用法
+
+```
+python3 get_kylinos_compatiable_list.py
+ 将https://eco.kylinos.cn上的兼容性清单导出到excel表格:kylinos_compatiable_list.xls
+```
diff --git a/tools/data_collection/get_ecology_compatiable_list.py b/tools/data_collection/get_ecology_compatiable_list.py
new file mode 100644
index 0000000..c5dc108
--- /dev/null
+++ b/tools/data_collection/get_ecology_compatiable_list.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# coding=utf-8
+import urllib3
+import json
+import xlwt
+
+class EcologyUos:
+ def __init__(self):
+ self.all_data = {}
+
+ def pull_all_data(self):
+ http = urllib3.PoolManager(10)
+ req = http.request('GET', 'https://ecology.chinauos.com/analysis/jingpin/search?page=1&limit=10&status=done&framework=all&url=&query=')
+
+ encoded_data = json.loads(req.data.decode('utf-8'))
+ count = encoded_data["count"]
+ url = "https://ecology.chinauos.com/analysis/jingpin/search?page=1&limit=" + str(count) + "&status=done&framework=all&url=&query="
+ req_all_data_hash = json.loads(http.request('GET', url).data.decode('utf-8'))
+ self.all_data = req_all_data_hash
+
+
+ def write_to_xlsx(self):
+ sheet_data = self.all_data["data"]
+ workbook = xlwt.Workbook(encoding='utf-8')
+ sheet1 = workbook.add_sheet("统信兼容性清单")
+ for col, head in enumerate(sheet_data[0].keys()):
+ sheet1.write(0, col, head)
+
+ for row, sheet_row in enumerate(sheet_data):
+ for col, row_key in enumerate(sheet_row.keys()):
+ sheet1.write(row+1, col, sheet_row[row_key])
+
+ workbook.save('./ecology_uos_compatiable_list.xls')
+
+if __name__ == '__main__':
+ eu = EcologyUos()
+ eu.pull_all_data()
+ eu.write_to_xlsx()
diff --git a/tools/data_collection/get_kylinos_compatiable_list.py b/tools/data_collection/get_kylinos_compatiable_list.py
new file mode 100644
index 0000000..60f222c
--- /dev/null
+++ b/tools/data_collection/get_kylinos_compatiable_list.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# coding=utf-8
+import urllib3
+import json
+import xlwt
+
+class EcologyUos:
+ def __init__(self):
+ self.all_data = []
+
+ def pull_all_data(self):
+ http = urllib3.PoolManager(10)
+ req = http.request('GET', 'https://eco.kylinos.cn/home/compatible/index.html?system_class=1&system_id=&small_version_id=&is_plan=0&page=1&limit=20')
+
+ encoded_data = json.loads(req.data.decode('utf-8'))
+ count = encoded_data["count"]
+ page_num = (count // 20) + 1
+ req_all_data_list = []
+ for i in range(page_num):
+ url = "https://eco.kylinos.cn/home/compatible/index.html?system_class=1&system_id=&small_version_id=&is_plan=0&page=" + str(i+1) + "&limit=20"
+ req_data = json.loads(http.request('GET', url).data.decode('utf-8'))["data"]
+ req_all_data_list.extend(req_data)
+ self.all_data = req_all_data_list
+
+ def write_to_xlsx(self):
+ sheet_data = self.all_data
+ workbook = xlwt.Workbook(encoding='utf-8')
+ sheet1 = workbook.add_sheet("麒麟软件兼容性清单")
+ for col, head in enumerate(sheet_data[0].keys()):
+ sheet1.write(0, col, head)
+
+ for row, sheet_row in enumerate(sheet_data):
+ for col, row_key in enumerate(sheet_row.keys()):
+ sheet1.write(row+1, col, sheet_row[row_key])
+
+ workbook.save('./kylinos_compatiable_list.xls')
+
+if __name__ == '__main__':
+ eu = EcologyUos()
+ eu.pull_all_data()
+ eu.write_to_xlsx()
--
Gitee
From 2a079c895cc0e0bebde935228ae9764109efa554 Mon Sep 17 00:00:00 2001
From: lipingEmmaSiguyi <1477412247@qq.com>
Date: Fri, 8 Jul 2022 10:24:10 +0800
Subject: [PATCH 2/4] modify get_kylinos_compatiable_list.py
[why]
The kylinos compatiable list is too long, need to enable multi-threaded
data crawling to improve efficiency.
---
.../get_kylinos_compatiable_list.py | 122 +++++++++++++-----
1 file changed, 87 insertions(+), 35 deletions(-)
diff --git a/tools/data_collection/get_kylinos_compatiable_list.py b/tools/data_collection/get_kylinos_compatiable_list.py
index 60f222c..94adacc 100644
--- a/tools/data_collection/get_kylinos_compatiable_list.py
+++ b/tools/data_collection/get_kylinos_compatiable_list.py
@@ -2,40 +2,92 @@
# coding=utf-8
import urllib3
import json
-import xlwt
-
-class EcologyUos:
- def __init__(self):
- self.all_data = []
-
- def pull_all_data(self):
- http = urllib3.PoolManager(10)
- req = http.request('GET', 'https://eco.kylinos.cn/home/compatible/index.html?system_class=1&system_id=&small_version_id=&is_plan=0&page=1&limit=20')
-
- encoded_data = json.loads(req.data.decode('utf-8'))
- count = encoded_data["count"]
- page_num = (count // 20) + 1
- req_all_data_list = []
- for i in range(page_num):
- url = "https://eco.kylinos.cn/home/compatible/index.html?system_class=1&system_id=&small_version_id=&is_plan=0&page=" + str(i+1) + "&limit=20"
- req_data = json.loads(http.request('GET', url).data.decode('utf-8'))["data"]
- req_all_data_list.extend(req_data)
- self.all_data = req_all_data_list
-
- def write_to_xlsx(self):
- sheet_data = self.all_data
- workbook = xlwt.Workbook(encoding='utf-8')
- sheet1 = workbook.add_sheet("麒麟软件兼容性清单")
- for col, head in enumerate(sheet_data[0].keys()):
- sheet1.write(0, col, head)
-
- for row, sheet_row in enumerate(sheet_data):
- for col, row_key in enumerate(sheet_row.keys()):
- sheet1.write(row+1, col, sheet_row[row_key])
-
- workbook.save('./kylinos_compatiable_list.xls')
+import openpyxl
+from queue import Queue
+import threading
+
+'''
+整体的思路:
+1、构造任务队列pageQueue ,存放所有要爬取的页面url
+2、用多线程爬虫,然后将抓取的页面内容存放到all_data中
+3、然后存放到的xlsx文件中
+'''
+
+class Crawl_thread(threading.Thread):
+ '''
+ 抓取线程类,注意需要继承线程类Thread
+ '''
+ def __init__(self,thread_id,queue):
+ threading.Thread.__init__(self) # 需要对父类的构造函数进行初始化
+ self.thread_id = thread_id
+ self.queue = queue # 任务队列
+
+ def run(self):
+ '''
+ 线程在调用过程中就会调用对应的run方法
+ :return:
+ '''
+ print('启动线程:',self.thread_id)
+ self.crawl_spider()
+ print('退出了该线程:',self.thread_id)
+
+ def crawl_spider(self):
+ while True:
+ if self.queue.empty(): #如果队列为空,则跳出
+ break
+ else:
+ page = self.queue.get()
+ print("第 %d 页" %(page))
+ print('当前工作的线程为:',self.thread_id," 正在采集:",page)
+ try:
+ http = urllib3.PoolManager(10)
+ url = "https://eco.kylinos.cn/home/compatible/index.html?system_class=1" \
+ "&system_id=&small_version_id=&is_plan=0&page=" + str(page) + "&limit=20"
+ req_data = json.loads(http.request('GET', url).data.decode('utf-8'))["data"]
+ all_data.extend(req_data)
+ print("数据队列长度:",len(all_data),end="\n")
+ except Exception as e:
+ print('采集线程错误',e)
+
+all_data = []
+def main():
+ http = urllib3.PoolManager(10)
+ req = http.request('GET', 'https://eco.kylinos.cn/home/compatible/index.html?'
+ 'system_class=1&system_id=&small_version_id=&is_plan=0&page=1&limit=20')
+
+ encoded_data = json.loads(req.data.decode('utf-8'))
+ count = encoded_data["count"]
+ page_num = (count // 20) + 1
+ pageQueue = Queue(count) # 任务队列,存放网页的队列
+ for page in range(1, page_num+1):
+ pageQueue.put(page) # 构造任务队列
+ # 初始化采集线程
+ crawl_threads = []
+ crawl_name_list = ['crawl_' + str(i) for i in range(1, 6)] # 总共构造5个爬虫线程
+ for thread_id in crawl_name_list:
+ thread = Crawl_thread(thread_id, pageQueue) # 启动爬虫线程
+ thread.start() # 启动线程
+ crawl_threads.append(thread)
+
+ # 等待队列情况,先进行网页的抓取
+ while not pageQueue.empty():
+ # 不为空,则继续阻塞
+ pass
+
+ # 等待所有线程结束
+ for t in crawl_threads:
+ t.join()
+
+ wb = openpyxl.Workbook()
+ sheet = wb.active
+
+ for col, head in enumerate(all_data[0].keys()):
+ sheet.cell(row=1, column=col+1, value=head)
+
+ for row, sheet_row in enumerate(all_data):
+ for col, row_key in enumerate(sheet_row.keys()):
+ sheet.cell(row=row+2, column=col+1, value=sheet_row[row_key])
+ wb.save(filename="kylinos_compatiable_list.xlsx")
if __name__ == '__main__':
- eu = EcologyUos()
- eu.pull_all_data()
- eu.write_to_xlsx()
+ main()
--
Gitee
From 2e37bd69ed7d7824d9cf58e05d3e5afd031e4311 Mon Sep 17 00:00:00 2001
From: lipingEmmaSiguyi <1477412247@qq.com>
Date: Mon, 11 Jul 2022 15:01:11 +0800
Subject: [PATCH 3/4] add 2 scripts to crawl suse hardware data and top 100
docker.
---
tools/data_collection/get_docker_info.py | 88 +++++++++
.../get_suse_hardware_compatiable_list.py | 173 ++++++++++++++++++
2 files changed, 261 insertions(+)
create mode 100644 tools/data_collection/get_docker_info.py
create mode 100644 tools/data_collection/get_suse_hardware_compatiable_list.py
diff --git a/tools/data_collection/get_docker_info.py b/tools/data_collection/get_docker_info.py
new file mode 100644
index 0000000..c8886a7
--- /dev/null
+++ b/tools/data_collection/get_docker_info.py
@@ -0,0 +1,88 @@
+# coding=utf-8
+import json
+import openpyxl
+import requests
+import re
+
+INLINE_LINK_RE = re.compile(r'\[([^\]]+)\]\(([^)]+)\)')
+FOOTNOTE_LINK_TEXT_RE = re.compile(r'\[([^\]]+)\]\[(\d+)\]')
+FOOTNOTE_LINK_URL_RE = re.compile(r'\[(\d+)\]:\s+(\S+)')
+
+class Spider:
+ def __init__(self):
+ self.url = "https://hub.docker.com/api/content/v1/products/search?operating_system=linux&page_size=25&q="
+ self.response = []
+
+ def pull_all_data(self):
+ payload = {}
+ headers = {
+ 'Accept': 'application/json',
+ 'Search-Version': 'v3'
+ }
+ response = json.loads(requests.request("GET", self.url, headers=headers, data=payload).text)
+ page_size = response["page_size"]
+ numFound = 100
+ page_num = numFound // page_size
+ for i in range(1, page_num+1):
+ url = "https://hub.docker.com/api/content/v1/products/search?operating_system=linux&page_size=25&page=" + str(i)
+ payload = {}
+ headers = {
+ 'Accept': 'application/json',
+ 'Search-Version': 'v3'
+ }
+ response = json.loads(requests.request("GET", url, headers=headers, data=payload).text)
+ next_url = ""
+ for item in response['summaries']:
+ name = item['name']
+ if name.split("/")[0] == item['name']:
+ name = "-".join([i.lower() for i in name.split(" ")])
+ next_url = "https://hub.docker.com/v2/repositories/library/" + name
+ else:
+ next_url = "https://hub.docker.com/v2/repositories/" + name.split('/')[0] + "/" + name.split('/')[1] + "/"
+ item["dockerhub_url"] = next_url
+ info = self.get_more_information(next_url)
+ dictMerged2 = dict(item, **info)
+ self.response.append(dictMerged2)
+
+ def get_more_information(self,url):
+ response = json.loads(requests.request("GET", url).text)
+ links = {}
+ if response.get("full_description"):
+ links = self.find_md_links(response['full_description'])
+ return links
+
+ def find_md_links(self, md):
+ """ Return dict of links in markdown """
+
+ links = dict(INLINE_LINK_RE.findall(md))
+ footnote_links = dict(FOOTNOTE_LINK_TEXT_RE.findall(md))
+ footnote_urls = dict(FOOTNOTE_LINK_URL_RE.findall(md))
+
+ for key, value in footnote_links.items():
+ footnote_links[key] = footnote_urls[value]
+ links.update(footnote_links)
+ urls = {}
+ url_list = []
+ for i in links.values():
+ if "Dockerfile" in i:
+ url_list.append(i)
+ urls["urls"] = "\n".join(url_list)
+
+ return urls
+
+ def write_to_xlsx(self):
+ wb = openpyxl.Workbook()
+ sheet = wb.active
+
+ for col, head in enumerate(self.response[0].keys()):
+ sheet.cell(row=1, column=col + 1, value=head)
+
+ for row, sheet_row in enumerate(self.response):
+ for col, row_key in enumerate(sheet_row.keys()):
+ sheet.cell(row=row + 2, column=col + 1, value=str(sheet_row[row_key]))
+ wb.save(filename="docker_top_100.xlsx")
+
+if __name__ == '__main__':
+ sp = Spider()
+ sp.pull_all_data()
+ sp.write_to_xlsx()
diff --git a/tools/data_collection/get_suse_hardware_compatiable_list.py b/tools/data_collection/get_suse_hardware_compatiable_list.py
new file mode 100644
index 0000000..119be21
--- /dev/null
+++ b/tools/data_collection/get_suse_hardware_compatiable_list.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python3
+# coding=utf-8
+import urllib3
+from queue import Queue
+import threading
+import re
+import html
+import openpyxl
+
+'''
+整体的思路:
+1、构造任务队列pageQueue ,存放所有要爬取的页面url
+2、用多线程爬虫将抓取的页面内容存放到data_queue中
+3、用多线程程序对data_queue中的页面内容进行解析,分别提取id, product, os, company, category,
+'''
+
+class Crawl_thread(threading.Thread):
+ '''
+ 抓取线程类,注意需要继承线程类Thread
+ '''
+ def __init__(self,thread_id,queue):
+ threading.Thread.__init__(self)
+ self.thread_id = thread_id
+ self.queue = queue # 任务队列
+
+ def run(self):
+ '''
+ 线程在调用过程中就会调用对应的run方法
+ :return:
+ '''
+ print('启动线程:',self.thread_id)
+ self.crawl_spider()
+ print('退出了该线程:',self.thread_id)
+
+ def crawl_spider(self):
+ while True:
+ if self.queue.empty(): #如果队列为空,则跳出
+ break
+ else:
+ page = self.queue.get()
+ print('当前工作的线程为:',self.thread_id," 正在采集:",page)
+ try:
+ http = urllib3.PoolManager(10)
+ url = "https://www.suse.com/nbswebapp/yesBulletin.jsp?bulletinNumber=" + str(page)
+ page_data = http.request('GET', url)
+ status_code = page_data.status
+ if status_code == 200:
+ page_html = page_data.data.decode('utf-8')
+ data_queue.put((page_html, page))
+ except Exception as e:
+ print('采集线程错误',e)
+
+class Parser_thread(threading.Thread):
+ '''
+ 解析网页的类,就是对采集结果进行解析,也是多线程方式进行解析
+ '''
+ def __init__(self,thread_id,queue):
+ threading.Thread.__init__(self)
+ self.thread_id = thread_id
+ self.queue = queue
+
+ def run(self):
+ print('启动线程:', self.thread_id)
+ while not flag:
+ try:
+ item = self.queue.get(False) # get参数为false时队列为空,会抛出异常
+ if not item:
+ pass
+ self.parse_data(item)
+ except Exception as e:
+ pass
+ print('退出了该线程:', self.thread_id)
+ def parse_data(self,item):
+ '''
+ 解析网页内容的函数
+ :param item:
+ :return:
+ '''
+ try:
+ print("开始解析:", self.thread_id)
+ page = item[0]
+ id = item[1]
+ product = ""
+ category = ""
+ company = ""
+ os = ""
+ regex = re.compile('
(.*?)', re.S)
+ result = regex.findall(page)
+ regex_category = re.compile('(.*?)', re.S)
+ result_category = regex_category.findall(page)
+ regex_company = re.compile('For more information regarding the specific test configuration, please contact:
(.*?)
(.*?)(.*?)', re.S)
+ result_company = regex_company.findall(page)
+ regex_os = re.compile('Operating Systems:(.*?)
(.*?)(.*?)(.*?)| (.*?) | ', re.S)
+ result_os = regex_os.findall(page)
+
+ for item in result:
+ product += html.unescape(item.strip()).strip()
+
+ for item in result_category:
+ category += html.unescape(item.strip()).strip()
+
+ for item in result_company:
+ company += html.unescape(item[-1].strip()).strip()
+
+ for item in result_os:
+ os += html.unescape(item[-1].strip()).strip()
+
+ response = {
+ 'id': id,
+ 'product': product,
+ 'category': category,
+ 'company': company,
+ 'os': os
+ }
+
+ all_data.append(response)
+ except Exception as e:
+ print('parse: ',e)
+
+
+data_queue = Queue(50)
+all_data = []
+flag = False
+def main():
+ pageQueue = Queue(100) # 任务队列,存放网页的队列
+ for page in range(101400, 101500):
+ pageQueue.put(page) # 构造任务队列
+ # 初始化采集线程
+ crawl_threads = []
+ crawl_name_list = ['crawl_' + str(i) for i in range(1, 6)] # 总共构造5个爬虫线程
+ for thread_id in crawl_name_list:
+ thread = Crawl_thread(thread_id, pageQueue) # 启动爬虫线程
+ thread.start() # 启动线程
+ crawl_threads.append(thread)
+
+ # 初始化解析线程
+ parse_thread = []
+ parser_name_list = ['parse_' + str(i) for i in range(1, 6)]
+ for thread_id in parser_name_list:
+ thread = Parser_thread(thread_id,data_queue)
+ thread.start() # 启动线程
+ parse_thread.append(thread)
+
+ # 等待队列情况,先进行网页的抓取
+ while not pageQueue.empty():
+ pass # 不为空,则继续阻塞
+
+ # 等待所有线程结束
+ for t in crawl_threads:
+ t.join()
+ # 等待队列情况,对采集的页面队列中的页面进行解析,等待所有页面解析完成
+ while not data_queue.empty():
+ print("队列里面没有数据")
+ pass
+ # 通知线程退出
+ global flag
+ flag = True
+ for t in parse_thread:
+ t.join() # 等待所有线程执行到此处再继续往下执行
+
+ wb = openpyxl.Workbook()
+ sheet = wb.active
+
+ for col, head in enumerate(all_data[0].keys()):
+ sheet.cell(row=1, column=col+1, value=head)
+
+ for row, sheet_row in enumerate(all_data):
+ for col, row_key in enumerate(sheet_row.keys()):
+ sheet.cell(row=row+2, column=col+1, value=sheet_row[row_key])
+ wb.save(filename="suse_compatiable_list.xlsx")
+
+if __name__ == '__main__':
+ main()
--
Gitee
From 874d38dfe271bfcd1baff6777f26b75b3300cff7 Mon Sep 17 00:00:00 2001
From: lipingEmmaSiguyi <1477412247@qq.com>
Date: Thu, 8 Dec 2022 20:48:47 +0800
Subject: [PATCH 4/4] update software-compatible doc
---
doc/software-compatibility/dist/oepkgs.png | Bin 0 -> 33661 bytes
...72\344\273\223\346\265\201\347\250\213.md" | 33 +++---
...46\347\273\206\346\214\207\345\257\274.md" | 108 +++++++++++++-----
3 files changed, 93 insertions(+), 48 deletions(-)
create mode 100644 doc/software-compatibility/dist/oepkgs.png
diff --git a/doc/software-compatibility/dist/oepkgs.png b/doc/software-compatibility/dist/oepkgs.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1e48ed6fb1643a004b47f2d28ca68dfe1723852
GIT binary patch
literal 33661
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://compass-ci.openeuler.org/jobs
-
+###### 2.1 可通过构建工程系统,以评论形式评论至仓库PR中
#### 3. rpmbuild脚本
在submit rpmbuild.yaml 时,测试用例**rpmbuild**会去引用脚本
@@ -125,8 +123,7 @@ os_version: $upstream_branch
如果构建成功,则通过upload_rpm_pkg函数先将测试机上打好的软件包放入```/srv/rpm/upload```,再通过update_repo_mq处理上传的软件包。处理完的包会先放入/srv/rpm/testing中,每天零点定时更新到/srv/rpm/pub中,也就是https://repo.oepkgs.net/openEuler/rpm/仓库中
#### 4. 测试构建的包能否正常安装
-###### 4.1 可以查看job_id(自动构建任务,无需提交,可通过job_id来查看日志,该job_id之后将由门禁系统,以评论形式评论至仓库PR中,目前暂无)
-https://compass-ci.openeuler.org/jobs
+###### 4.1 可以查看自动构建任务,无需提交,以评论形式评论至仓库PR中)
###### 4.2 手动提交install.yaml
需要加入以下参数
@@ -174,9 +171,9 @@ mount_repo_name: compatible/c7
### 如何查询软件包位置?
-[https://compass-ci.openeuler.org/oepkgs](https://compass-ci.openeuler.org/oepkgs)
+[https://search.oepkgs.net/](https://search.oepkgs.net/)
可在此查询引入到软件所的软件包
### 如何下载使用仓库中的软件包?
-在[https://compass-ci.openeuler.org/oepkgs](https://compass-ci.openeuler.org/oepkgs)
-查询软件包在软件所中的仓库存放位置之后,详见[openEuler社区开源软件适配流程.md](https://gitee.com/openeuler/oec-application/blob/master/doc/openEuler%E7%A4%BE%E5%8C%BA%E5%BC%80%E6%BA%90%E8%BD%AF%E4%BB%B6%E9%80%82%E9%85%8D%E6%B5%81%E7%A8%8B.md)的最后一节:**下载使用软件**,修改这一节中的示例中的**baseurl**即可。
+在[https://search.oepkgs.net/](https://search.oepkgs.net/)
+查询软件包在软件所中的仓库存放位置之后,点开软件包的详情页,按照安装指引便可下载使用软件包。
diff --git "a/doc/\345\214\227\345\220\221\345\274\200\346\272\220\350\275\257\344\273\266\345\214\205\351\200\202\351\205\215\350\277\201\347\247\273\350\257\246\347\273\206\346\214\207\345\257\274.md" "b/doc/\345\214\227\345\220\221\345\274\200\346\272\220\350\275\257\344\273\266\345\214\205\351\200\202\351\205\215\350\277\201\347\247\273\350\257\246\347\273\206\346\214\207\345\257\274.md"
index 1e3e20e..799f21d 100644
--- "a/doc/\345\214\227\345\220\221\345\274\200\346\272\220\350\275\257\344\273\266\345\214\205\351\200\202\351\205\215\350\277\201\347\247\273\350\257\246\347\273\206\346\214\207\345\257\274.md"
+++ "b/doc/\345\214\227\345\220\221\345\274\200\346\272\220\350\275\257\344\273\266\345\214\205\351\200\202\351\205\215\350\277\201\347\247\273\350\257\246\347\273\206\346\214\207\345\257\274.md"
@@ -1,58 +1,98 @@
-## 背景介绍
+[TOC]
+
+### 背景介绍
[oepkgs](https://oepkgs.net/zh/) 全称开放软件包服务(Open External Packages Service),是一个为 openEuler 以及其他 Linux 发行版提供软件包服务和容器镜像服务的第三方社区。
oepkgs 社区提供两种开源软件包适配方式,第一种方式开源软件包的源码合入 [src-oepkgs](https://gitee.com/src-oepkgs) 组织仓下面,由 src-oepkgs 的构建服务对软件包进行构建
测试,兼容性测试,并进入 oepkgs 的[主体仓库](https://repo.oepkgs.net/openEuler/rpm/)中。另一种方式用户通过网页快速构建软件包,软件包进入个人账户下面的某个仓库中。
+
### 开源软件引入oepkgs主仓总体流程
->**1. 获取到spec文件以及源码文件**
+>**1. 初始化RPM编译环境**
>**2. 在openEuler上进行编译构建**
>**3. 在openEuler上进行兼容性测试**
->**4. 将已经适配好的spec文件以及源码文件存放在src-oepkgs仓库中(建仓流程详见[rpm包构建及建仓流程](https://gitee.com/openeuler/oec-application/blob/master/doc/software-compatibility/rpm%E6%9E%84%E5%BB%BA%E4%BB%A5%E5%8F%8A%E5%BB%BA%E4%BB%93%E6%B5%81%E7%A8%8B.md))**
-### 1. 软件包spec及源码文件获取
+>**4. 使用src-oepkgs社区构建工程**
-**1.1 在一些网站上找到软件包的src.rpm包,解压获取spec文件以及软件包的源码文件:**
+#### 1. 初始化RPM编译环境
+执行命令,安装构建工具:
```
-https://pkgs.org/
-https://src.fedoraproject.org/projects/rpms/*
-https://koji.fedoraproject.org/koji/packages
-www.google.com
-www.baidu.com
-www.bing.com
+yum install -y dnf-plugins-core rpm-build
```
-以libvirt 4.5.0版本引入为例,在网上寻找src.rpm包的流程如下图所示:
-
-
+生成目录结构:
```
-rpm -i http://vault.centos.org/7.9.2009/os/Source/SPackages/libvirt-4.5.0-36.el7.src.rpm
+# 输入任意 **.spec,这一步报错,此时将自动生成目录
+rpmbuild -ba nginx.spec
+error: failed to stat /root/nginx.spec: No such file or directory
+# 查看自动生成的目录结构
+ls ~/rpmbuild/
+BUILD BUILDROOT RPMS SOURCES SPECS SRPMS
```
-
-
-如上图所示的```~/rpmbuild/SPECS/``` 和 ```~/rpmbuild/SOURCES/```目录下面分别存放了软件包的spec文件以及软件包的源码文件
-
-### 2. 在openEuler上进行编译构建:
-执行命令,安装构建工具:
+准备软件源码到SOURCES
```
-yum install -y dnf-plugins-core rpm-build
+wget http://nginx.org/download/nginx-1.20.1.tar.gz
+cp nginx-1.20.1.tar.gz ~/rpmbuild/SOURCES/
+```
+创建修改SPEC配置文件
+```
+编写后缀为.spec的文件
+vim ~/rpmbuild/SPECS/nginx.spec
+Name: nginx
+Version: 1.20.1
+Release: 10
+Summary: Nginx is a web server.
+License: GPL
+Group: Productivity/Networking/Web/Proxy
+URL: test.rpm.com
+Source0: nginx-1.20.1.tar.gz
+BuildRequires: gcc
+BuildRequires: pcre2-devel
+BuildRequires: pcre-devel
+BuildRequires: openssl-devel
+BuildRequires: gdb-headless
+
+%description
+Building a nginx-1.20.1.rpm from nginx-1.20.1.tar.gz
+
+%post
+useradd nginx
+
+%prep
+%setup -q
+
+%build
+./configure
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR=%{buildroot}
+
+%files
+%doc
+/usr/local/nginx/*
+
+%changelog
+* Sat Dec 06 2022 liping - 1.20.1 - 10
+- Release Nginx 1.20.1
```
+#### 2. 在openEuler上进行编译构建:
执行命令,安装软件包的依赖包
```
# yum-builddep -y ~/rpmbuild/SPECS/*.spec
-yum-builddep -y ~/rpmbuild/SPECS/libvirt.spec
+yum-builddep -y ~/rpmbuild/SPECS/nginx.spec
```
执行命令,对软件包进行编译构建
```
# rpmbuild -ba ~/rpmbuild/SPECS/*.spec
-rpmbuild -ba ~/rpmbuild/SPECS/libvirt.spec
+rpmbuild -ba ~/rpmbuild/SPECS/nginx.spec
```
编译构建通过就会在 ~/rpmbuild/RPMS/ 目录下面生成 rpm包
```
ls ~/rpmbuild/RPMS/*
```
-### 3. 在openEuler上进行兼容性测试
+#### 3. 在openEuler上进行兼容性测试
执行命令,测试软件包的安装、卸载
```
yum localinstall ~/rpmbuild/RPMS/x86_64/*
@@ -63,9 +103,17 @@ yum remove *
systemctl start *
systemctl stop *
```
-### 4. 将已经适配好的软件包的spec文件以及~/rpmbuild/SOURCE目录下面的源码文件存放在src-oepkgs仓库中(建仓流程详见[rpm包构建及建仓流程](https://gitee.com/openeuler/oec-application/blob/master/doc/software-compatibility/rpm%E6%9E%84%E5%BB%BA%E4%BB%A5%E5%8F%8A%E5%BB%BA%E4%BB%93%E6%B5%81%E7%A8%8B.md))
+#### 4. 使用src-oepkgs构建工程完成软件包上传
+
+将软件包的spec文件以及~/rpmbuild/SOURCE目录下面的源码文件存放在src-oepkgs仓库中
+
+建仓流程详见[rpm包构建及建仓流程](https://gitee.com/openeuler/oec-application/blob/master/doc/software-compatibility/rpm%E6%9E%84%E5%BB%BA%E4%BB%A5%E5%8F%8A%E5%BB%BA%E4%BB%93%E6%B5%81%E7%A8%8B.md))
+
+
+### 开源软件引入oepkgs个人仓总体流程
+
+build.dev.oepkgs.net 构建总体流程
-## build.oepkgs.net 构建总体流程
>**1. 创建个人软件包仓库**
>**2. 创建并提交构建任务**
@@ -73,14 +121,14 @@ systemctl stop *
>**3. 查看并分析构建日志**
>**4. 在个人仓库中下载使用软件包**
-### 1. 创建个人软件包仓库
+#### 1. 创建个人软件包仓库
在rpm包构建之前,我们可以先选择一个已有的软件包仓库地址或新增一个软件包仓库地址去存放我们待构建的软件包。
切换到构建页面,选择 RPM构建 ---> 仓库管理 ---> 新增仓库

-### 2. 新建一个构建任务
+#### 2. 新建一个构建任务
通过提交构建任务,编译构建出软件包,并发布到上一步骤创建的仓库中。

@@ -95,7 +143,7 @@ systemctl stop *

-### 3. 查看构建日志
+#### 3. 查看构建日志


--
Gitee
|