Skip to content

Commit 1b7a4cd

Browse files
ci: add cross-platform E2E tests, SBOMs, Cosign, and GHCR Docker release
Signed-off-by: night-slayer18 <samanuaia257@gmail.com>
1 parent 801b948 commit 1b7a4cd

File tree

4 files changed

+163
-8
lines changed

4 files changed

+163
-8
lines changed

.github/workflows/release.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ jobs:
1414
release:
1515
name: GoReleaser
1616
runs-on: ubuntu-latest
17+
permissions:
18+
contents: write
19+
packages: write
20+
id-token: write
1721
steps:
1822
- name: Checkout
1923
uses: actions/checkout@v4
@@ -26,6 +30,25 @@ jobs:
2630
go-version-file: go.mod
2731
cache-dependency-path: go.sum
2832

33+
- name: Install syft
34+
uses: anchore/sbom-action/download-syft@v0.20.10
35+
36+
- name: Set up QEMU
37+
uses: docker/setup-qemu-action@v3
38+
39+
- name: Set up Docker Buildx
40+
uses: docker/setup-buildx-action@v3
41+
42+
- name: Install cosign
43+
uses: sigstore/cosign-installer@v3.4.0
44+
45+
- name: Login to ghcr.io registry
46+
uses: docker/login-action@v3
47+
with:
48+
registry: ghcr.io
49+
username: ${{ github.actor }}
50+
password: ${{ secrets.GITHUB_TOKEN }}
51+
2952
- name: Run GoReleaser
3053
uses: goreleaser/goreleaser-action@v6
3154
with:
@@ -34,3 +57,4 @@ jobs:
3457
args: release --clean
3558
env:
3659
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60+
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

.goreleaser.yml

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,41 @@ nfpms:
5959
- archlinux
6060
bindir: /usr/bin
6161

62-
brews:
62+
homebrew_casks:
6363
- repository:
6464
owner: OpenSyntaxHQ
6565
name: homebrew-tweak
66+
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
6667
name: tweak
6768
homepage: "https://github.com/OpenSyntaxHQ/tweak"
68-
description: "A cross-platform CLI for 118+ text transformations."
69-
license: "Apache-2.0"
70-
skip_upload: auto
71-
install: |-
72-
bin.install "tweak"
73-
test: |
74-
system "#{bin}/tweak --version"
69+
description: "Fast CLI + TUI for text processing: encoding, hashing, transforms, and more."
70+
71+
dockers_v2:
72+
- images:
73+
- "ghcr.io/opensyntaxhq/tweak"
74+
tags:
75+
- "{{ .Version }}"
76+
- "latest"
77+
platforms:
78+
- linux/amd64
79+
- linux/arm64
80+
labels:
81+
org.opencontainers.image.created: "{{.Date}}"
82+
org.opencontainers.image.title: "{{.ProjectName}}"
83+
org.opencontainers.image.revision: "{{.FullCommit}}"
84+
org.opencontainers.image.version: "{{.Version}}"
85+
86+
sboms:
87+
- artifacts: archive
88+
89+
signs:
90+
- cmd: cosign
91+
env:
92+
- COSIGN_EXPERIMENTAL=1
93+
args:
94+
- "sign-blob"
95+
- "--yes"
96+
- "--output-signature=${signature}"
97+
- "--output-certificate=${certificate}"
98+
- "${artifact}"
99+
artifacts: checksum

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM scratch
2+
ARG TARGETOS
3+
ARG TARGETARCH
4+
COPY ${TARGETOS}/${TARGETARCH}/tweak /
5+
ENTRYPOINT ["/tweak"]

tests/e2e_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package tests
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"runtime"
9+
"strings"
10+
"testing"
11+
)
12+
13+
func TestE2E(t *testing.T) {
14+
binaryName := "tweak_e2e"
15+
if runtime.GOOS == "windows" {
16+
binaryName += ".exe"
17+
}
18+
19+
buildCmd := exec.Command("go", "build", "-o", binaryName, "../main.go")
20+
if err := buildCmd.Run(); err != nil {
21+
t.Fatalf("Failed to build binary for E2E tests: %v", err)
22+
}
23+
defer os.Remove(binaryName)
24+
25+
binaryPath, err := filepath.Abs(binaryName)
26+
if err != nil {
27+
t.Fatalf("Failed to get absolute path for binary: %v", err)
28+
}
29+
30+
tests := []struct {
31+
name string
32+
args []string
33+
stdin string
34+
expectedOutput string
35+
expectError bool
36+
}{
37+
{
38+
name: "Base64 Encoding from Args",
39+
args: []string{"base64-encode", "hello world"},
40+
expectedOutput: "aGVsbG8gd29ybGQ=",
41+
},
42+
{
43+
name: "Base64 Decoding from Stdin (Piping)",
44+
args: []string{"base64-decode"},
45+
stdin: "aGVsbG8gd29ybGQ=",
46+
expectedOutput: "hello world",
47+
},
48+
{
49+
name: "MD5 Hash (Arg with spaces)",
50+
args: []string{"md5", "tweak test"},
51+
expectedOutput: "4b284490e3437e4d4a48c09edb0040a0",
52+
},
53+
{
54+
name: "Uppercase transform",
55+
args: []string{"upper", "make me loud"},
56+
expectedOutput: "MAKE ME LOUD",
57+
},
58+
{
59+
name: "Invalid Command Error",
60+
args: []string{"fake-command-123"},
61+
expectError: true,
62+
},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
cmd := exec.Command(binaryPath, tt.args...)
68+
69+
// Set up stdin if provided (simulates bash/CMD piping: echo | tweak)
70+
if tt.stdin != "" {
71+
cmd.Stdin = strings.NewReader(tt.stdin)
72+
}
73+
74+
var stdout bytes.Buffer
75+
var stderr bytes.Buffer
76+
cmd.Stdout = &stdout
77+
cmd.Stderr = &stderr
78+
79+
err := cmd.Run()
80+
81+
if tt.expectError {
82+
if err == nil {
83+
t.Fatalf("Expected error but command succeeded. output: %s", stdout.String())
84+
}
85+
return
86+
}
87+
88+
if err != nil {
89+
t.Fatalf("Command failed: %v\nStderr: %s", err, stderr.String())
90+
}
91+
92+
// Clean up output (CRLF to LF for cross-platform comparison)
93+
got := strings.TrimSpace(stdout.String())
94+
got = strings.ReplaceAll(got, "\r\n", "\n")
95+
96+
if got != tt.expectedOutput {
97+
t.Errorf("\nGot: %q\nExpected: %q", got, tt.expectedOutput)
98+
}
99+
})
100+
}
101+
}

0 commit comments

Comments
 (0)