|
4 | 4 | workflow_call: |
5 | 5 | inputs: |
6 | 6 | git_ref: |
7 | | - description: 'Git reference to build from' |
8 | | - required: true |
9 | 7 | type: string |
10 | | - image_tag: |
11 | | - description: 'Tag to apply to the images' |
12 | 8 | required: true |
| 9 | + image_tag: |
13 | 10 | type: string |
14 | | - branch_name: |
15 | | - description: 'Formatted branch name for image naming' |
16 | 11 | required: true |
| 12 | + branch_name: |
17 | 13 | type: string |
18 | | - environment: |
19 | | - description: 'Target environment (pr, staging, preprod, production)' |
20 | 14 | required: true |
| 15 | + environment: |
21 | 16 | type: string |
| 17 | + required: true |
22 | 18 | push_to_prod_registry: |
23 | | - description: 'Whether to push to production registry (true for staging/preprod/prod)' |
24 | | - required: false |
25 | 19 | type: boolean |
26 | 20 | default: false |
| 21 | + |
27 | 22 | outputs: |
28 | | - web_digest: |
29 | | - description: 'Digest of the web image' |
30 | | - value: ${{ jobs.build-web.outputs.digest }} |
31 | | - node_digest: |
32 | | - description: 'Digest of the node image' |
33 | | - value: ${{ jobs.build-node.outputs.digest }} |
34 | | - asset_digest: |
35 | | - description: 'Digest of the asset image' |
36 | | - value: ${{ jobs.build-asset.outputs.digest }} |
37 | | - image_tag: |
38 | | - description: 'The tag applied to all images' |
39 | | - value: ${{ inputs.image_tag }} |
40 | 23 | web_image: |
41 | | - description: 'Full web image reference' |
42 | | - value: ${{ jobs.build-web.outputs.image }} |
| 24 | + value: ${{ jobs.calculate.outputs.web_image }} |
43 | 25 | node_image: |
44 | | - description: 'Full node image reference' |
45 | | - value: ${{ jobs.build-node.outputs.image }} |
| 26 | + value: ${{ jobs.calculate.outputs.node_image }} |
46 | 27 | asset_image: |
47 | | - description: 'Full asset image reference' |
48 | | - value: ${{ jobs.build-asset.outputs.image }} |
| 28 | + value: ${{ jobs.calculate.outputs.asset_image }} |
49 | 29 |
|
50 | 30 | jobs: |
| 31 | + calculate: |
| 32 | + name: "calculate variables for the pipeline" |
| 33 | + runs-on: ubuntu-latest |
| 34 | + |
| 35 | + outputs: |
| 36 | + web_image: ${{ steps.images.output.web_image }} |
| 37 | + node_image: ${{ steps.images.output.node_image }} |
| 38 | + asset_image: ${{ steps.images.output.asset_image }} |
| 39 | + datetime: ${{ steps.datetime.output.dt }} |
| 40 | + steps: |
| 41 | + - id: datetime |
| 42 | + run: echo "dt=$(date +'%Y%m%d%H%M')" >> $GITHUB_OUTPUT |
| 43 | + - name: Compute asset repo |
| 44 | + id: images |
| 45 | + run: | |
| 46 | + if [ "${{ inputs.push_to_prod_registry }}" = "true" ]; then |
| 47 | + project=${{ secrets.PROD_PROJECT }} |
| 48 | + else |
| 49 | + project=${{ secrets.DEV_PROJECT }} |
| 50 | + fi |
| 51 | +
|
| 52 | + web="us-east1-docker.pkg.dev/$project/containers/sefaria-web" |
| 53 | + node="us-east1-docker.pkg.dev/$project/containers/sefaria-node" |
| 54 | + asset="us-east1-docker.pkg.dev/$project/containers/sefaria-asset" |
| 55 | + if [ -n "${{ inputs.branch_name }}" ]; then |
| 56 | + web="$web-${{ inputs.branch_name }}" |
| 57 | + node="$node-${{ inputs.branch_name }}" |
| 58 | + asset="$asset-${{ inputs.branch_name }}" |
| 59 | + fi |
| 60 | + echo "web_image=$web" >> $GITHUB_OUTPUT |
| 61 | + echo "node_image=$node" >> $GITHUB_OUTPUT |
| 62 | + echo "asset_image=$asset" >> $GITHUB_OUTPUT |
| 63 | +
|
| 64 | + ########################################################################### |
| 65 | + # BUILD: web + node |
| 66 | + ########################################################################### |
51 | 67 | build-generic: |
52 | | - name: "Build ${{ matrix.app }} Image" |
| 68 | + name: "Build ${{ matrix.app }} image" |
53 | 69 | runs-on: ubuntu-latest |
| 70 | + |
54 | 71 | permissions: |
55 | 72 | contents: 'read' |
56 | 73 | id-token: 'write' |
| 74 | + |
| 75 | + needs: [calculate] |
57 | 76 | strategy: |
58 | 77 | matrix: |
59 | | - app: [ web, node ] |
60 | | - outputs: |
61 | | - web_digest: ${{ steps.output-web.outputs.digest }} |
62 | | - web_image: ${{ steps.output-web.outputs.image }} |
63 | | - node_digest: ${{ steps.output-node.outputs.digest }} |
64 | | - node_image: ${{ steps.output-node.outputs.image }} |
| 78 | + - app: web |
| 79 | + image: ${{ needs.calculate.outputs.web_image }} |
| 80 | + - app: node |
| 81 | + image: ${{ needs.calculate.outputs.node_image }} |
| 82 | + |
65 | 83 | steps: |
66 | | - - name: Checkout code |
67 | | - uses: actions/checkout@v4 |
68 | | - with: |
| 84 | + - uses: actions/checkout@v4 |
| 85 | + with: |
69 | 86 | ref: ${{ inputs.git_ref }} |
70 | | - - name: Set up QEMU |
71 | | - uses: docker/setup-qemu-action@v3 |
72 | | - - name: Set up Docker Buildx |
73 | | - uses: docker/setup-buildx-action@v3 |
| 87 | + |
| 88 | + - uses: docker/setup-qemu-action@v3 |
| 89 | + - uses: docker/setup-buildx-action@v3 |
| 90 | + |
| 91 | + ########################################################################### |
| 92 | + # AUTH |
| 93 | + ########################################################################### |
74 | 94 | - id: auth |
75 | | - name: Authenticate to Google Cloud |
76 | 95 | uses: google-github-actions/auth@v2 |
77 | 96 | with: |
78 | | - token_format: 'access_token' |
79 | | - workload_identity_provider: ${{ inputs.push_to_prod_registry && format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.PROD_GKE_PROJECT_ID) || format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.DEV_GKE_PROJECT_ID) }} |
80 | | - service_account: ${{ inputs.push_to_prod_registry && secrets.PROD_GKE_SA || secrets.DEV_GKE_SA }} |
81 | | - - name: Login to GAR |
82 | | - uses: docker/login-action@v3 |
| 97 | + token_format: access_token |
| 98 | + workload_identity_provider: >- |
| 99 | + ${{ inputs.push_to_prod_registry |
| 100 | + && format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.PROD_GKE_PROJECT_ID) |
| 101 | + || format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.DEV_GKE_PROJECT_ID) }} |
| 102 | + service_account: >- |
| 103 | + ${{ inputs.push_to_prod_registry && secrets.PROD_GKE_SA || secrets.DEV_GKE_SA }} |
| 104 | +
|
| 105 | + - uses: docker/login-action@v3 |
83 | 106 | with: |
84 | 107 | registry: us-east1-docker.pkg.dev |
85 | 108 | username: oauth2accesstoken |
86 | | - password: '${{ steps.auth.outputs.access_token }}' |
87 | | - - name: Get current date |
88 | | - id: date |
89 | | - run: echo "date=$(date +'%Y%m%d%H%M')" >> $GITHUB_OUTPUT |
90 | | - - name: Set registry path |
91 | | - id: registry |
92 | | - run: | |
93 | | - if [ "${{ inputs.push_to_prod_registry }}" = "true" ]; then |
94 | | - echo "project=${{ secrets.PROD_PROJECT }}" >> $GITHUB_OUTPUT |
95 | | - else |
96 | | - echo "project=${{ secrets.DEV_PROJECT }}" >> $GITHUB_OUTPUT |
97 | | - fi |
98 | | - - name: Determine Image URI |
99 | | - id: image_uri |
100 | | - run: | |
101 | | - if [ "${{ inputs.branch_name }}" = "" ]; then |
102 | | - echo "image_uri=us-east1-docker.pkg.dev/${{ steps.registry.outputs.project }}/containers/sefaria-${{ matrix.app }}" >> $GITHUB_OUTPUT |
103 | | - else |
104 | | - echo "image_uri=us-east1-docker.pkg.dev/${{ steps.registry.outputs.project }}/containers/sefaria-${{ matrix.app }}-${{ inputs.branch_name }}" >> $GITHUB_OUTPUT |
105 | | - fi |
106 | | - - name: Generate image metadata |
107 | | - id: meta |
| 109 | + password: ${{ steps.auth.outputs.access_token }} |
| 110 | + |
| 111 | + ########################################################################### |
| 112 | + # METADATA (tags) |
| 113 | + ########################################################################### |
| 114 | + - id: meta |
108 | 115 | uses: docker/metadata-action@v3 |
109 | 116 | with: |
110 | | - images: | |
111 | | - ${{ steps.image_uri.outputs.image_uri }} |
| 117 | + images: ${{ matrix.image }} |
112 | 118 | tags: | |
| 119 | + type=raw,value=${{ inputs.image_tag }}-${{ needs.calculate.output.datetime }} |
113 | 120 | type=raw,value=${{ inputs.image_tag }} |
114 | | - type=raw,value=${{ inputs.image_tag }}-${{ steps.date.outputs.date }} |
115 | 121 | type=raw,value=latest |
116 | | - flavor: | |
117 | | - latest=false |
118 | | - - name: Build and push |
119 | | - id: build |
| 122 | + flavor: latest=false |
| 123 | + |
| 124 | + ########################################################################### |
| 125 | + # BUILD AND PUSH |
| 126 | + ########################################################################### |
| 127 | + - id: build |
120 | 128 | uses: docker/build-push-action@v6 |
121 | 129 | with: |
122 | 130 | context: . |
123 | | - push: true |
124 | | - build-args: | |
125 | | - TYPE=build |
126 | 131 | file: ./build/${{ matrix.app }}/Dockerfile |
| 132 | + push: true |
127 | 133 | tags: ${{ steps.meta.outputs.tags }} |
128 | 134 | labels: ${{ steps.meta.outputs.labels }} |
129 | | - # Output digest for web |
130 | | - - name: Output web digest |
131 | | - id: output-web |
132 | | - if: matrix.app == 'web' |
133 | | - run: | |
134 | | - echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT |
135 | | - echo "image=${{steps.image_uri.outputs.image_uri }}" >> $GITHUB_OUTPUT |
136 | | - # Output digest for node |
137 | | - - name: Output node digest |
138 | | - id: output-node |
139 | | - if: matrix.app == 'node' |
140 | | - run: | |
141 | | - echo "digest=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT |
142 | | - echo "image=${{steps.image_uri.outputs.image_uri }}" >> $GITHUB_OUTPUT |
143 | 135 |
|
144 | | - # Consolidate outputs from matrix job |
145 | | - build-web: |
146 | | - name: "Web Image Output" |
147 | | - runs-on: ubuntu-latest |
148 | | - needs: build-generic |
149 | | - outputs: |
150 | | - digest: ${{ needs.build-generic.outputs.web_digest }} |
151 | | - image: ${{ needs.build-generic.outputs.web_image }} |
152 | | - steps: |
153 | | - - run: echo "Web image built" |
154 | 136 |
|
155 | | - build-node: |
156 | | - name: "Node Image Output" |
| 137 | + ########################################################################### |
| 138 | + # BUILD ASSET IMAGE (depends on full digest of web) |
| 139 | + ########################################################################### |
| 140 | + build_asset: |
| 141 | + needs: [calculate, build-generic] |
157 | 142 | runs-on: ubuntu-latest |
158 | | - needs: build-generic |
159 | | - outputs: |
160 | | - digest: ${{ needs.build-generic.outputs.node_digest }} |
161 | | - image: ${{ needs.build-generic.outputs.node_image }} |
162 | | - steps: |
163 | | - - run: echo "Node image built" |
164 | 143 |
|
165 | | - # Build derived image (asset) that depends on web |
166 | | - build-asset: |
167 | | - name: "Build Asset Image" |
168 | | - runs-on: ubuntu-latest |
169 | | - needs: build-generic |
170 | 144 | permissions: |
171 | 145 | contents: 'read' |
172 | 146 | id-token: 'write' |
173 | | - outputs: |
174 | | - digest: ${{ steps.build.outputs.digest }} |
175 | | - image: ${{ steps.output.outputs.image }} |
| 147 | + |
176 | 148 | steps: |
177 | | - - name: Checkout code |
178 | | - uses: actions/checkout@v4 |
179 | | - with: |
| 149 | + - uses: actions/checkout@v4 |
| 150 | + with: |
180 | 151 | ref: ${{ inputs.git_ref }} |
181 | | - - name: Set up QEMU |
182 | | - uses: docker/setup-qemu-action@v3 |
183 | | - - name: Set up Docker Buildx |
184 | | - uses: docker/setup-buildx-action@v3 |
| 152 | + |
| 153 | + - uses: docker/setup-qemu-action@v3 |
| 154 | + - uses: docker/setup-buildx-action@v3 |
| 155 | + |
185 | 156 | - id: auth |
186 | | - name: Authenticate to Google Cloud |
187 | 157 | uses: google-github-actions/auth@v2 |
188 | 158 | with: |
189 | | - token_format: 'access_token' |
190 | | - workload_identity_provider: ${{ inputs.push_to_prod_registry && format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.PROD_GKE_PROJECT_ID) || format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.DEV_GKE_PROJECT_ID) }} |
191 | | - service_account: ${{ inputs.push_to_prod_registry && secrets.PROD_GKE_SA || secrets.DEV_GKE_SA }} |
192 | | - - name: Login to GAR |
193 | | - uses: docker/login-action@v3 |
| 159 | + token_format: access_token |
| 160 | + workload_identity_provider: >- |
| 161 | + ${{ inputs.push_to_prod_registry |
| 162 | + && format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.PROD_GKE_PROJECT_ID) |
| 163 | + || format('projects/{0}/locations/global/workloadIdentityPools/github/providers/github', secrets.DEV_GKE_PROJECT_ID) }} |
| 164 | + service_account: >- |
| 165 | + ${{ inputs.push_to_prod_registry && secrets.PROD_GKE_SA || secrets.DEV_GKE_SA }} |
| 166 | +
|
| 167 | + - uses: docker/login-action@v3 |
194 | 168 | with: |
195 | 169 | registry: us-east1-docker.pkg.dev |
196 | 170 | username: oauth2accesstoken |
197 | | - password: '${{ steps.auth.outputs.access_token }}' |
198 | | - - name: Get current date |
199 | | - id: date |
200 | | - run: echo "date=$(date +'%Y%m%d%H%M')" >> $GITHUB_OUTPUT |
201 | | - - name: Set registry path |
202 | | - id: registry |
203 | | - run: | |
204 | | - if [ "${{ inputs.push_to_prod_registry }}" = "true" ]; then |
205 | | - echo "project=${{ secrets.PROD_PROJECT }}" >> $GITHUB_OUTPUT |
206 | | - else |
207 | | - echo "project=${{ secrets.DEV_PROJECT }}" >> $GITHUB_OUTPUT |
208 | | - fi |
209 | | - - name: Generate image metadata |
210 | | - id: meta |
| 171 | + password: ${{ steps.auth.outputs.access_token }} |
| 172 | + |
| 173 | + - id: meta |
211 | 174 | uses: docker/metadata-action@v3 |
212 | 175 | with: |
213 | | - images: | |
214 | | - us-east1-docker.pkg.dev/${{ steps.registry.outputs.project }}/containers/sefaria-asset-${{ inputs.branch_name }} |
| 176 | + images: ${{ needs.calculate.outputs.asset_image }} |
215 | 177 | tags: | |
| 178 | + type=raw,value=${{ inputs.image_tag }}-${{ needs.calculate.outputs.datetime }} |
216 | 179 | type=raw,value=${{ inputs.image_tag }} |
217 | | - type=raw,value=${{ inputs.image_tag }}-${{ steps.date.outputs.date }} |
218 | 180 | type=raw,value=latest |
219 | | - flavor: | |
220 | | - latest=false |
221 | | - - name: Build and push |
222 | | - id: build |
| 181 | + flavor: latest=false |
| 182 | + |
| 183 | + ########################################################################### |
| 184 | + # BUILD ASSET (uses full web digest reference) |
| 185 | + ########################################################################### |
| 186 | + - id: build |
223 | 187 | uses: docker/build-push-action@v6 |
224 | 188 | with: |
225 | 189 | context: . |
| 190 | + file: ./build/asset/Dockerfile |
226 | 191 | push: true |
227 | 192 | build-args: | |
228 | | - SRC_IMG=us-east1-docker.pkg.dev/${{ steps.registry.outputs.project }}/containers/sefaria-web-${{ inputs.branch_name }}:${{ inputs.image_tag }} |
229 | | - file: ./build/asset/Dockerfile |
| 193 | + SRC_IMG=${{ needs.calculate.outputs.web_image }}:${{ inputs.image_tag }} |
230 | 194 | tags: ${{ steps.meta.outputs.tags }} |
231 | 195 | labels: ${{ steps.meta.outputs.labels }} |
232 | | - - name: Output asset info |
233 | | - id: output |
234 | | - run: | |
235 | | - echo "image=us-east1-docker.pkg.dev/${{ steps.registry.outputs.project }}/containers/sefaria-asset-${{ inputs.branch_name }}:${{ inputs.image_tag }}" >> $GITHUB_OUTPUT |
236 | 196 |
|
237 | | - # Summary job |
238 | | - summary: |
239 | | - name: Build Summary |
240 | | - runs-on: ubuntu-latest |
241 | | - needs: [build-web, build-node, build-asset] |
242 | | - if: always() |
243 | | - steps: |
244 | | - - name: Output build summary |
245 | | - run: | |
246 | | - echo "### Docker Images Built and Pushed :whale:" >> $GITHUB_STEP_SUMMARY |
247 | | - echo "" >> $GITHUB_STEP_SUMMARY |
248 | | - echo "**Environment:** ${{ inputs.environment }}" >> $GITHUB_STEP_SUMMARY |
249 | | - echo "**Branch Name:** ${{ inputs.branch_name }}" >> $GITHUB_STEP_SUMMARY |
250 | | - echo "**Tag:** ${{ inputs.image_tag }}" >> $GITHUB_STEP_SUMMARY |
251 | | - echo "" >> $GITHUB_STEP_SUMMARY |
252 | | - echo "| Image | Status | Digest |" >> $GITHUB_STEP_SUMMARY |
253 | | - echo "|-------|--------|--------|" >> $GITHUB_STEP_SUMMARY |
254 | | - echo "| sefaria-web | ${{ needs.build-web.result }} | \`${{ needs.build-web.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY |
255 | | - echo "| sefaria-node | ${{ needs.build-node.result }} | \`${{ needs.build-node.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY |
256 | | - echo "| sefaria-asset | ${{ needs.build-asset.result }} | \`${{ needs.build-asset.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY |
0 commit comments