From 672b21f2219f46873a73e384e924933b3d78f2e5 Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 12:55:07 -0700 Subject: [PATCH 1/6] chore: copy v2 workflow --- .github/workflows/deploy-v3.yml | 530 ++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 .github/workflows/deploy-v3.yml diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml new file mode 100644 index 0000000..0e1752d --- /dev/null +++ b/.github/workflows/deploy-v3.yml @@ -0,0 +1,530 @@ +--- +name: Private Deployment +on: + workflow_call: + inputs: + env: + description: "Deploy to (stg|prod)" + required: true + default: "stg" + type: string + +jobs: + validate-prod-branch: + runs-on: ubuntu-latest + steps: + - name: Reject prod deployment from non-main branch + if: contains(inputs.env, 'prod') && github.ref != 'refs/heads/main' + uses: actions/github-script@v8 + with: + script: | + const branch = context.ref.replace('refs/heads/', ''); + core.setFailed(`Production deployment is only allowed from the 'main' branch. Current branch: '${branch}'. Please merge to 'main' and re-run, or use the Staging workflow to deploy a non-main branch.`); + + matrix_prep: + needs: [validate-prod-branch] + if: github.repository_owner == 'AdobeDocsPrivate' + runs-on: ${{ github.repository_owner == 'AdobeDocsPrivate' && (contains(inputs.env, 'prod') && 'developer-website-arc-prd-runners' || 'developer-website-arc-stg-runners') || 'ubuntu-latest' }} + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Checkout adp-devsite-workflow for runner matrix + uses: actions/checkout@v6 + with: + repository: AdobeDocsPrivate/adp-devsite-workflow-private + path: _workflow-config + + - name: Set Matrix + id: set-matrix + env: + REPO_OWNER: ${{ github.repository_owner }} + ENV_NAME: ${{ inputs.env }} + run: | + echo "================================================" + echo "MATRIX PREPARATION" + echo "================================================" + echo "Organization: $REPO_OWNER" + echo "Environment: $ENV_NAME" + + # Load the JSON data + matrix=$(jq --arg org "$REPO_OWNER" --arg env "$ENV_NAME" '[.[] | select(.org == $org and .env == $env)]' _workflow-config/.github/matrix/runner_matrix.json) + + # Set the formatted matrix as an output + echo "matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT + echo "✓ Matrix configuration loaded" + + set-state: + needs: matrix_prep + runs-on: + labels: ${{ matrix.runner }} + strategy: + matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} + outputs: + deploy_prod: ${{ contains(inputs.env, 'prod') }} + deploy_stg: ${{ contains(inputs.env, 'stg') }} + path_prefix: ${{ steps.get_path_prefix.outputs.path_prefix }} + branch_short_ref: ${{ steps.get_branch.outputs.branch }} + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Get pathPrefix + id: get_path_prefix + run: | + echo "================================================" + echo "EXTRACTING PATH PREFIX FROM CONFIG" + echo "================================================" + + # Extract pathPrefix from config.md + path_prefix=$(grep -A 1 "^- pathPrefix:" src/pages/config.md | tail -n 1 | sed 's/^[[:space:]]*-[[:space:]]*//' | tr -d '/') + echo "path_prefix=/${path_prefix}" >> $GITHUB_OUTPUT + + echo "✓ Path prefix extracted: /${path_prefix}" + - name: Get branch name + shell: bash + run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT + id: get_branch + + echo-state: + needs: [set-state, matrix_prep] + runs-on: + labels: ${{ matrix.runner }} + strategy: + matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} + steps: + - name: Echo state + env: + DEPLOY_STG: ${{ needs.set-state.outputs.deploy_stg }} + DEPLOY_PROD: ${{ needs.set-state.outputs.deploy_prod }} + REPO_ORG: ${{ github.event.repository.owner.login }} + REPO_NAME: ${{ github.event.repository.name }} + BRANCH_SHORT_REF: ${{ needs.set-state.outputs.branch_short_ref }} + PATH_PREFIX: ${{ needs.set-state.outputs.path_prefix }} + run: | + echo "Deploy to stg - $DEPLOY_STG" + echo "Deploy to prod - $DEPLOY_PROD" + echo "Repository org - $REPO_ORG" + echo "Repository name - $REPO_NAME" + echo "Repository branch - $BRANCH_SHORT_REF" + echo "Path prefix - $PATH_PREFIX" + + pre-build: + needs: [set-state, matrix_prep] + + runs-on: + labels: ${{ matrix.runner }} + + strategy: + matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} + + steps: + - name: Check stage Private Azure connection string + if: needs.set-state.outputs.deploy_stg == 'true' && env.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING == null + run: | + echo "::error::Please set the Azure Blob Storage connection string as AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING in Github Secrets" + exit 1 + env: + AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING }} + + - name: Check prod Private Azure connection string + if: needs.set-state.outputs.deploy_prod == 'true' && env.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING == null + run: | + echo "::error::Please set the Azure Blob Storage connection string as AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING in Github Secrets" + exit 1 + env: + AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING }} + + build: + defaults: + run: + shell: bash + needs: [set-state, pre-build, matrix_prep] + runs-on: + labels: ${{ matrix.runner }} + strategy: + matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} + steps: + - name: Select Connection String + id: select_connection_string + uses: actions/github-script@v8 + env: + AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING }} + AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING }} + with: + result-encoding: string + script: | + console.log('================================================'); + console.log('SELECTING AZURE CONNECTION STRING'); + console.log('================================================'); + + const isProd = '${{ inputs.env }}' === 'prod'; + console.log(`Environment: ${isProd ? 'Production' : 'Staging'}`); + + const connectionString = isProd + ? process.env.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING + : process.env.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING; + + console.log('✓ Connection string selected'); + return connectionString; + + - name: Checkout content repo + uses: actions/checkout@v6 + with: + path: content + + - name: Read site config + id: config + env: + SITE_PATH_INPUT: ${{ needs.set-state.outputs.path_prefix }} + run: | + # Use pathPrefix from set-state job + SITE_PATH="$SITE_PATH_INPUT" + + # Strip leading and trailing slashes if present + SITE_PATH=$(echo "$SITE_PATH" | sed 's|^/||' | sed 's|/$||') + + # Extract first path segment for prefix + SITE_PREFIX=$(echo "$SITE_PATH" | cut -d'/' -f1) + echo "SITE_PATH=$SITE_PATH" >> $GITHUB_ENV + echo "SITE_PREFIX=$SITE_PREFIX" >> $GITHUB_ENV + echo "site_path=$SITE_PATH" >> $GITHUB_OUTPUT + echo "✓ Site path validated: $SITE_PATH (prefix: $SITE_PREFIX)" + + - name: Clone adp-devsite + uses: actions/checkout@v6 + with: + repository: AdobeDocs/adp-devsite + ref: main + path: adp-devsite + + - name: Clone devsite-runtime-connector + uses: actions/checkout@v6 + with: + repository: aemsites/devsite-runtime-connector + path: devsite-runtime-connector + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "lts/krypton" + + - name: Install dependencies + run: | + cd content && npm install + cd ../adp-devsite && npm install + cd ../devsite-runtime-connector && npm install + + - name: Start development servers + env: + DEVSITE_CONNECTOR_FLAG: 'true' + DEVSITE_CONNECTOR_PUBLIC_ORIGIN: ${{ needs.set-state.outputs.deploy_prod == 'true' && 'https://developer.adobe.com' || 'https://developer-stage.adobe.com' }} + run: | + ROOT_DIR=$(pwd) + + # Start content server (port 3003) + (cd "$ROOT_DIR/content" && npm run dev > "$ROOT_DIR/content-server.log" 2>&1) & + echo $! > /tmp/content.pid + + # Start adp-devsite (port 3000) + (cd "$ROOT_DIR/adp-devsite" && npm run dev > "$ROOT_DIR/devsite-server.log" 2>&1) & + echo $! > /tmp/devsite.pid + + # Start runtime connector (port 3001) + (cd "$ROOT_DIR/devsite-runtime-connector" && npm run dev > "$ROOT_DIR/connector-server.log" 2>&1) & + echo $! > /tmp/connector.pid + + echo "Servers starting in background..." + + - name: Wait for servers to be ready + run: | + echo "Waiting for servers..." + for port in 3000 3003; do + for i in {1..60}; do + if curl -s "http://localhost:$port" > /dev/null 2>&1; then + echo "✓ Port $port is ready" + break + fi + if [ $i -eq 60 ]; then + echo "✗ Port $port timeout" + cat content-server.log devsite-server.log connector-server.log 2>/dev/null || true + exit 1 + fi + sleep 1 + done + done + + # Verify site is accessible with retry + echo "Verifying site at: http://localhost:3000/${SITE_PATH}/" + for i in {1..30}; do + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:3000/${SITE_PATH}/" 2>&1) + if [ "$HTTP_CODE" = "200" ]; then + echo "✓ Site is accessible (HTTP $HTTP_CODE)" + break + fi + if [ $i -eq 30 ]; then + echo "✗ Site verification failed (HTTP $HTTP_CODE)" + echo "=== Content Server Log ===" + tail -100 content-server.log 2>/dev/null || echo "No log found" + echo "=== DevSite Server Log ===" + tail -100 devsite-server.log 2>/dev/null || echo "No log found" + echo "=== Connector Server Log ===" + tail -100 connector-server.log 2>/dev/null || echo "No log found" + exit 1 + fi + echo "Attempt $i: HTTP $HTTP_CODE, retrying..." + sleep 2 + done + + - name: Generate static site with curl + run: | + ROOT_DIR=$(pwd) + mkdir -p "$ROOT_DIR/_site" + + # Generate URL list from markdown files + cd "$ROOT_DIR/content" + find src/pages -name "*.md" | sed 's|src/pages/||' | sed 's|\.md$||' | sed 's|/index$|/|' | while read path; do + echo "http://localhost:3000/${SITE_PATH}/$path" + done > "$ROOT_DIR/urls.txt" + + echo "Total URLs: $(wc -l < "$ROOT_DIR/urls.txt" | tr -d ' ')" + echo "Sample URLs from urls.txt:" + head -5 "$ROOT_DIR/urls.txt" + + cd "$ROOT_DIR/_site" + echo "Output directory: $(pwd)" + + # Download pages with curl + # Only index.html and config.html keep .html extension; all other pages save without extension + while IFS= read -r url; do + # Extract path from URL and strip SITE_PATH prefix + # http://localhost:3000/private-eds/guides/ -> guides/ + path=$(echo "$url" | sed 's|http://localhost:3000/||' | sed "s|^${SITE_PATH}/||") + + # Create directory structure and choose output filename + if [[ "$path" == */ ]] || [[ -z "$path" ]]; then + # Directory index -> index.html + mkdir -p "$path" + output_file="${path}index.html" + elif [[ "$path" == "index" ]]; then + # Root index page (e.g. from index.md) -> index.html + mkdir -p "$path" + output_file="index.html" + elif [[ "$path" == "config" ]]; then + # Config page -> config.html + dir=$(dirname "$path") + mkdir -p "$dir" + output_file="${path}.html" + else + # All other pages -> no extension + dir=$(dirname "$path") + mkdir -p "$dir" + output_file="${path}" + fi + + echo "Downloading: $url -> $output_file" + curl -sf "$url" -o "$output_file" || echo "Failed to download: $url" + done < "$ROOT_DIR/urls.txt" + + echo "✓ Site generation complete" + echo "Contents of _site:" + find . -type f | head -20 + + - name: Copy static assets + run: | + cd _site + + # Copy hlx_statics from adp-devsite source + mkdir -p localhost+3000/hlx_statics + cp -r ../adp-devsite/hlx_statics/* localhost+3000/hlx_statics/ + echo "✓ Copied $(find localhost+3000/hlx_statics -type f | wc -l | tr -d ' ') static files" + + - name: Copy spec, data, and static media files + run: | + ROOT_DIR=$(pwd) + cd "$ROOT_DIR/_site" + COUNT=0 + + # File types referenced from markdown (Redocly, images, downloads). Deployed beside HTML on Azure. + asset_pred=( -type f \( \ + -iname "*.json" -o -iname "*.yml" -o -iname "*.yaml" -o -iname "*.d.ts" \ + -o -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.svg" -o -iname "*.ico" \ + -o -iname "*.pdf" -o -iname "*.zip" -o -iname "*.tgz" -o -iname "*.gz" \ + -o -iname "*.mp4" -o -iname "*.webm" -o -iname "*.mov" -o -iname "*.wav" -o -iname "*.mp3" \ + -o -iname "*.woff" -o -iname "*.woff2" -o -iname "*.ttf" -o -iname "*.eot" \ + \) ) + + # Copy from content/static/ (preserve static/ prefix for /{pathPrefix}/static/... URLs) + if [ -d "$ROOT_DIR/content/static" ]; then + while IFS= read -r file; do + [ -f "$file" ] || continue + rel_path="${file#$ROOT_DIR/content/}" + mkdir -p "$(dirname "$rel_path")" + cp "$file" "$rel_path" + echo " Copied $rel_path" + COUNT=$((COUNT + 1)) + done < <(find "$ROOT_DIR/content/static" "${asset_pred[@]}") + fi + + # Copy from src/pages/ (exclude auto-generated JSON) + while IFS= read -r file; do + [ -f "$file" ] || continue + rel_path="${file#$ROOT_DIR/content/src/pages/}" + mkdir -p "$(dirname "$rel_path")" + cp "$file" "$rel_path" + echo " Copied $rel_path" + COUNT=$((COUNT + 1)) + done < <(find "$ROOT_DIR/content/src/pages" "${asset_pred[@]}" \ + ! -name "contributors.json" ! -name "adp-site-metadata.json") + + echo "✓ Copied $COUNT spec/data/media files" + + - name: Fetch API endpoints + run: | + cd _site + mkdir -p "localhost+3000/${SITE_PATH}" + mkdir -p localhost+3000/franklin_assets + + # Config (for side navigation) + curl -sf "http://localhost:3000/${SITE_PATH}/config" -o "localhost+3000/config.plain.html" && echo "✓ config" + + # Footer + curl -sf "http://localhost:3000/franklin_assets/footer.plain.html" -o localhost+3000/franklin_assets/footer.plain.html && echo "✓ footer" + + # Product index map + curl -sf "http://localhost:3000/franklin_assets/product-index-map.json" -o localhost+3000/franklin_assets/product-index-map.json && echo "✓ product-index-map" + + # Site-wide banner + curl -sf "http://localhost:3000/${SITE_PATH}/site-wide-banner.json" -o "localhost+3000/${SITE_PATH}/site-wide-banner.json" || echo "○ site-wide-banner (optional)" + + - name: Organize output structure + run: | + cd _site + + # Move content from localhost+3000 to root + if [ -d "localhost+3000" ]; then + mv localhost+3000/* . 2>/dev/null || true + rm -rf localhost+3000 + echo "✓ Output structure organized" + fi + + - name: Fix superhero block images + run: | + cd _site + # superhero.js expects ; Fix every file under _site that contains the block. + # Match any div whose class list includes "superhero" (default, half-width, etc.); first bare + # inside that open tag must not already follow (avoids double-wrapping) + SH_FILES=$(grep -rl 'class="[^"]*superhero' . 2>/dev/null || true) + if [ -z "$SH_FILES" ]; then + echo "No superhero blocks in _site — skip" + else + echo "Superhero HTML fix (wrap in ):" + echo "$SH_FILES" | sed 's/^/ /' + echo "$SH_FILES" | while IFS= read -r f; do + [ -z "$f" ] && continue + [ -f "$f" ] || continue + perl -i -0pe 's|(]*>[\s\S]*?)(?)]*)>|$1|gs' "$f" + echo " ✓ $f" + done + fi + + - name: Add security headers + run: | + cd _site + + # Content Security Policy to mitigate XSS attacks + # - 'self' allows resources from same origin + # - 'unsafe-inline' required for the interceptor script and inline styles + # - data: allows data URIs for images (used by some frameworks) + # - blob: allows blob URIs (used for dynamic content) + # - https: restricts external resources to HTTPS only + CSP_CONTENT="default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https:; frame-ancestors 'self';" + CSP_TAG="" + + # X-Content-Type-Options to prevent MIME sniffing + XCTO_TAG="" + + # Referrer-Policy for privacy + RP_TAG="" + + # Combine security meta tags + SECURITY_TAGS="${CSP_TAG}${XCTO_TAG}${RP_TAG}" + + # Inject security headers into all HTML files (after ) + # Include both *.html and extensionless page files + for htmlfile in $(find . -name "*.html" -type f) $(find "./${SITE_PATH}" -type f ! -name "*.*" 2>/dev/null); do + [ -f "$htmlfile" ] || continue + if ! grep -q "Content-Security-Policy" "$htmlfile" 2>/dev/null; then + perl -i -pe "s||${SECURITY_TAGS}|" "$htmlfile" 2>/dev/null || true + fi + done + + SECURED=$(find "./${SITE_PATH}" \( -name "*.html" -o ! -name "*.*" \) -type f -exec grep -l "Content-Security-Policy" {} \; 2>/dev/null | wc -l | tr -d ' ') + echo "✓ Security headers added to $SECURED files" + + - name: Stop servers + if: always() + run: | + for pidfile in /tmp/content.pid /tmp/devsite.pid /tmp/connector.pid; do + if [ -f "$pidfile" ]; then + kill $(cat "$pidfile") 2>/dev/null || true + fi + done + + - name: Verify build output + id: set_build_output + run: | + ROOT_DIR=$(pwd) + cd _site + echo "=== Build Summary ===" + echo "Total files: $(find . -type f | wc -l | tr -d ' ')" + echo "HTML files: $(find . -name '*.html' | wc -l | tr -d ' ')" + + if [ ! -d "${SITE_PATH}" ]; then + echo "ERROR: No content generated!" + exit 1 + fi + + echo "ROOT_DIR=${ROOT_DIR}" + echo "Expanded path: ${ROOT_DIR}/_site" + echo "✓ Build verification passed" + + - name: Deploy to Azure + uses: AdobeDocs/static-website-deploy@master + with: + enabled-static-website: "true" + source: "_site" + target: ${{ needs.set-state.outputs.path_prefix }} + connection-string: ${{ steps.select_connection_string.outputs.result }} + remove-existing-files: "false" + + - name: Deployment complete + run: | + echo "✓ Deployment to Azure completed successfully" + + - name: Purge Fastly Cache + env: + FASTLY_URL: ${{ needs.set-state.outputs.deploy_prod == 'true' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }} + PATH_PREFIX: ${{ needs.set-state.outputs.path_prefix }} + run: | + echo "================================================" + echo "PURGING FASTLY CACHE" + echo "================================================" + echo "URL: ${FASTLY_URL}${PATH_PREFIX}" + + - name: Purge cache + uses: AdobeDocs/adp-devsite-fastly-purge@main + with: + fastly-token: ${{ secrets.AIO_FASTLY_TOKEN }} + fastly-url: "${{ needs.set-state.outputs.deploy_prod == 'true' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }}${{ needs.set-state.outputs.path_prefix }}" + + - name: Cache purge complete + run: | + echo "✓ Fastly cache purged successfully" + echo "================================================" + echo "DEPLOYMENT COMPLETE" + echo "================================================" + From fb61221806c3aa1fead6f66a37abd73308e52ef4 Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 13:10:10 -0700 Subject: [PATCH 2/6] fix: correct checkout step name to match repo --- .github/workflows/deploy-v3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml index 0e1752d..839dc89 100644 --- a/.github/workflows/deploy-v3.yml +++ b/.github/workflows/deploy-v3.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Checkout adp-devsite-workflow for runner matrix + - name: Checkout adp-devsite-workflow-private for runner matrix uses: actions/checkout@v6 with: repository: AdobeDocsPrivate/adp-devsite-workflow-private From 1a739236a9a41639bdb9b2ac393a51f38ccdc65e Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 13:10:43 -0700 Subject: [PATCH 3/6] chore: remove dead default from env input --- .github/workflows/deploy-v3.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml index 839dc89..c542aaf 100644 --- a/.github/workflows/deploy-v3.yml +++ b/.github/workflows/deploy-v3.yml @@ -6,7 +6,6 @@ on: env: description: "Deploy to (stg|prod)" required: true - default: "stg" type: string jobs: From 546fd1bf6f6da01b0c112c8b6089d0ad5faa5642 Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 13:11:19 -0700 Subject: [PATCH 4/6] refactor: follow GitHub Actions security best practice for script inputs --- .github/workflows/deploy-v3.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml index c542aaf..b281c3e 100644 --- a/.github/workflows/deploy-v3.yml +++ b/.github/workflows/deploy-v3.yml @@ -151,6 +151,7 @@ jobs: id: select_connection_string uses: actions/github-script@v8 env: + ENV_INPUT: ${{ inputs.env }} AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING }} AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING }} with: @@ -160,7 +161,7 @@ jobs: console.log('SELECTING AZURE CONNECTION STRING'); console.log('================================================'); - const isProd = '${{ inputs.env }}' === 'prod'; + const isProd = process.env.ENV_INPUT === 'prod'; console.log(`Environment: ${isProd ? 'Production' : 'Staging'}`); const connectionString = isProd From 14e6bfb258b32a35fa982353c6931ac8331de476 Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 13:12:28 -0700 Subject: [PATCH 5/6] feat: simultaneous private deploys --- .github/workflows/deploy-v3.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml index b281c3e..eb9c1b5 100644 --- a/.github/workflows/deploy-v3.yml +++ b/.github/workflows/deploy-v3.yml @@ -4,7 +4,7 @@ on: workflow_call: inputs: env: - description: "Deploy to (stg|prod)" + description: "Deploy to (stage|prod)" required: true type: string @@ -40,16 +40,19 @@ jobs: id: set-matrix env: REPO_OWNER: ${{ github.repository_owner }} - ENV_NAME: ${{ inputs.env }} + ENV_INPUT: ${{ inputs.env }} run: | echo "================================================" echo "MATRIX PREPARATION" echo "================================================" echo "Organization: $REPO_OWNER" - echo "Environment: $ENV_NAME" + + # UI label "stage" maps to matrix key "stg" + ENV_NAME="${ENV_INPUT//stage/stg}" + echo "Environment: $ENV_NAME (input: $ENV_INPUT)" - # Load the JSON data - matrix=$(jq --arg org "$REPO_OWNER" --arg env "$ENV_NAME" '[.[] | select(.org == $org and .env == $env)]' _workflow-config/.github/matrix/runner_matrix.json) + # Match rows where .env is a substring of input (e.g. "stg & prod" → both rows) + matrix=$(jq --arg org "$REPO_OWNER" --arg env "$ENV_NAME" '[.[] | select(.org == $org and (.env as $e | $env | contains($e)))]' _workflow-config/.github/matrix/runner_matrix.json) # Set the formatted matrix as an output echo "matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT @@ -62,8 +65,6 @@ jobs: strategy: matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} outputs: - deploy_prod: ${{ contains(inputs.env, 'prod') }} - deploy_stg: ${{ contains(inputs.env, 'stg') }} path_prefix: ${{ steps.get_path_prefix.outputs.path_prefix }} branch_short_ref: ${{ steps.get_branch.outputs.branch }} @@ -97,8 +98,8 @@ jobs: steps: - name: Echo state env: - DEPLOY_STG: ${{ needs.set-state.outputs.deploy_stg }} - DEPLOY_PROD: ${{ needs.set-state.outputs.deploy_prod }} + DEPLOY_STG: ${{ matrix.env == 'stg' }} + DEPLOY_PROD: ${{ matrix.env == 'prod' }} REPO_ORG: ${{ github.event.repository.owner.login }} REPO_NAME: ${{ github.event.repository.name }} BRANCH_SHORT_REF: ${{ needs.set-state.outputs.branch_short_ref }} @@ -122,7 +123,7 @@ jobs: steps: - name: Check stage Private Azure connection string - if: needs.set-state.outputs.deploy_stg == 'true' && env.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING == null + if: matrix.env == 'stg' && env.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING == null run: | echo "::error::Please set the Azure Blob Storage connection string as AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING in Github Secrets" exit 1 @@ -130,7 +131,7 @@ jobs: AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING }} - name: Check prod Private Azure connection string - if: needs.set-state.outputs.deploy_prod == 'true' && env.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING == null + if: matrix.env == 'prod' && env.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING == null run: | echo "::error::Please set the Azure Blob Storage connection string as AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING in Github Secrets" exit 1 @@ -151,7 +152,7 @@ jobs: id: select_connection_string uses: actions/github-script@v8 env: - ENV_INPUT: ${{ inputs.env }} + ENV_NAME: ${{ matrix.env }} AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_DEV_PRIVATE_CONNECTION_STRING }} AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING: ${{ secrets.AIO_AZURE_PROD_PRIVATE_CONNECTION_STRING }} with: @@ -161,7 +162,7 @@ jobs: console.log('SELECTING AZURE CONNECTION STRING'); console.log('================================================'); - const isProd = process.env.ENV_INPUT === 'prod'; + const isProd = process.env.ENV_NAME === 'prod'; console.log(`Environment: ${isProd ? 'Production' : 'Staging'}`); const connectionString = isProd @@ -221,7 +222,7 @@ jobs: - name: Start development servers env: DEVSITE_CONNECTOR_FLAG: 'true' - DEVSITE_CONNECTOR_PUBLIC_ORIGIN: ${{ needs.set-state.outputs.deploy_prod == 'true' && 'https://developer.adobe.com' || 'https://developer-stage.adobe.com' }} + DEVSITE_CONNECTOR_PUBLIC_ORIGIN: ${{ matrix.env == 'prod' && 'https://developer.adobe.com' || 'https://developer-stage.adobe.com' }} run: | ROOT_DIR=$(pwd) @@ -507,7 +508,7 @@ jobs: - name: Purge Fastly Cache env: - FASTLY_URL: ${{ needs.set-state.outputs.deploy_prod == 'true' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }} + FASTLY_URL: ${{ matrix.env == 'prod' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }} PATH_PREFIX: ${{ needs.set-state.outputs.path_prefix }} run: | echo "================================================" @@ -519,7 +520,7 @@ jobs: uses: AdobeDocs/adp-devsite-fastly-purge@main with: fastly-token: ${{ secrets.AIO_FASTLY_TOKEN }} - fastly-url: "${{ needs.set-state.outputs.deploy_prod == 'true' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }}${{ needs.set-state.outputs.path_prefix }}" + fastly-url: "${{ matrix.env == 'prod' && secrets.AIO_FASTLY_PROD_URL || secrets.AIO_FASTLY_DEV_URL }}${{ needs.set-state.outputs.path_prefix }}" - name: Cache purge complete run: | From 0e5e0ca7d1e86b0847a41e4640a780114c795b5f Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 23 Jun 2026 15:47:44 -0700 Subject: [PATCH 6/6] chore: remove input description for consistency with public workflow --- .github/workflows/deploy-v3.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-v3.yml b/.github/workflows/deploy-v3.yml index eb9c1b5..ec99e40 100644 --- a/.github/workflows/deploy-v3.yml +++ b/.github/workflows/deploy-v3.yml @@ -4,7 +4,6 @@ on: workflow_call: inputs: env: - description: "Deploy to (stage|prod)" required: true type: string