2024-07-21 22:15:11 +02:00
|
|
|
|
package common
|
|
|
|
|
|
|
|
|
|
import (
|
2024-07-26 16:53:09 +02:00
|
|
|
|
"bytes"
|
2024-07-21 22:15:11 +02:00
|
|
|
|
"encoding/xml"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ObsClient struct {
|
|
|
|
|
baseUrl *url.URL
|
|
|
|
|
client *http.Client
|
|
|
|
|
user, password string
|
|
|
|
|
cookie string
|
2024-07-22 17:35:48 +02:00
|
|
|
|
|
|
|
|
|
HomeProject string
|
2024-07-21 22:15:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 17:09:45 +02:00
|
|
|
|
func NewObsClient(host string) (*ObsClient, error) {
|
2024-07-21 22:15:11 +02:00
|
|
|
|
baseUrl, err := url.Parse("https://" + host)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &ObsClient{
|
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
|
client: &http.Client{},
|
2024-07-22 17:09:45 +02:00
|
|
|
|
user: obsUser,
|
|
|
|
|
password: obsPassword,
|
2024-07-22 17:35:48 +02:00
|
|
|
|
|
|
|
|
|
HomeProject: fmt.Sprintf("home:%s", obsUser),
|
2024-07-21 22:15:11 +02:00
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 16:53:09 +02:00
|
|
|
|
type RepositoryPathMeta struct {
|
|
|
|
|
Project string `xml:"project,attr"`
|
|
|
|
|
Repository string `xml:"repository,attr"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-21 22:15:11 +02:00
|
|
|
|
type RepositoryMeta struct {
|
2024-07-26 16:53:09 +02:00
|
|
|
|
Name string `xml:"name,attr"`
|
|
|
|
|
Archs []string `xml:"arch"`
|
|
|
|
|
Paths []RepositoryPathMeta `xml:"path"`
|
2024-07-21 22:15:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 17:09:45 +02:00
|
|
|
|
type Flags struct {
|
|
|
|
|
Contents string `xml:",innerxml"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-21 22:15:11 +02:00
|
|
|
|
type ProjectMeta struct {
|
2024-07-26 16:53:09 +02:00
|
|
|
|
XMLName xml.Name `xml:"project"`
|
|
|
|
|
Name string `xml:"name,attr"`
|
|
|
|
|
Title string `xml:"title"`
|
|
|
|
|
Description string `xml:"description"`
|
|
|
|
|
ScmSync string `xml:"scmsync"`
|
|
|
|
|
Repositories []RepositoryMeta `xml:"repository"`
|
2024-07-22 17:09:45 +02:00
|
|
|
|
|
|
|
|
|
BuildFlags Flags `xml:"build"`
|
|
|
|
|
PublicFlags Flags `xml:"publish"`
|
2024-07-26 16:53:09 +02:00
|
|
|
|
DebugFlags Flags `xml:"debuginfo"`
|
|
|
|
|
UseForBuild Flags `xml:"useforbuild"`
|
2024-07-21 22:15:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseProjectMeta(data []byte) (*ProjectMeta, error) {
|
|
|
|
|
var meta ProjectMeta
|
|
|
|
|
err := xml.Unmarshal(data, &meta)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &meta, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) GetProjectMeta(project string) (*ProjectMeta, error) {
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", c.baseUrl.JoinPath("source", project, "_meta").String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
2024-07-26 16:53:09 +02:00
|
|
|
|
log.Printf("request: %#v", *req.URL)
|
|
|
|
|
log.Printf("headers: %#v", req.Header)
|
2024-07-21 22:15:11 +02:00
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch res.StatusCode {
|
|
|
|
|
case 200:
|
|
|
|
|
break
|
|
|
|
|
case 404:
|
|
|
|
|
return nil, nil
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := io.ReadAll(res.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parseProjectMeta(data)
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 16:53:09 +02:00
|
|
|
|
func ObsSafeProjectName(prjname string) string {
|
|
|
|
|
if len(prjname) < 1 {
|
|
|
|
|
return prjname
|
|
|
|
|
} else if len(prjname) > 200 {
|
|
|
|
|
prjname = prjname[:199]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch prjname[0] {
|
|
|
|
|
case '_', '.', ':':
|
|
|
|
|
prjname = "X" + prjname[1:]
|
|
|
|
|
// no UTF-8 in OBS :(
|
|
|
|
|
// prjname = "_" + prjname[1:]
|
|
|
|
|
// case ':':
|
|
|
|
|
// prjname = ":" + prjname[1:]
|
|
|
|
|
// case '.':
|
|
|
|
|
// prjname = "․" + prjname[1:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return prjname
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) SetProjectMeta(meta *ProjectMeta) (error) {
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("PUT", c.baseUrl.JoinPath("source", meta.Name, "_meta").String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
xml, err := xml.Marshal(meta)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.Body = io.NopCloser(bytes.NewReader(xml))
|
|
|
|
|
log.Printf("headers: %#v", req.Header)
|
|
|
|
|
log.Printf("xml: %s", xml)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch res.StatusCode {
|
|
|
|
|
case 200:
|
|
|
|
|
break
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-21 22:15:11 +02:00
|
|
|
|
func (c *ObsClient) DeleteProject(project string) error {
|
|
|
|
|
req, err := http.NewRequest("DELETE", c.baseUrl.JoinPath("source", project).String(), nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
|
return fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BuildStatus struct {
|
|
|
|
|
Package string `xml:"package,attr"`
|
|
|
|
|
Code string `xml:"code,attr"`
|
|
|
|
|
Details string `xml:"details"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BuildResult struct {
|
|
|
|
|
Project string `xml:"project,attr"`
|
|
|
|
|
Repository string `xml:"repository,attr"`
|
|
|
|
|
Arch string `xml:"arch,attr"`
|
|
|
|
|
Code string `xml:"code,attr"`
|
|
|
|
|
Status []BuildStatus `xml:"status"`
|
|
|
|
|
Binaries []BinaryList `xml:"binarylist"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Binary struct {
|
|
|
|
|
Size uint64 `xml:"size,attr"`
|
|
|
|
|
Filename string `xml:"filename,attr"`
|
|
|
|
|
Mtime uint64 `xml:"mtime,attr"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BinaryList struct {
|
|
|
|
|
Package string `xml:"package,attr"`
|
|
|
|
|
Binary []Binary `xml:"binary"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BuildResultList struct {
|
|
|
|
|
XMLName xml.Name `xml:"resultlist"`
|
|
|
|
|
Result []BuildResult `xml:"result"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseBuildResults(data []byte) (*BuildResultList, error) {
|
|
|
|
|
result := BuildResultList{}
|
|
|
|
|
err := xml.Unmarshal(data, &result)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ObsClient) BuildStatus(project string) (*BuildResultList, error) {
|
|
|
|
|
u := c.baseUrl.JoinPath("build", project, "_result")
|
|
|
|
|
query := u.Query()
|
|
|
|
|
query.Add("view", "status")
|
|
|
|
|
query.Add("view", "binarylist")
|
|
|
|
|
query.Add("multibuild", "1")
|
|
|
|
|
u.RawQuery = query.Encode()
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
|
log.Print(u.String())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.SetBasicAuth(c.user, c.password)
|
|
|
|
|
res, err := c.client.Do(req)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected return code: %d", res.StatusCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := io.ReadAll(res.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return parseBuildResults(data)
|
|
|
|
|
}
|