diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..49513723 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,64 @@ +# Build and test workflow for Launchpad +# Triggered on PRs and pushes to main. + +name: Build and Test +permissions: + contents: read + packages: write # Required for uploading artifacts + +on: + push: + branches: [ main ] + +jobs: + build: + name: Build Binaries + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Build binaries + run: | + mkdir -p dist + platforms=("linux/amd64" "linux/arm64" "windows/amd64" "windows/arm64" "darwin/amd64" "darwin/arm64") + for platform in "${platforms[@]}"; do + GOOS=${platform%/*} + GOARCH=${platform#*/} + output_name="dist/launchpad_${GOOS}_${GOARCH}" + if [ "$GOOS" = "windows" ]; then + output_name+=".exe" + fi + echo "Building $output_name" + GOOS=$GOOS GOARCH=$GOARCH go build -o "$output_name" ./main.go + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: launchpad-binaries + path: dist/ + + test: + name: Run Tests + runs-on: ubuntu-latest + needs: build + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.22" + + - name: Run unit tests + run: go test -v ./... + + - name: Run integration tests + run: go test -v -tags=integration ./test/integration diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml deleted file mode 100644 index c45423da..00000000 --- a/.github/workflows/golangci-lint.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Go lint -on: - pull_request: - paths: - - '**.go' - - 'go.mod' - - 'go.sum' - - '.golangci.yml' - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - if: github.ref != 'refs/heads/main' - steps: - - name: Check out code into the Go module directory - uses: actions/checkout@v6 - - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version-file: go.mod - - - name: Check go.mod/go.sum to be consistent - run: go mod tidy -v && git diff --exit-code - - - name: golangci-lint - uses: golangci/golangci-lint-action@v9 - with: - version: latest - skip-cache: true - only-new-issues: false - args: --verbose diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..6fa57186 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,88 @@ +# PR validation workflow for Launchpad +# Triggered on PRs to main branch. + +name: PR Validation + +permissions: + contents: read + pull-requests: write # Required for PR comments or labels + +on: + pull_request: + branches: [ main ] + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "test/**" + - "examples/**" + - ".github/workflows/**" + paths-ignore: + - "**.md" + - "docs/**" + +jobs: + lint: + name: Lint Code + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Run golangci-lint + run: make lint + + unit-test: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Run unit tests + run: make unit-test + + integration-test: + name: Integration Tests + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Run integration tests + run: make integration-test + + + + security-scan: + name: Security Scan + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + + - name: Run security scan + run: govulncheck ./... \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..d8cd0a61 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +# Release workflow for Launchpad +# Triggered on tags (e.g., v1.5.16). + +name: Release +permissions: + contents: read # Top-level: Restricts all jobs by default + packages: write # Required for uploading artifacts + +on: + push: + tags: + - "v*" + +jobs: + build: + name: Build Release Binaries + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Build binaries + run: | + mkdir -p dist + platforms=("linux/amd64" "linux/arm64" "windows/amd64" "windows/arm64" "darwin/amd64" "darwin/arm64") + for platform in "${platforms[@]}"; do + GOOS=${platform%/*} + GOARCH=${platform#*/} + output_name="dist/launchpad_${GOOS}_${GOARCH}" + if [ "$GOOS" = "windows" ]; then + output_name+=".exe" + fi + echo "Building $output_name" + GOOS=$GOOS GOARCH=$GOARCH go build -o "$output_name" ./main.go + done + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: launchpad-release-binaries + path: dist/ + + release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: build + permissions: + contents: write + steps: + - name: Download binaries + uses: actions/download-artifact@v4 + with: + name: launchpad-release-binaries + path: dist/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: dist/* + generate_release_notes: true + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # TODO: Add Digicert signing here. + # TODO: Push signed artifacts to S3 here. \ No newline at end of file diff --git a/Makefile b/Makefile index 05a7687d..cea13a3f 100644 --- a/Makefile +++ b/Makefile @@ -1,73 +1,32 @@ -GO=$(shell which go) - -RELEASE_FOLDER=dist/release - -CHECKSUM=$(shell which sha256sum) - -VOLUME_MOUNTS=-v "$(CURDIR):/v" -SIGN?=docker run --rm -i $(VOLUME_MOUNTS) -e SM_API_KEY -e SM_CLIENT_CERT_PASSWORD -e SM_CLIENT_CERT_FILE -v "$(SM_CLIENT_CERT_FILE):$(SM_CLIENT_CERT_FILE)" -w "/v" registry.mirantis.com/prodeng/digicert-keytools-jsign:latest sign - -GOLANGCI_LINT?=docker run -t --rm -v "$(CURDIR):/data" -w "/data" golangci/golangci-lint:latest golangci-lint - -SEGMENT_TOKEN?="" - .PHONY: clean clean: rm -fr dist -# Sign release binaries (Windows) -# (build may need to be run in a separate make run) -.PHONY: sign-release -sign-release: $(RELEASE_FOLDER) - for f in `find $(RELEASE_FOLDER)/*.exe`; do echo $(SIGN) "$$f"; done +# TODO: Digicert signing will be reimplemented in GitHub Actions. -# Force a clean build of the artifacts by first cleaning -# and then building -.PHONY: build-release -build-release: clean $(RELEASE_FOLDER) -# build all the binaries for release, using goreleaser, but -# don't use any of the other features of goreleaser - because -# we need to use digicert to sign the binaries first, and -# goreleaser doesn't allow for that (some pro features may -# allow it in a round about way.) -# -# If you are using more than one tag for a commit, then use -# the GORELEASER_CURRENT_TAG env var to clarify the version to -# avoid having the wrong tag version applied -$(RELEASE_FOLDER): - SEGMENT_TOKEN=${SEGMENT_TOKEN} goreleaser build --clean --config=.goreleaser.release.yml - -.PHONY: create-checksum -create-checksum: - cd $(RELEASE_FOLDER) && \ - for f in *; do \ - $(CHECKSUM) $$f > $$f.sha256; \ - done - -.PHONY: verify-checksum -verify-checksum: - for f in $(RELEASE_FOLDER)/*.sha256; do \ - $(CHECKSUM) -c $$f; \ - echo "Verified checksum for $$f"; \ - done - -# clean out any existing release build -.PHONY: clean-release -clean-release: - rm -fr $(RELEASE_FOLDER) - -# Local build of the plugin. This saves time building platforms that you -# won't test locally. To use it, find the path to your build binary path -# and alias it. +# Local build of the plugin. This saves time building only the host platform. +# Uses native Go commands to avoid Goreleaser dependency. .PHONY: local local: - SEGMENT_TOKEN=${SEGMENT_TOKEN} goreleaser build --clean --single-target --skip=validate --snapshot --config .goreleaser.local.yml + mkdir -p dist + GOOS=$(shell go env GOOS) GOARCH=$(shell go env GOARCH) \ + output_name="dist/launchpad_$${GOOS}_$${GOARCH}"; \ + if [ "$${GOOS}" = "windows" ]; then \ + output_name="$${output_name}.exe"; \ + fi; \ + go build -o "$${output_name}" ./main.go && \ + ./$${output_name} --help # run linting .PHONY: lint lint: - $(GOLANGCI_LINT) run + golangci-lint run + +# security scanning +.PHONY: security-scan +security-scan: + govulncheck ./... # Testing related targets @@ -75,7 +34,7 @@ lint: TEST_FLAGS?= .PHONY: unit-test unit-test: - $(GO) test -v --tags 'testing' $(TEST_FLAGS) ./pkg/... + go test -v --tags 'testing' $(TEST_FLAGS) ./pkg/... .PHONY: functional-test functional-test: diff --git a/go.mod b/go.mod index 4e6dc47a..4c7f6f24 100644 --- a/go.mod +++ b/go.mod @@ -157,7 +157,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde // indirect github.com/tmccombs/hcl2json v0.6.4 // indirect - github.com/ulikunitz/xz v0.5.14 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect diff --git a/go.sum b/go.sum index f0e7f5d2..f4fb1754 100644 --- a/go.sum +++ b/go.sum @@ -425,8 +425,8 @@ github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde h1:AMNpJRc7P+GTw github.com/tidwall/transform v0.0.0-20201103190739-32f242e2dbde/go.mod h1:MvrEmduDUz4ST5pGZ7CABCnOU5f3ZiOAZzT6b1A6nX8= github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw= github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk= -github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= -github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=