Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 7 additions & 92 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,106 +1,21 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

env:
REGISTRY: ghcr.io
HARBOR_REGISTRY: registry.goharbor.io/bupd
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.25"

- name: Verify formatting
run: test -z "$(gofmt -l .)"

- name: Lint
run: go vet ./...

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...

docker:
test:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
container:
image: golang:1.25-bookworm
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v4

- name: Setup QEMU
uses: docker/setup-qemu-action@v3

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install cosign
uses: sigstore/cosign-installer@v3

- name: Install crane
uses: imjasonh/setup-crane@v0.4

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: registry.goharbor.io
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=sha,prefix=

- name: Build and push
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
sbom: true
provenance: mode=max
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Sign GHCR image
run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}

- name: Copy image to Harbor
run: crane copy ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }} ${{ env.HARBOR_REGISTRY }}/shitpost:latest
- name: Install Task
run: go install github.com/go-task/task/v3/cmd/task@v3.45.4

- name: Sign Harbor image
run: cosign sign --yes ${{ env.HARBOR_REGISTRY }}/shitpost:latest
- name: Run task CI
run: task ci
84 changes: 55 additions & 29 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ on:

env:
REGISTRY: ghcr.io
HARBOR_REGISTRY: registry.goharbor.io/bupd
IMAGE_NAME: ${{ github.repository }}

jobs:
goreleaser:
binaries:
runs-on: ubuntu-latest
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -28,17 +30,48 @@ jobs:
- name: Install syft
uses: anchore/sbom-action/download-syft@v0

- name: Install cosign
uses: sigstore/cosign-installer@v3

- name: Run GoReleaser
id: run-goreleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docker:
- name: Generate SLSA subjects
id: hash
env:
ARTIFACTS: ${{ steps.run-goreleaser.outputs.artifacts }}
run: |
set -euo pipefail
hashes=$(echo "$ARTIFACTS" | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0)
if test "$hashes" = ""; then
checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select(.type=="Checksum") | .path')
hashes=$(base64 -w0 "$checksum_file")
fi
echo "hashes=$hashes" >> "$GITHUB_OUTPUT"

binary-provenance:
needs: binaries
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
with:
base64-subjects: ${{ needs.binaries.outputs.hashes }}
upload-assets: true

image:
runs-on: ubuntu-latest
needs: goreleaser
needs: binaries
outputs:
digest: ${{ steps.build-and-push.outputs.digest }}
image: ${{ steps.image.outputs.image }}
permissions:
contents: read
packages: write
Expand All @@ -55,23 +88,13 @@ jobs:
- name: Install cosign
uses: sigstore/cosign-installer@v3

- name: Install crane
uses: imjasonh/setup-crane@v0.4

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: registry.goharbor.io
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
Expand All @@ -83,7 +106,7 @@ jobs:
type=semver,pattern={{major}}
type=raw,value=latest

- name: Build and push
- name: Build and push GHCR image
id: build-and-push
uses: docker/build-push-action@v6
with:
Expand All @@ -97,20 +120,23 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Output image name
id: image
run: echo "image=${REGISTRY}/${IMAGE_NAME}" >> "$GITHUB_OUTPUT"

- name: Sign GHCR image
run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}

- name: Get version tag
id: version
run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

- name: Copy image to Harbor
run: crane copy ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }} ${{ env.HARBOR_REGISTRY }}/shitpost:${{ steps.version.outputs.tag }}

- name: Copy latest to Harbor
run: crane copy ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }} ${{ env.HARBOR_REGISTRY }}/shitpost:latest

- name: Sign Harbor images
run: |
cosign sign --yes ${{ env.HARBOR_REGISTRY }}/shitpost:${{ steps.version.outputs.tag }}
cosign sign --yes ${{ env.HARBOR_REGISTRY }}/shitpost:latest
image-provenance:
needs: image
permissions:
actions: read
id-token: write
packages: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
with:
image: ${{ needs.image.outputs.image }}
digest: ${{ needs.image.outputs.digest }}
registry-username: ${{ github.actor }}
secrets:
registry-password: ${{ secrets.GITHUB_TOKEN }}
14 changes: 12 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,25 @@ builds:

archives:
- id: default
format: tar.gz
formats: [tar.gz]
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
format: zip
formats: [zip]

checksum:
name_template: "checksums.txt"

signs:
- cmd: cosign
signature: "${artifact}.sigstore.json"
args:
- sign-blob
- "--bundle=${signature}"
- "${artifact}"
- --yes
artifacts: checksum

sboms:
- artifacts: archive
cmd: syft
Expand Down
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ A lightweight, self-hosted alternative to Postiz, Buffer, and Hootsuite for deve
- Supports text, images, videos, and documents
- Alt-text support for accessible image posts
- Self-hosted and privacy-focused
- Multi-arch Docker images (amd64, arm64)
- Multi-arch GHCR Docker images (amd64, arm64)
- Lightweight Go binary with minimal dependencies

## Supported Platforms
Expand Down Expand Up @@ -54,10 +54,6 @@ A lightweight, self-hosted alternative to Postiz, Buffer, and Hootsuite for deve
Pre-built multi-arch images (amd64, arm64) are available:

```sh
# Primary (Harbor)
docker pull registry.goharbor.io/bupd/shitpost:latest

# Alternative (GitHub Container Registry)
docker pull ghcr.io/bupd/shitpost:latest
```

Expand Down Expand Up @@ -87,7 +83,7 @@ For production, use a versioned tag (e.g., `v1.0.0`) to avoid unexpected updates
docker run -d --name shitpost \
--env-file .env \
-v ./downloads:/app/downloads \
registry.goharbor.io/bupd/shitpost:latest
ghcr.io/bupd/shitpost:latest
```

3. Check logs:
Expand Down Expand Up @@ -209,6 +205,8 @@ Built as a lightweight wrapper around [humanwhocodes/crosspost](https://github.c
- No third-party analytics or tracking
- Media files stored locally (configure volume mounts)
- Consider implementing allowlists for public-facing bots
- See [docs/release-security-plan.md](docs/release-security-plan.md) for the OWASP-based release hardening plan.
- See [docs/slsa.md](docs/slsa.md) for the release provenance target and verification commands.

## Troubleshooting

Expand Down
15 changes: 14 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3"

vars:
ENV_FILE: '{{default ".env" .ENV_FILE}}'
IMAGE: '{{default "registry.goharbor.io/bupd/shitpost" .IMAGE}}'
IMAGE: '{{default "ghcr.io/bupd/shitpost" .IMAGE}}'
TAG: '{{default "dev" .TAG}}'
CROSSPOST_REPO: '{{default "https://github.com/bupd/crosspost.git" .CROSSPOST_REPO}}'
CROSSPOST_REF: '{{default "main" .CROSSPOST_REF}}'
Expand Down Expand Up @@ -92,6 +92,19 @@ tasks:
cmds:
- gofmt -w main.go

fmt:check:
desc: Check Go formatting
cmds:
- test -z "$(gofmt -l .)"

ci:
desc: Run CI checks without modifying files
cmds:
- task: fmt:check
- task: lint
- task: test
- task: build

validate:
desc: Format, lint, test, and build
cmds:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
shitpost-bot:
image: registry.goharbor.io/bupd/shitpost:latest
image: ghcr.io/bupd/shitpost:latest
build:
context: .
args:
Expand Down
Loading
Loading