Skip to content

Add flow-cytometry-shiny app example #13

Add flow-cytometry-shiny app example

Add flow-cytometry-shiny app example #13

Workflow file for this run

name: Build PR Images
on:
pull_request:
paths:
- '**/Dockerfile'
env:
REGISTRY: ghcr.io
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Detect changed Dockerfiles
id: changes
uses: dorny/paths-filter@v3
with:
filters: |
ttyd:
- 'ttyd/**'
streamlit:
- 'streamlit/**'
cellxgene:
- 'cellxgene/**'
shiny:
- 'shiny-simple-example/**'
marimo:
- 'marimo/**'
- name: Set matrix
id: set-matrix
run: |
CONTAINERS="[]"
if [[ "${{ steps.changes.outputs.ttyd }}" == "true" ]]; then
CONTAINERS=$(echo "$CONTAINERS" | jq -c '. + [{"name":"ttyd","path":"./ttyd"}]')
fi
if [[ "${{ steps.changes.outputs.streamlit }}" == "true" ]]; then
CONTAINERS=$(echo "$CONTAINERS" | jq -c '. + [{"name":"streamlit","path":"./streamlit"}]')
fi
if [[ "${{ steps.changes.outputs.cellxgene }}" == "true" ]]; then
CONTAINERS=$(echo "$CONTAINERS" | jq -c '. + [{"name":"cellxgene","path":"./cellxgene"}]')
fi
if [[ "${{ steps.changes.outputs.shiny }}" == "true" ]]; then
CONTAINERS=$(echo "$CONTAINERS" | jq -c '. + [{"name":"shiny","path":"./shiny-simple-example"}]')
fi
if [[ "${{ steps.changes.outputs.marimo }}" == "true" ]]; then
CONTAINERS=$(echo "$CONTAINERS" | jq -c '. + [{"name":"marimo","path":"./marimo"}]')
fi
echo "matrix={\"container\":$CONTAINERS}" >> "$GITHUB_OUTPUT"
build:
runs-on: ubuntu-latest
needs: detect-changes
if: ${{ fromJson(needs.detect-changes.outputs.matrix).container[0] != null }}
permissions:
contents: read
packages: write
pull-requests: write
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}/development
tags: |
type=ref,event=pr,prefix=${{ matrix.container.name }}-pr
- name: Build and push ${{ matrix.container.name }}
uses: docker/build-push-action@v5
with:
context: ${{ matrix.container.path }}
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.container.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.container.name }}
- name: Get image reference for scan
id: scanref
run: |
echo "ref=${{ fromJSON(steps.meta.outputs.json).tags[0] }}" >> $GITHUB_OUTPUT
- name: Run security scan
uses: aquasecurity/trivy-action@0.31.0
with:
image-ref: ${{ steps.scanref.outputs.ref }}
format: "table"
output: "trivy-results.txt"
severity: "CRITICAL,HIGH,MEDIUM"
exit-code: "0"
- name: Display security scan results
if: always()
run: |
echo "## 🔒 Security Scan Results - ${{ matrix.container.name }}" >> $GITHUB_STEP_SUMMARY
if [ -f trivy-results.txt ]; then
echo '```' >> $GITHUB_STEP_SUMMARY
cat trivy-results.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
else
echo "No security scan results found" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload security scan results
uses: actions/upload-artifact@v4
if: always()
with:
name: trivy-scan-${{ matrix.container.name }}-${{ github.run_id }}
path: trivy-results.txt
retention-days: 30
- name: Generate build summary
if: always()
run: |
echo "## 🐳 Build Summary - ${{ matrix.container.name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: \`${{ env.REGISTRY }}/${{ github.repository }}/development\`" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: \`${{ matrix.container.name }}-pr${{ github.event.pull_request.number }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: ${{ github.head_ref }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const imageName = '${{ matrix.container.name }}';
const imageTag = `${{ env.REGISTRY }}/${{ github.repository }}/development:${imageName}-pr${{ github.event.pull_request.number }}`;
const commentId = `build-results-${imageName}`;
let securityStatus = '✅ Security scan completed';
try {
if (fs.existsSync('trivy-results.txt')) {
const results = fs.readFileSync('trivy-results.txt', 'utf8');
const lines = results.split('\n');
const criticalCount = lines.filter(line => line.includes('CRITICAL')).length;
const highCount = lines.filter(line => line.includes('HIGH')).length;
const mediumCount = lines.filter(line => line.includes('MEDIUM')).length;
if (criticalCount > 0 || highCount > 0) {
securityStatus = `⚠️ Found ${criticalCount} critical, ${highCount} high, ${mediumCount} medium vulnerabilities`;
} else if (mediumCount > 0) {
securityStatus = `✅ ${mediumCount} medium vulnerabilities found`;
} else {
securityStatus = '✅ No vulnerabilities found';
}
}
} catch (e) {
securityStatus = '⚠️ Security scan results could not be parsed';
}
const output = `## 🐳 Docker Build Results - ${imageName}
✅ Docker image built successfully
| Property | Value |
|----------|-------|
| **Image** | \`${imageTag}\` |
| **Branch** | ${{ github.head_ref }} |
| **Commit** | \`${{ github.sha }}\` |
| **Security** | ${securityStatus} |
\`\`\`bash
docker pull ${imageTag}
\`\`\`
📁 Download the full security scan report from the workflow artifacts.
<!-- ${commentId} -->`;
const comments = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
const existingComment = comments.data.find(comment =>
comment.body.includes(`<!-- ${commentId} -->`)
);
if (existingComment) {
await github.rest.issues.updateComment({
comment_id: existingComment.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
} else {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
}