Skip to content

Commit

Permalink
Merge pull request #213 from bupd/fix-release
Browse files Browse the repository at this point in the history
Fix Release Pipeline
  • Loading branch information
Vad1mo authored Oct 22, 2024
2 parents 90f544f + d697fe2 commit 04c0305
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 108 deletions.
17 changes: 14 additions & 3 deletions .github/workflows/docker_publish.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
name: Docker Publish

on:
release:
types: [published]
push:
tags:
- "v*"

jobs:
docker-publish:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
environment: production
env:
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_ADDRESS: ${{ vars.REGISTRY_ADDRESS }}
PUBLISH_ADDRESS: ${{ vars.PUBLISH_ADDRESS }}
TAG: ${{ github.ref_name }}

steps:
- name: Checkout repo
uses: actions/checkout@v4
Expand All @@ -21,4 +32,4 @@ jobs:
with:
version: "latest"
verb: call
args: publish-image --source=. --cosign-key=${{ secrets.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }}
args: "publish-image --source=. --cosign-password='${{ env.COSIGN_PASSWORD }}' --cosign-key='${{ env.COSIGN_KEY }}' --reg-username='${{ env.REGISTRY_USERNAME }}' --reg-password='${{ env.REGISTRY_PASSWORD }}' --reg-address='${{ env.REGISTRY_ADDRESS }}' --publish-address='${{ env.PUBLISH_ADDRESS }}' --tag='${{ env.TAG }}'"
27 changes: 17 additions & 10 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
version: 2
project_name: harbor

before:
hooks:
- go mod tidy

builds:
- main: ./cmd/harbor/main.go

env:
- CGO_ENABLED=0
- CGO_ENABLED=0
ldflags:
- -w -s -X github.com/goharbor/harbor-cli/cmd/harbor/internal/version.GitCommit={{.FullCommit}}
goos:
Expand All @@ -18,7 +19,7 @@ builds:
goarch:
- amd64
- arm64
ignore:
ignore:
- goos: windows
goarch: arm
- goos: windows
Expand Down Expand Up @@ -46,21 +47,27 @@ sboms:

checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
snapshot:
version_template: "{{ .Tag }}-next"
release:
name_template: "HarborCLI {{.Tag}}"
# draft: true
# prerelease: auto

draft: false # Set to false to ensure that releases are published, not kept as drafts
prerelease: auto # Auto-detect prereleases based on tag
disable: false # Ensure release publishing is enabled
github:
owner: goharbor # Your GitHub repository owner
name: harbor-cli # Your GitHub repository name

changelog:
sort: asc
use: github
format: "{{.SHA}}: {{.Message}} (@{{.AuthorUsername}})"
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "merge conflict"

groups:
- title: Dependency updates
regexp: '^.*?(.+)\(deps\)!?:.+$'
Expand All @@ -81,4 +88,4 @@ changelog:
regexp: ^.*?(build|ci)(\(.+\))??!?:.+$
order: 400
- title: Other work
order: 9999
order: 9999
102 changes: 102 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
## Overview

This document provides a step-by-step guide for maintainers to create and publish a release for the project using GoReleaser. The release process is automated via GitHub Actions, and includes generating a changelog, signing the release, and pushing artifacts to the specified container registry.

## Prerequisites

Before creating a release, ensure the following:
- You have **write access** to the repository.
- The required **repository secrets** and **environment variables** are set.
- You have **cosign** installed locally to generate the signing key-pair for release verification.

---

### 1. Set Up Cosign Key-Pair

Before releasing, you need to generate a cosign key-pair (in local env) to sign the release.

**Steps**:
1. Install cosign (if not installed):
```bash
cosign install
```
2. Generate a new cosign key-pair:
```bash
cosign generate-key-pair
```
This will generate two files:
- `cosign.key` (the private key)
- `cosign.pub` (the public key)

3. Set the **private key** and **password** as GitHub repository secrets:
- **COSIGN_KEY**: Content of `cosign.key`
- **COSIGN_PASSWORD**: Password used to generate the key-pair

Navigate to **Settings > Secrets and Variables > Repository secrets** and add both secrets.

---

### 2. Configure GitHub Environments

Next, create a new GitHub environment called **`production`** with the necessary secrets and variables for the release.

