Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 346 additions & 0 deletions .github/workflows/build-live-iso.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
name: Build Live ISO draft release
run-name: Build Live ISO for ${{ inputs.tag || 'site-takeover-live-v0.1.0-rc1' }}

on:
# workflow_dispatch is shown only after this file reaches the default branch.
# This exact feature branch trigger bootstraps the first draft release.
push:
branches:
- "feat/live-usb-b2r"
workflow_dispatch:
inputs:
tag:
description: "Draft prerelease tag, for example site-takeover-v0.1.0"
required: true
default: "site-takeover-live-v0.1.0-rc1"
type: string

permissions: {}

concurrency:
group: live-iso-${{ inputs.tag || 'site-takeover-live-v0.1.0-rc1' }}
cancel-in-progress: false

jobs:
checks:
name: Required checks
uses: ./.github/workflows/test.yml
permissions:
contents: read

build:
name: Build release assets
needs: checks
runs-on: ubuntu-24.04
timeout-minutes: 180
permissions:
contents: read

env:
ISO_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.iso
SHA256_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.iso.sha256
BOOT_REPORT_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.boot.txt

steps:
- name: Check out selected source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.sha }}

- name: Validate tag and selected commit
env:
RELEASE_TAG: ${{ inputs.tag || 'site-takeover-live-v0.1.0-rc1' }}
SELECTED_SHA: ${{ github.sha }}
shell: bash
run: |
set -Eeuo pipefail

