diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dba0753..8cb04fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,3 +80,20 @@ jobs: cache: true - name: kubeconform (Tier 2) run: make validate-schemas + + release-config: + name: Release config + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + # Catch .goreleaser.yaml mistakes on PRs instead of at tag time. + - name: goreleaser check + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: latest + args: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 677dc03..3dbfeeb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,7 @@ on: permissions: contents: write # create GitHub release id-token: write # cosign keyless signing + packages: write # push image to GHCR jobs: release: @@ -29,6 +30,18 @@ jobs: # Install syft for SBOM generation. - uses: anchore/sbom-action/download-syft@v0 + # Multi-arch Docker builds need QEMU + buildx. + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + + # Authenticate to GHCR so GoReleaser can push the image. + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: @@ -37,6 +50,10 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # PAT with write access to InsightSoftmax/homebrew-tap. Falls back to + # GITHUB_TOKEN so a release without the secret still succeeds for every + # other artifact (the Homebrew push is the only step that needs it). + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN || secrets.GITHUB_TOKEN }} # Sign checksums file — this is the common pattern for Go CLIs. # Consumers verify with: cosign verify-blob --certificate-identity-regexp ... diff --git a/.goreleaser.yaml b/.goreleaser.yaml index b0e76e8..28f2b71 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -31,12 +31,12 @@ builds: archives: - id: bammm - builds: [bammm] + ids: [bammm] # Version-less names so `releases/latest/download/` URLs are stable. name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" format_overrides: - goos: windows - format: zip + formats: [zip] files: - LICENSE - README.md @@ -59,8 +59,89 @@ changelog: release: github: - owner: finos - name: bammm + owner: InsightSoftmax + name: BAMMM draft: false prerelease: auto name_template: "{{ .ProjectName }} {{ .Tag }}" + +# ── Linux packages ─────────────────────────────────────────────────────────── +# Native .deb/.rpm/.apk built from the same binaries and attached to the GitHub +# release (users download and `dpkg -i` / `rpm -i` / `apk add`). No apt/yum repo +# hosting — that would need an external service (Cloudsmith/Fury). +nfpms: + - id: bammm + package_name: bammm + ids: [bammm] + # Version-less names so `releases/latest/download/` URLs stay stable. + file_name_template: "{{ .PackageName }}_{{ .Os }}_{{ .Arch }}" + vendor: Insight Softmax Consulting + homepage: https://github.com/InsightSoftmax/BAMMM + maintainer: BAMMM maintainers + description: Convert batch job specifications between scheduler formats via SPLAT. + license: Apache-2.0 + formats: [deb, rpm, apk] + +# ── Container image ────────────────────────────────────────────────────────── +# Multi-arch static-binary image published to GHCR using the built-in +# GITHUB_TOKEN (no external registry account). +dockers: + - id: bammm-amd64 + goos: linux + goarch: amd64 + dockerfile: Dockerfile.goreleaser + use: buildx + image_templates: + - "ghcr.io/insightsoftmax/bammm:{{ .Version }}-amd64" + - "ghcr.io/insightsoftmax/bammm:latest-amd64" + build_flag_templates: + - "--platform=linux/amd64" + - "--label=org.opencontainers.image.source=https://github.com/InsightSoftmax/BAMMM" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.licenses=Apache-2.0" + - id: bammm-arm64 + goos: linux + goarch: arm64 + dockerfile: Dockerfile.goreleaser + use: buildx + image_templates: + - "ghcr.io/insightsoftmax/bammm:{{ .Version }}-arm64" + - "ghcr.io/insightsoftmax/bammm:latest-arm64" + build_flag_templates: + - "--platform=linux/arm64" + - "--label=org.opencontainers.image.source=https://github.com/InsightSoftmax/BAMMM" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.licenses=Apache-2.0" + +docker_manifests: + - name_template: "ghcr.io/insightsoftmax/bammm:{{ .Version }}" + image_templates: + - "ghcr.io/insightsoftmax/bammm:{{ .Version }}-amd64" + - "ghcr.io/insightsoftmax/bammm:{{ .Version }}-arm64" + - name_template: "ghcr.io/insightsoftmax/bammm:latest" + image_templates: + - "ghcr.io/insightsoftmax/bammm:latest-amd64" + - "ghcr.io/insightsoftmax/bammm:latest-arm64" + +# ── Homebrew (macOS) ───────────────────────────────────────────────────────── +# Pushes a cask to the InsightSoftmax/homebrew-tap repo (GoReleaser replaced the +# deprecated `brews` formulae with `homebrew_casks`, which are macOS-only; Linux +# users install via the .deb/.rpm, the release tarball, or `go install`). +# Requires a PAT with write access to that repo, exposed to the release workflow +# as HOMEBREW_TAP_TOKEN (the default GITHUB_TOKEN cannot push to another repo). +homebrew_casks: + - name: bammm + repository: + owner: InsightSoftmax + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + directory: Casks + homepage: https://github.com/InsightSoftmax/BAMMM + description: Convert batch job specifications between scheduler formats via SPLAT. + # Strip the quarantine attribute so Gatekeeper doesn't block the unsigned CLI. + hooks: + post: + install: | + if OS.mac? + system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/bammm"] + end diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser new file mode 100644 index 0000000..321d344 --- /dev/null +++ b/Dockerfile.goreleaser @@ -0,0 +1,6 @@ +# Image built by GoReleaser: it drops the prebuilt static `bammm` binary into +# the build context, so there is no compile step here. distroless/static keeps +# the image tiny and rootless. +FROM gcr.io/distroless/static:nonroot +COPY bammm /usr/local/bin/bammm +ENTRYPOINT ["/usr/local/bin/bammm"] diff --git a/README.md b/README.md index 35e267e..128d5b9 100644 --- a/README.md +++ b/README.md @@ -112,12 +112,27 @@ curl -sSL https://github.com/InsightSoftmax/BAMMM/releases/latest/download/bammm sudo install bammm /usr/local/bin/bammm ``` -**Homebrew** (coming soon): +**Linux packages** (`.deb` / `.rpm` / `.apk` are attached to each [release](https://github.com/InsightSoftmax/BAMMM/releases/latest)): + +```sh +# Debian/Ubuntu +curl -sSLO https://github.com/InsightSoftmax/BAMMM/releases/latest/download/bammm_linux_amd64.deb +sudo dpkg -i bammm_linux_amd64.deb +# RHEL/Fedora: sudo rpm -i bammm_linux_amd64.rpm +``` + +**Homebrew** (macOS / Linux): ```sh brew install InsightSoftmax/tap/bammm ``` +**Container image** (GHCR, multi-arch): + +```sh +docker run --rm -i ghcr.io/insightsoftmax/bammm:latest convert -f slurm -t kueue < job.sh +``` + ## SPLAT format quick start SPLAT is the format you write if you want a job that runs *anywhere*. Specify both a container image and a shell script — K8s emitters use the container, HPC emitters use the script.