#### Secrets for the Production Environment

1. **REGISTRY_USERNAME**: The username for authenticating with the container registry.
2. **REGISTRY_PASSWORD**: The password for authenticating with the container registry.

**Steps**:
- Go to **Settings > Environments**.
- Click **Add environment** and name it `production`.
- Add the secrets **REGISTRY_USERNAME** and **REGISTRY_PASSWORD** under the `production` environment.

#### Environment Variables for the Production Environment

1. **REGISTRY_ADDRESS**: The address of the registry (e.g., `registry.bupd.xyz`).
2. **PUBLISH_ADDRESS**: The address to which the CLI artifacts will be published (e.g., `registry.bupd.xyz/harbor/cli`).

**Steps**:
- After adding secrets, add the following environment variables under `production`:
- **REGISTRY_ADDRESS**
- **PUBLISH_ADDRESS**

---

### 3. Create a GitHub Release

Once the secrets and environment are set, follow these steps to create a release:

1. Go to the **GitHub repository** and click on **Releases**.
2. Click **Draft a new release**.
3. In the **Tag version** field, specify the version number (e.g., `v0.2.0`).
4. **Do not add a description**—the changelog will be generated automatically via GitHub Actions.
5. Click **Publish Release**.

Once the release is created, the GitHub Actions workflow will:
- Generate the release changelog.
- Sign the release using `cosign` (with the `COSIGN_KEY` and `COSIGN_PASSWORD`).
- Push the CLI binaries to the container registry.

---
### 4. Verifying the Release

Once the release is completed, you can verify it by:

- Checking the GitHub Actions log for successful execution.
- Pulling the image or artifact from the registry using:
```bash
# example
docker pull registry.bupd.xyz/harbor/cli:v0.2.0
```

---

### 5. Troubleshooting

- **Missing GITHUB_TOKEN, GITLAB_TOKEN, or GITEA_TOKEN**:
Ensure the required environment variables are set in GitHub secrets and accessible to the workflow.

- **Error Signing Release**:
Double-check that the `COSIGN_KEY` and `COSIGN_PASSWORD` secrets are correctly set in GitHub.

---
91 changes: 72 additions & 19 deletions dagger/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ const (
GOLANGCILINT_VERSION = "v1.61.0"
GO_VERSION = "1.22.5"
SYFT_VERSION = "v1.9.0"
GORELEASER_VERSION = "v2.1.0"
APP_NAME = "dagger-harbor-cli"
PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3"
GORELEASER_VERSION = "v2.3.2"
)

