diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml new file mode 100644 index 0000000..166691d --- /dev/null +++ b/.github/workflows/go.yaml @@ -0,0 +1,210 @@ +name: Go + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + +jobs: + build: + name: Build binaries + runs-on: ubuntu-latest + strategy: + matrix: + cmd: ["bx2cloud", "bx2cloud-api", "terraform-provider-bx2cloud"] + goos: ["linux", "windows", "darwin"] + goarch: ["amd64", "arm64"] + exclude: + - cmd: bx2cloud-api + goos: windows + - cmd: bx2cloud-api + goos: darwin + include: + - goos: windows + ext: .exe + - cmd: bx2cloud-api + goos: linux + goarch: amd64 + env: + CGO_ENABLED: 1 + - cmd: bx2cloud-api + goos: linux + goarch: arm64 + env: + CGO_ENABLED: 1 + CC: aarch64-linux-gnu-gcc + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Get the version + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + + - name: Install cross-compiler for ARM64 + if: matrix.env.CC == 'aarch64-linux-gnu-gcc' + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + + - name: Build + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: ${{ matrix.env.CGO_ENABLED || 0 }} + CC: ${{ matrix.env.CC }} + run: | + go build -v -trimpath -ldflags="-s -w" -o "dist/${{ matrix.cmd }}_${{ env.VERSION }}_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.ext }}" ./cmd/${{ matrix.cmd }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bin-${{ matrix.cmd }}-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/${{ matrix.cmd }}_${{ env.VERSION }}_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.ext }} + if-no-files-found: error + + container-images: + name: Build container images + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + cmd: ["bx2cloud", "bx2cloud-api"] + goos: ["linux"] + goarch: ["amd64", "arm64"] + include: + - cmd: bx2cloud + dockerfile: Dockerfile.cli + - cmd: bx2cloud-api + dockerfile: Dockerfile.api + - cmd: bx2cloud + image_name: bx2cloud-cli + - cmd: bx2cloud-api + # API requires CGO when building, so binary built on ubuntu (glibc) fails to run on alpine (musl) + base: ubuntu:24.04 + env: + DOCKER_BUILD_SUMMARY: false + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download binary + uses: actions/download-artifact@v4 + with: + name: bin-${{ matrix.cmd }}-${{ matrix.goos }}-${{ matrix.goarch }} + path: bin + + - name: Get the version + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV + + - name: Rename binary + run: mv bin/${{ matrix.cmd }}_${{ env.VERSION }}_${{ matrix.goos }}_${{ matrix.goarch }}${{ matrix.ext }} bin/${{ matrix.cmd }}${{ matrix.ext }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.goos }}/${{ matrix.goarch }} + tags: ${{ vars.DOCKERHUB_USERNAME }}/${{ matrix.image_name || matrix.cmd }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + context: bin/ + file: ${{ matrix.dockerfile }} + + - name: Create file for digest + run: | + mkdir -p digests + digest="${{ steps.build.outputs.digest }}" + touch "digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.image_name || matrix.cmd }}-${{ matrix.goos }}-${{ matrix.goarch }} + path: digests/* + if-no-files-found: error + retention-days: 1 + + container-images-index: + name: Create manifest index + runs-on: ubuntu-latest + needs: container-images + strategy: + matrix: + image_name: ["bx2cloud-cli", "bx2cloud-api"] + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: digests + pattern: digests-${{ matrix.image_name }}-* + merge-multiple: true + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Retrieve container image tags + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ vars.DOCKERHUB_USERNAME }}/${{ matrix.image_name }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Create manifest index and push + working-directory: digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ vars.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ vars.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ steps.meta.outputs.version }} + + release: + name: Release to GitHub Releases + runs-on: ubuntu-latest + permissions: + contents: write + needs: container-images-index + steps: + - name: Download all binaries + uses: actions/download-artifact@v4 + with: + path: release-assets + pattern: bin-* + merge-multiple: true + + - name: List downloaded binaries + run: ls -R release-assets + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + if: github.ref_type == 'tag' + with: + files: release-assets/* + generate_release_notes: true + fail_on_unmatched_files: true \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 247de31..0000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+*' - -permissions: - contents: write - -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - name: Install ARM64 cross-compiler - run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 - with: - version: "~> v2" - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index 205c8f8..0000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json - -version: 2 - -project_name: bx2cloud -release: - github: - owner: BenasB - name: bx2cloud - -builds: - - id: api - main: ./cmd/bx2cloud-api - binary: bx2cloud-api - goos: [linux] - goarch: [amd64, arm64] - env: - - CGO_ENABLED=1 - overrides: - - goos: linux - goarch: arm64 - env: - - CGO_ENABLED=1 - - CC=aarch64-linux-gnu-gcc - - - id: cli - main: ./cmd/bx2cloud - binary: bx2cloud - goos: [linux, darwin, windows] - goarch: [amd64, arm64] - - - id: terraform-provider - main: ./cmd/terraform-provider-bx2cloud - binary: terraform-provider-bx2cloud - goos: [linux, darwin, windows] - goarch: [amd64, arm64] - -archives: - - formats: [binary] - name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" - -changelog: - use: git - diff --git a/Dockerfile.api b/Dockerfile.api index 1ca0cf2..314b829 100644 --- a/Dockerfile.api +++ b/Dockerfile.api @@ -1,26 +1,9 @@ -# syntax=docker/dockerfile:1 +FROM ubuntu:24.04 -FROM golang:1.24 AS build-stage +RUN apt-get update && apt-get install -y \ + iptables \ + ca-certificates -WORKDIR /app +COPY --chmod=755 bx2cloud-api /app/bx2cloud-api -COPY go.mod go.sum ./ -RUN go mod download - -COPY cmd/api/ cmd/api -COPY internal/api/ internal/api/ - -RUN CGO_ENABLED=0 GOOS=linux go build -o /bx2cloud-api cmd/api/main.go - -FROM build-stage AS run-test-stage -RUN go test -v ./... - -FROM alpine:3 AS build-release-stage - -WORKDIR / - -COPY --from=build-stage /bx2cloud-api /bx2cloud-api - -EXPOSE 8080 - -ENTRYPOINT ["/bx2cloud-api"] \ No newline at end of file +ENTRYPOINT [ "/app/bx2cloud-api" ] diff --git a/Dockerfile.cli b/Dockerfile.cli new file mode 100644 index 0000000..deea3ba --- /dev/null +++ b/Dockerfile.cli @@ -0,0 +1,5 @@ +FROM alpine:3 + +COPY --chmod=755 bx2cloud /app/bx2cloud + +ENTRYPOINT [ "/app/bx2cloud" ] diff --git a/cmd/bx2cloud-api/main_linux.go b/cmd/bx2cloud-api/main_linux.go index 3db6678..ab824bb 100644 --- a/cmd/bx2cloud-api/main_linux.go +++ b/cmd/bx2cloud-api/main_linux.go @@ -14,7 +14,7 @@ import ( ) func main() { - address := "localhost:8080" + address := ":8080" // TODO: Make this configurable lis, err := net.Listen("tcp", address) if err != nil { log.Fatalf("Failed to listen: %v", err) diff --git a/docs/docs/api/installation.md b/docs/docs/api/installation.md index 789c886..15db3a5 100644 --- a/docs/docs/api/installation.md +++ b/docs/docs/api/installation.md @@ -20,9 +20,19 @@ Installing bx2cloud API can be done in multiple ways: It's possible to download a pre-built binary of the API from [GitHub releases](https://github.com/BenasB/bx2cloud/releases). API builds start with `bx2cloud-api_`. +:::warning + +Pre-built API binaries on GitHub releases are linked with glibc, so running them on a system that does not have it (e.g. Alpine uses musl instead of glibc) will not work. For more details refer to "3. Building from source". + +::: + ### 2. Container image -A container image is available on Docker Hub. WIP 🚧 +A container image is available on [Docker Hub](https://hub.docker.com/r/benasbudrys/bx2cloud-api). + +```sh +docker run -p 8080:8080 --privileged benasbudrys/bx2cloud-api +``` ### 3. Building from source diff --git a/docs/docs/cli/installation.md b/docs/docs/cli/installation.md index b334492..f401beb 100644 --- a/docs/docs/cli/installation.md +++ b/docs/docs/cli/installation.md @@ -7,13 +7,24 @@ sidebar_position: 2 Installing bx2cloud CLI can be done in multiple ways: 1. Binary download -2. Building from source +2. Container image +3. Building from source ### 1. Binary download It's possible to download a pre-built binary of the CLI from [GitHub releases](https://github.com/BenasB/bx2cloud/releases). CLI builds start with `bx2cloud_`. Name it `bx2cloud` and place it somewhere on your `PATH`. -### 2. Building from source +### 2. Container image + +A container image is available on [Docker Hub](https://hub.docker.com/r/benasbudrys/bx2cloud-cli). + +As an example, running `bx2cloud container list` can be achieved with: + +```sh +docker run --rm benasbudrys/bx2cloud-cli -t : container list +``` + +### 3. Building from source You can install the CLI using `go`: diff --git a/internal/cli/cli.go b/internal/cli/cli.go index c5a9b44..1ca07e8 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -26,7 +26,21 @@ func Run(args []string) exits.ExitCode { command := args[0] args = args[1:] - conn, err := newConn() + target := "localhost:8080" + if command == "-t" { + target = args[0] + args = args[1:] + + if len(args) < 1 { + fmt.Fprintf(os.Stderr, "Missing command\n") + return exits.MISSING_COMMAND + } + + command = args[0] + args = args[1:] + } + + conn, err := newConn(target) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return exits.SERVER_ERROR @@ -307,9 +321,9 @@ func Run(args []string) exits.ExitCode { return exits.SUCCESS } -func newConn() (*grpc.ClientConn, error) { +func newConn(target string) (*grpc.ClientConn, error) { opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} - conn, err := grpc.NewClient("localhost:8080", opts...) + conn, err := grpc.NewClient(target, opts...) if err != nil { return nil, err }