diff --git a/.github/workflows/sourcehutbuild.go b/.github/workflows/sourcehutbuild.go new file mode 100644 index 000000000..cedc131f5 --- /dev/null +++ b/.github/workflows/sourcehutbuild.go @@ -0,0 +1,164 @@ +// Copyright 2021 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore +// +build ignore + +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "time" +) + +var ( + flagManifest = flag.String("manifest", "", "manifest file path") + flagNote = flag.String("note", "", "note for the build") +) + +func init() { + flag.Parse() +} + +var secret string + +func init() { + secret = os.Getenv("SOURCEHUT_OAUTH_TOKEN") +} + +type JobRequest struct { + Manifest string `json:"manifest"` + Note string `json:"note,omitempty"` + Tags []string `json:"tags,omitempty"` + Execute *bool `json:"execute,omitempty"` + Secrets *bool `json:"secrets,omitempty"` +} + +type JobResponse struct { + ID int `json:"id"` + Status string `json:"status"` +} + +func httpRequest(method string, path string, body interface{}) (io.ReadCloser, error) { + const baseURL = `https://builds.sr.ht` + + var reqBody io.Reader + if body != nil { + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + if err := enc.Encode(body); err != nil { + return nil, err + } + reqBody = &buf + } + + req, err := http.NewRequest(method, baseURL+path, reqBody) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Bearer "+secret) + if reqBody != nil { + req.Header.Add("Content-Type", "application/json") + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + if got, want := res.StatusCode, http.StatusOK; got != want { + resBody, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + return nil, fmt.Errorf("status code must be %d but %d; URL: %s, response: %s", want, got, req.URL.String(), string(resBody)) + } + + return res.Body, nil +} + +func run() error { + manifest, err := os.ReadFile(*flagManifest) + if err != nil { + return err + } + + body, err := httpRequest(http.MethodPost, "/api/jobs", &JobRequest{ + Manifest: string(manifest), + Note: *flagNote, + }) + if err != nil { + return err + } + defer body.Close() + + var jobRes JobResponse + dec := json.NewDecoder(body) + if err := dec.Decode(&jobRes); err != nil { + return err + } + fmt.Printf("Job ID: %d\n", jobRes.ID) + + // Poll the queued job's status + const maxAttempt = 60 +loop: + for i := 0; i < maxAttempt; i++ { + fmt.Printf("Polling the status... (%d)\n", i+1) + + body, err := httpRequest(http.MethodGet, fmt.Sprintf("/api/jobs/%d", jobRes.ID), nil) + if err != nil { + return err + } + + var jobRes JobResponse + dec := json.NewDecoder(body) + if err := dec.Decode(&jobRes); err != nil { + return err + } + + switch jobRes.Status { + case "pending": + // Do nothing + case "queued": + // Do nothing + case "running": + // Do nothing + case "success": + break loop + case "failed": + resBody, err := io.ReadAll(body) + if err != nil { + return err + } + return fmt.Errorf("failed; response: %s", resBody) + } + + time.Sleep(10 * time.Second) + } + + return nil +} + +func main() { + if err := run(); err != nil { + log.Fatal(err) + } +} diff --git a/.github/workflows/sourcehutbuild.yml b/.github/workflows/sourcehutbuild.yml new file mode 100644 index 000000000..0e21a3b27 --- /dev/null +++ b/.github/workflows/sourcehutbuild.yml @@ -0,0 +1,30 @@ +name: Sourcehut Build + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + os: [alpine, arch, debian, fedora, ubuntu] + name: Test on ${{ matrix.os }} + runs-on: ubuntu-latest + env: + SOURCEHUT_OAUTH_TOKEN: ${{ secrets.SOURCEHUT_OAUTH_TOKEN }} + defaults: + run: + shell: bash + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: 1.17.x + + - name: Run + run: | + go run .github/workflows/sourcehutbuild.go \ + -manifest .github/workflows/sourcehutmanifests/${{ matrix.os }}.yml \ + -note '${{ format('{0} ({1})', github.event.head_commit.id, matrix.os) }}' diff --git a/.builds/alpine.yml b/.github/workflows/sourcehutmanifests/alpine.yml similarity index 100% rename from .builds/alpine.yml rename to .github/workflows/sourcehutmanifests/alpine.yml diff --git a/.builds/arch.yml b/.github/workflows/sourcehutmanifests/arch.yml similarity index 100% rename from .builds/arch.yml rename to .github/workflows/sourcehutmanifests/arch.yml diff --git a/.builds/debian.yml b/.github/workflows/sourcehutmanifests/debian.yml similarity index 100% rename from .builds/debian.yml rename to .github/workflows/sourcehutmanifests/debian.yml diff --git a/.builds/fedora.yml b/.github/workflows/sourcehutmanifests/fedora.yml similarity index 100% rename from .builds/fedora.yml rename to .github/workflows/sourcehutmanifests/fedora.yml diff --git a/.builds/ubuntu.yml b/.github/workflows/sourcehutmanifests/ubuntu.yml similarity index 100% rename from .builds/ubuntu.yml rename to .github/workflows/sourcehutmanifests/ubuntu.yml diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml index 5845c1351..c0ba32cc6 100644 --- a/.github/workflows/steam.yml +++ b/.github/workflows/steam.yml @@ -1,4 +1,4 @@ -name: steam +name: Steam on: [push, pull_request] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b804999f0..c3cd3c75a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test +name: Test on: [push, pull_request]