2626 runs-on : ubuntu-latest
2727 outputs :
2828 run-build : ${{ steps.check.outputs.run }}
29+ core-sha : ${{ steps.stamp.outputs.core }}
30+ eo-sha : ${{ steps.stamp.outputs.eo }}
31+ hub-sha : ${{ steps.stamp.outputs.hub }}
32+ cli-sha : ${{ steps.stamp.outputs.cli }}
33+ expected-manifest : ${{ steps.stamp.outputs.manifest }}
2934 steps :
3035 - uses : actions/checkout@v4
36+
3137 - id : check
3238 run : |
3339 if [[ "${{ github.event_name }}" == "repository_dispatch" ]]; then
3844 else echo "run=false" >> $GITHUB_OUTPUT; fi
3945 else echo "run=true" >> $GITHUB_OUTPUT; fi
4046
47+ - id : stamp
48+ run : |
49+ CORE="${{ inputs.repo-core-ref || (github.event.client_payload.source_repo == 'mapchete' && github.event.client_payload.sha) || 'main' }}"
50+ EO="${{ inputs.repo-eo-ref || (github.event.client_payload.source_repo == 'mapchete-eo' && github.event.client_payload.sha) || 'main' }}"
51+ HUB="${{ inputs.repo-hub-ref || (github.event.client_payload.source_repo == 'mapchete-hub' && github.event.client_payload.sha) || 'main' }}"
52+ CLI="${{ inputs.repo-cli-ref || (github.event.client_payload.source_repo == 'mapchete-hub-cli' && github.event.client_payload.sha) || 'main' }}"
53+
54+ if [[ "${{ inputs.image-name }}" == "mapchete" ]]; then
55+ MANIFEST="${CORE}|${EO}|${HUB}|none"
56+ else
57+ MANIFEST="none|none|${HUB}|${CLI}"
58+ fi
59+
60+ echo "manifest=$MANIFEST" >> $GITHUB_OUTPUT
61+ echo "core=$CORE" >> $GITHUB_OUTPUT
62+ echo "eo=$EO" >> $GITHUB_OUTPUT
63+ echo "hub=$HUB" >> $GITHUB_OUTPUT
64+ echo "cli=$CLI" >> $GITHUB_OUTPUT
65+
4166 build :
4267 needs : prepare
4368 if : needs.prepare.outputs.run-build == 'true'
@@ -59,30 +84,67 @@ jobs:
5984 load : true
6085 tags : local-test-image:latest
6186 build-args : |
62- REPO_CORE_REF=${{ inputs.repo-core-ref || (github.event.client_payload.source_repo == 'mapchete' && github.event.client_payload.sha) || 'main' }}
63- REPO_EO_REF=${{ inputs.repo-eo-ref || (github.event.client_payload.source_repo == 'mapchete-eo' && github.event.client_payload.sha) || 'main' }}
64- REPO_HUB_REF=${{ inputs.repo-hub-ref || (github.event.client_payload.source_repo == 'mapchete-hub' && github.event.client_payload.sha) || 'main' }}
65- REPO_CLI_REF=${{ inputs.repo-cli-ref || (github.event.client_payload.source_repo == 'mapchete-hub-cli' && github.event.client_payload.sha) || 'main' }}
87+ REPO_CORE_REF=${{ needs.prepare.outputs.core-sha }}
88+ REPO_EO_REF=${{ needs.prepare.outputs.eo-sha }}
89+ REPO_HUB_REF=${{ needs.prepare.outputs.hub-sha }}
90+ REPO_CLI_REF=${{ needs.prepare.outputs.cli-sha }}
91+
92+ - name : Verify Build & Sync Check
93+ id : uv_check
94+ run : |
95+ # 1. Verify Git Sync Integrity
96+ ACTUAL_MANIFEST=$(docker run --rm local-test-image:latest cat /app/build_manifest)
97+ EXPECTED_MANIFEST="${{ needs.prepare.outputs.expected-manifest }}"
98+ if [ "$ACTUAL_MANIFEST" != "$EXPECTED_MANIFEST" ]; then
99+ echo "❌ Git SHA Mismatch! CI expected $EXPECTED_MANIFEST but Docker built $ACTUAL_MANIFEST"
100+ exit 1
101+ fi
102+
103+ # 2. Extract UV Fingerprint
104+ UV_HASH=$(docker run --rm local-test-image:latest cat /app/uv_fingerprint)
105+ echo "uv-hash=$UV_HASH" >> $GITHUB_OUTPUT
106+
107+ # 3. Compare with Remote Registry Label
108+ REMOTE_TAG="dev"
109+ if [[ "${{ github.event_name }}" == "repository_dispatch" && "${{ github.event.client_payload.image_tag }}" == "latest" ]]; then REMOTE_TAG="latest"; fi
110+
111+ REMOTE_HASH=$(docker manifest inspect ghcr.io/mapchete/${{ inputs.image-name }}:$REMOTE_TAG 2>/dev/null | jq -r '.config.labels["org.mapchete.uv_lock_hash"]' || echo "none")
112+
113+ if [[ "$UV_HASH" == "$REMOTE_HASH" && "${{ github.ref_type }}" != "tag" ]]; then
114+ echo "changed=false" >> $GITHUB_OUTPUT
115+ echo "✅ Content Match: $UV_HASH matches registry. Skipping push."
116+ else
117+ echo "changed=true" >> $GITHUB_OUTPUT
118+ echo "🚀 Content Change: New fingerprint $UV_HASH"
119+ fi
66120
67121 - name : Integration Tests
68- if : inputs.image-name == 'mapchete'
122+ if : steps.uv_check.outputs.changed == 'true'
123+ id : tests
69124 run : |
70- docker run --rm \
71- -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
72- -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
73- -e CDSE_S3_ACCESS_KEY=${{ secrets.CDSE_S3_ACCESS_KEY }} \
74- -e CDSE_S3_SECRET_KEY=${{ secrets.CDSE_S3_SECRET_KEY }} \
75- -e AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
76- local-test-image:latest \
77- /bin/bash -c "cd deps/mapchete-eo && uv pip install -e .[test] && uv run pytest"
125+ if docker run --rm local-test-image:latest ls -d deps/mapchete-eo/tests >/dev/null 2>&1; then
126+ echo "found=true" >> $GITHUB_OUTPUT
127+ docker run --rm \
128+ -e AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} \
129+ -e AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} \
130+ -e CDSE_S3_ACCESS_KEY=${{ secrets.CDSE_S3_ACCESS_KEY }} \
131+ -e CDSE_S3_SECRET_KEY=${{ secrets.CDSE_S3_SECRET_KEY }} \
132+ -e AWS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
133+ local-test-image:latest \
134+ /bin/bash -c "cd deps/mapchete-eo && uv pip install -e .[test] && uv run pytest"
135+ else
136+ echo "found=false" >> $GITHUB_OUTPUT
137+ fi
78138
79139 - uses : docker/login-action@v3
140+ if : steps.uv_check.outputs.changed == 'true'
80141 with :
81142 registry : ghcr.io
82143 username : ${{ github.actor }}
83144 password : ${{ secrets.GITHUB_TOKEN }}
84145
85146 - id : meta
147+ if : steps.uv_check.outputs.changed == 'true'
86148 uses : docker/metadata-action@v5
87149 with :
88150 images : ghcr.io/mapchete/${{ inputs.image-name }}
@@ -91,45 +153,32 @@ jobs:
91153 tags : |
92154 type=raw,value=${{ inputs.image-tag }},enable=${{ inputs.image-tag != '' }}
93155 type=ref,event=tag
94-
95- # BUILD LATEST:
96- # - Only on push to main or 'latest' dispatch
97156 type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || (github.event_name == 'repository_dispatch' && github.event.client_payload.image_tag == 'latest') }}
98-
99- # BUILD DEV:
100- # - On PRs
101- # - On push to main
102- # - On 'dev' dispatch
103- # - On 'latest' dispatch (as requested)
104157 type=raw,value=dev,enable=${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/main' || (github.event_name == 'repository_dispatch' && (github.event.client_payload.image_tag == 'dev' || github.event.client_payload.image_tag == 'latest')) }}
105-
106158 type=ref,event=pr
107159
108160 - id : push
161+ if : steps.uv_check.outputs.changed == 'true'
109162 uses : docker/build-push-action@v5
110163 with :
111164 context : ${{ inputs.image-name }}/
112165 push : true
113166 tags : ${{ steps.meta.outputs.tags }}
114167 labels : ${{ steps.meta.outputs.labels }}
115168 build-args : |
116- REPO_CORE_REF=${{ inputs.repo-core-ref || (github.event.client_payload.source_repo == 'mapchete' && github.event.client_payload.sha) || 'main' }}
117- REPO_EO_REF=${{ inputs.repo-eo-ref || (github.event.client_payload.source_repo == 'mapchete-eo' && github.event.client_payload.sha) || 'main' }}
118- REPO_HUB_REF=${{ inputs.repo-hub-ref || (github.event.client_payload.source_repo == 'mapchete-hub' && github.event.client_payload.sha) || 'main' }}
119- REPO_CLI_REF=${{ inputs.repo-cli-ref || (github.event.client_payload.source_repo == 'mapchete-hub-cli' && github.event.client_payload.sha) || 'main' }}
169+ REPO_CORE_REF=${{ needs.prepare.outputs.core-sha }}
170+ REPO_EO_REF=${{ needs.prepare.outputs.eo-sha }}
171+ REPO_HUB_REF=${{ needs.prepare.outputs.hub-sha }}
172+ REPO_CLI_REF=${{ needs.prepare.outputs.cli-sha }}
173+ IMAGE_FINGERPRINT=${{ steps.uv_check.outputs.uv-hash }}
120174
121175 - uses : actions/attest-build-provenance@v2
122- if : steps.push.outputs.digest != ''
176+ if : steps.uv_check.outputs.changed == 'true' && steps. push.outputs.digest != ''
123177 with :
124178 subject-name : ghcr.io/mapchete/${{ inputs.image-name }}
125179 subject-digest : ${{ steps.push.outputs.digest }}
126180 push-to-registry : true
127181
128- - name : Update Version File
129- if : github.event_name == 'repository_dispatch' && github.event.client_payload.type == 'version'
130- run : |
131- echo "${{ github.event.client_payload.source_repo }}: ${{ github.event.client_payload.image_tag }}" > ${{ inputs.image-name }}/VERSION.txt
132-
133182 - name : Create or Update Pull Request
134183 if : github.event_name == 'repository_dispatch' && github.event.client_payload.type == 'version'
135184 uses : peter-evans/create-pull-request@v6
@@ -141,6 +190,8 @@ jobs:
141190 body : |
142191 Automated update triggered by `${{ github.event.client_payload.source_repo }}`.
143192 - **New Version:** `${{ github.event.client_payload.image_tag }}`
193+ - **UV Environment Hash:** `${{ steps.uv_check.outputs.uv-hash }}`
194+ - **Sync Integrity Check:** PASSED ✅
144195 base : main
145196 delete-branch : true
146197 assignees : " scartography"
0 commit comments