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
125 changes: 125 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
name: Docker Publish

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Extra image tag to publish (in addition to latest / git ref tags)"
required: false
default: "latest"

permissions:
contents: read
packages: write

env:
GHCR_IMAGE: ghcr.io/paladini/team-ai-sync
DOCKERHUB_IMAGE: paladini/team-ai-sync

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Detect Docker Hub credentials
id: dockerhub
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
run: |
if [ -n "$DOCKERHUB_USERNAME" ] && [ -n "$DOCKERHUB_PASSWORD" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "Docker Hub secrets not set; publishing to GHCR only (Bitbucket tag pipeline remains the Docker Hub path)."
fi

- name: Log in to Docker Hub
if: steps.dockerhub.outputs.enabled == 'true'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Prepare image list
id: images
run: |
images="${GHCR_IMAGE}"
if [ "${{ steps.dockerhub.outputs.enabled }}" = "true" ]; then
images="${images}"$'\n'"${DOCKERHUB_IMAGE}"
fi
{
echo "list<<EOF"
printf '%s\n' "$images"
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.images.outputs.list }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable=${{ github.ref_type == 'tag' || github.event_name == 'workflow_dispatch' }}
type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' && inputs.tag != '' }}
type=sha,prefix=sha-,format=short

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false

- name: Make GHCR package public
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# First publish is often private; try API, fall back to UI note in docs.
gh api --method PUT \
-H "Accept: application/vnd.github+json" \
"/user/packages/container/team-ai-sync/visibility" \
-f visibility=public

- name: Summary
run: |
{
echo "## Published images"
echo ""
echo '```'
echo "${{ steps.meta.outputs.tags }}"
echo '```'
echo ""
echo "- GHCR: \`ghcr.io/paladini/team-ai-sync\`"
if [ "${{ steps.dockerhub.outputs.enabled }}" = "true" ]; then
echo "- Docker Hub: \`paladini/team-ai-sync\` (also pushed from this workflow)"
else
echo "- Docker Hub: still published from Bitbucket tag pipeline (unchanged)"
fi
echo ""
echo "### Public visibility"
echo "If the package is private, open"
echo "[Package settings](https://github.com/users/paladini/packages/container/team-ai-sync/settings)"
echo "→ **Change visibility** → **Public**."
} >> "$GITHUB_STEP_SUMMARY"
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![CI](https://github.com/paladini/team-ai-sync/actions/workflows/ci.yml/badge.svg)](https://github.com/paladini/team-ai-sync/actions/workflows/ci.yml)
[![GitHub Marketplace](https://img.shields.io/badge/GitHub%20Marketplace-team--ai--sync-blue?logo=github)](https://github.com/marketplace/actions/team-ai-sync)
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-paladini%2Fteam--ai--sync-2496ed?logo=docker&logoColor=white)](https://hub.docker.com/r/paladini/team-ai-sync)
[![GHCR](https://img.shields.io/badge/GHCR-ghcr.io%2Fpaladini%2Fteam--ai--sync-black?logo=github)](https://github.com/paladini/team-ai-sync/pkgs/container/team-ai-sync)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

Sync shared AI guidance files from one source repository to many target
Expand All @@ -20,7 +21,7 @@ Pipe. Each package is designed for repositories hosted on that same platform.

**Documentation:** [docs/README.md](docs/README.md)

**Docker image:** [paladini/team-ai-sync](https://hub.docker.com/r/paladini/team-ai-sync)
**Docker image:** [Docker Hub](https://hub.docker.com/r/paladini/team-ai-sync) · [GHCR](https://github.com/paladini/team-ai-sync/pkgs/container/team-ai-sync)

## Why teams use it

Expand Down Expand Up @@ -79,7 +80,7 @@ is not the supported operating model.
| GitHub | [GitHub Action](https://github.com/marketplace/actions/team-ai-sync) | Pull requests |
| GitLab | [GitLab CI/CD Component](https://gitlab.com/explore/catalog/paladini/team-ai-sync) | Merge requests |
| Bitbucket | `paladini/team-ai-sync:1.0.0` Bitbucket Pipe | Pull requests |
| Docker | [paladini/team-ai-sync](https://hub.docker.com/r/paladini/team-ai-sync) | Runtime for GitLab and Bitbucket wrappers |
| Docker | [Docker Hub](https://hub.docker.com/r/paladini/team-ai-sync) / [GHCR](https://github.com/paladini/team-ai-sync/pkgs/container/team-ai-sync) | Runtime for GitLab and Bitbucket wrappers |

## Quick start for GitHub Actions

Expand Down
19 changes: 18 additions & 1 deletion docs/platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,28 @@ curation process.

## OCI image

GitLab and Bitbucket wrappers run the same versioned OCI image:
GitLab and Bitbucket wrappers run the same versioned OCI image. The image is
published to both registries:

```text
paladini/team-ai-sync:<semver>
ghcr.io/paladini/team-ai-sync:<semver>
```

- **Docker Hub** — published from Bitbucket Pipelines on git tags (existing flow).
- **GHCR** — published from GitHub Actions (`.github/workflows/docker-publish.yml`)
on `v*` tags and via `workflow_dispatch`. When `DOCKERHUB_USERNAME` /
`DOCKERHUB_PASSWORD` secrets are set on GitHub, the same workflow also pushes
to Docker Hub; otherwise Docker Hub remains Bitbucket-only.

Pin production usage to a SemVer tag. Avoid relying on mutable image tags for
team-wide automation.

### GHCR visibility

The first GHCR package may be created as private. To make it public:

1. Open [github.com/users/paladini/packages](https://github.com/users/paladini/packages)
(or **Packages** on the repository page).
2. Select `team-ai-sync`.
3. **Package settings** → **Change visibility** → **Public**.
Loading