type HarborCli struct{}
Expand All @@ -22,12 +20,13 @@ func (m *HarborCli) Build(
ctx context.Context,
// +optional
// +defaultPath="./"
source *dagger.Directory) *dagger.Directory {
source *dagger.Directory,
) []*dagger.Container {
var builds []*dagger.Container

fmt.Println("🛠️ Building with Dagger...")
oses := []string{"linux", "darwin", "windows"}
arches := []string{"amd64", "arm64"}
outputs := dag.Directory()
for _, goos := range oses {
for _, goarch := range arches {
bin_path := fmt.Sprintf("build/%s/%s/", goos, goarch)
Expand All @@ -41,12 +40,13 @@ func (m *HarborCli) Build(
WithEnvVariable("GOCACHE", "/go/build-cache").
WithEnvVariable("GOOS", goos).
WithEnvVariable("GOARCH", goarch).
WithExec([]string{"go", "build", "-o", bin_path + "harbor", "/src/cmd/harbor/main.go"})
// Get reference to build output directory in container
outputs = outputs.WithDirectory(bin_path, builder.Directory(bin_path))
WithExec([]string{"go", "build", "-o", bin_path + "harbor", "/src/cmd/harbor/main.go"}).
WithWorkdir(bin_path).WithExec([]string{"ls"}).WithEntrypoint([]string{"./harbor"})

builds = append(builds, builder)
}
}
return outputs
return builds
}

func (m *HarborCli) Lint(
Expand All @@ -65,14 +65,14 @@ func (m *HarborCli) Lint(
WithMountedDirectory("/src", source).
WithWorkdir("/src").
WithExec([]string{"golangci-lint", "run", "--timeout", "5m"})

}

func (m *HarborCli) PullRequest(ctx context.Context,
// +optional
// +defaultPath="./"
source *dagger.Directory,
githubToken string) {
githubToken string,
) {
goreleaser := goreleaserContainer(source, githubToken).WithExec([]string{"release", "--snapshot", "--clean"})
_, err := goreleaser.Stderr(ctx)
if err != nil {
Expand All @@ -87,8 +87,9 @@ func (m *HarborCli) Release(
// +optional
// +defaultPath="./"
source *dagger.Directory,
githubToken string) {
goreleaser := goreleaserContainer(source, githubToken).WithExec([]string{"release", "--clean"})
githubToken string,
) {
goreleaser := goreleaserContainer(source, githubToken).WithExec([]string{"ls", "-la"}).WithExec([]string{"goreleaser", "release", "--clean"})
_, err := goreleaser.Stderr(ctx)
if err != nil {
log.Printf("Error occured during release: %s", err)
Expand All @@ -97,36 +98,88 @@ func (m *HarborCli) Release(
log.Println("Release tasks completed successfully 🎉")
}

// PublishImage publishes a Docker image to a registry with a specific tag and signs it using Cosign.
// cosignKey: the secret used for signing the image
// cosignPassword: the password for the cosign secret
// regUsername: the username for the registry
// regPassword: the password for the registry
// publishAddress: the address of the registry to publish the image
// tag: the version tag for the image
func (m *HarborCli) PublishImage(
ctx context.Context,
// +optional
// +defaultPath="./"
source *dagger.Directory,
cosignKey *dagger.Secret,
cosignKey string,
cosignPassword string,
regUsername string,
regPassword string,
regAddress string,
publishAddress string,
tag string,
) string {
var container *dagger.Container
var filteredBuilders []*dagger.Container

builders := m.Build(ctx, source)
if len(builders) > 0 {
fmt.Println(len(builders))
container = builders[0]
builders = builders[3:6]
}
dir := dag.Directory()
dir = dir.WithDirectory(".", container.Directory("."))

builder := m.Build(ctx, source)
// Create a minimal cli_runtime container
cli_runtime := dag.Container().
From("alpine:latest").
WithWorkdir("/root/").
WithFile("/root/harbor", builder.File("/")).
WithFile("/root/harbor", dir.File("./harbor")).
WithExec([]string{"ls"}).
WithExec([]string{"./harbor", "--help"}).
WithEntrypoint([]string{"./harbor"})

addr, _ := cli_runtime.Publish(ctx, PUBLISH_ADDRESS)
for _, builder := range builders {
if !(buildPlatform(ctx, builder) == "linux/amd64") {
filteredBuilders = append(filteredBuilders, builder)
}
}

cosign_key := dag.SetSecret("cosign_key", cosignKey)
cosign_password := dag.SetSecret("cosign_password", cosignPassword)
regpassword := dag.SetSecret("reg_password", regPassword)
_, err := dag.Cosign().Sign(ctx, cosignKey, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword})

publisher := cli_runtime.WithRegistryAuth(regAddress, regUsername, regpassword)
// Push the versioned tag
versionedAddress := fmt.Sprintf("%s:%s", publishAddress, tag)
addr, err := publisher.Publish(ctx, versionedAddress, dagger.ContainerPublishOpts{PlatformVariants: filteredBuilders})
if err != nil {
panic(err)
}
fmt.Printf("Published to %s 🎉\n", addr)
// Push the latest tag
latestAddress := fmt.Sprintf("%s:latest", publishAddress)
addr, err = publisher.Publish(ctx, latestAddress)
if err != nil {
panic(err)
}

_, err = dag.Cosign().Sign(ctx, cosign_key, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword})
if err != nil {
panic(err)
}
fmt.Printf("Successfully published image to %s 🎉\n", addr)

return addr
}

func buildPlatform(ctx context.Context, container *dagger.Container) string {
platform, err := container.Platform(ctx)
if err != nil {
log.Fatalf("error getting platform", err)
}
return string(platform)
}

func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *dagger.Container {
token := dag.SetSecret("github_token", githubToken)

Expand Down
Loading

0 comments on commit 04c0305

Please sign in to comment.