diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000..4b5881df --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,67 @@ +version: 2 + +project_name: smartling-cli +dist: bin + +env: + - CGO_ENABLED=0 + +before: + hooks: + - sh -c 'test -n "$GORELEASER_CURRENT_TAG" || { echo "GORELEASER_CURRENT_TAG must be set; use make build"; exit 1; }' + +release: + disable: true + +builds: + - + binary: smartling-cli + + goos: + - linux + - darwin + - windows + + goarch: + - amd64 + - arm64 + + flags: + - -trimpath + + ldflags: + - -s -w + - -X github.com/Smartling/smartling-cli/cmd/helpers/build.CliVersion={{ .Tag }} + - -X github.com/Smartling/smartling-cli/cmd/helpers/build.CommitID={{ .FullCommit }} + - -X github.com/Smartling/smartling-cli/cmd/helpers/build.Date={{ .Date }} + - -X github.com/Smartling/smartling-cli/cmd/helpers/build.BuiltBy=GoReleaser + +snapshot: + version_template: "{{ .Tag }}" + +archives: + - formats: [binary] + name_template: "{{ .ProjectName }}_latest_{{ .Os }}_{{ .Arch }}" + +nfpms: + - id: smartling + package_name: smartling + file_name_template: "{{ .PackageName }}_latest_{{ .Os }}_{{ .Arch }}" + vendor: Smartling + homepage: https://github.com/Smartling/smartling-cli + maintainer: Alex Koval + description: CLI for Smartling Platform + license: MIT + formats: + - deb + - rpm + bindir: /usr/bin + section: unknown + priority: extra + contents: + - src: /usr/bin/smartling-cli + dst: /usr/bin/smartling + type: symlink + +checksum: + name_template: "checksums.txt" diff --git a/CLAUDE.md b/CLAUDE.md index bbf52475..a57f2678 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,17 +29,19 @@ The CLI uses dependency injection through service initializers, making it testab The project uses a Makefile-based build system that cross-compiles for multiple platforms: ```bash -make all # Clean, get dependencies, and build for all platforms -make build # Build for darwin, windows, linux (outputs to bin/ directory) -go build # Build for current platform only +make all # Clean, fetch deps, and run a full release build +make build # Cross-compile via GoReleaser (requires `goreleaser` installed) +go build # Build for current platform only (fast dev iteration) ``` -**Build outputs:** -- `bin/smartling.darwin` - macOS binary -- `bin/smartling.windows.exe` - Windows binary -- `bin/smartling.linux` - Linux binary +`make build` invokes GoReleaser configured via `.goreleaser.yml`. It cross-compiles for linux/darwin/windows × amd64/arm64 and, for linux, also produces `.deb` and `.rpm` packages via nfpms. -**Build time:** Typically takes 10-30 seconds depending on whether dependencies need to be downloaded. +**Build outputs (under `bin/`):** +- Per-target binaries: `bin/smartling-cli___/smartling-cli` (e.g. `bin/smartling-cli_linux_amd64_v1/smartling-cli`, `bin/smartling-cli_windows_amd64_v1/smartling-cli.exe`) +- Linux packages: `bin/smartling__linux_amd64.deb`, `.rpm`, plus arm64 variants — install the binary at `/usr/bin/smartling-cli` with a `/usr/bin/smartling` symlink +- `bin/checksums.txt` — SHA256 sums for verification + +**Build time:** Cross-compile typically 5-15 seconds; `go build` for one platform is under 5 seconds. ### Testing @@ -78,10 +80,8 @@ make docs # Generate command documentation ``` ### Package Building -```bash -make deb VERSION=1.0.0 # Build Debian package -make rpm VERSION=1.0.0 # Build RPM package -``` + +`.deb` and `.rpm` packages are produced automatically by `make build` via GoReleaser's nfpms integration — no separate make targets. Package metadata (name, maintainer, license, install paths) lives in the `nfpms` block of `.goreleaser.yml`. ## Configuration diff --git a/Jenkinsfile b/Jenkinsfile index 457711f7..caa3fe68 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,8 +10,14 @@ pipeline { stages { stage('Build') { steps { - sh "docker pull golang" - sh "docker run -t --rm -v ${WORKSPACE}:/go/src/cli -w /go/src/cli golang make" + sh "docker pull goreleaser/goreleaser:v2.15.4" + sh """ + docker run -t --rm \\ + -v ${WORKSPACE}:/go/src/cli -w /go/src/cli \\ + --entrypoint sh \\ + goreleaser/goreleaser:v2.15.4 \\ + -c 'apk add --no-cache make && make build' + """ } } @@ -32,18 +38,9 @@ pipeline { branch env.TARGET_BRANCH } steps { - sh "aws-profile connectors-staging aws s3 cp ${WORKSPACE}/bin s3://smartling-connectors-releases/cli/ --recursive --acl public-read" - } - } - - stage('Generate Packages') { - when { - branch env.TARGET_BRANCH - } - steps { - sh "docker run -t --rm -v ${WORKSPACE}:/go/src/cli -w /go/src/cli gvangool/rpmbuilder:centos7 bash -c 'make rpm'" - // TODO : Replace with special docker image - sh "docker run -t --rm -v ${WORKSPACE}:/go/src/cli -w /go/src/cli debian bash -c 'apt-get update && apt-get install -y make git && make deb'" + sh ''' + aws-profile connectors-staging aws s3 cp ${WORKSPACE}/bin s3://smartling-connectors-releases/cli/ --acl public-read --exclude "*" --include "smartling-cli*" --include "smartling_*.deb" --include "smartling_*.rpm" --include "checksums.txt" --recursive + ''' } } } diff --git a/Makefile b/Makefile index 4a806998..3cbbe90d 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ -MAINTAINER = Alex Koval -DESCRIPTION = CLI for Smartling Platform - -LDFLAGS ?= -s -w -GO_BUILD_FLAGS ?= -mod=mod -trimpath -ldflags="$(LDFLAGS)" +VERSION := $(shell grep -E '\bCliVersion\s*=\s*"' cmd/helpers/build/build.go | head -1 | sed -E 's/.*"([^"]+)".*/\1/') +ifeq ($(VERSION),) +$(error Could not extract CliVersion from cmd/helpers/build/build.go) +endif .PHONY: all all: clean get build @ .PHONY: build -build: darwin windows.exe linux - @ +build: + @echo "Building version $(VERSION)" + GORELEASER_CURRENT_TAG=$(VERSION) goreleaser release --clean --skip=publish --snapshot .PHONY: get get: @@ -18,59 +18,9 @@ get: .PHONY: clean clean: - rm -rf bin pkg + rm -rf bin mkdir bin -_PKG = pkg/build - -_CONTROL = echo >> $(_PKG)/DEBIAN/control - -.PHONY: deb -deb: _pkg-init - mkdir -p $(_PKG)/usr/bin - cp bin/smartling.linux $(_PKG)/usr/bin/smartling - mkdir -p $(_PKG)/DEBIAN - $(_CONTROL) "Package: smartling" - $(_CONTROL) "Version: $(VERSION)" - $(_CONTROL) "Architecture: all" - $(_CONTROL) "Section: unknown" - $(_CONTROL) "Priority: extra" - $(_CONTROL) "Maintainer: $(MAINTAINER)" - $(_CONTROL) "Homepage: https://github.com/Smartling/smartling-cli" - $(_CONTROL) "Description: $(DESCRIPTION)" - dpkg -b $(_PKG) pkg/smartling-$(VERSION)_all.deb - rm -rf $(_PKG) - -_SPEC = echo >> $(_PKG)/smartling.spec - -.PHONY: rpm -rpm: _pkg-init - $(_SPEC) "Name: smartling" - $(_SPEC) "Version: $(VERSION)" - $(_SPEC) "Release: 1%{?dist}" - $(_SPEC) "Summary: $(DESCRIPTION)" - $(_SPEC) "License: MIT" - $(_SPEC) "%description" - $(_SPEC) "%install" - $(_SPEC) "mkdir -p %{buildroot}/%{_bindir}" - $(_SPEC) "cp $(PWD)/bin/smartling.linux %{buildroot}/%{_bindir}/smartling" - $(_SPEC) "%files" - $(_SPEC) "%{_bindir}/smartling" - $(_SPEC) "%define _rpmdir $(_PKG)" - rpmbuild -bb $(_PKG)/smartling.spec - cp $(_PKG)/*/*.rpm pkg/ - rm -rf $(_PKG) - -.PHONY: _pkg-init -_pkg-init: - rm -rf $(_PKG) - mkdir -p $(_PKG) - $(eval VERSION ?= \ - $(shell git rev-list --count HEAD).$(shell git rev-parse --short HEAD)) - -%: - CGO_ENABLED=0 GOOS=$(basename $@) go build $(GO_BUILD_FLAGS) -o bin/smartling.$@ - .PHONY: docs docs: go run ./main.go docs diff --git a/README.md b/README.md index a1f4023e..7a5bcf82 100644 --- a/README.md +++ b/README.md @@ -5,37 +5,24 @@ See the [Wiki](https://github.com/Smartling/smartling-cli/wiki) page for this re # Development For developers interested in modifying the tool. -## Building package +## Building + +Local development build: ``` -make +go build ./... ``` -Where target is: - -* `deb` for building Debian packages: - ``` - make deb - ``` - -* `rpm` for building Fedora packages: - ``` - make rpm - ``` - -Specific settings can be set in built-time: - -*VERSION*: +Full release build (cross-compiles all platforms and produces deb/rpm packages via [GoReleaser](https://goreleaser.com/) nfpms — requires `goreleaser` installed locally): ``` -make VERSION= +make build ``` -*MAINTAINER*: +Outputs land in `bin/`: -``` -make MAINTAINER= -``` +- Per-platform binaries under `bin/smartling-cli___/smartling-cli` +- Linux packages: `bin/smartling__linux_amd64.deb`, `.rpm`, plus arm64 variants +- `bin/checksums.txt` -An executable named `smartling-cli` should become available in your -`$GOPATH/bin`. +Package metadata (maintainer, description, license) and cross-compile targets are configured in `.goreleaser.yml`. diff --git a/cmd/build/build.go b/cmd/build/build.go new file mode 100644 index 00000000..60eadf99 --- /dev/null +++ b/cmd/build/build.go @@ -0,0 +1,21 @@ +package build + +import ( + "fmt" + + "github.com/Smartling/smartling-cli/cmd/helpers/build" + + "github.com/spf13/cobra" +) + +// NewBuildCmd creates a new build command. +func NewBuildCmd() *cobra.Command { + buildCmd := &cobra.Command{ + Use: "build", + Short: "Print the build information", + Run: func(_ *cobra.Command, _ []string) { + fmt.Println(build.String()) + }, + } + return buildCmd +} diff --git a/cmd/cmd_root.go b/cmd/cmd_root.go index 78ccec87..b8d22d06 100644 --- a/cmd/cmd_root.go +++ b/cmd/cmd_root.go @@ -3,6 +3,8 @@ package cmd import ( "strings" + "github.com/Smartling/smartling-cli/cmd/helpers/build" + "github.com/spf13/cobra" ) @@ -30,7 +32,7 @@ func NewRootCmd() *cobra.Command { rootCmd := &cobra.Command{ Use: "smartling-cli", Short: "Manage translation files using Smartling CLI.", - Version: "3.2", + Version: build.CliVersion, Long: `Manage translation files using Smartling CLI. Complete documentation is available at https://www.smartling.com`, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { diff --git a/cmd/helpers/build/build.go b/cmd/helpers/build/build.go new file mode 100644 index 00000000..e5780432 --- /dev/null +++ b/cmd/helpers/build/build.go @@ -0,0 +1,41 @@ +package build + +import ( + "fmt" + "runtime" +) + +// These variables will be set via ldflags at build time. +var ( + // CliVersion is version information + CliVersion = "3.3" + // CommitID is the git commit hash + CommitID = "" + // Date is date of binary built + Date = "" + // BuiltBy identifies the build pipeline (e.g. "GoReleaser", "make"). + // Defaults to "unknown" so binaries built without ldflag injection + // don't mislabel themselves. + BuiltBy = "unknown" +) + +// String returns the version information as a formatted string. +func String() string { + return fmt.Sprintf(` +Smartling-cli is a library and CLI tool for managing Smartling projects. + +Version: %s +GitCommit: %s +BuildDate: %s +BuiltBy: %s +GoVersion: %s +Platform: %s +`, + CliVersion, + CommitID, + Date, + BuiltBy, + runtime.Version(), + runtime.GOOS+"/"+runtime.GOARCH, + ) +} diff --git a/main.go b/main.go index c5e48714..7c45e42e 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "github.com/Smartling/smartling-cli/cmd" + "github.com/Smartling/smartling-cli/cmd/build" "github.com/Smartling/smartling-cli/cmd/docs" "github.com/Smartling/smartling-cli/cmd/files" deletecmd "github.com/Smartling/smartling-cli/cmd/files/delete" @@ -32,6 +33,9 @@ func main() { docsCmd := docs.NewDocsCmd() rootCmd.AddCommand(docsCmd) + buildCmd := build.NewBuildCmd() + rootCmd.AddCommand(buildCmd) + initSrvInitializer := initialize.NewSrvInitializer() initCmd := initialize.NewInitCmd(initSrvInitializer) rootCmd.AddCommand(initCmd)