Skip to content

Commit

Permalink
feat: add copacetic-action
Browse files Browse the repository at this point in the history
  • Loading branch information
mhrabovcin committed Feb 19, 2024
1 parent d3780e6 commit f5c3e86
Show file tree
Hide file tree
Showing 23 changed files with 1,744 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .github/actions/copacetic-action/.envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Automatically sets up your devbox environment whenever you cd into this
# directory via our direnv integration:

eval "$(devbox generate direnv --print-envrc)"

# check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/
# for more details
96 changes: 96 additions & 0 deletions .github/actions/copacetic-action/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Patch images
description: 'Run copacetic patch on provided list of images and publish those to given container registry'
inputs:
github-token:
description: 'Github token'
required: true
type: string
images:
description: 'List of images to process separated by newline'
required: true
type: string
# buildkit-version:
# description: 'Buildkit version'
# type: string
skip-upload:
description: 'Skip uploading to remote registry'
default: false
required: false
type: boolean
debug:
description: 'Enable additional output'
default: false
required: false
type: boolean
timeout:
description: 'Run timeout (default 1h)'
default: 1h
required: false
type: string
outputs:
result:
description: "Patching result"
value: ${{ steps.execute-patch.outputs.result }}
markdown:
description: "Markdown report"
value: ${{ steps.generate-md-report.outputs.report }}
runs:
using: composite
steps:

- uses: cachix/install-nix-action@v25
with:
github_access_token: ${{ inputs.github-token }}

- name: Install devbox
uses: jetpack-io/[email protected]
with:
# TODO(mh): Cache restore fails due to tar permission errors
enable-cache: false
skip-nix-installation: true
project-path: ${{ github.action_path }}

- name: docker create builder
working-directory: ${{ github.action_path }}
shell: bash
run: |
devbox run -- docker buildx create --name copacetic-action
- name: run patching
id: execute-patch
working-directory: ${{ github.action_path }}
shell: bash
run: |
IMAGES_PATH=$(mktemp)
echo "$IMAGES" > $IMAGES_PATH
RESULT="$(devbox run -- go run main.go patch $IMAGES_PATH \
--skip-upload=${{ inputs.skip-upload }} \
--debug=${{ inputs.debug }} \
--timeout=${{ inputs.timeout }})"
echo "result=$RESULT" >> $GITHUB_OUTPUT
env:
IMAGES: ${{ inputs.images }}
GCO_ENABLED: "0"
GOWORK: "off"
BUILDX_BUILDER: "copacetic-action"

- name: generate-md-report
id: generate-md-report
working-directory: ${{ github.action_path }}
shell: bash
run: |
JSON_REPORT_PATH=$(mktemp)
echo "$JSON_REPORT" > $JSON_REPORT_PATH
RESULT="$(devbox run -- go run main.go markdown $JSON_REPORT_PATH)"
echo "$RESULT" >> $GITHUB_STEP_SUMMARY
env:
JSON_REPORT: ${{ steps.execute-patch.outputs.result }}
GOWORK: "off"

- name: cleanup builder
working-directory: ${{ github.action_path }}
shell: bash
if: always()
run: |
devbox run -- docker buildx rm copacetic-action
97 changes: 97 additions & 0 deletions .github/actions/copacetic-action/cmd/copa-action/patch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package copa_action

import (
"bytes"
"context"
"errors"
"io"
"log/slog"
"os"
"time"

"github.com/goware/prefixer"
"github.com/spf13/cobra"

"github.com/d2iq-labs/copacetic-action/pkg/cli"
"github.com/d2iq-labs/copacetic-action/pkg/image"
"github.com/d2iq-labs/copacetic-action/pkg/patch"
"github.com/d2iq-labs/copacetic-action/pkg/registry"
)

var (
ghcrOrg = "mesosphere/dkp-container-images"

// <original>-d2iq.<version>
imageTagSuffix = "d2iq"
debug = false
timeout = 1 * time.Hour
skipUpload = false
format = "json"
)

func NewPatchCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "patch PATH | -",
Short: "Patch runs copatetic patch operation on list of provided images",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(cmd.Context(), timeout)
defer cancel()

input, err := cli.OpenFileOrStdin(args[0])
if err != nil {
return err
}

images, err := cli.ReadImages(input)
if err != nil {
return err
}

ghcr := registry.NewGHCR(ghcrOrg)
if skipUpload {
ghcr.WithSkipUploads(slog.Default())
}

tasks := []*patch.Task{}
for _, imageRef := range images {
slog.Info("patching image", "imageRef", imageRef)
logger, _ := getImageLogger(imageRef, debug)
task, err := patch.Run(ctx, imageRef, ghcr, imageTagSuffix, debug, logger)
if err != nil {
cmdErr := &image.CmdErr{}
if errors.As(err, &cmdErr) {
slog.Error("failed patching", "err", err)
io.Copy(os.Stderr, prefixer.New(bytes.NewReader(cmdErr.Output), "output | "))
} else {
slog.Error("failed patching", "err", err)
}
}
tasks = append(tasks, task)
}

return patch.WriteJSON(tasks, os.Stdout)
},
}

cmd.Flags().BoolVar(&debug, "debug", debug, "enable debugging")
cmd.Flags().BoolVar(&skipUpload, "skip-upload", skipUpload, "skip uploading to remote registry")
cmd.Flags().DurationVar(&timeout, "timeout", timeout, "total timeout of run")
cmd.Flags().StringVar(&ghcrOrg, "ghcr-org", ghcrOrg, "name of ghcr.io org where patched images will get published")
cmd.Flags().StringVar(&imageTagSuffix, "patched-tag-suffix", imageTagSuffix, "name of patched images tag suffix, e.g v1.0.0 will become v1.0.0-d2iq.0")

return cmd
}

func getImageLogger(imageRef string, debug bool) (*slog.Logger, io.Reader) {
data := &bytes.Buffer{}
var imagePatchLogs io.Writer
imagePatchLogs = data
if debug {
imagePatchLogs = io.MultiWriter(data, os.Stderr)
}

logger := slog.New(slog.NewTextHandler(
imagePatchLogs, &slog.HandlerOptions{})).With("imageRef", imageRef)
return logger, data
}
38 changes: 38 additions & 0 deletions .github/actions/copacetic-action/cmd/copa-action/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package copa_action

import (
"encoding/json"
"fmt"
"io"
"os"

"github.com/d2iq-labs/copacetic-action/pkg/cli"
"github.com/d2iq-labs/copacetic-action/pkg/patch"
"github.com/spf13/cobra"
)

func NewMarkdownCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "markdown PATH | -",
Short: "Generate markdown report from JSON output",
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
input, err := cli.OpenFileOrStdin(args[0])
if err != nil {
return err
}
data, err := io.ReadAll(input)
if err != nil {
return err
}

report := patch.Report{}
if err := json.Unmarshal(data, &report); err != nil {
return fmt.Errorf("failed to read JSON report: %w", err)
}

return patch.WriteMarkdown(report, os.Stdout)
},
}
return cmd
}
25 changes: 25 additions & 0 deletions .github/actions/copacetic-action/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"

copa_action "github.com/d2iq-labs/copacetic-action/cmd/copa-action"
)

var rootCmd = &cobra.Command{
Use: "copa-action",
Short: "Copacetic Action",
}

