Skip to content

refactor: deduplicate CI workflows and fix double triggers#138

Merged
Junyi-99 merged 1 commit intostagingfrom
refactor/deduplicate-ci-workflows
Mar 28, 2026
Merged

refactor: deduplicate CI workflows and fix double triggers#138
Junyi-99 merged 1 commit intostagingfrom
refactor/deduplicate-ci-workflows

Conversation

@Junyi-99
Copy link
Copy Markdown
Member

Summary

  • 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). Also fixes a bug where a staging tag push could accidentally trigger a production build
  • Delete the no-op build-ext-dev.yml

8 files changed, net -177 lines.

Test plan

  • Verify staging dispatch triggers build-backend-stg and build-ext-stg correctly
  • Verify production dispatch triggers build-backend-prd and build-ext-prd correctly
  • Verify push to development branch triggers build-backend-dev correctly
  • Confirm no double builds on tag push

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings March 28, 2026 06:29
@Junyi-99 Junyi-99 merged commit 041d463 into staging Mar 28, 2026
@Junyi-99 Junyi-99 deleted the refactor/deduplicate-ci-workflows branch March 28, 2026 06:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors GitHub Actions CI/CD by extracting duplicated backend and Chrome extension build/deploy logic into reusable workflows, and adjusts triggers to avoid double builds on tag pushes (relying on repository_dispatch from bump-version.yml for stg/prd).

Changes:

  • Added reusable workflows: _build-backend.yml and _build-ext.yml.
  • Updated dev/stg/prd backend workflows and stg/prd extension workflows to call the reusable workflows.
  • Removed push: tags: v* triggers from stg/prd backend workflows and deleted the unused build-ext-dev.yml.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
.github/workflows/_build-backend.yml New reusable workflow that builds/pushes backend image and deploys manifests for dev/stg/prd.
.github/workflows/_build-ext.yml New reusable workflow that builds and uploads the Chrome extension for stg/prd.
.github/workflows/build-backend-dev.yml Refactored to call _build-backend.yml for dev branch pushes.
.github/workflows/build-backend-stg.yml Refactored to call _build-backend.yml for repository_dispatch (stg).
.github/workflows/build-backend-prd.yml Refactored to call _build-backend.yml for repository_dispatch (prd).
.github/workflows/build-ext-stg.yml Refactored to call _build-ext.yml for repository_dispatch (stg).
.github/workflows/build-ext-prd.yml Refactored to call _build-ext.yml for repository_dispatch (prd).
.github/workflows/build-ext-dev.yml Deleted no-op dev extension workflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

zip_file: 'dist.zip'
build-and-publish:
uses: ./.github/workflows/_build-ext.yml
secrets: inherit
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using secrets: inherit passes all repository/environment secrets into the called workflow, which is broader than needed for the extension build/publish. After declaring required secrets in _build-ext.yml, pass only the needed secrets explicitly from this caller.

Suggested change
secrets: inherit
secrets:
CHROME_WEBSTORE_CLIENT_ID: ${{ secrets.CHROME_WEBSTORE_CLIENT_ID }}
CHROME_WEBSTORE_CLIENT_SECRET: ${{ secrets.CHROME_WEBSTORE_CLIENT_SECRET }}
CHROME_WEBSTORE_REFRESH_TOKEN: ${{ secrets.CHROME_WEBSTORE_REFRESH_TOKEN }}

Copilot uses AI. Check for mistakes.
type: string # staging, main
extension_id:
required: true
type: string
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_build-ext.yml is a reusable workflow (workflow_call) but it doesn’t declare any on.workflow_call.secrets. In reusable workflows, only declared secrets are made available to the secrets.* context, so the Chrome Web Store and Grafana secrets referenced later may be empty at runtime. Declare the required secrets under on.workflow_call.secrets (and consider passing only those from callers instead of secrets: inherit).

Suggested change
type: string
type: string
secrets:
GRAFANA_API_KEY:
required: true
CHROME_EXT_CLIENT_ID:
required: true
CHROME_EXT_CLIENT_SECRET:
required: true
CHROME_EXT_REFRESH_TOKEN:
required: true

