11name : Docker Build & Push All Services
22
33on :
4+ delete :
45 push :
56 tags :
67 - " v*.*.*"
2021 - name : 🧠 Discover components and services
2122 id : set-matrix
2223 run : |
23- # Customize these lists as needed
24- EXCLUDED_COMPONENTS=(core validator)
25- EXCLUDED_SERVICES=()
26-
27- should_exclude() {
28- local item=$1
29- shift
30- local list=("$@")
31- for ex in "${list[@]}"; do
32- if [[ "$item" == "$ex" ]]; then
33- return 0
34- fi
35- done
36- return 1
37- }
38-
3924 mkdir -p .build/tmp_matrix
4025 echo '{ "include": [' > .build/tmp_matrix/matrix.json
4126 FIRST=true
@@ -44,32 +29,15 @@ jobs:
4429 [ -d "$comp" ] || continue
4530 comp_name=$(basename "$comp")
4631
47- # 🔥 Skip excluded components
48- if should_exclude "$comp_name" "${EXCLUDED_COMPONENTS[@]}"; then
49- echo "⏭️ Skipping excluded component: $comp_name"
50- continue
51- fi
52-
53- for service in "$comp"/*; do
54- [ -d "$service" ] || continue
55- service_name=$(basename "$service")
56-
57- # 🔥 Skip excluded services
58- if should_exclude "$service_name" "${EXCLUDED_SERVICES[@]}"; then
59- echo "⏭️ Skipping excluded service: $service_name"
60- continue
32+ # ✅ Include only if it has a pyproject or version.py
33+ if [[ -f "$comp/pyproject.toml" || -f "$comp/version.py" ]]; then
34+ if [ "$FIRST" = true ]; then
35+ FIRST=false
36+ else
37+ echo "," >> .build/tmp_matrix/matrix.json
6138 fi
62-
63- # ✅ Include only if it has a pyproject or version.py
64- if [[ -f "$service/pyproject.toml" || -f "$service/version.py" ]]; then
65- if [ "$FIRST" = true ]; then
66- FIRST=false
67- else
68- echo "," >> .build/tmp_matrix/matrix.json
69- fi
70- echo " { \"component\": \"$comp_name\", \"service\": \"$service_name\" }" >> .build/tmp_matrix/matrix.json
71- fi
72- done
39+ echo " { \"component\": \"$comp_name\" }" >> .build/tmp_matrix/matrix.json
40+ fi
7341 done
7442
7543 echo "] }" >> .build/tmp_matrix/matrix.json
12492 fi
12593
12694 build :
95+ if : github.event_name == 'push' || github.event_name == 'delete'
12796 needs : [discover, wheel-builder]
12897 runs-on : ubuntu-latest
12998 permissions :
@@ -166,30 +135,48 @@ jobs:
166135 - name : 🚀 Build and push version-tagged image (on tag push only)
167136 if : startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
168137 run : |
169- COMP=${{ matrix.component }}
170- SERVICE=${{ matrix.service }}
171- IMAGE="subvortex/subvortex-$COMP-$SERVICE "
138+ COMP=" ${{ matrix.component }}"
139+ REPO_NAME="subvortex-${COMP//_/-}" && echo "$COMP"
140+ IMAGE="subvortex/$REPO_NAME "
172141 WHEEL_IMAGE="${{ needs.wheel-builder.outputs.tag }}"
173- RAW_VERSION_TAG="${{ steps.taginfo.outputs.version_tag }}"
174- VERSION_TAG="${RAW_VERSION_TAG#v}"
175- DOCKERFILE="subvortex/$COMP/$SERVICE/Dockerfile"
142+ VERSION_TAG="${{ steps.taginfo.outputs.version_tag }}"
143+ VERSION="${VERSION_TAG#v}"
144+ DOCKERFILE="subvortex/$COMP/Dockerfile"
145+
146+ echo "🔍 Searching for component version... $COMP / $IMAGE"
147+ COMPONENT_PATH="subvortex/$COMP"
148+ if [ -f "$COMPONENT_PATH/pyproject.toml" ]; then
149+ echo "✅ Found pyproject.toml"
150+ COMPONENT_VERSION=$(grep -E '^version\s*=' "$COMPONENT_PATH/pyproject.toml" | head -1 | sed -E 's/version\s*=\s*"([^"]+)"/\1/')
151+ elif [ -f "$COMPONENT_PATH/version.py" ]; then
152+ echo "✅ Found version.py"
153+ COMPONENT_VERSION=$(python -c "import ast; f=open('$COMPONENT_PATH/version.py'); print([n.value.s for n in ast.walk(ast.parse(f.read())) if isinstance(n, ast.Assign) and n.targets[0].id == '__version__'][0])")
154+ else
155+ echo "❌ No version file found for component"
156+ exit 1
157+ fi
158+
159+ echo "🧾 Final versions:"
160+ echo "VERSION=$VERSION"
161+ echo "COMPONENT_VERSION=$COMPONENT_VERSION"
176162
177- echo "🚀 Building image $IMAGE:$VERSION_TAG "
163+ echo "🚀 Building image $IMAGE:$VERSION "
178164
179165 docker buildx build \
180166 --squash \
181167 --platform linux/amd64,linux/arm64 \
182168 --build-context wheelbuilder=docker-image://$WHEEL_IMAGE \
183- --build-arg VERSION=$VERSION_TAG \
184- --build-arg COMPONENT_VERSION=$VERSION_TAG \
185- --cache-from=type=gha,scope=wheels_${COMP}_${SERVICE}_${ ARCH} \
186- --cache-to=type=gha,mode=max,scope=wheels_${COMP}_${SERVICE}_${ ARCH} \
187- --tag $IMAGE:$VERSION_TAG \
169+ --build-arg VERSION=$VERSION \
170+ --build-arg COMPONENT_VERSION=$COMPONENT_VERSION \
171+ --cache-from=type=gha,scope=wheels_${COMP}_${ARCH} \
172+ --cache-to=type=gha,mode=max,scope=wheels_${COMP}_${ARCH} \
173+ --tag $IMAGE:$VERSION \
188174 --file $DOCKERFILE \
189175 --push \
190176 .
191177
192178 release :
179+ if : github.event_name == 'release'
193180 needs : [discover]
194181 runs-on : ubuntu-latest
195182 permissions :
@@ -232,11 +219,11 @@ jobs:
232219 - name : 🚀 Retag and push floating tags (on release or prerelease)
233220 if : github.event_name == 'release' && github.event.action != 'deleted'
234221 run : |
235- COMP=${{ matrix.component }}
236- SERVICE=${{ matrix.service }}
237- IMAGE="subvortex/subvortex-$COMP-$SERVICE "
222+ COMP=" ${{ matrix.component }}"
223+ REPO_NAME="subvortex-${COMP//_/-}" && echo "$COMP"
224+ IMAGE="subvortex/$REPO_NAME "
238225 RAW_VERSION_TAG="${{ steps.taginfo.outputs.version_tag }}"
239- VERSION_TAG ="${RAW_VERSION_TAG#v}"
226+ VERSION ="${RAW_VERSION_TAG#v}"
240227 FLOATING_TAGS="${{ steps.taginfo.outputs.floating_tags }}"
241228 IS_PRERELEASE=${{ github.event.release.prerelease }}
242229 IS_DRAFT=${{ github.event.release.draft }}
@@ -249,8 +236,8 @@ jobs:
249236 exit 0
250237 fi
251238
252- echo "🔍 Getting manifest for $IMAGE:$VERSION_TAG "
253- docker buildx imagetools inspect $IMAGE:$VERSION_TAG
239+ echo "🔍 Getting manifest for $IMAGE:$VERSION "
240+ docker buildx imagetools inspect $IMAGE:$VERSION
254241
255242 for TAG in $FLOATING_TAGS; do
256243 # Skip "latest" for prereleases
@@ -259,27 +246,26 @@ jobs:
259246 continue
260247 fi
261248
262- echo "🔁 Creating manifest for $IMAGE:$TAG from $IMAGE:$VERSION_TAG "
249+ echo "🔁 Creating manifest for $IMAGE:$TAG from $IMAGE:$VERSION "
263250 docker buildx imagetools create \
264251 --tag $IMAGE:$TAG \
265- $IMAGE:$VERSION_TAG
252+ $IMAGE:$VERSION
266253 done
267254
268255 - name : 🧹 Remove floating tags (on release or prerelease delete)
269256 if : github.event_name == 'release' && github.event.action == 'deleted'
270257 run : |
271- COMP=${{ matrix.component }}
272- SERVICE=${{ matrix.service }}
273- IMAGE="subvortex/subvortex-$COMP-$SERVICE "
258+ COMP=" ${{ matrix.component }}"
259+ REPO_NAME="subvortex-${COMP//_/-}" && echo "$COMP"
260+ IMAGE="subvortex/$REPO_NAME "
274261 RAW_VERSION_TAG="${{ github.event.release.tag_name }}"
275- VERSION_TAG ="${RAW_VERSION_TAG#v}"
262+ VERSION ="${RAW_VERSION_TAG#v}"
276263 FLOATING_TAGS="${{ steps.taginfo.outputs.floating_tags }}"
277264 USERNAME="${{ secrets.DOCKER_USERNAME }}"
278265 PASSWORD="${{ secrets.DOCKER_PASSWORD }}"
279- REPO_NAME="subvortex-$COMP-$SERVICE"
280266
281- echo "🗑️ Release deleted: $RAW_VERSION_TAG "
282- echo "🔍 Attempting to delete floating tags: $FLOATING_TAGS"
267+ echo "🗑️ Release deleted: $VERSION "
268+ echo "🔍 Handling floating tags: $FLOATING_TAGS"
283269
284270 echo "🔐 Requesting Docker Hub JWT token..."
285271 TOKEN=$(curl -s -X POST https://hub.docker.com/v2/users/login/ \
@@ -291,18 +277,51 @@ jobs:
291277 exit 1
292278 fi
293279
280+ echo "📦 Fetching all tags from Docker Hub (excluding deleted tag: $VERSION)..."
281+ ALL_TAGS=$(curl -s -H "Authorization: JWT $TOKEN" \
282+ "https://hub.docker.com/v2/repositories/$USERNAME/$REPO_NAME/tags?page_size=100" | jq -r '.results[].name' | grep -v "^$VERSION$")
283+
284+ RELEASE_TAGS=$(echo "$ALL_TAGS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' || true)
285+ PRERELEASE_TAGS=$(echo "$ALL_TAGS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+-(alpha|rc)\.[0-9]+$' || true)
286+
294287 for TAG in $FLOATING_TAGS; do
295- echo "❌ Deleting tag: $IMAGE:$TAG"
296- RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
297- "https://hub.docker.com/v2/repositories/$USERNAME/$REPO_NAME/tags/$TAG/" \
298- -H "Authorization: JWT $TOKEN")
299-
300- if [ "$RESPONSE" = "204" ]; then
301- echo "✅ Successfully deleted $IMAGE:$TAG"
302- elif [ "$RESPONSE" = "404" ]; then
303- echo "⚠️ Tag $TAG not found (already deleted or never pushed)"
288+ echo "🔁 Handling floating tag: $TAG"
289+
290+ case "$TAG" in
291+ dev)
292+ TARGET=$(echo "$PRERELEASE_TAGS" | grep 'alpha' | sort -Vr | head -n1)
293+ ;;
294+ stage)
295+ TARGET=$(echo "$PRERELEASE_TAGS" | grep 'rc' | sort -Vr | head -n1)
296+ ;;
297+ latest)
298+ TARGET=$(echo "$RELEASE_TAGS" | sort -Vr | head -n1)
299+ ;;
300+ *)
301+ echo "⚠️ Unknown floating tag: $TAG"
302+ continue
303+ ;;
304+ esac
305+
306+ if [ -n "$TARGET" ]; then
307+ echo "🔄 Re-pointing $TAG to $TARGET as multi-platform manifest"
308+ echo "$PASSWORD" | docker login -u "$USERNAME" --password-stdin
309+ docker buildx imagetools create \
310+ --tag "$IMAGE:$TAG" \
311+ "$IMAGE:$TARGET"
312+ docker logout
304313 else
305- echo "❌ Failed to delete tag $TAG (HTTP $RESPONSE)"
314+ echo "🗑️ No matching version for $TAG. Deleting..."
315+ RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
316+ "https://hub.docker.com/v2/repositories/$USERNAME/$REPO_NAME/tags/$TAG/" \
317+ -H "Authorization: JWT $TOKEN")
318+ if [ "$RESPONSE" = "204" ]; then
319+ echo "✅ Deleted $IMAGE:$TAG"
320+ elif [ "$RESPONSE" = "404" ]; then
321+ echo "⚠️ Tag $TAG not found"
322+ else
323+ echo "❌ Failed to delete $TAG (HTTP $RESPONSE)"
324+ fi
306325 fi
307326 done
308327
0 commit comments