func Execute() {
rootCmd.AddCommand(copa_action.NewPatchCmd())
rootCmd.AddCommand(copa_action.NewMarkdownCmd())

if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
16 changes: 16 additions & 0 deletions .github/actions/copacetic-action/devbox.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"packages": [
"trivy@latest",
"path:./hack/flakes#copacetic",
"go@latest",
"docker@latest",
"docker-buildx@latest",
],
"shell": {
"scripts": {
"test": [
"echo \"Error: no test specified\" && exit 1",
],
},
},
}
85 changes: 85 additions & 0 deletions .github/actions/copacetic-action/devbox.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"lockfile_version": "1",
"packages": {
"docker-buildx@latest": {
"last_modified": "2024-01-29T00:15:04Z",
"resolved": "github:NixOS/nixpkgs/90f456026d284c22b3e3497be980b2e47d0b28ac#docker-buildx",
"source": "devbox-search",
"version": "0.12.1",
"systems": {
"aarch64-darwin": {
"store_path": "/nix/store/qa5b39af6fpywkh2gxny08kh6ra39lhq-docker-buildx-0.12.1"
},
"aarch64-linux": {
"store_path": "/nix/store/z7mw5d6fkdbzl3140ay1yp4fwrrifdgy-docker-buildx-0.12.1"
},
"x86_64-darwin": {
"store_path": "/nix/store/w1r0zbxswmh7fg8k3cdf9y7gc3gxam1i-docker-buildx-0.12.1"
},
"x86_64-linux": {
"store_path": "/nix/store/g921mfcvdyl59mkv96nq2jd7znl9yi1r-docker-buildx-0.12.1"
}
}
},
"docker@latest": {
"last_modified": "2024-01-30T04:45:19Z",
"resolved": "github:NixOS/nixpkgs/222c1940fafeda4dea161858ffe6ebfc853d3db5#docker",
"source": "devbox-search",
"version": "24.0.5",
"systems": {
"aarch64-darwin": {
"store_path": "/nix/store/b1wvdq94zxwkpjsxdzhm9hf9igb83siz-docker-24.0.5"
},
"aarch64-linux": {
"store_path": "/nix/store/h10712wm42kns4wjy78qg0ycmw3v8hai-docker-24.0.5"
},
"x86_64-darwin": {
"store_path": "/nix/store/16wz1cd6kz75rs0fnhihaqn1c7a5l8fy-docker-24.0.5"
},
"x86_64-linux": {
"store_path": "/nix/store/kahw87dwsj3m4czp636a58791qrpnj25-docker-24.0.5"
}
}
},
"go@latest": {
"last_modified": "2024-01-27T14:55:31Z",
"resolved": "github:NixOS/nixpkgs/160b762eda6d139ac10ae081f8f78d640dd523eb#go",
"source": "devbox-search",
"version": "1.21.6",
"systems": {
"aarch64-darwin": {
"store_path": "/nix/store/8m3wjb23sfbjpjsj4l82b4zh9xnw62hh-go-1.21.6"
},
"aarch64-linux": {
"store_path": "/nix/store/ia1z2slwgdgibad4z42rpd1dib7gk999-go-1.21.6"
},
"x86_64-darwin": {
"store_path": "/nix/store/dm4s4dfrah5zhl6larnrbh07v76j4pgy-go-1.21.6"
},
"x86_64-linux": {
"store_path": "/nix/store/cw9dqybf9w6wp7827h23pb3ym8gs8h47-go-1.21.6"
}
}
},
"trivy@latest": {
"last_modified": "2024-01-27T14:55:31Z",
"resolved": "github:NixOS/nixpkgs/160b762eda6d139ac10ae081f8f78d640dd523eb#trivy",
"source": "devbox-search",
"version": "0.48.3",
"systems": {
"aarch64-darwin": {
"store_path": "/nix/store/jcz6japkpm081wvqx8sa11nihb17ladq-trivy-0.48.3"
},
"aarch64-linux": {
"store_path": "/nix/store/sar0a567lz1v04yq90clrlrjbj1i4w3y-trivy-0.48.3"
},
"x86_64-darwin": {
"store_path": "/nix/store/pm5vh07v1pyr14dkzvj056si91mlp8rp-trivy-0.48.3"
},
"x86_64-linux": {
"store_path": "/nix/store/pkp2km72f2nhv0b5w6gfc9si49dhd3r4-trivy-0.48.3"
}
}
}
}
}
Loading

0 comments on commit f5c3e86

Please sign in to comment.