Copilot uses AI. Check for mistakes.
type: string # development, staging, main
version_tag:
required: true
type: string # e.g. "development" or "v2.1.3"
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_build-backend.yml is a reusable workflow (workflow_call) but it doesn’t declare any on.workflow_call.secrets. Secrets like GH_PAT and the environment-specific *_DEV/_STG/_PRD values referenced in env: will not be available unless they’re declared for workflow_call. Add a secrets: block under on.workflow_call listing all secrets this workflow reads.

Suggested change
type: string # e.g. "development" or "v2.1.3"
type: string # e.g. "development" or "v2.1.3"
secrets:
GH_PAT:
required: true

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +85
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 }}
XTRAMCP_DOI_EMAIL_ADDRESS: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_DOI_EMAIL_ADDRESS_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_DOI_EMAIL_ADDRESS_STG || secrets.XTRAMCP_DOI_EMAIL_ADDRESS_PRD }}
XTRAMCP_ACL_METADATA_DB_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_ACL_METADATA_DB_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_ACL_METADATA_DB_URL_STG || secrets.XTRAMCP_ACL_METADATA_DB_URL_PRD }}
XTRAMCP_ARXIV_METADATA_DB_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_ARXIV_METADATA_DB_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_ARXIV_METADATA_DB_URL_STG || secrets.XTRAMCP_ARXIV_METADATA_DB_URL_PRD }}
XTRAMCP_MONGO_URI: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_MONGO_URI_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_MONGO_URI_STG || secrets.XTRAMCP_MONGO_URI_PRD }}
MONGO_URI: ${{ inputs.environment == 'dev' && secrets.MONGO_URI_DEV || inputs.environment == 'stg' && secrets.MONGO_URI_STG || secrets.MONGO_URI_PRD }}
GHCR_DOCKER_CONFIG: ${{ inputs.environment == 'dev' && secrets.GHCR_DOCKER_CONFIG_DEV || inputs.environment == 'stg' && secrets.GHCR_DOCKER_CONFIG_STG || secrets.GHCR_DOCKER_CONFIG_PRD }}
CLOUDFLARE_TUNNEL_TOKEN: ${{ inputs.environment == 'dev' && secrets.CLOUDFLARE_TUNNEL_TOKEN_DEV || inputs.environment == 'stg' && secrets.CLOUDFLARE_TUNNEL_TOKEN_STG || secrets.CLOUDFLARE_TUNNEL_TOKEN_PRD }}
run: |
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chained &&/|| expressions selecting secrets will silently fall back to the PRD secret when inputs.environment isn’t exactly dev or stg (and can also fall through if a selected secret is an empty string). Add an explicit validation/branching step (e.g., a shell case that sets env vars and errors on unknown values) so a typo can’t accidentally select production configuration.

