package artifacts import ( "encoding/base64" "encoding/json" "fmt" "os" "strings" ) // run, job, err func JobInfo() (string, string, error) { rt := os.Getenv("ACTIONS_RUNTIME_TOKEN") var empty string if rt == "" { return empty, empty, fmt.Errorf("env ACTIONS_RUNTIME_TOKEN is only available from the context of an action") } jt, err := newJwtDecode(rt) if err != nil { return empty, empty, err } scp, ok := jt.Payload["scp"].(string) if !ok { return empty, empty, fmt.Errorf("no scp in ACTIONS_RUNTIME_TOKEN") } for _, scopes := range strings.Split(scp, " ") { parts := strings.Split(scopes, ":") if parts[0] != "Actions.Results" { continue } if len(parts) != 3 { return empty, empty, fmt.Errorf("invalid scp in ACTIONS_RUNTIME_TOKEN") } return parts[1], parts[2], nil } return empty, empty, fmt.Errorf("no backend ids found") } type jwt struct { Header map[string]any Payload map[string]any Signature string } func newJwtDecode(token string) (*jwt, error) { parts := strings.Split(token, ".") if len(parts) != 3 { return nil, fmt.Errorf("invalid JWT: expected 3 segments") } decode := func(seg string) ([]byte, error) { if l := len(seg) % 4; l != 0 { seg += strings.Repeat("=", 4-l) } return base64.URLEncoding.DecodeString(seg) } headerBytes, err := decode(parts[0]) if err != nil { return nil, fmt.Errorf("invalid JWT header: %v", err) } var header map[string]any if err := json.Unmarshal(headerBytes, &header); err != nil { return nil, fmt.Errorf("invalid JWT header json: %v", err) } payloadBytes, err := decode(parts[1]) if err != nil { return nil, fmt.Errorf("invalid JWT payload: %v", err) } var payload map[string]any if err := json.Unmarshal(payloadBytes, &payload); err != nil { return nil, fmt.Errorf("invalid JWT payload json: %v", err) } return &jwt{Header: header, Payload: payload, Signature: parts[2]}, nil }