Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions .github/workflows/publish-agent-helper.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: Publish Agent Helper Image

on:
release:
types: [published]
push:
branches: [main]
paths:
- images/agent-helper/**
- hack/agent_helper_image/**
- .github/workflows/publish-agent-helper.yml
workflow_dispatch:
inputs:
tag:
description: optional version override (defaults to the latest git tag)
description: optional version override (defaults to images/agent-helper/VERSION)
required: false
type: string
pull_request:
Expand All @@ -26,8 +30,6 @@ jobs:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
Expand All @@ -38,24 +40,17 @@ jobs:
version: 0.16.0

- name: cross-compile helper binaries
run: go run ./hack/agent_helper_image
run: go run ./hack/agent_helper_image build

- name: resolve version
id: version
run: |
set -euo pipefail
if [ -n "${{ inputs.tag }}" ]; then
version="${{ inputs.tag }}"
elif [ "${{ github.event_name }}" = "release" ]; then
version="${{ github.ref_name }}"
else
version="$(git describe --tags --abbrev=0)"
fi
echo "value=$version" >> "$GITHUB_OUTPUT"
env:
INPUT_TAG: ${{ inputs.tag }}
run: go run ./hack/agent_helper_image version -tag "$INPUT_TAG"

- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0

# Publish only on release / manual dispatch; pull_request runs build-only.
# Publish on push to main / manual dispatch; pull_request runs build-only.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: generate GitHub App token
id: app-token
if: ${{ github.event_name != 'pull_request' }}
Expand All @@ -72,21 +67,29 @@ jobs:
username: ${{ steps.app-token.outputs.app-slug }}
password: ${{ steps.app-token.outputs.token }}

- name: check tag exists
id: exists
if: ${{ github.event_name != 'pull_request' }}
env:
IMAGE: ${{ env.IMAGE }}
VERSION: ${{ steps.version.outputs.value }}
run: go run ./hack/agent_helper_image exists -image "$IMAGE" -tag "$VERSION"

- name: docker metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ${{ env.IMAGE }}
tags: |
type=raw,value=${{ steps.version.outputs.value }}
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}
type=raw,value=latest,enable=${{ github.event_name != 'pull_request' }}

- name: build and push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: images/agent-helper
file: images/agent-helper/Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
push: ${{ github.event_name != 'pull_request' && steps.exists.outputs.value != 'true' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
131 changes: 127 additions & 4 deletions hack/agent_helper_image/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
package main

import (
"bytes"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
)

type target struct {
Expand All @@ -22,16 +25,39 @@ var targets = []target{
}

func main() {
dir := flag.String("dir", "images/agent-helper", "agent-helper image directory")
flag.Parse()
args := os.Args[1:]
cmd := "build"
if len(args) > 0 && !strings.HasPrefix(args[0], "-") {
cmd, args = args[0], args[1:]
}

if err := run(*dir); err != nil {
var err error
switch cmd {
case "build":
err = runBuild(args)
case "version":
err = runVersion(args)
case "exists":
err = runExists(args)
default:
err = fmt.Errorf("unknown command %q", cmd)
}
if err != nil {
fmt.Fprintln(os.Stderr, "agent_helper_image:", err)
os.Exit(1)
}
}

func run(dir string) error {
func runBuild(args []string) error {
fs := flag.NewFlagSet("build", flag.ExitOnError)
dir := fs.String("dir", "images/agent-helper", "agent-helper image directory")
if err := fs.Parse(args); err != nil {
return err
}
return build(*dir)
}

func build(dir string) error {
distDir := filepath.Join(dir, "dist")
if err := os.MkdirAll(distDir, 0o755); err != nil {
return err
Expand All @@ -57,3 +83,100 @@ func run(dir string) error {
}
return nil
}

func runVersion(args []string) error {
fs := flag.NewFlagSet("version", flag.ExitOnError)
dir := fs.String("dir", "images/agent-helper", "agent-helper image directory")
tag := fs.String("tag", "", "explicit version override")
if err := fs.Parse(args); err != nil {
return err
}

version := strings.TrimSpace(*tag)
if version == "" {
data, err := os.ReadFile(filepath.Join(*dir, "VERSION"))
if err != nil {
return err
}
version = strings.TrimSpace(string(data))
}
if version == "" {
return fmt.Errorf("agent-helper version is empty")
}

fmt.Println(version)
return writeOutput("value", version)
}

func runExists(args []string) error {
fs := flag.NewFlagSet("exists", flag.ExitOnError)
image := fs.String("image", "", "image repository (without tag)")
tag := fs.String("tag", "", "image tag to check")
if err := fs.Parse(args); err != nil {
return err
}
if *image == "" || *tag == "" {
return fmt.Errorf("both -image and -tag are required")
}

ref := *image + ":" + *tag
exists, err := manifestExists(ref)
if err != nil {
return err
}
if exists {
fmt.Printf("::notice::%s already exists; skipping push\n", ref)
}
return writeOutput("value", strconv.FormatBool(exists))
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

func manifestExists(ref string) (bool, error) {
var stderr bytes.Buffer
cmd := exec.Command("docker", "manifest", "inspect", ref)
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
msg := strings.TrimSpace(stderr.String())
if isAbsent(msg) {
return false, nil
}
return false, fmt.Errorf(
"docker manifest inspect %s: %w: %s", ref, err, msg,
)
}
return true, nil
}

func isAbsent(stderr string) bool {
s := strings.ToLower(stderr)
for _, marker := range []string{
"no such manifest",
"manifest unknown",
"name unknown",
"not found",
"denied",
"unauthorized",
"forbidden",
} {
if strings.Contains(s, marker) {
return true
}
}
return false
}

func writeOutput(key, value string) error {
out := os.Getenv("GITHUB_OUTPUT")
if out == "" {
return nil
}
f, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
_, writeErr := fmt.Fprintf(f, "%s=%s\n", key, value)
closeErr := f.Close()
if writeErr != nil {
return writeErr
}
return closeErr
}
1 change: 1 addition & 0 deletions images/agent-helper/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
Loading