@@ -2,7 +2,6 @@ name: CI/CD
22
33on :
44 pull_request :
5- pull_request_target :
65 types : [opened, synchronize, reopened]
76 release :
87 types : [created, published, edited]
1514 lint :
1615 name : Lint
1716 runs-on : ubuntu-latest
18- # Only run on pull_request, not pull_request_target
19- if : github.event_name != 'pull_request_target'
2017 steps :
2118 - uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2219 - uses : pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
6461 ci :
6562 name : Integration
6663 runs-on : ubuntu-latest
67- if : github.event_name != 'pull_request_target'
6864 steps :
6965 - uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
7066 - uses : pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
@@ -77,190 +73,10 @@ jobs:
7773 - name : Run integration tests
7874 run : pnpm run test
7975
80- pr-preview :
81- name : PR Preview
82- # Only run for pull_request_target or if author is a member of the org
83- if : github.event_name == 'pull_request_target' || contains(github.event.pull_request.author_association, 'MEMBER')
84- needs : [ci, lint]
85- runs-on : ubuntu-latest
86- permissions :
87- contents : read
88- packages : write
89- pull-requests : write
90- steps :
91- # SECURITY: Checkout the base ref (trusted code) for the workflow
92- # This prevents untrusted PR code from accessing secrets
93- - name : Checkout base (trusted)
94- uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
95- with :
96- ref : ${{ github.event.pull_request.base.sha }}
97-
98- - name : Get PR head SHA
99- id : sha
100- run : echo "sha=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
101-
102- - name : Get Docker tag
103- id : docker-tag
104- run : echo "tag=pr-${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
105-
106- - name : Get package version
107- id : package
108- run : |
109- VERSION=$(jq -r '
110- .version |
111- if test("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$")
112- then .
113- else error("Invalid semver format in package.json")
114- end
115- ' package.json 2>&1)
116-
117- if [ $? -ne 0 ]; then
118- echo "::error::$VERSION" # Expand the error into an annotation
119- exit 1
120- fi
121-
122- echo "version=$VERSION" >> $GITHUB_OUTPUT
123-
124- - name : Validate inputs (security check)
125- run : |
126- # Ensure tag matches expected pattern
127- if [[ ! "${{ steps.docker-tag.outputs.tag }}" =~ ^pr-[0-9]+$ ]]; then
128- echo "::error::Security violation: Invalid PR tag pattern"
129- exit 1
130- fi
131- # Ensure version is a valid semver
132- if ! echo "${{ steps.package.outputs.version }}" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then
133- echo "::error::Security violation: Invalid version format"
134- exit 1
135- fi
136-
137- - name : Collect Docker labels & tags
138- id : docker-labels-tags
139- run : |
140- echo 'labels<<__LABELS_EOF__' >> $GITHUB_OUTPUT
141- echo "org.opencontainers.image.title=47ng/actions-clever-cloud" >> $GITHUB_OUTPUT
142- echo "org.opencontainers.image.description=GitHub action to deploy to Clever Cloud (PR preview)" >> $GITHUB_OUTPUT
143- echo "org.opencontainers.image.version=${{ steps.package.outputs.version }}-pr.${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
144- echo "org.opencontainers.image.revision=${{ steps.sha.outputs.sha }}" >> $GITHUB_OUTPUT
145- echo "org.opencontainers.image.licenses=MIT" >> $GITHUB_OUTPUT
146- echo "org.opencontainers.image.source=https://github.com/${{github.repository}}/tree/${{ steps.sha.outputs.sha }}" >> $GITHUB_OUTPUT
147- echo "org.opencontainers.image.documentation=https://github.com/${{github.repository}}/pull/${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
148- echo "org.opencontainers.image.url=https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}" >> $GITHUB_OUTPUT
149- echo '__LABELS_EOF__' >> $GITHUB_OUTPUT
150- echo 'tags<<__TAGS_EOF__' >> $GITHUB_OUTPUT
151- echo "ghcr.io/47ng/actions-clever-cloud:${{ steps.docker-tag.outputs.tag }}" >> $GITHUB_OUTPUT
152- echo "ghcr.io/47ng/actions-clever-cloud:git-${{ steps.sha.outputs.sha }}" >> $GITHUB_OUTPUT
153- echo '__TAGS_EOF__' >> $GITHUB_OUTPUT
154-
155- - name : Set up QEMU
156- uses : docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
157-
158- - name : Set up Docker Buildx
159- uses : docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
160-
161- - name : Login to GitHub Container Registry
162- uses : docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
163- with :
164- registry : ghcr.io
165- username : ${{ github.repository_owner }}
166- password : ${{ secrets.GITHUB_TOKEN }}
167- # SECURITY: Checkout the PR head for building
168- # We do this AFTER extracting all metadata to prevent tampering
169- - name : Checkout PR head (untrusted code)
170- uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
171- with :
172- ref : ${{ steps.sha.outputs.sha }}
173- - name : Build and push
174- id : docker-build-push
175- uses : docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
176- with :
177- context : .
178- labels : " ${{ steps.docker-labels-tags.outputs.labels }}"
179- tags : " ${{ steps.docker-labels-tags.outputs.tags }}"
180- push : true
181- - name : Generate step summary
182- run : |
183- echo "## 🐳 Docker image" >> $GITHUB_STEP_SUMMARY
184- echo "Digest: \`${{ steps.docker-build-push.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY
185- echo "### 📌 Tags" >> $GITHUB_STEP_SUMMARY
186- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
187- echo "${{ steps.docker-labels-tags.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
188- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
189- echo "### 🏷 Labels" >> $GITHUB_STEP_SUMMARY
190- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
191- echo "${{ steps.docker-labels-tags.outputs.labels }}" >> $GITHUB_STEP_SUMMARY
192- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
193- - name : Comment on PR
194- uses : actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
195- with :
196- script : |
197- const body = `<!-- docker-build-comment -->
198- ## 🐳 Docker image preview
199-
200- Preview this PR in your workflow:
201-
202- \`\`\`yaml
203- # Use the PR number to follow changes in this PR
204- - uses: docker://ghcr.io/47ng/actions-clever-cloud:${{ steps.docker-tag.outputs.tag }}
205-
206- # Use the git SHA for pinning to a specific commit
207- - uses: docker://ghcr.io/47ng/actions-clever-cloud:git-${{ steps.sha.outputs.sha }}
208- \`\`\`
209-
210- <details>
211- <summary>Image metadata</summary>
212- <br/>
213-
214- Digest: \`${{ steps.docker-build-push.outputs.digest }}\`
215-
216- ### 📌 Tags
217- \`\`\`
218- ${{ steps.docker-labels-tags.outputs.tags }}
219- \`\`\`
220-
221- ### 🏷 Labels
222- \`\`\`
223- ${{ steps.docker-labels-tags.outputs.labels }}
224- \`\`\`
225-
226- </details>
227-
228- ---
229- <sub>🤖 This comment is updated on every push</sub>`;
230-
231- // Find existing comment by marker
232- const { data: comments } = await github.rest.issues.listComments({
233- owner: context.repo.owner,
234- repo: context.repo.repo,
235- issue_number: context.issue.number,
236- });
237-
238- const botComment = comments.find(comment =>
239- comment.body.startsWith('<!-- docker-build-comment -->')
240- );
241-
242- if (botComment) {
243- // Update existing comment
244- await github.rest.issues.updateComment({
245- owner: context.repo.owner,
246- repo: context.repo.repo,
247- comment_id: botComment.id,
248- body: body
249- });
250- } else {
251- // Create new comment
252- await github.rest.issues.createComment({
253- owner: context.repo.owner,
254- repo: context.repo.repo,
255- issue_number: context.issue.number,
256- body: body
257- });
258- }
259-
26076 cd :
26177 name : Deployment
26278 # Only run for push events (master, releases) and releases
263- if : github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
79+ if : github.event_name != 'pull_request'
26480 needs : [ci, lint]
26581 runs-on : ubuntu-latest
26682 steps :
0 commit comments