synapsecns/sanguine

View on GitHub
contrib/git-changes-action/main.go

Summary

Maintainability
A
35 mins
Test Coverage
// Package main provides the entrypoint for the action
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"
    "sort"
    "strings"
    "time"

    moduledetector "github.com/synapsecns/sanguine/contrib/git-changes-action/detector/module"
    packagedetector "github.com/synapsecns/sanguine/contrib/git-changes-action/detector/package"
    "github.com/synapsecns/sanguine/contrib/git-changes-action/detector/tree"

    "github.com/sethvargo/go-githubactions"
    "github.com/synapsecns/sanguine/contrib/git-changes-action/detector/git"
)

const defaultTimeout = "1m"

func main() {
    token := githubactions.GetInput("github_token")
    dependencyLevelResolution := githubactions.GetInput("dependencyLevelResolution")

    workingDirectory, err := os.Getwd()
    if err != nil {
        panic(err)
    }

    ref := githubactions.GetInput("ref")
    base := githubactions.GetInput("base")

    timeout, err := getTimeout()
    if err != nil {
        panic(fmt.Errorf("failed to parse timeout: %w", err))
    }

    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    ct, err := git.GetChangeTree(ctx, workingDirectory, ref, token, base)
    if err != nil {
        panic(err)
    }

    noDepChanged, noDepUnchanged, _, err := outputModuleChanges(workingDirectory, ct, false, dependencyLevelResolution)
    if err != nil {
        panic(err)
    }

    depChanged, depUnchanged, allModules, err := outputModuleChanges(workingDirectory, ct, true, dependencyLevelResolution)
    if err != nil {
        panic(err)
    }

    githubactions.SetOutput("changed_modules", noDepChanged)
    githubactions.SetOutput("unchanged_modules", noDepUnchanged)

    githubactions.SetOutput("changed_modules_deps", depChanged)
    githubactions.SetOutput("unchanged_modules_deps", depUnchanged)

    githubactions.SetOutput("all_modules", allModules)

}

// outputModuleChanges outputs the changed modules.
// this wraps detector.DetectChangedModules and handles the output formatting to be parsable by github actions.
// the final output is a json array of strings.
func outputModuleChanges(workingDirectory string, ct tree.Tree, includeDeps bool, dependencyLevelResolution string) (changedJSON string, unchangedJSON string, allModulesJSON string, err error) {
    var modules map[string]bool

    if dependencyLevelResolution == "packages" {
        modules, err = packagedetector.DetectChangedModules(workingDirectory, ct, includeDeps)
    } else {
        modules, err = moduledetector.DetectChangedModules(workingDirectory, ct, includeDeps)
    }

    if err != nil {
        return changedJSON, unchangedJSON, allModulesJSON, fmt.Errorf("failed to detect changed modules w/ include deps set to %v: %w", includeDeps, err)
    }

    var changedModules, unchangedModules, allModules []string
    for module, changed := range modules {
        modName := strings.TrimPrefix(module, "./")
        allModules = append(allModules, modName)

        if changed {
            changedModules = append(changedModules, modName)
        } else {
            unchangedModules = append(unchangedModules, modName)
        }
    }

    sort.Strings(changedModules)
    sort.Strings(unchangedModules)
    sort.Strings(allModules)

    marshalledChanged, err := json.Marshal(changedModules)
    if err != nil {
        return changedJSON, unchangedJSON, allModulesJSON, fmt.Errorf("failed to marshall changed module json w/ include deps set to %v: %w", includeDeps, err)
    }

    marshalledUnchanged, err := json.Marshal(unchangedModules)
    if err != nil {
        return changedJSON, unchangedJSON, allModulesJSON, fmt.Errorf("failed to marshall unchanged module json w/ include deps set to %v: %w", includeDeps, err)
    }

    marshalledAll, err := json.Marshal(allModules)
    if err != nil {
        return changedJSON, unchangedJSON, allModulesJSON, fmt.Errorf("failed to marshall all modules json w/ include deps set to %v: %w", includeDeps, err)
    }

    return string(marshalledChanged), string(marshalledUnchanged), string(marshalledAll), nil
}

// getTimeout gets the timeout setting. If it is not set, it defaults to 1 minute.
// Errors if the timeout is not a valid duration.
func getTimeout() (timeout time.Duration, err error) {
    rawTimeout := githubactions.GetInput("timeout")
    if rawTimeout == "" {
        rawTimeout = defaultTimeout
    }

    timeout, err = time.ParseDuration(rawTimeout)
    if err != nil {
        return 0, fmt.Errorf("failed to parse timeout: %w", err)
    }
    return timeout, nil
}