|
| 1 | +name: Build NAT64 Appliance Image |
| 2 | + |
| 3 | +on: |
| 4 | + # Allows you to run this workflow manually from the Actions tab |
| 5 | + workflow_dispatch: |
| 6 | + inputs: |
| 7 | + release_tag: |
| 8 | + description: 'Release tag (e.g., v1.0.0)' |
| 9 | + required: true |
| 10 | + ci_framework_repo: |
| 11 | + description: 'ci-framework repository in owner/repo format (for testing forks)' |
| 12 | + required: false |
| 13 | + default: 'openstack-k8s-operators/ci-framework' |
| 14 | + ci_framework_ref: |
| 15 | + description: 'ci-framework branch/tag/PR (e.g., main, my-branch, refs/pull/123/head)' |
| 16 | + required: false |
| 17 | + default: 'main' |
| 18 | + |
| 19 | + # Run weekly on Mondays at 00:00 UTC |
| 20 | + schedule: |
| 21 | + - cron: '0 0 * * 1' |
| 22 | + |
| 23 | +jobs: |
| 24 | + build-image: |
| 25 | + runs-on: ubuntu-22.04 |
| 26 | + |
| 27 | + # Permission needed to create a release and upload assets |
| 28 | + permissions: |
| 29 | + contents: write |
| 30 | + |
| 31 | + env: |
| 32 | + WORK_DIR: ${{ github.workspace }}/ci-framework-data |
| 33 | + |
| 34 | + steps: |
| 35 | + - name: 1. Install System Dependencies |
| 36 | + run: | |
| 37 | + echo "Installing system dependencies for diskimage-builder..." |
| 38 | + sudo apt-get update |
| 39 | + sudo apt-get install -y \ |
| 40 | + python3-pip \ |
| 41 | + python3-venv \ |
| 42 | + qemu-utils \ |
| 43 | + dosfstools \ |
| 44 | + xfsprogs \ |
| 45 | + kpartx \ |
| 46 | + debootstrap \ |
| 47 | + gdisk \ |
| 48 | + git |
| 49 | + echo "System dependencies installed" |
| 50 | +
|
| 51 | + - name: 2. Clone ci-framework Repository |
| 52 | + uses: actions/checkout@v4 |
| 53 | + with: |
| 54 | + repository: ${{ inputs.ci_framework_repo || 'openstack-k8s-operators/ci-framework' }} |
| 55 | + ref: ${{ inputs.ci_framework_ref || 'main' }} |
| 56 | + path: ci-framework |
| 57 | + fetch-depth: 1 |
| 58 | + |
| 59 | + - name: 3. Setup Python Virtual Environment |
| 60 | + run: | |
| 61 | + echo "Creating Python virtual environment at ~/nat64_venv..." |
| 62 | + python3 -m venv ~/nat64_venv |
| 63 | + source ~/nat64_venv/bin/activate |
| 64 | +
|
| 65 | + pip install --upgrade pip setuptools wheel |
| 66 | +
|
| 67 | + echo "Installing Ansible and diskimage-builder..." |
| 68 | + pip install ansible-core |
| 69 | + pip install diskimage-builder |
| 70 | +
|
| 71 | + echo "Environment setup complete" |
| 72 | +
|
| 73 | + - name: 4. Create Ansible Playbook for Building NAT64 Image |
| 74 | + run: | |
| 75 | + cat > ${{ github.workspace }}/build-nat64.yml << 'EOF' |
| 76 | + --- |
| 77 | + - name: Build nat64-appliance image |
| 78 | + hosts: localhost |
| 79 | + connection: local |
| 80 | + gather_facts: true |
| 81 | + vars: |
| 82 | + cifmw_basedir: "${{ env.WORK_DIR }}" |
| 83 | + cifmw_nat64_appliance_run_dib_as_root: false |
| 84 | + cifmw_nat64_appliance_use_ci_script: false |
| 85 | + cifmw_nat64_appliance_venv_dir: "{{ ansible_env.HOME }}/nat64_venv" |
| 86 | + roles: |
| 87 | + - nat64_appliance |
| 88 | + EOF |
| 89 | +
|
| 90 | + echo "Ansible playbook created" |
| 91 | + cat ${{ github.workspace }}/build-nat64.yml |
| 92 | +
|
| 93 | + - name: 5. Run Ansible Playbook to Build Image |
| 94 | + run: | |
| 95 | + echo "Building NAT64 appliance image using Ansible..." |
| 96 | + echo "Using ci-framework role defaults for DIB configuration" |
| 97 | + echo "Skipping package installation (already done in step 3)" |
| 98 | +
|
| 99 | + cd ${{ github.workspace }}/ci-framework |
| 100 | +
|
| 101 | + # Activate the venv and run the playbook |
| 102 | + source ~/nat64_venv/bin/activate |
| 103 | +
|
| 104 | + # Skip 'packages' tag since we already installed dependencies |
| 105 | + # Use our venv at ~/nat64_venv instead of letting role create one |
| 106 | + ANSIBLE_ROLES_PATH=${{ github.workspace }}/ci-framework/roles \ |
| 107 | + ansible-playbook -v \ |
| 108 | + --skip-tags packages \ |
| 109 | + ${{ github.workspace }}/build-nat64.yml |
| 110 | +
|
| 111 | + echo "Image build completed successfully" |
| 112 | +
|
| 113 | + - name: 6. Verify Built Image |
| 114 | + run: | |
| 115 | + IMAGE_PATH="${{ env.WORK_DIR }}/nat64_appliance/nat64-appliance.qcow2" |
| 116 | +
|
| 117 | + if [ -f "$IMAGE_PATH" ]; then |
| 118 | + echo "Built image details:" |
| 119 | + ls -lh "$IMAGE_PATH" |
| 120 | + qemu-img info "$IMAGE_PATH" |
| 121 | + else |
| 122 | + echo "ERROR: Image file not found at $IMAGE_PATH" |
| 123 | + echo "Checking work directory contents:" |
| 124 | + ls -laR "${{ env.WORK_DIR }}" |
| 125 | + exit 1 |
| 126 | + fi |
| 127 | +
|
| 128 | + - name: 7. Create Unique Release Tag and Rename Image |
| 129 | + id: release_tag |
| 130 | + run: | |
| 131 | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then |
| 132 | + # Manual run: use provided tag |
| 133 | + RELEASE_TAG="${{ inputs.release_tag }}" |
| 134 | + else |
| 135 | + # Scheduled run: generate timestamp-based tag |
| 136 | + RELEASE_TAG="build-$(date +%Y%m%d-%H%M%S)" |
| 137 | + fi |
| 138 | +
|
| 139 | + echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT |
| 140 | + echo "Release tag: $RELEASE_TAG" |
| 141 | +
|
| 142 | + # Rename image to include the release tag |
| 143 | + TAGGED_IMAGE="nat64-appliance-${RELEASE_TAG}.qcow2" |
| 144 | + mv ${{ env.WORK_DIR }}/nat64_appliance/nat64-appliance.qcow2 \ |
| 145 | + ${{ github.workspace }}/${TAGGED_IMAGE} |
| 146 | +
|
| 147 | + echo "tagged_image=$TAGGED_IMAGE" >> $GITHUB_OUTPUT |
| 148 | + echo "Image renamed to: $TAGGED_IMAGE" |
| 149 | + ls -lh ${{ github.workspace }}/${TAGGED_IMAGE} |
| 150 | +
|
| 151 | + - name: 8. Create Versioned GitHub Release |
| 152 | + uses: softprops/action-gh-release@v2 |
| 153 | + with: |
| 154 | + tag_name: ${{ steps.release_tag.outputs.release_tag }} |
| 155 | + name: "NAT64 Appliance ${{ steps.release_tag.outputs.release_tag }}" |
| 156 | + body: | |
| 157 | + NAT64 Appliance image built by GitHub Actions. |
| 158 | +
|
| 159 | + ## Build Configuration |
| 160 | +
|
| 161 | + - **ci-framework repository**: `${{ inputs.ci_framework_repo || 'openstack-k8s-operators/ci-framework' }}` |
| 162 | + - **ci-framework reference**: `${{ inputs.ci_framework_ref || 'main' }}` |
| 163 | + - **Build type**: ${{ github.event_name == 'schedule' && 'Scheduled (weekly)' || 'Manual dispatch' }} |
| 164 | +
|
| 165 | + DIB configuration uses defaults from the ci-framework nat64_appliance role. |
| 166 | +
|
| 167 | + ## Usage |
| 168 | +
|
| 169 | + For usage instructions and deployment examples, see the |
| 170 | + [nat64_appliance README](https://github.com/openstack-k8s-operators/ci-framework/blob/main/roles/nat64_appliance/README.md). |
| 171 | + files: ${{ steps.release_tag.outputs.tagged_image }} |
| 172 | + |
| 173 | + - name: 9. Update 'latest' Release Tag |
| 174 | + uses: softprops/action-gh-release@v2 |
| 175 | + with: |
| 176 | + tag_name: latest |
| 177 | + name: "NAT64 Appliance (Latest)" |
| 178 | + prerelease: true |
| 179 | + body: | |
| 180 | + This is the latest NAT64 Appliance build. This release is automatically updated. |
| 181 | +
|
| 182 | + **Latest Build**: ${{ steps.release_tag.outputs.release_tag }} |
| 183 | +
|
| 184 | + ## Download |
| 185 | +
|
| 186 | + Use the static URL for the latest build: |
| 187 | + ```bash |
| 188 | + curl -L -O https://github.com/openstack-k8s-operators/openstack-k8s-operators-ci/releases/download/latest/nat64-appliance-latest.qcow2 |
| 189 | + ``` |
| 190 | +
|
| 191 | + Or download a specific version from the [releases page](https://github.com/openstack-k8s-operators/openstack-k8s-operators-ci/releases) |
| 192 | + to pin your CI to a known-good build. |
| 193 | +
|
| 194 | + ## Usage |
| 195 | +
|
| 196 | + For usage instructions and deployment examples, see the |
| 197 | + [nat64_appliance README](https://github.com/openstack-k8s-operators/ci-framework/blob/main/roles/nat64_appliance/README.md). |
| 198 | + files: ${{ steps.release_tag.outputs.tagged_image }} |
| 199 | + |
| 200 | + - name: 10. Upload Image with Static Filename to Latest Release |
| 201 | + run: | |
| 202 | + echo "Creating static filename copy for easy downloading..." |
| 203 | +
|
| 204 | + # Copy the image with a static name |
| 205 | + cp ${{ github.workspace }}/${{ steps.release_tag.outputs.tagged_image }} \ |
| 206 | + ${{ github.workspace }}/nat64-appliance-latest.qcow2 |
| 207 | +
|
| 208 | + echo "Uploading to latest release with static filename..." |
| 209 | + # Upload to latest release, replacing any existing file with the same name |
| 210 | + gh release upload latest nat64-appliance-latest.qcow2 --clobber --repo ${{ github.repository }} |
| 211 | +
|
| 212 | + echo "Static URL available at:" |
| 213 | + echo "https://github.com/${{ github.repository }}/releases/download/latest/nat64-appliance-latest.qcow2" |
| 214 | + env: |
| 215 | + GH_TOKEN: ${{ github.token }} |
| 216 | + |
| 217 | + - name: 11. Cleanup Old Releases (Keep Last 4) |
| 218 | + if: github.event_name == 'schedule' |
| 219 | + run: | |
| 220 | + echo "Cleaning up old scheduled builds, keeping last 4..." |
| 221 | +
|
| 222 | + # Get all releases with 'build-' prefix, sort by creation date (newest first) |
| 223 | + # Skip the first 4 (keep them), delete the rest |
| 224 | + OLD_RELEASES=$(gh release list --limit 100 --json tagName,createdAt --repo ${{ github.repository }} \ |
| 225 | + | jq -r '.[] | select(.tagName | startswith("build-")) | .tagName' \ |
| 226 | + | sort -r \ |
| 227 | + | tail -n +5) |
| 228 | +
|
| 229 | + if [ -n "$OLD_RELEASES" ]; then |
| 230 | + echo "Deleting old releases:" |
| 231 | + echo "$OLD_RELEASES" |
| 232 | + echo "$OLD_RELEASES" | xargs -I {} gh release delete {} --yes --cleanup-tag --repo ${{ github.repository }} |
| 233 | + echo "Cleanup complete" |
| 234 | + else |
| 235 | + echo "No old releases to delete (fewer than 5 total)" |
| 236 | + fi |
| 237 | + env: |
| 238 | + GH_TOKEN: ${{ github.token }} |
0 commit comments