if [[ ! "${RELEASE_TAG}" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
printf 'Invalid tag: use 1-128 ASCII letters, digits, dots, underscores, or hyphens.\n' >&2
exit 1
fi

actual_sha="$(git rev-parse HEAD)"
[[ "${actual_sha}" == "${SELECTED_SHA}" ]] || {
printf 'Checked-out commit %s does not match selected commit %s.\n' "${actual_sha}" "${SELECTED_SHA}" >&2
exit 1
}

if git show-ref --verify --quiet "refs/tags/${RELEASE_TAG}"; then
tag_sha="$(git rev-list -n 1 "refs/tags/${RELEASE_TAG}")"
[[ "${tag_sha}" == "${SELECTED_SHA}" ]] || {
printf 'Existing tag %s points to %s, not selected commit %s.\n' \
"${RELEASE_TAG}" "${tag_sha}" "${SELECTED_SHA}" >&2
exit 1
}
fi

- name: Build in privileged Debian 13
shell: bash
run: |
set -Eeuo pipefail
docker run --rm --privileged \
--volume "${GITHUB_WORKSPACE}:/workspace" \
--workdir /workspace \
debian:13-slim \
bash -Eeuo pipefail -c '
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
ca-certificates \
git \
live-build \
rsync \
wget \
xorriso
rm -rf /var/lib/apt/lists/*
git config --global --add safe.directory /workspace
./labs/site-takeover/live/build.sh
'

- name: Verify release assets
shell: bash
run: |
set -Eeuo pipefail
[[ -s "${ISO_PATH}" ]]
[[ -s "${SHA256_PATH}" ]]
[[ -s "${BOOT_REPORT_PATH}" ]]

iso_dir="$(dirname "${ISO_PATH}")"
iso_name="$(basename "${ISO_PATH}")"
checksum_name="$(basename "${SHA256_PATH}")"
(
cd "${iso_dir}"
sha256sum --check "${checksum_name}"
)

checksum_target="$(awk 'NR == 1 { print $2 }' "${SHA256_PATH}")"
[[ "${checksum_target}" == "${iso_name}" ]] || {
printf 'Checksum asset names %s, expected %s.\n' "${checksum_target}" "${iso_name}" >&2
exit 1
}

- name: Transfer verified release assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: site-takeover-live-release-assets
path: |
labs/site-takeover/live/dist/site-takeover-live-amd64.iso
labs/site-takeover/live/dist/site-takeover-live-amd64.iso.sha256
labs/site-takeover/live/dist/site-takeover-live-amd64.boot.txt
if-no-files-found: error
compression-level: 0
overwrite: true
retention-days: 1

release:
name: Update draft prerelease
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write

env:
ISO_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.iso
SHA256_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.iso.sha256
BOOT_REPORT_PATH: labs/site-takeover/live/dist/site-takeover-live-amd64.boot.txt
OPERATOR_KIT_PATH: labs/site-takeover/live/dist/site-takeover-operator-kit.zip
OPERATOR_KIT_SHA256_PATH: labs/site-takeover/live/dist/site-takeover-operator-kit.zip.sha256

steps:
- name: Check out selected source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.sha }}

- name: Download verified release assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: site-takeover-live-release-assets
path: labs/site-takeover/live/dist

- name: Package operator methods from selected source
env:
SELECTED_SHA: ${{ github.sha }}
shell: bash
run: |
set -Eeuo pipefail

rm -f -- "${OPERATOR_KIT_PATH}" "${OPERATOR_KIT_SHA256_PATH}"
git archive \
--format=zip \
--output="${OPERATOR_KIT_PATH}" \
"${SELECTED_SHA}" \
-- \
METHODS.md \
README.md \
PROJECT_CONSTITUTION.md \
DESIGN.md \
TASK_CONTRACT.md \
labs/site-takeover/README.md \
labs/site-takeover/VERIFICATION.md \
labs/site-takeover/live/README.md \
labs/site-takeover/operator/company-bootstrap.ps1 \
labs/site-takeover/operator/COMPANY-SETUP.md \
labs/site-takeover/operator/USB.md \
labs/site-takeover/operator/DAY-OF.md \
labs/site-takeover/operator/KALI-PREFLIGHT.md \
labs/site-takeover/operator/ROOT-BONUS.md

(
cd "$(dirname "${OPERATOR_KIT_PATH}")"
sha256sum "$(basename "${OPERATOR_KIT_PATH}")" \
> "$(basename "${OPERATOR_KIT_SHA256_PATH}")"
sha256sum --check "$(basename "${OPERATOR_KIT_SHA256_PATH}")"
)

- name: Revalidate tag and release assets
env:
RELEASE_TAG: ${{ inputs.tag || 'site-takeover-live-v0.1.0-rc1' }}
SELECTED_SHA: ${{ github.sha }}
shell: bash
run: |
set -Eeuo pipefail

if [[ ! "${RELEASE_TAG}" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]; then
printf 'Invalid release tag.\n' >&2
exit 1
fi

actual_sha="$(git rev-parse HEAD)"
[[ "${actual_sha}" == "${SELECTED_SHA}" ]] || {
printf 'Checked-out commit %s does not match selected commit %s.\n' "${actual_sha}" "${SELECTED_SHA}" >&2
exit 1
}

if git show-ref --verify --quiet "refs/tags/${RELEASE_TAG}"; then
tag_sha="$(git rev-list -n 1 "refs/tags/${RELEASE_TAG}")"
[[ "${tag_sha}" == "${SELECTED_SHA}" ]] || {
printf 'Existing tag %s moved to %s during the build.\n' "${RELEASE_TAG}" "${tag_sha}" >&2
exit 1
}
fi

[[ -s "${ISO_PATH}" ]]
[[ -s "${SHA256_PATH}" ]]
[[ -s "${BOOT_REPORT_PATH}" ]]
[[ -s "${OPERATOR_KIT_PATH}" ]]
[[ -s "${OPERATOR_KIT_SHA256_PATH}" ]]
(
cd "$(dirname "${ISO_PATH}")"
sha256sum --check "$(basename "${SHA256_PATH}")"
sha256sum --check "$(basename "${OPERATOR_KIT_SHA256_PATH}")"
)

- name: Create or update draft prerelease
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag || 'site-takeover-live-v0.1.0-rc1' }}
SELECTED_SHA: ${{ github.sha }}
shell: bash
run: |
set -Eeuo pipefail

notes_file="$(mktemp)"
trap 'rm -f -- "${notes_file}"' EXIT
cat > "${notes_file}" <<EOF
Automated Live ISO build.

- Source commit: \`${SELECTED_SHA}\`
- Workflow run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}
- Operator kit: source hierarchy archived from the same commit, with a separate SHA-256
- Status: draft prerelease; operator validation is still required
EOF

release_info=""
if release_info="$(
gh release view "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--json isDraft,targetCommitish \
--jq '[.isDraft, .targetCommitish] | @tsv' \
2>/dev/null
)"; then
IFS=$'\t' read -r is_draft target_commitish <<< "${release_info}"
[[ "${is_draft}" == "true" ]] || {
printf 'Refusing to modify non-draft release %s.\n' "${RELEASE_TAG}" >&2
exit 1
}

if ! git show-ref --verify --quiet "refs/tags/${RELEASE_TAG}"; then
[[ "${target_commitish}" == "${SELECTED_SHA}" ]] || {
printf 'Draft release %s targets %s, not selected commit %s.\n' \
"${RELEASE_TAG}" "${target_commitish}" "${SELECTED_SHA}" >&2
exit 1
}
fi

gh release edit "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--draft \
--prerelease \
--title "Site Takeover Live ISO ${RELEASE_TAG}" \
--notes-file "${notes_file}"
else
gh release create "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--target "${SELECTED_SHA}" \
--draft \
--prerelease \
--title "Site Takeover Live ISO ${RELEASE_TAG}" \
--notes-file "${notes_file}"
fi

release_assets=(
"${ISO_PATH}"
"${SHA256_PATH}"
"${BOOT_REPORT_PATH}"
"${OPERATOR_KIT_PATH}"
"${OPERATOR_KIT_SHA256_PATH}"
"labs/site-takeover/operator/COMPANY-SETUP.md"
)

upload_state="$(
gh release view "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--json isDraft \
--jq '.isDraft'
)"
[[ "${upload_state}" == "true" ]] || {
printf 'Refusing to upload to non-draft release %s.\n' "${RELEASE_TAG}" >&2
exit 1
}

gh release upload "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--clobber \
"${release_assets[@]}"

final_state="$(
gh release view "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--json isDraft,isPrerelease \
--jq '[.isDraft, .isPrerelease] | @tsv'
)"
[[ "${final_state}" == $'true\ttrue' ]] || {
printf 'Release safety check failed: %s\n' "${final_state}" >&2
exit 1
}

released_assets="$(
gh release view "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--json assets \
--jq '.assets[].name'
)"
for release_asset in "${release_assets[@]}"; do
required_asset="$(basename "${release_asset}")"
grep -Fxq "${required_asset}" <<< "${released_assets}" || {
printf 'Draft release is missing asset %s.\n' "${required_asset}" >&2
exit 1
}
done
Loading
Loading