Files
hdf5/.github/workflows/publish-release.yml
Mark KittisopikulandClaude Opus 4.8 fe17c9b1cd Harden S3 publish workflows against zizmor findings (#6543)
Address GitHub Actions static-analysis (zizmor/CodeQL) findings in the
S3 publishing workflows:

- publish-release.yml: hoist every inputs/secrets/vars value used inside
  run blocks into job-level env vars and reference plain shell variables
  ($USE_TAG, $FILE_NAME, $TARGET_DIR, $S3_BUCKET, $TARGET_PATH, $DRY_RUN),
  eliminating 41 template-injection findings. Add persist-credentials:
  false to the checkout (artipacked).
- publish-branch.yml: add persist-credentials: false to the checkout
  (artipacked); its S3 sync already used env vars.

No behavioral change: the same values are used, only via the shell
environment instead of direct ${{ }} expansion into the script body.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 16:40:58 -04:00

300 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: hdf5 publish release
# Triggers the workflow on demand
on:
workflow_dispatch:
inputs:
use_tag:
description: HDF5 Release version tag (e.g., 2.0.0)
type: string
required: true
file_name:
description: HDF5 Release file name base
type: string
required: true
target_dir:
description: HDF5 target bucket directory
type: string
required: true
dry_run:
description: Dry run mode (skip S3 upload)
type: boolean
default: false
permissions:
contents: read
jobs:
publish-tag:
runs-on: ubuntu-latest
timeout-minutes: 30
# Expose the templated inputs/secrets/vars as environment variables so the
# run steps below reference plain shell variables ($USE_TAG, ...) instead of
# interpolating ${{ ... }} directly into the script body, which avoids
# template-injection (see zizmor/CodeQL findings).
env:
USE_TAG: ${{ inputs.use_tag }}
FILE_NAME: ${{ inputs.file_name }}
TARGET_DIR: ${{ inputs.target_dir }}
DRY_RUN: ${{ inputs.dry_run }}
S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
TARGET_PATH: ${{ vars.TARGET_PATH }}
steps:
- name: Validate Inputs
run: |
set -euo pipefail
echo "Validating inputs..."
if [[ ! "$USE_TAG" =~ ^hdf5[_-][0-9]+[._][0-9]+[._][0-9]+([._][0-9]+)?(-.*)?$ ]]; then
echo "❌ Invalid tag format. Expected: hdf5_X.Y.Z, hdf5-X_Y_Z, or hdf5_X.Y.Z.W"
exit 1
fi
if [[ "$TARGET_DIR" == *".."* ]] || [[ "$TARGET_DIR" == /* ]]; then
echo "❌ Invalid target_dir. Cannot contain '..' or start with '/'"
exit 1
fi
if [[ "$FILE_NAME" == *".."* ]] || [[ "$FILE_NAME" == *"/"* ]] || [[ "$FILE_NAME" =~ [^a-zA-Z0-9._-] ]]; then
echo "❌ Invalid file_name. Can only contain alphanumeric, dots, underscores, and hyphens"
exit 1
fi
echo "✅ Input validation passed"
# Checks-out your repository under $GITHUB_WORKSPACE
- name: Get Sources
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # Updated to latest stable v7.0.1
with:
fetch-depth: 0
ref: '${{ github.head_ref || github.ref_name }}'
persist-credentials: false
- name: Create download directory
run: mkdir -p HDF5
- name: Get HDF5 release assets
uses: robinraju/release-downloader@28fc21f50d76778e7023361aa1f863e717d3d56f # More reliable alternative v1.13
with:
repository: HDFGroup/hdf5
tag: ${{ inputs.use_tag }}
fileName: ${{ inputs.file_name }}*
out-file-path: HDF5
extract: false
- name: Verify downloaded files
run: |
set -euo pipefail
echo "📁 Downloaded files:"
ls -la HDF5/
# Check for expected files
EXPECTED_FILES=("${FILE_NAME}.doxygen.zip" "${FILE_NAME}.html.abi.reports.tar.gz")
for file in "${EXPECTED_FILES[@]}"; do
if [ ! -f "HDF5/$file" ]; then
echo "⚠️ Warning: Expected file not found: $file"
else
echo "✅ Found: $file"
fi
done
- name: Setup AWS CLI
if: ${{ !inputs.dry_run }}
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Generate index.html for downloads directory
run: |
set -euo pipefail
echo "📄 Generating index.html for downloads directory..."
chmod +x .github/scripts/generate-index-html.sh
.github/scripts/generate-index-html.sh \
"./HDF5" \
"HDF5 ${USE_TAG} - Downloads" \
"Release binaries, source code, and documentation packages for HDF5 ${USE_TAG}" \
"../"
echo "✅ Downloads index.html generated"
- name: Sync release files to S3 bucket
if: ${{ !inputs.dry_run }}
run: |
set -euo pipefail
echo "🚀 Syncing release files to S3..."
aws s3 sync ./HDF5 "s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads" \
--delete \
--exclude="*" \
--include="*.tar.gz" \
--include="*.zip" \
--include="*.msi" \
--include="*.dmg" \
--include="*.exe" \
--include="*.sha256" \
--include="index.html" \
--exact-timestamps
# Upload index.html with proper content type
aws s3 cp ./HDF5/index.html \
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads/index.html" \
--content-type "text/html" \
--metadata-directive REPLACE
echo "✅ Release files sync completed"
- name: Process documentation
env:
FILE_NAME: ${{ inputs.file_name }}
run: |
set -euo pipefail
DOC_FILE="HDF5/${FILE_NAME}.doxygen.zip"
if [ -f "$DOC_FILE" ]; then
echo "📚 Processing documentation..."
unzip -q "$DOC_FILE"
if [ -d "${FILE_NAME}.doxygen" ]; then
echo "✅ Documentation extracted successfully"
else
echo "❌ Documentation extraction failed"
exit 1
fi
# The doxygen tag file is published alongside the HTML docs
if [ -f "${FILE_NAME}.doxygen/hdf5.tag" ]; then
echo "✅ Found: hdf5.tag"
else
echo "❌ hdf5.tag not found in documentation"
exit 1
fi
else
echo "⚠️ Documentation file not found, skipping..."
fi
- name: Generate index.html for documentation directory
run: |
set -euo pipefail
if [ -d "${FILE_NAME}.doxygen" ]; then
echo "📄 Generating index.html for documentation directory..."
.github/scripts/generate-index-html.sh \
"./${FILE_NAME}.doxygen" \
"HDF5 ${USE_TAG} - Documentation" \
"Doxygen API documentation for HDF5 ${USE_TAG}" \
"../../"
echo "✅ Documentation index.html generated"
else
echo "⚠️ No documentation directory found, skipping index generation..."
fi
- name: Sync documentation to S3 bucket
if: ${{ !inputs.dry_run }}
run: |
set -euo pipefail
if [ -d "${FILE_NAME}.doxygen" ]; then
echo "📚 Syncing documentation to S3..."
aws s3 sync "./${FILE_NAME}.doxygen" \
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/documentation/doxygen" \
--delete \
--content-type "text/html" \
--metadata-directive REPLACE
echo "✅ Documentation sync completed"
else
echo "⚠️ No documentation directory found, skipping..."
fi
- name: Process compatibility reports
run: |
set -euo pipefail
COMPAT_FILE="HDF5/${FILE_NAME}.html.abi.reports.tar.gz"
if [ -f "$COMPAT_FILE" ]; then
echo "📊 Processing compatibility reports..."
tar -xzf "$COMPAT_FILE"
if [ -d "hdf5" ]; then
echo "✅ Compatibility reports extracted successfully"
else
echo "❌ Compatibility reports extraction failed"
exit 1
fi
else
echo "⚠️ Compatibility reports file not found, skipping..."
fi
- name: Generate index.html for compatibility reports directory
run: |
set -euo pipefail
if [ -d "hdf5" ]; then
echo "📄 Generating index.html for compatibility reports directory..."
.github/scripts/generate-index-html.sh \
"./hdf5" \
"HDF5 ${USE_TAG} - Compatibility Reports" \
"ABI/API compatibility reports for HDF5 ${USE_TAG}" \
"../"
echo "✅ Compatibility reports index.html generated"
else
echo "⚠️ No compatibility reports directory found, skipping index generation..."
fi
- name: Sync compatibility reports to S3 bucket
if: ${{ !inputs.dry_run }}
run: |
set -euo pipefail
if [ -d "hdf5" ]; then
echo "📊 Syncing compatibility reports to S3..."
aws s3 sync ./hdf5 \
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/compat_report" \
--delete \
--content-type "text/html" \
--metadata-directive REPLACE
echo "✅ Compatibility reports sync completed"
else
echo "⚠️ No compatibility reports directory found, skipping..."
fi
- name: Generate main release directory index.html
run: |
set -euo pipefail
echo "📄 Generating main release directory index.html..."
# Create a temporary directory structure to mimic the S3 layout
mkdir -p "release_root/${TARGET_DIR}"/{downloads,documentation,compat_report}
# Create placeholder files so the script can list them
touch "release_root/${TARGET_DIR}/downloads/.placeholder"
touch "release_root/${TARGET_DIR}/documentation/.placeholder"
touch "release_root/${TARGET_DIR}/compat_report/.placeholder"
# Generate index for the release directory
.github/scripts/generate-index-html.sh \
"release_root/${TARGET_DIR}" \
"HDF5 ${USE_TAG}" \
"Release files, documentation, and compatibility reports for HDF5 ${USE_TAG}" \
"../"
echo "✅ Main release index.html generated"
- name: Upload main release directory index.html
if: ${{ !inputs.dry_run }}
run: |
set -euo pipefail
echo "📤 Uploading main release directory index.html..."
aws s3 cp "release_root/${TARGET_DIR}/index.html" \
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/index.html" \
--content-type "text/html" \
--metadata-directive REPLACE
echo "✅ Main index.html uploaded"
- name: Summary
run: |
set -euo pipefail
echo "🎉 HDF5 Release Publication Summary"
echo "=================================="
echo "🏷️ Tag: ${USE_TAG}"
echo "📁 File Base: ${FILE_NAME}"
echo "🎯 Target Directory: ${TARGET_DIR}"
echo "🌐 Dry Run: ${DRY_RUN}"
echo ""
if [ "$DRY_RUN" == "true" ]; then
echo "ℹ️ This was a dry run - no files were uploaded to S3"
else
echo "✅ Release published successfully!"
echo "📍 Main page: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/index.html"
echo "📍 Downloads: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads"
echo "📖 Documentation: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/documentation/doxygen"
echo "📊 Reports: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/compat_report"
fi