Created
September 24, 2025 22:35
-
-
Save askb/1be8f3235a0ef5877cbda5ec8c5bfdc5 to your computer and use it in GitHub Desktop.
packer merge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # Copyright (c) 2025 The Linux Foundation | |
| name: Gerrit CI Packer Merge using Tailscale VPN and SSH Tunnel | |
| # yamllint disable-line rule:truthy | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| GERRIT_BRANCH: | |
| description: "Branch that change is against" | |
| required: true | |
| type: string | |
| GERRIT_CHANGE_ID: | |
| description: "The ID for the change" | |
| required: true | |
| type: string | |
| GERRIT_CHANGE_NUMBER: | |
| description: "The Gerrit number" | |
| required: true | |
| type: string | |
| GERRIT_CHANGE_URL: | |
| description: "URL to the change" | |
| required: true | |
| type: string | |
| GERRIT_EVENT_TYPE: | |
| description: "Type of Gerrit event" | |
| required: true | |
| type: string | |
| GERRIT_PATCHSET_NUMBER: | |
| description: "The patch number for the change" | |
| required: true | |
| type: string | |
| GERRIT_PATCHSET_REVISION: | |
| description: "The revision sha" | |
| required: true | |
| type: string | |
| GERRIT_PROJECT: | |
| description: "Project in Gerrit" | |
| required: true | |
| type: string | |
| GERRIT_REFSPEC: | |
| description: "Gerrit refspec to fetch change" | |
| required: true | |
| type: string | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PACKER_VERSION: "1.11.2" | |
| OS_CLOUD: "vex" | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has-changes: ${{ steps.check.outputs.has-changes }} | |
| steps: | |
| - name: Gerrit Checkout | |
| # yamllint disable-line rule:line-length | |
| uses: lfit/checkout-gerrit-change-action@54d751e8bd167bc91f7d665dabe33fae87aaaa63 # v0.9 | |
| with: | |
| gerrit-refspec: ${{ inputs.GERRIT_REFSPEC || github.ref }} | |
| delay: "0s" | |
| - name: Clone git submodules | |
| shell: bash | |
| run: git submodule update --init | |
| - name: Check for changes | |
| # yamllint disable-line rule:line-length | |
| uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 | |
| id: check | |
| with: | |
| base: ${{ inputs.GERRIT_BRANCH || github.event.repository.default_branch }} | |
| ref: ${{ inputs.GERRIT_PATCHSET_REVISION || github.sha }} | |
| filters: | | |
| has-changes: | |
| - 'vars/**' | |
| - 'templates/**' | |
| - 'provision/**' | |
| packer-build: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| if: needs.prepare.outputs.has-changes == 'true' | |
| steps: | |
| - name: Checkout code | |
| # yamllint disable-line rule:line-length | |
| uses: lfit/checkout-gerrit-change-action@54d751e8bd167bc91f7d665dabe33fae87aaaa63 # v0.9 | |
| with: | |
| gerrit-refspec: ${{ inputs.GERRIT_REFSPEC || github.ref }} | |
| delay: "0s" | |
| - name: Clone git submodules | |
| shell: bash | |
| run: git submodule update --init | |
| # Step 1: Connect to Tailscale VPN | |
| - name: Setup Tailscale VPN | |
| uses: tailscale/github-action@v2 | |
| with: | |
| oauth-key: ${{ secrets.TAILSCALE_OAUTH_KEY }} | |
| tags: tag:ci | |
| hostname: github-actions-${{ github.run_id }} | |
| # Step 2: Verify Tailscale connection | |
| - name: Verify Tailscale connection | |
| run: | | |
| echo "Tailscale status:" | |
| sudo tailscale status | |
| echo "Checking connectivity to Jenkins server..." | |
| ping -c 3 ${{ secrets.JENKINS_TAILSCALE_IP }} || echo "Ping failed, but SSH might still work" | |
| # Step 3: Setup SSH credentials | |
| - name: Setup SSH key | |
| env: | |
| SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| JENKINS_TAILSCALE_IP: ${{ secrets.JENKINS_TAILSCALE_IP }} | |
| run: | | |
| mkdir -p ~/.ssh | |
| echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa | |
| chmod 600 ~/.ssh/id_rsa | |
| # Add Jenkins server's Tailscale IP to known hosts | |
| ssh-keyscan -H $JENKINS_TAILSCALE_IP >> ~/.ssh/known_hosts | |
| # Step 4: Start SSH tunnel through Tailscale | |
| - name: Start SSH tunnel via Tailscale | |
| env: | |
| JENKINS_TAILSCALE_IP: ${{ secrets.JENKINS_TAILSCALE_IP }} | |
| JENKINS_USER: ${{ secrets.JENKINS_USER }} | |
| run: | | |
| # Start SSH tunnel using Tailscale IP | |
| ssh -D 1080 -f -N -o StrictHostKeyChecking=no \ | |
| -o ConnectTimeout=30 -o ServerAliveInterval=60 \ | |
| ${JENKINS_USER}@${JENKINS_TAILSCALE_IP} | |
| # Store PID and verify tunnel | |
| sleep 5 | |
| SSH_PID=$(pgrep -f "ssh -D 1080") | |
| echo "SSH_TUNNEL_PID=${SSH_PID}" >> $GITHUB_ENV | |
| if [ ! -z "$SSH_PID" ]; then | |
| echo "✅ SSH tunnel started successfully with PID: ${SSH_PID}" | |
| else | |
| echo "❌ Failed to start SSH tunnel" | |
| exit 1 | |
| fi | |
| # Step 5: Setup Packer | |
| - name: Setup packer | |
| uses: hashicorp/setup-packer@main | |
| id: setup | |
| with: | |
| version: ${{ env.PACKER_VERSION }} | |
| # Step 6: Create cloud configuration files | |
| - name: Create cloud-env file required for packer | |
| id: create-cloud-env-file | |
| shell: bash | |
| run: | | |
| echo "${{ secrets.CLOUD_ENV_B64 }}" | base64 --decode \ | |
| > "${GITHUB_WORKSPACE}/cloud-env.pkrvars.hcl" | |
| - name: Create cloud.yaml file for openstack client | |
| id: create-cloud-yaml-file | |
| shell: bash | |
| run: | | |
| mkdir -p "$HOME/.config/openstack" | |
| echo "${{ secrets.CLOUDS_YAML_B64 }}" | base64 --decode \ | |
| > "$HOME/.config/openstack/clouds.yaml" | |
| - name: Setup Python | |
| # yamllint disable-line rule:line-length | |
| uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Install openstack deps | |
| id: install-openstack-deps | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install python-openstackclient | |
| pip freeze | |
| # Step 7: Configure proxy if needed | |
| - name: Configure proxy for Packer | |
| run: | | |
| # Set SOCKS proxy for applications that need it | |
| echo "HTTP_PROXY=socks5://127.0.0.1:1080" >> $GITHUB_ENV | |
| echo "HTTPS_PROXY=socks5://127.0.0.1:1080" >> $GITHUB_ENV | |
| # Step 8: Initialize and validate Packer templates | |
| - name: Initialize and validate Packer files | |
| shell: bash | |
| run: | | |
| set -x | |
| varfiles=(vars/*.pkrvars.hcl) | |
| templates=(templates/*.pkr.hcl) | |
| mkdir -p "${GITHUB_WORKSPACE}/logs" | |
| PACKER_LOGS_DIR="${GITHUB_WORKSPACE}/logs" | |
| for varfile in "${varfiles[@]}"; do | |
| if [[ "$varfile" == *"cloud-env.json"* ]] || \ | |
| [[ "$varfile" == "vars/*.json" ]] || \ | |
| [[ "$varfile" == *"cloud-env.pkrvars.hcl"* ]] || \ | |
| [[ "$varfile" == *"cloud-env-aws.pkrvars.hcl"* ]] || \ | |
| [[ "$varfile" == "vars/*.pkrvars.hcl" ]]; then | |
| continue | |
| fi | |
| echo "::group::Validating $varfile" | |
| echo "-----> Test var: $varfile" | |
| for template in "${templates[@]}"; do | |
| if [[ "$template" == *"variables.pkr.hcl"* ]] || \ | |
| [[ "$template" == *"variables.auto.pkr.hcl"* ]]; then | |
| continue | |
| fi | |
| if [[ "${template#*.}" == "pkr.hcl" ]]; then | |
| echo "packer init $template ..." | |
| packer init "$template" | |
| fi | |
| LOG_FILE="packer-validate-${varfile##*/}-${template##*/}.log" | |
| export PACKER_LOG="yes" | |
| export PACKER_LOG_PATH="$PACKER_LOGS_DIR/$LOG_FILE" | |
| if output=$(OS_CLOUD=${{ env.OS_CLOUD }} packer validate \ | |
| -var-file="${GITHUB_WORKSPACE}/cloud-env.pkrvars.hcl" \ | |
| -var-file="$varfile" "$template"); then | |
| echo "$template: $output" | |
| else | |
| echo "$template: $output" | |
| exit 1 | |
| fi | |
| done | |
| echo "::endgroup::" | |
| done | |
| # Step 9: Run Packer build (selective - only build specific templates) | |
| - name: Run Packer build | |
| shell: bash | |
| env: | |
| # Limit builds to prevent resource exhaustion | |
| BUILD_LIMIT: 1 | |
| run: | | |
| set -x | |
| varfiles=(vars/*.pkrvars.hcl) | |
| templates=(templates/*.pkr.hcl) | |
| mkdir -p "${GITHUB_WORKSPACE}/build-logs" | |
| PACKER_LOGS_DIR="${GITHUB_WORKSPACE}/build-logs" | |
| build_count=0 | |
| max_builds=${BUILD_LIMIT:-1} | |
| for varfile in "${varfiles[@]}"; do | |
| # Skip example and cloud-env files | |
| if [[ "$varfile" == *"cloud-env.json"* ]] || \ | |
| [[ "$varfile" == "vars/*.json" ]] || \ | |
| [[ "$varfile" == *"cloud-env.pkrvars.hcl"* ]] || \ | |
| [[ "$varfile" == *"cloud-env-aws.pkrvars.hcl"* ]] || \ | |
| [[ "$varfile" == "vars/*.pkrvars.hcl" ]]; then | |
| continue | |
| fi | |
| # Only build Ubuntu 22.04 by default to limit resource usage | |
| if [[ "$varfile" != *"ubuntu-22.04"* ]]; then | |
| echo "Skipping $varfile (not ubuntu-22.04)" | |
| continue | |
| fi | |
| if [ $build_count -ge $max_builds ]; then | |
| echo "Reached maximum builds limit ($max_builds), stopping." | |
| break | |
| fi | |
| echo "::group::Building with $varfile" | |
| echo "-----> Building var: $varfile" | |
| for template in "${templates[@]}"; do | |
| if [[ "$template" == *"variables.pkr.hcl"* ]] || \ | |
| [[ "$template" == *"variables.auto.pkr.hcl"* ]] || \ | |
| [[ "$template" == *"windows"* ]]; then | |
| continue | |
| fi | |
| # Only build basic templates | |
| if [[ "$template" != *"builder.pkr.hcl" ]]; then | |
| echo "Skipping $template (not builder.pkr.hcl)" | |
| continue | |
| fi | |
| LOG_FILE="packer-build-${varfile##*/}-${template##*/}.log" | |
| export PACKER_LOG="yes" | |
| export PACKER_LOG_PATH="$PACKER_LOGS_DIR/$LOG_FILE" | |
| echo "Building $template with $varfile..." | |
| if OS_CLOUD=${{ env.OS_CLOUD }} packer build \ | |
| -var-file="${GITHUB_WORKSPACE}/cloud-env.pkrvars.hcl" \ | |
| -var-file="$varfile" "$template"; then | |
| echo "✅ Successfully built $template with $varfile" | |
| build_count=$((build_count + 1)) | |
| else | |
| echo "❌ Failed to build $template with $varfile" | |
| # Continue with other builds instead of failing completely | |
| echo "::warning::Build failed for $template with $varfile" | |
| fi | |
| # Only build one template per varfile to limit resource usage | |
| break | |
| done | |
| echo "::endgroup::" | |
| done | |
| echo "Completed $build_count builds" | |
| # Step 10: Upload build artifacts and logs | |
| - name: Upload Packer logs | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: packer-logs | |
| path: | | |
| logs/ | |
| build-logs/ | |
| retention-days: 30 | |
| # Step 11: Cleanup (always runs) | |
| - name: Cleanup SSH tunnel and Tailscale | |
| if: always() | |
| run: | | |
| # Kill SSH tunnel | |
| if [ ! -z "$SSH_TUNNEL_PID" ]; then | |
| kill $SSH_TUNNEL_PID 2>/dev/null || true | |
| echo "SSH tunnel cleaned up" | |
| fi | |
| # Tailscale cleanup (optional - runner will be destroyed anyway) | |
| sudo tailscale logout || true | |
| vote: | |
| if: always() && needs.prepare.outputs.has-changes == 'true' | |
| needs: [prepare, packer-build-tailscale] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Get workflow conclusion | |
| uses: technote-space/workflow-conclusion-action@v3 | |
| - name: Set vote | |
| uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8 | |
| with: | |
| host: ${{ vars.GERRIT_SERVER }} | |
| username: ${{ vars.GERRIT_SSH_USER }} | |
| key: ${{ secrets.GERRIT_SSH_PRIVKEY }} | |
| known_hosts: ${{ secrets.GERRIT_KNOWN_HOSTS }} | |
| gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }} | |
| gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }} | |
| vote-type: ${{ env.WORKFLOW_CONCLUSION }} | |
| comment-only: true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment