Skip to content

Commit 0afebb0

Browse files
authored
Merge pull request #1 from newrelic/mvick/first-test
feat: Add initial new action
2 parents 91d947c + b883f60 commit 0afebb0

File tree

6 files changed

+452
-0
lines changed

6 files changed

+452
-0
lines changed

action.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: 'Agent Metadata Action'
2+
description: 'Fetches and validates agent configuration metadata'
3+
4+
inputs:
5+
agent-repo:
6+
description: 'Repository containing agent configurations'
7+
required: true
8+
github-token:
9+
description: 'GitHub token for API access'
10+
required: true
11+
12+
runs:
13+
using: 'composite'
14+
steps:
15+
- name: Setup Go
16+
uses: actions/setup-go@v4
17+
with:
18+
go-version: '1.21'
19+
20+
- name: Build and Run
21+
id: run-action
22+
shell: bash
23+
env:
24+
AGENT_REPO: ${{ inputs.agent-repo }}
25+
GITHUB_TOKEN: ${{ inputs.github-token }}
26+
run: |
27+
cd ${{ github.action_path }}
28+
go build -o agent-metadata-action
29+
./agent-metadata-action

go.mod

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module agent-metadata-action
2+
3+
go 1.25.3
4+
5+
require (
6+
github.com/stretchr/testify v1.11.1
7+
gopkg.in/yaml.v3 v3.0.1
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
6+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package main
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"os"
10+
"time"
11+
12+
"gopkg.in/yaml.v3"
13+
)
14+
15+
type AgentMetadata struct {
16+
Schema string `json:"schema"`
17+
Configuration ConfigJson `json:"configuration"`
18+
Metadata string `json:"metadata"`
19+
}
20+
21+
type ConfigYaml struct {
22+
Name string `yaml:"name"`
23+
Slug string `yaml:"slug"`
24+
Platform string `yaml:"platform"`
25+
Description string `yaml:"description"`
26+
Type string `yaml:"type"`
27+
Version string `yaml:"version"`
28+
Schema string `yaml:"schema"`
29+
}
30+
31+
type ConfigJson struct {
32+
Name string `json:"name"`
33+
Slug string `json:"slug"`
34+
Platform string `json:"platform"`
35+
Description string `json:"description"`
36+
Type string `json:"type"`
37+
Version string `json:"version"`
38+
}
39+
40+
type ConfigFile struct {
41+
Configs []ConfigYaml `yaml:"configs"`
42+
}
43+
44+
type GitHubConfig struct {
45+
AgentRepo string
46+
GitHubToken string
47+
Branch string // Add branch field
48+
}
49+
50+
type GitHubClient struct {
51+
client *http.Client
52+
token string
53+
}
54+
55+
type GitHubContent struct {
56+
Content string `json:"content"`
57+
Encoding string `json:"encoding"`
58+
}
59+
60+
func main() {
61+
config, err := loadGitHubConfig()
62+
if err != nil {
63+
fmt.Fprintf(os.Stderr, "::error::Error loading config: %v\n", err)
64+
os.Exit(1)
65+
}
66+
67+
configs, err := readConfigsFileToJson(
68+
config.AgentRepo,
69+
config.GitHubToken,
70+
)
71+
if err != nil {
72+
fmt.Fprintf(os.Stderr, "::error::Error reading configs: %v\n", err)
73+
os.Exit(1)
74+
}
75+
76+
fmt.Println("::notice::Successfully fetched and validated agent metadata")
77+
printConfigs(configs)
78+
}
79+
80+
func printConfigs(configs []ConfigJson) {
81+
for i, config := range configs {
82+
fmt.Printf("Config %d:\n", i+1)
83+
fmt.Printf(" Name: %s\n", config.Name)
84+
fmt.Printf(" Slug: %s\n", config.Slug)
85+
fmt.Printf(" Platform: %s\n", config.Platform)
86+
fmt.Printf(" Description: %s\n", config.Description)
87+
fmt.Printf(" Type: %s\n", config.Type)
88+
fmt.Printf(" Version: %s\n", config.Version)
89+
fmt.Println()
90+
}
91+
}
92+
93+
func loadGitHubConfig() (*GitHubConfig, error) {
94+
agentRepo := os.Getenv("AGENT_REPO")
95+
if agentRepo == "" {
96+
return nil, fmt.Errorf("AGENT_REPO environment variable not set")
97+
}
98+
99+
githubToken := os.Getenv("GITHUB_TOKEN")
100+
if githubToken == "" {
101+
return nil, fmt.Errorf(
102+
"GITHUB_TOKEN environment variable not set",
103+
)
104+
}
105+
106+
// Branch is optional, defaults to empty (uses default branch)
107+
branch := os.Getenv("BRANCH")
108+
109+
return &GitHubConfig{
110+
AgentRepo: agentRepo,
111+
GitHubToken: githubToken,
112+
Branch: branch,
113+
}, nil
114+
}
115+
116+
func readConfigsFileToJson(
117+
agentRepo, githubToken string,
118+
) ([]ConfigJson, error) {
119+
config, err := loadGitHubConfig()
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
data, err := fetchFromGitHub(agentRepo, githubToken, config.Branch)
125+
if err != nil {
126+
return nil, fmt.Errorf("failed to fetch from GitHub: %w", err)
127+
}
128+
129+
var configFile ConfigFile
130+
if err := yaml.Unmarshal(data, &configFile); err != nil {
131+
return nil, fmt.Errorf("failed to parse YAML: %w", err)
132+
}
133+
134+
return convertToConfigJson(configFile.Configs), nil
135+
}
136+
137+
func convertToConfigJson(yamlConfigs []ConfigYaml) []ConfigJson {
138+
configs := make([]ConfigJson, 0, len(yamlConfigs))
139+
for _, c := range yamlConfigs {
140+
configs = append(configs, ConfigJson{
141+
Name: c.Name,
142+
Slug: c.Slug,
143+
Platform: c.Platform,
144+
Description: c.Description,
145+
Type: c.Type,
146+
Version: c.Version,
147+
})
148+
}
149+
return configs
150+
}
151+
152+
// @todo update to fetch from tagged release
153+
func fetchFromGitHub(repo, token, branch string) ([]byte, error) {
154+
client := NewGitHubClient(token)
155+
return client.FetchFile(repo, ".fleetControl/configs.yml", branch)
156+
}
157+
158+
func NewGitHubClient(token string) *GitHubClient {
159+
return &GitHubClient{
160+
client: &http.Client{
161+
Timeout: 30 * time.Second,
162+
},
163+
token: token,
164+
}
165+
}
166+
167+
func (gc *GitHubClient) FetchFile(
168+
repo, path, branch string,
169+
) ([]byte, error) {
170+
url := fmt.Sprintf(
171+
"https://api.github.com/repos/%s/contents/%s",
172+
repo,
173+
path,
174+
)
175+
176+
// Add branch query parameter if specified
177+
if branch != "" {
178+
url = fmt.Sprintf("%s?ref=%s", url, branch)
179+
}
180+
181+
req, err := http.NewRequest("GET", url, nil)
182+
if err != nil {
183+
return nil, fmt.Errorf("failed to create request: %w", err)
184+
}
185+
186+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", gc.token))
187+
req.Header.Set("Accept", "application/vnd.github.v3+json")
188+
189+
resp, err := gc.client.Do(req)
190+
if err != nil {
191+
return nil, fmt.Errorf("request failed: %w", err)
192+
}
193+
defer resp.Body.Close()
194+
195+
if resp.StatusCode != http.StatusOK {
196+
body, _ := io.ReadAll(resp.Body)
197+
return nil, fmt.Errorf(
198+
"GitHub API returned status %d: %s",
199+
resp.StatusCode,
200+
string(body),
201+
)
202+
}
203+
204+
body, err := io.ReadAll(resp.Body)
205+
if err != nil {
206+
return nil, fmt.Errorf("failed to read response: %w", err)
207+
}
208+
209+
var content GitHubContent
210+
if err := json.Unmarshal(body, &content); err != nil {
211+
return nil, fmt.Errorf(
212+
"failed to parse GitHub response: %w",
213+
err,
214+
)
215+
}
216+
217+
if content.Encoding != "base64" {
218+
return nil, fmt.Errorf(
219+
"unexpected encoding: %s",
220+
content.Encoding,
221+
)
222+
}
223+
224+
decoded, err := base64.StdEncoding.DecodeString(content.Content)
225+
if err != nil {
226+
return nil, fmt.Errorf(
227+
"failed to decode base64 content: %w",
228+
err,
229+
)
230+
}
231+
232+
return decoded, nil
233+
}

0 commit comments

Comments
 (0)