From 6ecc20bcf6297b7974096965d5fca4fe4809af67 Mon Sep 17 00:00:00 2001 From: "Sean P. Kane" Date: Mon, 5 Jan 2026 06:59:51 -0800 Subject: [PATCH] ci: Fix Linux container naming and add `actionlint` to pre-commit hooks (#13) * Fix container tagging in Docker workflow * ci: Add actionlint to GitHub Actions workflows * Fix actionlint syntax error in macro-test workflow --- .github/workflows/docker-release.yaml | 50 ++++++++++--------- .github/workflows/docker.yaml | 11 +++-- .github/workflows/macro-release.yaml | 42 ++++++++-------- .github/workflows/macro-test.yaml | 17 ++++--- .github/workflows/pypi-release.yaml | 70 +++++++++++++++------------ .pre-commit-config.yaml | 9 ++++ 6 files changed, 114 insertions(+), 85 deletions(-) diff --git a/.github/workflows/docker-release.yaml b/.github/workflows/docker-release.yaml index 05ac3bb..37e4b21 100644 --- a/.github/workflows/docker-release.yaml +++ b/.github/workflows/docker-release.yaml @@ -31,7 +31,7 @@ jobs: id: version run: | TAG="${GITHUB_REF#refs/tags/}" - echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> "$GITHUB_OUTPUT" # Validate semantic versioning format (v1.2.3 or v1.2.3-prerelease) if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then @@ -41,24 +41,26 @@ jobs: # Extract version without 'v' prefix VERSION="${TAG#v}" - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> "$GITHUB_OUTPUT" # Extract major.minor for additional tag MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2) - echo "major_minor=$MAJOR_MINOR" >> $GITHUB_OUTPUT + echo "major_minor=$MAJOR_MINOR" >> "$GITHUB_OUTPUT" # Extract major for additional tag MAJOR=$(echo "$VERSION" | cut -d. -f1) - echo "major=$MAJOR" >> $GITHUB_OUTPUT + echo "major=$MAJOR" >> "$GITHUB_OUTPUT" # Check if this is a prerelease if [[ "$TAG" =~ -[a-zA-Z0-9.]+ ]]; then - echo "is_prerelease=true" >> $GITHUB_OUTPUT + echo "is_prerelease=true" >> "$GITHUB_OUTPUT" + IS_PRERELEASE="true" else - echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "is_prerelease=false" >> "$GITHUB_OUTPUT" + IS_PRERELEASE="false" fi - echo "Parsed version: $VERSION (major: $MAJOR, major.minor: $MAJOR_MINOR, prerelease: ${{ steps.version.outputs.is_prerelease }})" + echo "Parsed version: $VERSION (major: $MAJOR, major.minor: $MAJOR_MINOR, prerelease: $IS_PRERELEASE)" - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -129,19 +131,21 @@ jobs: - name: Generate release summary run: | - echo "## Docker Release Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "**Prerelease:** ${{ steps.version.outputs.is_prerelease }}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Published Tags" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Pull Command" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + { + echo "## Docker Release Summary" + echo "" + echo "**Version:** ${{ steps.version.outputs.version }}" + echo "**Prerelease:** ${{ steps.version.outputs.is_prerelease }}" + echo "" + echo "### Published Tags" + echo "" + echo "\`\`\`" + echo "${{ steps.meta.outputs.tags }}" + echo "\`\`\`" + echo "" + echo "### Pull Command" + echo "" + echo "\`\`\`bash" + echo "docker pull ${{ env.DOCKERHUB_REPO }}:${{ steps.version.outputs.version }}" + echo "\`\`\`" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index ca7c1b9..14e78c7 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -59,7 +59,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr @@ -74,17 +74,18 @@ jobs: # For tagged releases: v1.0.0 -> 1.0.0 # For dev builds: v1.0.0-5-g1234567 -> 1.0.0.dev5+g1234567 if git describe --tags --exact-match 2>/dev/null; then - VERSION=$(git describe --tags --exact-match | sed 's/^v//') + TAG=$(git describe --tags --exact-match) + VERSION="${TAG#v}" else # Get the latest tag or use 0.0.0 if none exists LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") - BASE_VERSION=$(echo "$LATEST_TAG" | sed 's/^v//') + BASE_VERSION="${LATEST_TAG#v}" # Count commits since tag COMMITS=$(git rev-list "${LATEST_TAG}..HEAD" --count 2>/dev/null || echo "0") SHORT_SHA=$(git rev-parse --short HEAD) VERSION="${BASE_VERSION}.dev${COMMITS}+g${SHORT_SHA}" fi - echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" echo "Detected version: $VERSION" - name: Build and push Docker image @@ -116,7 +117,7 @@ jobs: if: github.event_name != 'pull_request' uses: aquasecurity/trivy-action@master with: - image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} + image-ref: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} format: "sarif" output: "trivy-results.sarif" continue-on-error: true diff --git a/.github/workflows/macro-release.yaml b/.github/workflows/macro-release.yaml index a7d7689..724cc7b 100644 --- a/.github/workflows/macro-release.yaml +++ b/.github/workflows/macro-release.yaml @@ -27,11 +27,11 @@ jobs: id: version run: | TAG="${GITHUB_REF#refs/tags/}" - echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> "$GITHUB_OUTPUT" # Extract version without 'v' prefix VERSION="${TAG#v}" - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Packaging macros for version: $VERSION" @@ -40,7 +40,7 @@ jobs: VERSION="${{ steps.version.outputs.version }}" # Create a staging directory - mkdir -p staging/freecad-macros-${VERSION} + mkdir -p "staging/freecad-macros-${VERSION}" # Copy macro directories (excluding test files and development artifacts) for macro_dir in macros/*/; do @@ -99,7 +99,7 @@ jobs: EOF # List included macros in the README - for macro_dir in staging/freecad-macros-${VERSION}/*/; do + for macro_dir in "staging/freecad-macros-${VERSION}/"*/; do if [ -d "$macro_dir" ]; then macro_name=$(basename "$macro_dir") echo "- **${macro_name}**: See ${macro_name}/README*.md for details" >> "staging/freecad-macros-${VERSION}/README.md" @@ -131,7 +131,7 @@ jobs: cd .. echo "Created archives:" - ls -la freecad-macros-${VERSION}.* + ls -la "freecad-macros-${VERSION}."* - name: Upload macro archives to release uses: softprops/action-gh-release@v2 @@ -146,24 +146,28 @@ jobs: run: | VERSION="${{ steps.version.outputs.version }}" - echo "## FreeCAD Macros Release" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Version:** ${VERSION}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Packaged Macros" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + { + echo "## FreeCAD Macros Release" + echo "" + echo "**Version:** ${VERSION}" + echo "" + echo "### Packaged Macros" + echo "" + } >> "$GITHUB_STEP_SUMMARY" for macro_dir in macros/*/; do if [ -d "$macro_dir" ]; then macro_name=$(basename "$macro_dir") - echo "- ${macro_name}" >> $GITHUB_STEP_SUMMARY + echo "- ${macro_name}" >> "$GITHUB_STEP_SUMMARY" fi done - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Download" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "The macro archives have been attached to the GitHub release:" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- \`freecad-macros-${VERSION}.tar.gz\` (Linux/macOS)" >> $GITHUB_STEP_SUMMARY - echo "- \`freecad-macros-${VERSION}.zip\` (Windows)" >> $GITHUB_STEP_SUMMARY + { + echo "" + echo "### Download" + echo "" + echo "The macro archives have been attached to the GitHub release:" + echo "" + echo "- \`freecad-macros-${VERSION}.tar.gz\` (Linux/macOS)" + echo "- \`freecad-macros-${VERSION}.zip\` (Windows)" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/macro-test.yaml b/.github/workflows/macro-test.yaml index 58b8a86..dfe46d5 100644 --- a/.github/workflows/macro-test.yaml +++ b/.github/workflows/macro-test.yaml @@ -42,7 +42,7 @@ jobs: RELEASE_INFO=$(curl -s https://api.github.com/repos/FreeCAD/FreeCAD/releases/latest) TAG_NAME=$(echo "$RELEASE_INFO" | jq -r '.tag_name') echo "Latest FreeCAD release: $TAG_NAME" - echo "tag=$TAG_NAME" >> $GITHUB_OUTPUT + echo "tag=$TAG_NAME" >> "$GITHUB_OUTPUT" # Find the Linux AppImage asset URL APPIMAGE_URL=$(echo "$RELEASE_INFO" | jq -r '.assets[] | select(.name | test("Linux-x86_64.*\\.AppImage$")) | .browser_download_url' | head -1) @@ -55,8 +55,8 @@ jobs: echo "AppImage URL: $APPIMAGE_URL" echo "AppImage name: $APPIMAGE_NAME" - echo "url=$APPIMAGE_URL" >> $GITHUB_OUTPUT - echo "name=$APPIMAGE_NAME" >> $GITHUB_OUTPUT + echo "url=$APPIMAGE_URL" >> "$GITHUB_OUTPUT" + echo "name=$APPIMAGE_NAME" >> "$GITHUB_OUTPUT" - name: Cache FreeCAD AppImage id: cache-freecad @@ -126,7 +126,7 @@ jobs: # Capture stdout to a file so we can extract the instance ID freecadcmd src/freecad_mcp/freecad_plugin/headless_server.py > /tmp/freecad_bridge.log 2>&1 & FREECAD_PID=$! - echo "FREECAD_PID=$FREECAD_PID" >> $GITHUB_ENV + echo "FREECAD_PID=$FREECAD_PID" >> "$GITHUB_ENV" # Wait for bridge to be ready (check XML-RPC port) echo "Waiting for MCP bridge to start..." @@ -138,10 +138,11 @@ jobs: echo "MCP bridge is ready (took ${i}s)" break fi - if [ $i -eq 60 ]; then + if [ "$i" -eq 60 ]; then echo "ERROR: MCP bridge did not start within 60s" - echo "=== Bridge log ===" && cat /tmp/freecad_bridge.log || true - kill $FREECAD_PID 2>/dev/null || true + echo "=== Bridge log ===" + cat /tmp/freecad_bridge.log || true + kill "$FREECAD_PID" 2>/dev/null || true exit 1 fi sleep 1 @@ -176,7 +177,7 @@ jobs: if: always() run: | if [ -n "$FREECAD_PID" ]; then - kill $FREECAD_PID 2>/dev/null || true + kill "$FREECAD_PID" 2>/dev/null || true fi lint-macros: diff --git a/.github/workflows/pypi-release.yaml b/.github/workflows/pypi-release.yaml index d40c753..d04ea2d 100644 --- a/.github/workflows/pypi-release.yaml +++ b/.github/workflows/pypi-release.yaml @@ -26,7 +26,7 @@ jobs: id: version run: | TAG="${GITHUB_REF#refs/tags/}" - echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> "$GITHUB_OUTPUT" # Validate semantic versioning format (v1.2.3 or v1.2.3-prerelease) if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then @@ -36,16 +36,18 @@ jobs: # Extract version without 'v' prefix VERSION="${TAG#v}" - echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> "$GITHUB_OUTPUT" # Check if this is a prerelease if [[ "$TAG" =~ -[a-zA-Z0-9.]+ ]]; then - echo "is_prerelease=true" >> $GITHUB_OUTPUT + echo "is_prerelease=true" >> "$GITHUB_OUTPUT" + IS_PRERELEASE="true" else - echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "is_prerelease=false" >> "$GITHUB_OUTPUT" + IS_PRERELEASE="false" fi - echo "Parsed version: $VERSION (prerelease: ${{ steps.version.outputs.is_prerelease }})" + echo "Parsed version: $VERSION (prerelease: $IS_PRERELEASE)" - name: Install uv uses: astral-sh/setup-uv@v7 @@ -205,32 +207,40 @@ jobs: steps: - name: Generate release summary + env: + REF_NAME: ${{ github.ref_name }} run: | - echo "## PyPI Release Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Tag:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY + { + echo "## PyPI Release Summary" + echo "" + echo "**Tag:** $REF_NAME" + echo "" + } >> "$GITHUB_STEP_SUMMARY" - if [[ "${{ github.ref }}" == *"-"* ]]; then - echo "**Type:** Prerelease (published to TestPyPI)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Install from TestPyPI" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ freecad-robust-mcp" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + if [[ "$REF_NAME" == *"-"* ]]; then + { + echo "**Type:** Prerelease (published to TestPyPI)" + echo "" + echo "### Install from TestPyPI" + echo "" + echo "\`\`\`bash" + echo "pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ freecad-robust-mcp" + echo "\`\`\`" + } >> "$GITHUB_STEP_SUMMARY" else - echo "**Type:** Stable release (published to PyPI)" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Install from PyPI" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "pip install freecad-robust-mcp" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Or with uv:" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "uv pip install freecad-robust-mcp" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + { + echo "**Type:** Stable release (published to PyPI)" + echo "" + echo "### Install from PyPI" + echo "" + echo "\`\`\`bash" + echo "pip install freecad-robust-mcp" + echo "\`\`\`" + echo "" + echo "Or with uv:" + echo "" + echo "\`\`\`bash" + echo "uv pip install freecad-robust-mcp" + echo "\`\`\`" + } >> "$GITHUB_STEP_SUMMARY" fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eab5fc5..998fdad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -193,6 +193,15 @@ repos: - id: check-dependabot name: validate Dependabot config + # ========================================================================== + # GitHub Actions Linting + # ========================================================================== + - repo: https://github.com/rhysd/actionlint + rev: v1.7.4 + hooks: + - id: actionlint + name: actionlint (GitHub Actions linter) + # ========================================================================== # Shell Script Linting # ==========================================================================