refactor: deduplicate CI workflows and fix double triggers#141
refactor: deduplicate CI workflows and fix double triggers#141
Conversation
Extract reusable workflows (_build-backend.yml, _build-ext.yml) to eliminate duplicated build/deploy logic across dev/stg/prd. Remove `push: tags: v*` trigger from stg/prd backend workflows to prevent double builds (bump-version already dispatches via repository_dispatch). Delete the no-op build-ext-dev.yml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When mongo.in_cluster is true, PD_MONGO_URI was not set in the configmap, causing the Go app to fall back to localhost:27017 which doesn't work inside the cluster. Now uses the FQDN mongo.<namespace>.svc.cluster.local with the replicaSet parameter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the CI/CD GitHub Actions setup by extracting duplicated backend/extension build logic into reusable workflows, and adjusts triggers to prevent duplicate backend builds for stg/prd. It also updates the Helm chart to set PD_MONGO_URI based on whether MongoDB runs in-cluster.
Changes:
- Add reusable workflows (
_build-backend.yml,_build-ext.yml) and switch env-specific workflows to call them. - Remove
push: tags: v*triggers from stg/prd backend workflows to avoid double builds (rely onrepository_dispatch). - Update Helm template to always set
PD_MONGO_URI, including an in-cluster MongoDB URI.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| helm-chart/templates/paperdebugger.yaml | Sets PD_MONGO_URI based on mongo.in_cluster (in-cluster vs external). |
| .github/workflows/_build-backend.yml | New reusable workflow for backend build + deploy across dev/stg/prd. |
| .github/workflows/_build-ext.yml | New reusable workflow for Chrome extension build + upload. |
| .github/workflows/build-backend-dev.yml | Refactored to call _build-backend.yml for dev. |
| .github/workflows/build-backend-stg.yml | Refactored to call _build-backend.yml for stg; tag push trigger removed. |
| .github/workflows/build-backend-prd.yml | Refactored to call _build-backend.yml for prd; tag push trigger removed. |
| .github/workflows/build-ext-stg.yml | Refactored to call _build-ext.yml for stg. |
| .github/workflows/build-ext-prd.yml | Refactored to call _build-ext.yml for prd. |
| .github/workflows/build-ext-dev.yml | Deleted (previously a no-op “unsupported” workflow). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {{- if .Values.mongo.in_cluster }} | ||
| PD_MONGO_URI: "mongodb://mongo.{{ .Values.namespace }}.svc.cluster.local:27017/?replicaSet=in-cluster" | ||
| {{- else }} | ||
| PD_MONGO_URI: "{{ .Values.mongo.uri }}" | ||
| {{ end }} No newline at end of file | ||
| {{- end }} No newline at end of file |
There was a problem hiding this comment.
This Helm chart change (Mongo URI selection) is not mentioned in the PR description, which focuses on CI workflow deduplication/double triggers. Please confirm this is intended scope (or split it into a separate PR) so the deployment/CI refactor can be reviewed and rolled out independently.
| JWT_SIGNING_KEY: "{{ .Values.jwt_signing_key }}" | ||
| {{ if not .Values.mongo.in_cluster }} | ||
| {{- if .Values.mongo.in_cluster }} | ||
| PD_MONGO_URI: "mongodb://mongo.{{ .Values.namespace }}.svc.cluster.local:27017/?replicaSet=in-cluster" |
There was a problem hiding this comment.
PD_MONGO_URI hardcodes the Kubernetes cluster DNS suffix as svc.cluster.local. That suffix is configurable on many clusters, so this may break name resolution outside the default cluster domain. Prefer using the service DNS name (mongo:27017 or mongo.<namespace>.svc) or make the cluster domain configurable via values.
| PD_MONGO_URI: "mongodb://mongo.{{ .Values.namespace }}.svc.cluster.local:27017/?replicaSet=in-cluster" | |
| PD_MONGO_URI: "mongodb://mongo.{{ .Values.namespace }}.svc.{{ .Values.clusterDomain | default \"cluster.local\" }}:27017/?replicaSet=in-cluster" |
| - name: Generate kubernetes manifests | ||
| env: | ||
| OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.OPENAI_BASE_URL_STG || secrets.OPENAI_BASE_URL_PRD }} | ||
| OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.OPENAI_API_KEY_STG || secrets.OPENAI_API_KEY_PRD }} | ||
| INFERENCE_BASE_URL: ${{ inputs.environment == 'dev' && secrets.INFERENCE_BASE_URL_DEV || inputs.environment == 'stg' && secrets.INFERENCE_BASE_URL_STG || secrets.INFERENCE_BASE_URL_PRD }} | ||
| INFERENCE_API_KEY: ${{ inputs.environment == 'dev' && secrets.INFERENCE_API_KEY_DEV || inputs.environment == 'stg' && secrets.INFERENCE_API_KEY_STG || secrets.INFERENCE_API_KEY_PRD }} | ||
| MCP_BASIC_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_BASIC_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_BASIC_KEY_STG || secrets.MCP_BASIC_KEY_PRD }} | ||
| MCP_PAPERSCORE_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_PAPERSCORE_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_PAPERSCORE_KEY_STG || secrets.MCP_PAPERSCORE_KEY_PRD }} | ||
| XTRAMCP_OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_BASE_URL_STG || secrets.XTRAMCP_OPENAI_BASE_URL_PRD }} | ||
| XTRAMCP_OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_API_KEY_STG || secrets.XTRAMCP_OPENAI_API_KEY_PRD }} | ||
| XTRAMCP_OPENREVIEW_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_STG || secrets.XTRAMCP_OPENREVIEW_BASE_URL_PRD }} | ||
| XTRAMCP_OPENREVIEW_USERNAME: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_USERNAME_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_USERNAME_STG || secrets.XTRAMCP_OPENREVIEW_USERNAME_PRD }} | ||
| XTRAMCP_OPENREVIEW_PASSWORD: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_STG || secrets.XTRAMCP_OPENREVIEW_PASSWORD_PRD }} | ||
| XTRAMCP_CROSSREF_EMAIL_ADDRESS: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG || secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD }} |
There was a problem hiding this comment.
The reusable backend workflow selects secrets via dev || stg || prd short-circuit expressions; any unexpected inputs.environment value will silently fall back to PRD secrets. Add an explicit validation/guard (fail fast) or an explicit == 'prd' branch to avoid accidental PRD deployments with the wrong environment input.
| - name: Generate kubernetes manifests | |
| env: | |
| OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.OPENAI_BASE_URL_STG || secrets.OPENAI_BASE_URL_PRD }} | |
| OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.OPENAI_API_KEY_STG || secrets.OPENAI_API_KEY_PRD }} | |
| INFERENCE_BASE_URL: ${{ inputs.environment == 'dev' && secrets.INFERENCE_BASE_URL_DEV || inputs.environment == 'stg' && secrets.INFERENCE_BASE_URL_STG || secrets.INFERENCE_BASE_URL_PRD }} | |
| INFERENCE_API_KEY: ${{ inputs.environment == 'dev' && secrets.INFERENCE_API_KEY_DEV || inputs.environment == 'stg' && secrets.INFERENCE_API_KEY_STG || secrets.INFERENCE_API_KEY_PRD }} | |
| MCP_BASIC_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_BASIC_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_BASIC_KEY_STG || secrets.MCP_BASIC_KEY_PRD }} | |
| MCP_PAPERSCORE_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_PAPERSCORE_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_PAPERSCORE_KEY_STG || secrets.MCP_PAPERSCORE_KEY_PRD }} | |
| XTRAMCP_OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_BASE_URL_STG || secrets.XTRAMCP_OPENAI_BASE_URL_PRD }} | |
| XTRAMCP_OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_API_KEY_STG || secrets.XTRAMCP_OPENAI_API_KEY_PRD }} | |
| XTRAMCP_OPENREVIEW_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_STG || secrets.XTRAMCP_OPENREVIEW_BASE_URL_PRD }} | |
| XTRAMCP_OPENREVIEW_USERNAME: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_USERNAME_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_USERNAME_STG || secrets.XTRAMCP_OPENREVIEW_USERNAME_PRD }} | |
| XTRAMCP_OPENREVIEW_PASSWORD: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_STG || secrets.XTRAMCP_OPENREVIEW_PASSWORD_PRD }} | |
| XTRAMCP_CROSSREF_EMAIL_ADDRESS: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG || secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD }} | |
| - name: Validate environment input | |
| run: | | |
| if [ "${{ inputs.environment }}" != "dev" ] && [ "${{ inputs.environment }}" != "stg" ] && [ "${{ inputs.environment }}" != "prd" ]; then | |
| echo "Invalid environment: ${{ inputs.environment }}. Must be one of: dev, stg, prd." | |
| exit 1 | |
| fi | |
| - name: Generate kubernetes manifests | |
| env: | |
| OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.OPENAI_BASE_URL_STG || inputs.environment == 'prd' && secrets.OPENAI_BASE_URL_PRD }} | |
| OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.OPENAI_API_KEY_STG || inputs.environment == 'prd' && secrets.OPENAI_API_KEY_PRD }} | |
| INFERENCE_BASE_URL: ${{ inputs.environment == 'dev' && secrets.INFERENCE_BASE_URL_DEV || inputs.environment == 'stg' && secrets.INFERENCE_BASE_URL_STG || inputs.environment == 'prd' && secrets.INFERENCE_BASE_URL_PRD }} | |
| INFERENCE_API_KEY: ${{ inputs.environment == 'dev' && secrets.INFERENCE_API_KEY_DEV || inputs.environment == 'stg' && secrets.INFERENCE_API_KEY_STG || inputs.environment == 'prd' && secrets.INFERENCE_API_KEY_PRD }} | |
| MCP_BASIC_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_BASIC_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_BASIC_KEY_STG || inputs.environment == 'prd' && secrets.MCP_BASIC_KEY_PRD }} | |
| MCP_PAPERSCORE_KEY: ${{ inputs.environment == 'dev' && secrets.MCP_PAPERSCORE_KEY_DEV || inputs.environment == 'stg' && secrets.MCP_PAPERSCORE_KEY_STG || inputs.environment == 'prd' && secrets.MCP_PAPERSCORE_KEY_PRD }} | |
| XTRAMCP_OPENAI_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_BASE_URL_STG || inputs.environment == 'prd' && secrets.XTRAMCP_OPENAI_BASE_URL_PRD }} | |
| XTRAMCP_OPENAI_API_KEY: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENAI_API_KEY_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENAI_API_KEY_STG || inputs.environment == 'prd' && secrets.XTRAMCP_OPENAI_API_KEY_PRD }} | |
| XTRAMCP_OPENREVIEW_BASE_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_STG || inputs.environment == 'prd' && secrets.XTRAMCP_OPENREVIEW_BASE_URL_PRD }} | |
| XTRAMCP_OPENREVIEW_USERNAME: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_USERNAME_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_USERNAME_STG || inputs.environment == 'prd' && secrets.XTRAMCP_OPENREVIEW_USERNAME_PRD }} | |
| XTRAMCP_OPENREVIEW_PASSWORD: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_STG || inputs.environment == 'prd' && secrets.XTRAMCP_OPENREVIEW_PASSWORD_PRD }} | |
| XTRAMCP_CROSSREF_EMAIL_ADDRESS: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG || inputs.environment == 'prd' && secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD }} |
| zip -r dist.zip dist/* | ||
|
|
||
| - name: Upload to Chrome Web Store (upload only) | ||
| uses: mobilefirstllc/cws-publish@latest |
There was a problem hiding this comment.
mobilefirstllc/cws-publish@latest is not pinned, so the workflow can change behavior (or be compromised) without a code change. Pin to a specific release tag or commit SHA for supply-chain safety and reproducibility.
| uses: mobilefirstllc/cws-publish@latest | |
| uses: mobilefirstllc/cws-publish@v1 |
Extract reusable workflows (_build-backend.yml, _build-ext.yml) to
eliminate duplicated build/deploy logic across dev/stg/prd. Remove
push: tags: v*trigger from stg/prd backend workflows to preventdouble builds (bump-version already dispatches via repository_dispatch).
Delete the no-op build-ext-dev.yml.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com