Suggested change
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 }}
XTRAMCP_DOI_EMAIL_ADDRESS: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_DOI_EMAIL_ADDRESS_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_DOI_EMAIL_ADDRESS_STG || secrets.XTRAMCP_DOI_EMAIL_ADDRESS_PRD }}
XTRAMCP_ACL_METADATA_DB_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_ACL_METADATA_DB_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_ACL_METADATA_DB_URL_STG || secrets.XTRAMCP_ACL_METADATA_DB_URL_PRD }}
XTRAMCP_ARXIV_METADATA_DB_URL: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_ARXIV_METADATA_DB_URL_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_ARXIV_METADATA_DB_URL_STG || secrets.XTRAMCP_ARXIV_METADATA_DB_URL_PRD }}
XTRAMCP_MONGO_URI: ${{ inputs.environment == 'dev' && secrets.XTRAMCP_MONGO_URI_DEV || inputs.environment == 'stg' && secrets.XTRAMCP_MONGO_URI_STG || secrets.XTRAMCP_MONGO_URI_PRD }}
MONGO_URI: ${{ inputs.environment == 'dev' && secrets.MONGO_URI_DEV || inputs.environment == 'stg' && secrets.MONGO_URI_STG || secrets.MONGO_URI_PRD }}
GHCR_DOCKER_CONFIG: ${{ inputs.environment == 'dev' && secrets.GHCR_DOCKER_CONFIG_DEV || inputs.environment == 'stg' && secrets.GHCR_DOCKER_CONFIG_STG || secrets.GHCR_DOCKER_CONFIG_PRD }}
CLOUDFLARE_TUNNEL_TOKEN: ${{ inputs.environment == 'dev' && secrets.CLOUDFLARE_TUNNEL_TOKEN_DEV || inputs.environment == 'stg' && secrets.CLOUDFLARE_TUNNEL_TOKEN_STG || secrets.CLOUDFLARE_TUNNEL_TOKEN_PRD }}
run: |
OPENAI_BASE_URL_DEV: ${{ secrets.OPENAI_BASE_URL_DEV }}
OPENAI_BASE_URL_STG: ${{ secrets.OPENAI_BASE_URL_STG }}
OPENAI_BASE_URL_PRD: ${{ secrets.OPENAI_BASE_URL_PRD }}
OPENAI_API_KEY_DEV: ${{ secrets.OPENAI_API_KEY_DEV }}
OPENAI_API_KEY_STG: ${{ secrets.OPENAI_API_KEY_STG }}
OPENAI_API_KEY_PRD: ${{ secrets.OPENAI_API_KEY_PRD }}
INFERENCE_BASE_URL_DEV: ${{ secrets.INFERENCE_BASE_URL_DEV }}
INFERENCE_BASE_URL_STG: ${{ secrets.INFERENCE_BASE_URL_STG }}
INFERENCE_BASE_URL_PRD: ${{ secrets.INFERENCE_BASE_URL_PRD }}
INFERENCE_API_KEY_DEV: ${{ secrets.INFERENCE_API_KEY_DEV }}
INFERENCE_API_KEY_STG: ${{ secrets.INFERENCE_API_KEY_STG }}
INFERENCE_API_KEY_PRD: ${{ secrets.INFERENCE_API_KEY_PRD }}
MCP_BASIC_KEY_DEV: ${{ secrets.MCP_BASIC_KEY_DEV }}
MCP_BASIC_KEY_STG: ${{ secrets.MCP_BASIC_KEY_STG }}
MCP_BASIC_KEY_PRD: ${{ secrets.MCP_BASIC_KEY_PRD }}
MCP_PAPERSCORE_KEY_DEV: ${{ secrets.MCP_PAPERSCORE_KEY_DEV }}
MCP_PAPERSCORE_KEY_STG: ${{ secrets.MCP_PAPERSCORE_KEY_STG }}
MCP_PAPERSCORE_KEY_PRD: ${{ secrets.MCP_PAPERSCORE_KEY_PRD }}
XTRAMCP_OPENAI_BASE_URL_DEV: ${{ secrets.XTRAMCP_OPENAI_BASE_URL_DEV }}
XTRAMCP_OPENAI_BASE_URL_STG: ${{ secrets.XTRAMCP_OPENAI_BASE_URL_STG }}
XTRAMCP_OPENAI_BASE_URL_PRD: ${{ secrets.XTRAMCP_OPENAI_BASE_URL_PRD }}
XTRAMCP_OPENAI_API_KEY_DEV: ${{ secrets.XTRAMCP_OPENAI_API_KEY_DEV }}
XTRAMCP_OPENAI_API_KEY_STG: ${{ secrets.XTRAMCP_OPENAI_API_KEY_STG }}
XTRAMCP_OPENAI_API_KEY_PRD: ${{ secrets.XTRAMCP_OPENAI_API_KEY_PRD }}
XTRAMCP_OPENREVIEW_BASE_URL_DEV: ${{ secrets.XTRAMCP_OPENREVIEW_BASE_URL_DEV }}
XTRAMCP_OPENREVIEW_BASE_URL_STG: ${{ secrets.XTRAMCP_OPENREVIEW_BASE_URL_STG }}
XTRAMCP_OPENREVIEW_BASE_URL_PRD: ${{ secrets.XTRAMCP_OPENREVIEW_BASE_URL_PRD }}
XTRAMCP_OPENREVIEW_USERNAME_DEV: ${{ secrets.XTRAMCP_OPENREVIEW_USERNAME_DEV }}
XTRAMCP_OPENREVIEW_USERNAME_STG: ${{ secrets.XTRAMCP_OPENREVIEW_USERNAME_STG }}
XTRAMCP_OPENREVIEW_USERNAME_PRD: ${{ secrets.XTRAMCP_OPENREVIEW_USERNAME_PRD }}
XTRAMCP_OPENREVIEW_PASSWORD_DEV: ${{ secrets.XTRAMCP_OPENREVIEW_PASSWORD_DEV }}
XTRAMCP_OPENREVIEW_PASSWORD_STG: ${{ secrets.XTRAMCP_OPENREVIEW_PASSWORD_STG }}
XTRAMCP_OPENREVIEW_PASSWORD_PRD: ${{ secrets.XTRAMCP_OPENREVIEW_PASSWORD_PRD }}
XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV: ${{ secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV }}
XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG: ${{ secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG }}
XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD: ${{ secrets.XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD }}
XTRAMCP_DOI_EMAIL_ADDRESS_DEV: ${{ secrets.XTRAMCP_DOI_EMAIL_ADDRESS_DEV }}
XTRAMCP_DOI_EMAIL_ADDRESS_STG: ${{ secrets.XTRAMCP_DOI_EMAIL_ADDRESS_STG }}
XTRAMCP_DOI_EMAIL_ADDRESS_PRD: ${{ secrets.XTRAMCP_DOI_EMAIL_ADDRESS_PRD }}
XTRAMCP_ACL_METADATA_DB_URL_DEV: ${{ secrets.XTRAMCP_ACL_METADATA_DB_URL_DEV }}
XTRAMCP_ACL_METADATA_DB_URL_STG: ${{ secrets.XTRAMCP_ACL_METADATA_DB_URL_STG }}
XTRAMCP_ACL_METADATA_DB_URL_PRD: ${{ secrets.XTRAMCP_ACL_METADATA_DB_URL_PRD }}
XTRAMCP_ARXIV_METADATA_DB_URL_DEV: ${{ secrets.XTRAMCP_ARXIV_METADATA_DB_URL_DEV }}
XTRAMCP_ARXIV_METADATA_DB_URL_STG: ${{ secrets.XTRAMCP_ARXIV_METADATA_DB_URL_STG }}
XTRAMCP_ARXIV_METADATA_DB_URL_PRD: ${{ secrets.XTRAMCP_ARXIV_METADATA_DB_URL_PRD }}
XTRAMCP_MONGO_URI_DEV: ${{ secrets.XTRAMCP_MONGO_URI_DEV }}
XTRAMCP_MONGO_URI_STG: ${{ secrets.XTRAMCP_MONGO_URI_STG }}
XTRAMCP_MONGO_URI_PRD: ${{ secrets.XTRAMCP_MONGO_URI_PRD }}
MONGO_URI_DEV: ${{ secrets.MONGO_URI_DEV }}
MONGO_URI_STG: ${{ secrets.MONGO_URI_STG }}
MONGO_URI_PRD: ${{ secrets.MONGO_URI_PRD }}
GHCR_DOCKER_CONFIG_DEV: ${{ secrets.GHCR_DOCKER_CONFIG_DEV }}
GHCR_DOCKER_CONFIG_STG: ${{ secrets.GHCR_DOCKER_CONFIG_STG }}
GHCR_DOCKER_CONFIG_PRD: ${{ secrets.GHCR_DOCKER_CONFIG_PRD }}
CLOUDFLARE_TUNNEL_TOKEN_DEV: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN_DEV }}
CLOUDFLARE_TUNNEL_TOKEN_STG: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN_STG }}
CLOUDFLARE_TUNNEL_TOKEN_PRD: ${{ secrets.CLOUDFLARE_TUNNEL_TOKEN_PRD }}
run: |
set -euo pipefail
case "${{ inputs.environment }}" in
dev)
export OPENAI_BASE_URL="$OPENAI_BASE_URL_DEV"
export OPENAI_API_KEY="$OPENAI_API_KEY_DEV"
export INFERENCE_BASE_URL="$INFERENCE_BASE_URL_DEV"
export INFERENCE_API_KEY="$INFERENCE_API_KEY_DEV"
export MCP_BASIC_KEY="$MCP_BASIC_KEY_DEV"
export MCP_PAPERSCORE_KEY="$MCP_PAPERSCORE_KEY_DEV"
export XTRAMCP_OPENAI_BASE_URL="$XTRAMCP_OPENAI_BASE_URL_DEV"
export XTRAMCP_OPENAI_API_KEY="$XTRAMCP_OPENAI_API_KEY_DEV"
export XTRAMCP_OPENREVIEW_BASE_URL="$XTRAMCP_OPENREVIEW_BASE_URL_DEV"
export XTRAMCP_OPENREVIEW_USERNAME="$XTRAMCP_OPENREVIEW_USERNAME_DEV"
export XTRAMCP_OPENREVIEW_PASSWORD="$XTRAMCP_OPENREVIEW_PASSWORD_DEV"
export XTRAMCP_CROSSREF_EMAIL_ADDRESS="$XTRAMCP_CROSSREF_EMAIL_ADDRESS_DEV"
export XTRAMCP_DOI_EMAIL_ADDRESS="$XTRAMCP_DOI_EMAIL_ADDRESS_DEV"
export XTRAMCP_ACL_METADATA_DB_URL="$XTRAMCP_ACL_METADATA_DB_URL_DEV"
export XTRAMCP_ARXIV_METADATA_DB_URL="$XTRAMCP_ARXIV_METADATA_DB_URL_DEV"
export XTRAMCP_MONGO_URI="$XTRAMCP_MONGO_URI_DEV"
export MONGO_URI="$MONGO_URI_DEV"
export GHCR_DOCKER_CONFIG="$GHCR_DOCKER_CONFIG_DEV"
export CLOUDFLARE_TUNNEL_TOKEN="$CLOUDFLARE_TUNNEL_TOKEN_DEV"
;;
stg)
export OPENAI_BASE_URL="$OPENAI_BASE_URL_STG"
export OPENAI_API_KEY="$OPENAI_API_KEY_STG"
export INFERENCE_BASE_URL="$INFERENCE_BASE_URL_STG"
export INFERENCE_API_KEY="$INFERENCE_API_KEY_STG"
export MCP_BASIC_KEY="$MCP_BASIC_KEY_STG"
export MCP_PAPERSCORE_KEY="$MCP_PAPERSCORE_KEY_STG"
export XTRAMCP_OPENAI_BASE_URL="$XTRAMCP_OPENAI_BASE_URL_STG"
export XTRAMCP_OPENAI_API_KEY="$XTRAMCP_OPENAI_API_KEY_STG"
export XTRAMCP_OPENREVIEW_BASE_URL="$XTRAMCP_OPENREVIEW_BASE_URL_STG"
export XTRAMCP_OPENREVIEW_USERNAME="$XTRAMCP_OPENREVIEW_USERNAME_STG"
export XTRAMCP_OPENREVIEW_PASSWORD="$XTRAMCP_OPENREVIEW_PASSWORD_STG"
export XTRAMCP_CROSSREF_EMAIL_ADDRESS="$XTRAMCP_CROSSREF_EMAIL_ADDRESS_STG"
export XTRAMCP_DOI_EMAIL_ADDRESS="$XTRAMCP_DOI_EMAIL_ADDRESS_STG"
export XTRAMCP_ACL_METADATA_DB_URL="$XTRAMCP_ACL_METADATA_DB_URL_STG"
export XTRAMCP_ARXIV_METADATA_DB_URL="$XTRAMCP_ARXIV_METADATA_DB_URL_STG"
export XTRAMCP_MONGO_URI="$XTRAMCP_MONGO_URI_STG"
export MONGO_URI="$MONGO_URI_STG"
export GHCR_DOCKER_CONFIG="$GHCR_DOCKER_CONFIG_STG"
export CLOUDFLARE_TUNNEL_TOKEN="$CLOUDFLARE_TUNNEL_TOKEN_STG"
;;
prd)
export OPENAI_BASE_URL="$OPENAI_BASE_URL_PRD"
export OPENAI_API_KEY="$OPENAI_API_KEY_PRD"
export INFERENCE_BASE_URL="$INFERENCE_BASE_URL_PRD"
export INFERENCE_API_KEY="$INFERENCE_API_KEY_PRD"
export MCP_BASIC_KEY="$MCP_BASIC_KEY_PRD"
export MCP_PAPERSCORE_KEY="$MCP_PAPERSCORE_KEY_PRD"
export XTRAMCP_OPENAI_BASE_URL="$XTRAMCP_OPENAI_BASE_URL_PRD"
export XTRAMCP_OPENAI_API_KEY="$XTRAMCP_OPENAI_API_KEY_PRD"
export XTRAMCP_OPENREVIEW_BASE_URL="$XTRAMCP_OPENREVIEW_BASE_URL_PRD"
export XTRAMCP_OPENREVIEW_USERNAME="$XTRAMCP_OPENREVIEW_USERNAME_PRD"
export XTRAMCP_OPENREVIEW_PASSWORD="$XTRAMCP_OPENREVIEW_PASSWORD_PRD"
export XTRAMCP_CROSSREF_EMAIL_ADDRESS="$XTRAMCP_CROSSREF_EMAIL_ADDRESS_PRD"
export XTRAMCP_DOI_EMAIL_ADDRESS="$XTRAMCP_DOI_EMAIL_ADDRESS_PRD"
export XTRAMCP_ACL_METADATA_DB_URL="$XTRAMCP_ACL_METADATA_DB_URL_PRD"
export XTRAMCP_ARXIV_METADATA_DB_URL="$XTRAMCP_ARXIV_METADATA_DB_URL_PRD"
export XTRAMCP_MONGO_URI="$XTRAMCP_MONGO_URI_PRD"
export MONGO_URI="$MONGO_URI_PRD"
export GHCR_DOCKER_CONFIG="$GHCR_DOCKER_CONFIG_PRD"
export CLOUDFLARE_TUNNEL_TOKEN="$CLOUDFLARE_TUNNEL_TOKEN_PRD"
;;
*)
echo "ERROR: Invalid environment '${{ inputs.environment }}'. Expected 'dev', 'stg', or 'prd'." >&2
exit 1
;;
esac

