mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
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>
300 lines
11 KiB
YAML
300 lines
11 KiB
YAML
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
|