From c1744579a3f9a013189b8137ea0aa13bc4659d92 Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:07:23 -0700 Subject: [PATCH] Slightly modify goreleaser configs for CCIP --- .../workflows/build-publish-develop-pr.yml | 4 +- .goreleaser.develop.yaml | 4 +- .goreleaser.devspace.yaml | 83 +--- tools/goreleaser-config/gen_config.go | 388 ++++++++++++++++++ tools/goreleaser-config/main.go | 24 ++ 5 files changed, 423 insertions(+), 80 deletions(-) create mode 100644 tools/goreleaser-config/gen_config.go create mode 100644 tools/goreleaser-config/main.go diff --git a/.github/workflows/build-publish-develop-pr.yml b/.github/workflows/build-publish-develop-pr.yml index 2868616ace..2df3b7d1c8 100644 --- a/.github/workflows/build-publish-develop-pr.yml +++ b/.github/workflows/build-publish-develop-pr.yml @@ -26,8 +26,6 @@ jobs: fail-fast: false matrix: include: - - image-name: chainlink - goreleaser-config: .goreleaser.develop.yaml - image-name: ccip goreleaser-config: .goreleaser.ccip.develop.yaml runs-on: ubuntu-20.04 @@ -112,4 +110,4 @@ jobs: basic-auth: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_INTERNAL_HOST }} this-job-name: goreleaser-build-publish-${{ matrix.image-name }} - continue-on-error: true \ No newline at end of file + continue-on-error: true diff --git a/.goreleaser.develop.yaml b/.goreleaser.develop.yaml index f8757676f8..70f7683103 100644 --- a/.goreleaser.develop.yaml +++ b/.goreleaser.develop.yaml @@ -1,7 +1,5 @@ -project_name: chainlink - version: 2 - +project_name: ccip env: - ZIG_EXEC={{ if index .Env "ZIG_EXEC" }}{{ .Env.ZIG_EXEC }}{{ else }}zig{{ end }} - IMAGE_PREFIX={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }} diff --git a/.goreleaser.devspace.yaml b/.goreleaser.devspace.yaml index ccea1de96b..a951fcdd39 100644 --- a/.goreleaser.devspace.yaml +++ b/.goreleaser.devspace.yaml @@ -1,8 +1,5 @@ -## goreleaser <1.14.0 -project_name: ccip - version: 2 - +project_name: ccip env: - ZIG_EXEC={{ if index .Env "ZIG_EXEC" }}{{ .Env.ZIG_EXEC }}{{ else }}zig{{ end }} - IMAGE_LABEL_DESCRIPTION="node of the decentralized oracle network, bridging on and off-chain computation" @@ -23,74 +20,12 @@ builds: goarch: - amd64 hooks: - post: ./tools/bin/goreleaser_utils build_post_hook {{ dir .Path }} {{ .Os }} {{ .Arch }} - env: - - CGO_ENABLED=1 - - CC=$ZIG_EXEC cc -target x86_64-linux-gnu -Wno-error=unused-command-line-argument - - CCX=$ZIG_EXEC c++ -target x86_64-linux-gnu -Wno-error=unused-command-line-argument - flags: - - -trimpath - - -buildmode=pie - ldflags: - - -s -w -r=$ORIGIN/libs - - -X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }} - - -X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }} - -# See https://goreleaser.com/customization/docker/ -dockers: - - id: linux-amd64 - dockerfile: core/chainlink.goreleaser.Dockerfile - use: buildx - goos: linux - goarch: amd64 - extra_files: - - tmp/linux_amd64/libs - - tmp/linux_amd64/plugins - - tools/bin/ldd_fix - build_flag_templates: - - "--platform=linux/amd64" - - "--pull" - - "--build-arg=CHAINLINK_USER=chainlink" - - "--build-arg=COMMIT_SHA={{ .FullCommit }}" - - "--build-arg=CL_MEDIAN_CMD=chainlink-feeds" - - "--build-arg=CL_MERCURY_CMD=chainlink-mercury" - - "--build-arg=CL_SOLANA_CMD=chainlink-solana" - - "--build-arg=CL_STARKNET_CMD=chainlink-starknet" - - "--label=org.opencontainers.image.created={{ .Date }}" - - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" - - "--label=org.opencontainers.image.licenses={{ .Env.IMAGE_LABEL_LICENSES }}" - - "--label=org.opencontainers.image.revision={{ .FullCommit }}" - - "--label=org.opencontainers.image.source={{ .Env.IMAGE_LABEL_SOURCE }}" - - "--label=org.opencontainers.image.title={{ .ProjectName }}" - - "--label=org.opencontainers.image.version={{ .Version }}" - - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" - image_templates: - - "{{ .Env.IMAGE }}" - -# See https://goreleaser.com/customization/docker_manifest/ -docker_manifests: - - name_template: "{{ .Env.IMAGE }}" - image_templates: - - "{{ .Env.IMAGE }}" - -checksum: - name_template: "checksums.txt" - -snapshot: - version_template: '{{ .Env.CHAINLINK_VERSION }}-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}' - -changelog: - sort: asc - filters: - exclude: - - "^docs:" - - "^test:" - -# See https://goreleaser.com/customization/git/ + - cmd: go mod tidy + - cmd: ./tools/bin/goreleaser_utils before_hook git: - ignore_tags: - - contracts-ccip/v1.5.0-beta.0 - -# modelines, feel free to remove those if you don't want/use them: -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json -# vim: set ts=2 sw=2 tw=0 fo=cnqoj + ignore_tags: + - contracts-ccip/v1.5.0-beta.0 +partial: + by: target +nightly: + version_template: v0.0.0-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }} diff --git a/tools/goreleaser-config/gen_config.go b/tools/goreleaser-config/gen_config.go new file mode 100644 index 0000000000..142035432d --- /dev/null +++ b/tools/goreleaser-config/gen_config.go @@ -0,0 +1,388 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/goreleaser/goreleaser-pro/v2/pkg/config" +) + +// Generate creates the goreleaser configuration based on the environment. +var validEnvironments = []string{"devspace", "develop"} + +func Generate(environment string) config.Project { + checkEnvironments(environment) + architectures := []string{"amd64", "arm64"} + + project := config.Project{ + ProjectName: "ccip", + Version: 2, + Env: commonEnv(environment), + Before: config.Before{ + Hooks: []config.Hook{ + { + Cmd: "go mod tidy", + }, + { + Cmd: "./tools/bin/goreleaser_utils before_hook", + }, + }, + }, + Builds: builds(environment), + Dockers: dockers(environment, architectures), + DockerManifests: dockerManifests(environment), + Checksum: config.Checksum{ + NameTemplate: "checksums.txt", + }, + Snapshot: config.Snapshot{ + VersionTemplate: "{{ .Env.VERSION }}-{{ .ShortCommit }}", + }, + Nightly: config.Nightly{ + VersionTemplate: "{{ .Env.VERSION }}-{{ .Env.IMG_TAG }}", + }, + Partial: config.Partial{ + By: "target", + }, + Release: config.Release{ + Disable: "true", + }, + Archives: []config.Archive{ + { + Format: "binary", + }, + }, + Changelog: config.Changelog{ + Disable: "true", + }, + } + if environment == "devspace" { + versionTemplate := `v0.0.0-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}` + project.Snapshot = config.Snapshot{VersionTemplate: versionTemplate} + project.Nightly = config.Nightly{VersionTemplate: versionTemplate} + project.Git.IgnoreTags = []string{"contracts-ccip/v1.5.0-beta.0"} + } + + // Add SBOMs if needed + if environment == "production" { + project.Changelog = config.Changelog{ + Sort: "asc", + Filters: config.Filters{ + Exclude: []string{ + "^docs:", + "^test:", + }, + }, + } + project.Archives = []config.Archive{ + { + Format: "tar.gz", + }, + } + project.SBOMs = []config.SBOM{ + { + Artifacts: "archive", + }, + } + } + + return project +} + +func checkEnvironments(environment string) { + valid := false + for _, env := range validEnvironments { + if environment == env { + valid = true + break + } + } + if !valid { + panic(fmt.Sprintf("invalid environment: %s, valid environments are %v", environment, validEnvironments)) + } +} + +// commonEnv returns the common environment variables used across environments. +func commonEnv(environment string) []string { + envs := []string{ + `IMG_PRE={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }}`, + `IMG_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }}`, + `CGO_ENABLED=1`, + } + + if environment != "devspace" { + envs = append(envs, `VERSION={{ if index .Env "CHAINLINK_VERSION" }}{{ .Env.CHAINLINK_VERSION }}{{ else }}v0.0.0-local{{ end }}`) + } + return envs +} + +// builds returns the build configurations based on the environment. +func builds(environment string) []config.Build { + switch environment { + case "devspace": + return []config.Build{ + build(true), + } + case "develop", "production": + return []config.Build{ + build(false), + } + + default: + return nil + } +} + +// build creates a build configuration. +func build(isDevspace bool) config.Build { + dynamicLinker := `{{ if contains .Runtime.Goarch "amd64" -}} +/lib64/ld-linux-x86-64.so.2 +{{- else if contains .Runtime.Goarch "arm64" -}} +/lib/ld-linux-aarch64.so.1 +{{- end }}` + + ldflags := []string{ + "-s -w -r=$ORIGIN/libs", + "-X github.com/smartcontractkit/chainlink/v2/core/static.Sha={{ .FullCommit }}", + fmt.Sprintf(`-extldflags "-Wl,--dynamic-linker=%s"`, dynamicLinker), + } + + if isDevspace { + ldflags = append(ldflags, "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Version }}") + } else { + ldflags = append(ldflags, "-X github.com/smartcontractkit/chainlink/v2/core/static.Version={{ .Env.VERSION }}") + } + + return config.Build{ + Binary: "chainlink", + NoUniqueDistDir: "true", + Targets: []string{"go_first_class"}, + Hooks: config.BuildHookConfig{ + Post: []config.Hook{ + {Cmd: "./tools/bin/goreleaser_utils build_post_hook {{ dir .Path }}"}, + }, + }, + BuildDetails: config.BuildDetails{ + Flags: []string{"-trimpath", "-buildmode=pie"}, + Ldflags: ldflags, + }, + } +} + +// dockers returns the docker configurations based on the environment. +func dockers(environment string, architectures []string) []config.Docker { + var dockers []config.Docker + switch environment { + case "devspace": + dockers = []config.Docker{ + docker("linux-amd64", "linux", "amd64", environment, true), + docker("linux-arm64", "linux", "arm64", environment, true), + } + + case "develop", "production": + imageNames := []string{"chainlink", "ccip"} + + for _, imageName := range imageNames { + for _, arch := range architectures { + id := fmt.Sprintf("linux-%s-%s", arch, imageName) + pluginId := fmt.Sprintf("%s-plugins", id) + + dockers = append(dockers, docker(id, "linux", arch, environment, false)) + dockers = append(dockers, docker(pluginId, "linux", arch, environment, false)) + } + } + } + return dockers +} + +// docker creates a docker configuration. +func docker(id, goos, goarch, environment string, isDevspace bool) config.Docker { + isCCIP := strings.Contains(id, "ccip") + isPlugins := strings.Contains(id, "plugins") + extraFiles := []string{"tmp/libs"} + if isPlugins || isDevspace { + extraFiles = append(extraFiles, "tmp/plugins") + } + if isCCIP { + extraFiles = append(extraFiles, "ccip/config") + } + + buildFlagTemplates := []string{ + fmt.Sprintf("--platform=%s/%s", goos, goarch), + "--pull", + "--build-arg=CHAINLINK_USER=chainlink", + "--build-arg=COMMIT_SHA={{ .FullCommit }}", + } + + if strings.Contains(id, "ccip") { + buildFlagTemplates = append(buildFlagTemplates, + "--build-arg=CL_CHAIN_DEFAULTS=/chainlink/ccip-config") + } + + if strings.Contains(id, "plugins") || isDevspace { + buildFlagTemplates = append(buildFlagTemplates, + "--build-arg=CL_MEDIAN_CMD=chainlink-feeds", + "--build-arg=CL_MERCURY_CMD=chainlink-mercury", + "--build-arg=CL_SOLANA_CMD=chainlink-solana", + "--build-arg=CL_STARKNET_CMD=chainlink-starknet", + ) + } + + buildFlagTemplates = append(buildFlagTemplates, + `--label=org.opencontainers.image.created={{ .Date }}`, + `--label=org.opencontainers.image.description="node of the decentralized oracle network, bridging on and off-chain computation"`, + `--label=org.opencontainers.image.licenses=MIT`, + `--label=org.opencontainers.image.revision={{ .FullCommit }}`, + `--label=org.opencontainers.image.source=https://github.com/smartcontractkit/chainlink`, + `--label=org.opencontainers.image.title=chainlink`, + `--label=org.opencontainers.image.url=https://github.com/smartcontractkit/chainlink`, + ) + if !isDevspace { + buildFlagTemplates = append(buildFlagTemplates, + `--label=org.opencontainers.image.version={{ .Env.VERSION }}`, + ) + } + + dockerConfig := config.Docker{ + ID: id, + Dockerfile: "core/chainlink.goreleaser.Dockerfile", + Use: "buildx", + Goos: goos, + Goarch: goarch, + Files: extraFiles, + BuildFlagTemplates: buildFlagTemplates, + } + + // We always want to build both versions as a test, but + // only push the relevant version based on the tag name + // + // We also expect the production config file to only be run during a tag push, + // enforced inside our github actions workflow, "build-publish" + if environment == "production" { + if isCCIP { + dockerConfig.SkipPush = "{{ not (contains .Tag \"-ccip\") }}" + } else { + dockerConfig.SkipPush = "{{ contains .Tag \"-ccip\" }}" + } + } + + // This section handles the image templates for the docker configuration + if environment == "devspace" { + dockerConfig.ImageTemplates = []string{"{{ .Env.IMAGE }}"} + } else { + base := "{{ .Env.IMG_PRE }}" + // On production envs, we have the ECR prefix for the image + if environment == "production" { + if isCCIP { + base = base + "/chainlink/chainlink-ccip-experimental-goreleaser" + } else { + base = base + "/chainlink/chainlink-experimental-goreleaser" + } + } else { + if isCCIP { + base = base + "/ccip" + } else { + base = base + "/chainlink" + } + } + + imageTemplates := []string{} + if strings.Contains(id, "plugins") { + taggedBase := fmt.Sprintf("%s:{{ .Env.IMG_TAG }}-plugins", base) + // We have a default, non-arch specific image for plugins that defaults to amd64 + if goarch == "amd64" { + imageTemplates = append(imageTemplates, taggedBase) + } + imageTemplates = append(imageTemplates, + fmt.Sprintf("%s-%s", taggedBase, archSuffix(id)), + fmt.Sprintf("%s:sha-{{ .ShortCommit }}-plugins-%s", base, archSuffix(id))) + } else { + taggedBase := fmt.Sprintf("%s:{{ .Env.IMG_TAG }}", base) + // We have a default, non-arch specific image for plugins that defaults to amd64 + if goarch == "amd64" { + imageTemplates = append(imageTemplates, taggedBase) + } + imageTemplates = append(imageTemplates, + fmt.Sprintf("%s-%s", taggedBase, archSuffix(id)), + fmt.Sprintf("%s:sha-{{ .ShortCommit }}-%s", base, archSuffix(id))) + } + + dockerConfig.ImageTemplates = imageTemplates + } + + return dockerConfig +} + +// archSuffix returns the architecture suffix for image tags. +func archSuffix(id string) string { + if strings.Contains(id, "arm64") { + return "arm64" + } + return "amd64" +} + +// dockerManifests returns the docker manifest configurations based on the environment. +func dockerManifests(environment string) []config.DockerManifest { + if environment == "devspace" { + return []config.DockerManifest{ + { + NameTemplate: "{{ .Env.IMAGE }}", + ImageTemplates: []string{"{{ .Env.IMAGE }}"}, + }, + } + } + + // Define the image names based on the environment + imageNames := []string{"chainlink", "ccip"} + + // FIXME: This is duplicated + if environment == "production" { + imageNames = []string{"chainlink/chainlink-experimental-goreleaser", "chainlink/chainlink-ccip-experimental-goreleaser"} + } + var manifests []config.DockerManifest + + for _, imageName := range imageNames { + fullImageName := fmt.Sprintf("{{ .Env.IMAGE_PREFIX }}/%s", imageName) + + manifestConfigs := []struct { + ID string + Suffix string + }{ + {ID: "tagged", Suffix: ":{{ .Env.IMG_TAG }}"}, + {ID: "sha", Suffix: ":sha-{{ .ShortCommit }}"}, + {ID: "tagged-plugins", Suffix: ":{{ .Env.IMG_TAG }}-plugins"}, + {ID: "sha-plugins", Suffix: ":sha-{{ .ShortCommit }}-plugins"}, + } + for _, cfg := range manifestConfigs { + nameTemplate := fmt.Sprintf("%s%s", fullImageName, cfg.Suffix) + manifest := config.DockerManifest{ + ID: strings.ReplaceAll(fmt.Sprintf("%s-%s", cfg.ID, imageName), "/", "-"), + NameTemplate: nameTemplate, + ImageTemplates: manifestImages(nameTemplate), + } + if environment == "production" { + if strings.Contains(nameTemplate, "ccip") { + manifest.SkipPush = "{{ not (contains .Tag \"-ccip\") }}" + } else { + manifest.SkipPush = "{{ contains .Tag \"-ccip\" }}" + } + } + manifests = append(manifests, manifest) + } + } + + return manifests +} + +// manifestImages generates image templates for docker manifests. +func manifestImages(imageName string) []string { + architectures := []string{"amd64", "arm64"} + var images []string + // Add the default image for tagged images + if !strings.Contains(imageName, "sha") { + images = append(images, imageName) + } + for _, arch := range architectures { + images = append(images, fmt.Sprintf("%s-%s", imageName, arch)) + } + return images +} diff --git a/tools/goreleaser-config/main.go b/tools/goreleaser-config/main.go new file mode 100644 index 0000000000..e7376dfb89 --- /dev/null +++ b/tools/goreleaser-config/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "gopkg.in/yaml.v3" + "os" +) + +func main() { + environments := []string{"develop", "devspace"} + for _, e := range environments { + cfg := Generate(e) + data, err := yaml.Marshal(&cfg) + if err != nil { + panic(err) + } + filename := fmt.Sprintf("../../.goreleaser.%s.yaml", e) + err = os.WriteFile(filename, data, 0644) + if err != nil { + panic(err) + } + fmt.Printf("Generated %s\n", filename) + } +}