Copilot uses AI. Check for mistakes.
zip -r dist.zip dist/*

- name: Upload to Chrome Web Store (upload only)
uses: mobilefirstllc/cws-publish@latest
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mobilefirstllc/cws-publish@latest is a floating ref, which can change behavior unexpectedly and is a supply-chain risk. Pin to a specific version tag or (preferably) a commit SHA so builds are reproducible and updates are intentional.

Suggested change
uses: mobilefirstllc/cws-publish@latest
uses: mobilefirstllc/cws-publish@v1.0.6

Copilot uses AI. Check for mistakes.
ref: ${{ inputs.ref }}
- name: Clone deploy repo
run: |
git clone https://${{ secrets.GH_PAT }}@github.com/paperdebugger/deploy.git ../deploy
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloning with https://${{ secrets.GH_PAT }}@github.com/... can break if the token contains URL-significant characters and also embeds the credential in the command string. Prefer actions/checkout with repository: + path: + token:, or use the https://x-access-token:<token>@github.com/... form to avoid parsing issues.

Suggested change
git clone https://${{ secrets.GH_PAT }}@github.com/paperdebugger/deploy.git ../deploy
git clone https://x-access-token:${{ secrets.GH_PAT }}@github.com/paperdebugger/deploy.git ../deploy

Copilot uses AI. Check for mistakes.
zip_file: 'dist.zip'
build-and-publish:
uses: ./.github/workflows/_build-ext.yml
secrets: inherit
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using secrets: inherit passes all repository/environment secrets into the called workflow, which is broader than needed for the extension build/publish and increases blast radius if the called workflow is modified later. After declaring required secrets in _build-ext.yml, pass only the needed secrets explicitly from this caller.

Suggested change
secrets: inherit
secrets:
CHROME_WEBSTORE_CLIENT_ID: ${{ secrets.CHROME_WEBSTORE_CLIENT_ID }}
CHROME_WEBSTORE_CLIENT_SECRET: ${{ secrets.CHROME_WEBSTORE_CLIENT_SECRET }}
CHROME_WEBSTORE_REFRESH_TOKEN: ${{ secrets.CHROME_WEBSTORE_REFRESH_TOKEN }}

Copilot uses AI. Check for mistakes.
Junyi-99 added a commit that referenced this pull request Mar 28, 2026
Co-authored-by: Junyi Hou <junyi@xtras3.tail08d22c.ts.net>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix in #138 (staging) and #140 (main).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants