aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/workflows/main.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-09-28 17:37:30 +0900
committerOphestra <cat@gensokyo.uk>2026-09-28 17:37:30 +0900
commitd2afde1c95355621282d1ac1a9ae73c3b665b395 (patch)
tree910f2dff6a1e86491840357bf150df5ad685a37e /internal/workflows/main.go
parentd621c53cc1ad96adb36612e6143691dd0c79362b (diff)
internal/workflows: migrate workflows
This represents workflow documents via compound literals and removes the ugly and footgun-heavy yaml documents. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/workflows/main.go')
-rw-r--r--internal/workflows/main.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/internal/workflows/main.go b/internal/workflows/main.go
new file mode 100644
index 00000000..46f7e054
--- /dev/null
+++ b/internal/workflows/main.go
@@ -0,0 +1,149 @@
+//go:build workflows
+
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sync"
+)
+
+// KV represents a string to value pair.
+type KV[T any] struct {
+ K string
+ V T
+}
+
+// A Map represents an ordered set of key-value pairs.
+type Map[T any] []KV[T]
+
+// MarshalJSON encodes m as a key-value pair in JSON.
+func (m *Map[T]) MarshalJSON() ([]byte, error) {
+ if m == nil || len(*m) == 0 {
+ return []byte("{}"), nil
+ }
+
+ buf := bytes.NewBuffer(make([]byte, 0, len(*m)<<5))
+ buf.WriteByte('{')
+
+ last := len(*m) - 1
+ for i, pair := range *m {
+ p, err := json.Marshal(pair.K)
+ if err != nil {
+ return nil, fmt.Errorf("key %q: %w", pair.K, err)
+ }
+ buf.Write(p)
+ buf.WriteByte(':')
+
+ p, err = json.Marshal(pair.V)
+ if err != nil {
+ return nil, fmt.Errorf("value of key %q: %w", pair.K, err)
+ }
+ buf.Write(p)
+
+ if i < last {
+ buf.WriteByte(',')
+ }
+ }
+
+ buf.WriteByte('}')
+ return buf.Bytes(), nil
+}
+
+// A Step is a task ran as part of a [Job].
+type Step struct {
+ // A name for the step.
+ Name string `json:"name,omitempty"`
+ // Selects an action to run as part of a step in the job.
+ Uses string `json:"uses,omitempty"`
+ // A unique identifier for the step.
+ ID string `json:"id,omitempty"`
+ // Prevent a step from running unless a condition is met.
+ If string `json:"if,omitempty"`
+ // Runs command-line programs that do not exceed 21,000 characters using the
+ // operating system's shell.
+ Run string `json:"run,omitempty"`
+ // Key-value pairs of input parameters defined by the action.
+ With Map[any] `json:"with,omitempty"`
+}
+
+// A Job is an entry in a [Workflow] document.
+type Job struct {
+ // Set a name for the job.
+ Name string `json:"name,omitempty"`
+ // Identifies any jobs that must complete successfully before this job will
+ // run.
+ Needs []string `json:"needs,omitempty"`
+ // Define the type of machine to run the job on.
+ On string `json:"runs-on,omitempty"`
+ // A sequence of tasks.
+ Steps []Step `json:"steps,omitempty"`
+}
+
+// Workflow describes a workflow document.
+type Workflow struct {
+ // The name of the workflow.
+ Name string `json:"name,omitempty"`
+ // The name for workflow runs generated from the workflow.
+ RunName string `json:"run-name,omitempty"`
+ // Define which events can cause the workflow to run.
+ On any `json:"on"`
+
+ // Named jobs part of the workflow run.
+ Jobs Map[Job] `json:"jobs,omitempty"`
+}
+
+// workflowsDir is the relative pathname of the workflows directory.
+const workflowsDir = "../../.gitea/workflows"
+
+// writeFile writes the action to the .gitea directory.
+func (w *Workflow) writeFile(name string) error {
+ f, err := os.OpenFile(
+ filepath.Join(workflowsDir, name+".yml"),
+ os.O_CREATE|os.O_EXCL|os.O_WRONLY,
+ 0444,
+ )
+ if err != nil {
+ return err
+ }
+
+ err = json.NewEncoder(f).Encode(w)
+ if closeErr := f.Close(); err == nil {
+ err = closeErr
+ }
+ return err
+}
+
+// removeOnce synchronises removal of stale workflow files.
+var removeOnce sync.Once
+
+// removeWorkflows removes existing workflow files once.
+func removeWorkflows() {
+ removeOnce.Do(func() {
+ if err := os.RemoveAll(workflowsDir); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ if err := os.Mkdir(workflowsDir, 0755); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ })
+}
+
+// mustWriteFile is like writeFile, but terminates the program if a non-nil
+// error is returned.
+func (w *Workflow) mustWriteFile(name string) (_ struct{}) {
+ removeWorkflows()
+
+ if err := w.writeFile(name); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ return
+}
+
+func main() {}