Skip to content

Commit

Permalink
build stuff (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
crenshaw-dev authored Dec 10, 2022
1 parent 10f1b76 commit cd2c8f4
Show file tree
Hide file tree
Showing 16 changed files with 547 additions and 34 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Build image

on:
push:
branches:
- main
pull_request:
branches:
- main
types: [ opened, synchronize, reopened ]
workflow_call:
inputs:
tag:
type: string
required: true
secrets:
DOCKERHUB_PASSWORD:
required: true

permissions:
contents: read

jobs:
publish:
if: github.repository == 'crenshaw-dev/github-executor-plugin'
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Test
run: make test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: crenshawdotdev
password: ${{ secrets.DOCKERHUB_PASSWORD || inputs.DOCKERHUB_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: crenshawdotdev/github-executor-plugin:${{ inputs.tag || 'latest' }}
cache-from: type=gha
cache-to: type=gha,mode=max
32 changes: 32 additions & 0 deletions .github/workflows/codegen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Codegen

on:
push:
branches:
- main
pull_request:
branches:
- main
types: [ opened, synchronize, reopened ]

permissions:
contents: read

jobs:
publish:
if: github.repository == 'crenshaw-dev/github-executor-plugin'
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- run: |
# Install Argo CLI.
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.4.4/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
mv ./argo-linux-amd64 /usr/local/bin/argo
argo version
make manifests
git diff --exit-code
66 changes: 66 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Create release
on:
workflow_dispatch:
inputs:
tag:
required: true

permissions:
contents: read

jobs:
build:
uses: crenshaw-dev/github-executor-plugin/.github/workflows/build.yaml@main
with:
tag: ${{ github.event.inputs.tag }}
secrets:
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
prepare-release:
needs: build
permissions:
contents: write # To push changes to release branch
name: Release
if: github.repository == 'crenshaw-dev/github-executor-plugin'
runs-on: ubuntu-22.04
env:
GIT_USERNAME: crenshaw-dev
GIT_EMAIL: [email protected]
RELEASE_TAG: ${{ github.event.inputs.tag }}
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Update plugin.yaml image tag
id: manifests
uses: mikefarah/yq@master
with:
cmd: yq -i '.spec.sidecar.container.image = "crenshawdotdev/github-executor-plugin:" + strenv(RELEASE_TAG)' manifests/plugin.yaml
- name: Update install manifest
run: |
# Download the binary
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.4.3/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
./argo-linux-amd64 executor-plugin build ./manifests
- name: Push release tag
run: |
set -ue
git config --global user.email "${GIT_EMAIL}"
git config --global user.name "${GIT_USERNAME}"
git commit manifests/ -m "Bump version to $RELEASE_TAG"
git push origin HEAD
git tag ${RELEASE_TAG}
git push origin ${RELEASE_TAG} --force
- name: Create GitHub release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ env.RELEASE_TAG }}
tag_name: ${{ env.RELEASE_TAG }}
generate_release_notes: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.idea
cover.out
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ build:
.PHONY: manifests
manifests:
argo executor-plugin build .

.PHONY: test
test:
go test -v ./... -coverprofile cover.out
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* Needs: >= v3.3
* Image: crenshawdotdev/github-executor-plugin:latest

This is an Argo Workflow executor plugin for interacting with GitHub.
[![codecov](https://codecov.io/gh/crenshaw-dev/github-executor-plugin/branch/main/graph/badge.svg?token=MD2ZVGZ5G9)](https://codecov.io/gh/crenshaw-dev/github-executor-plugin)

This is an Argo Workflows executor plugin for interacting with GitHub.

## Example

Expand Down
3 changes: 2 additions & 1 deletion cmd/github-plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ func main() {
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
executor := githubexecutor.NewGitHubExecutor(client, string(agentToken))
gitHubClient := &githubexecutor.GitHubClient{Issues: client.Issues}
executor := githubexecutor.NewGitHubExecutor(gitHubClient, string(agentToken))
http.HandleFunc("/api/v1/template.execute", githubexecutor.GitHubPlugin(&executor))
err = http.ListenAndServe(":4356", nil)
if err != nil {
Expand Down
14 changes: 7 additions & 7 deletions github-executor-plugin-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ data:
kind: ConfigMap
metadata:
annotations:
workflows.argoproj.io/description: "This is an Argo Workflow executor plugin for
interacting with GitHub.\n\n## Example\n\n```yaml\napiVersion: argoproj.io/v1alpha1\nkind:
Workflow\nmetadata:\n generateName: github-example-\nspec:\n entrypoint: main\n
\ templates:\n - name: main\n plugin:\n github:\n #
Use `issue` to create comments for PRs - the GitHub API considers PRs to be
issues.\n issue:\n comment:\n body: \"Hello,
world!\"\n number: \"1\" # PR number, from the \n owner:
workflows.argoproj.io/description: "[![codecov](https://codecov.io/gh/crenshaw-dev/github-executor-plugin/branch/main/graph/badge.svg?token=MD2ZVGZ5G9)](https://codecov.io/gh/crenshaw-dev/github-executor-plugin)\n\nThis
is an Argo Workflows executor plugin for interacting with GitHub.\n\n## Example\n\n```yaml\napiVersion:
argoproj.io/v1alpha1\nkind: Workflow\nmetadata:\n generateName: github-example-\nspec:\n
\ entrypoint: main\n templates:\n - name: main\n plugin:\n github:\n
\ # Use `issue` to create comments for PRs - the GitHub API considers
PRs to be issues.\n issue:\n comment:\n body:
\"Hello, world!\"\n number: \"1\" # PR number, from the \n owner:
crenshaw-dev\n repo: github-executor-plugin\n```\n\n## Prerequisites\n\n###
Set up a GitHub personal access token\n\nSee [GitHub's instructions](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)\nto
set up your token.\n\nThen create a secret using that token.\n\n```bash\n# First,
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.19
require (
github.com/argoproj/argo-workflows/v3 v3.4.4
github.com/google/go-github v17.0.0+incompatible
github.com/stretchr/testify v1.8.1
golang.org/x/oauth2 v0.2.0
)

Expand All @@ -26,6 +27,8 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/term v0.2.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,18 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
81 changes: 59 additions & 22 deletions internal/github_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import (
)

type GitHubExecutor struct {
client *github.Client
client *GitHubClient
agentToken string
}

func NewGitHubExecutor(client *github.Client, agentToken string) GitHubExecutor {
func NewGitHubExecutor(client *GitHubClient, agentToken string) GitHubExecutor {
return GitHubExecutor{client: client, agentToken: agentToken}
}

Expand Down Expand Up @@ -78,53 +78,90 @@ func (e *GitHubExecutor) runAction(plugin *PluginSpec) (string, error) {
return "", fmt.Errorf("failed to parse timeout: %w", err)
}
defer cancel()
body, owner, repo, number, err := validateIssueAction(plugin.GitHub.Issue)
if err != nil {
return "", fmt.Errorf("invalid issue action: %w", err)
var response *github.Response
var expectedResponseCode int
if plugin.GitHub.Issue != nil {
response, expectedResponseCode, err = e.runIssueAction(ctx, plugin.GitHub.Issue)
} else {
return "", fmt.Errorf("unsupported action")
}
_, response, err := e.client.Issues.CreateComment(ctx, owner, repo, number, &github.IssueComment{
Body: &body,
})
if err != nil {
return "", fmt.Errorf("failed to create issue comment: %w", err)
return "", fmt.Errorf("failed to run action: %w", err)
}
if response.StatusCode != 201 {
if response.StatusCode != expectedResponseCode {
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
}
return "", fmt.Errorf("failed to create issue comment: %s", string(responseBody))
return "", fmt.Errorf("expected response code %d but got %d: %s", expectedResponseCode, response.StatusCode, string(responseBody))
}
return "", nil
}

func validateIssueAction(action *IssueActionSpec) (body, owner, repo string, number int, err error) {
if action == nil {
return "", "", "", -1, fmt.Errorf("the only available action for the GitHub plugin is 'issue'")
func (e *GitHubExecutor) runIssueAction(ctx context.Context, issueAction *IssueActionSpec) (*github.Response, int, error) {
if err := validateIssueAction(issueAction); err != nil {
return nil, 0, fmt.Errorf("failed to validate issue action: %w", err)
}
if issueAction.Comment != nil {
body, owner, repo, number, err := validateIssueCreateCommentAction(issueAction.Comment)
if err != nil {
return nil, 0, fmt.Errorf("invalid issue comment action: %w", err)
}
_, response, err := e.client.Issues.CreateComment(ctx, owner, repo, number, &github.IssueComment{
Body: &body,
})
return response, 201, err
} else if issueAction.Create != nil {
if err := validateIssueCreateAction(issueAction.Create); err != nil {
return nil, 0, fmt.Errorf("invalid issue create action: %w", err)
}
_, response, err := e.client.Issues.Create(ctx, issueAction.Create.Owner, issueAction.Create.Repo, issueAction.Create.Request)
return response, 201, err
}
return nil, 0, fmt.Errorf("unsupported issue action")
}

func validateIssueAction(action *IssueActionSpec) error {
if action.Comment == nil && action.Create == nil {
return fmt.Errorf("the only available issue actions are 'comment' and 'create")
}
if action.Comment == nil {
return "", "", "", -1, fmt.Errorf("the only available action for issues is `comment`")
if action.Comment != nil && action.Create != nil {
return fmt.Errorf("only one issue action can be specified")
}
if action.Comment.Body == "" {
return nil
}

func validateIssueCreateCommentAction(action *IssueCommentAction) (body, owner, repo string, number int, err error) {
if action.Body == "" {
return "", "", "", -1, fmt.Errorf("the issue comment body is required")
}
if action.Comment.Owner == "" {
if action.Owner == "" {
return "", "", "", -1, fmt.Errorf("the issue owner is required")
}
if action.Comment.Repo == "" {
if action.Repo == "" {
return "", "", "", -1, fmt.Errorf("the issue repo is required")
}
if action.Comment.Number == "" {
if action.Number == "" {
return "", "", "", -1, fmt.Errorf("the issue number is required")
}
number, err = strconv.Atoi(action.Comment.Number)
number, err = strconv.Atoi(action.Number)
if err != nil {
return "", "", "", -1, fmt.Errorf("the issue number must be an integer")
}
if number < 0 {
return "", "", "", -1, fmt.Errorf("the issue number must be greater than or equal to 0")
}
return action.Comment.Body, action.Comment.Owner, action.Comment.Repo, number, nil
return action.Body, action.Owner, action.Repo, number, nil
}

func validateIssueCreateAction(action *IssueCreateAction) error {
if action.Owner == "" {
return fmt.Errorf("the issue owner is required")
}
if action.Repo == "" {
return fmt.Errorf("the issue repo is required")
}
return nil
}

// durationStringToContext parses a duration string and returns a context and cancel function. If timeout is empty, the
Expand Down
Loading

0 comments on commit cd2c8f4

Please sign in to comment.