diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a0c9f5f07f..3acd44b646 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,27 +1,38 @@ name: "Run build" on: - pull_request: - # Run when the PR is opened, reopened, or updated (synchronize) - types: [opened, ready_for_review, reopened, synchronize] workflow_dispatch: inputs: image_formats: + type: string description: | Space-separated vendor formats to build. required: true default: qemu_uefi + custom_sdk_version: + type: string + required: false + description: | + Custom SDK container version to use for this build. -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} - cancel-in-progress: true + workflow_call: + inputs: + image_formats: + type: string + description: | + Space-separated vendor formats to build. + required: true + default: qemu_uefi + custom_sdk_version: + type: string + required: false + description: | + Custom SDK container version to use for this build. permissions: pull-requests: write jobs: packages: - # Do not run when still in draft mode but a review was requested anyway - if: github.event.pull_request.draft == false name: "Build Flatcar packages" runs-on: - self-hosted @@ -70,7 +81,6 @@ jobs: set -euo pipefail git checkout ${{ github.event.pull_request.head.sha }} - git submodule update - name: Set environment shell: bash @@ -91,6 +101,10 @@ jobs: # this with its IP address. echo "TORCX_TESTS_PACKAGE_URL=http://localhost:12345" >> $GITHUB_ENV + if [ -n "${{ github.event.inputs.custom_sdk_version }}" ] ; then + echo "CUSTOM_SDK_VERSION=${{ github.event.inputs.custom_sdk_version }}" >> $GITHUB_ENV + fi + - name: Build packages shell: bash run: | @@ -103,7 +117,7 @@ jobs: version="alpha-$FLATCAR_VERSION_ID" check_version_string "$version" - sdk_version="${FLATCAR_SDK_VERSION}" + sdk_version="${CUSTOM_SDK_VERSION:-$FLATCAR_SDK_VERSION}" sdk_name="flatcar-sdk-${arch}" docker_sdk_vernum="$(vernum_to_docker_image_version "${sdk_version}")" diff --git a/.github/workflows/pr-comment-build-dispatcher.yaml b/.github/workflows/pr-comment-build-dispatcher.yaml new file mode 100644 index 0000000000..2dac4943af --- /dev/null +++ b/.github/workflows/pr-comment-build-dispatcher.yaml @@ -0,0 +1,71 @@ +name: "PR command build dispatcher" +on: + issue_comment: + types: [created] + +permissions: + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-pr-command-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +jobs: + check_maintainer_membership: + # Only run if this is a PR comment that contains a valid command + if: | + ${{ github.event.issue.pull_request }} && + ( contains(github.event.comment.body, '/update-sdk') + || contains(github.event.comment.body, '/build-image') ) + name: Check if commenter is in the Flatcar maintainers team + outputs: + maintainers: steps.step1.output.maintainers + runs-on: + - ubuntu-latest + steps: + - name: Fetch members of the maintainers team + env: + requester: ${{ github.event.comment.user.login }} + shell: bash + run: | + set -euo pipefail + curl --fail --show-error -L --silent \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GH_ACTIONS_ORG_READ }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/orgs/flatcar/teams/flatcar-maintainers/members \ + | jq -r '.[].login' > maintainers.txt + + echo "Current members of the maintainers team:" + cat maintainers.txt + + res=false + echo "Checking for membership of '${{ env.requester }}'" + if grep -qE "^${{ env.requester }}$" maintainers.txt ; then + echo "Succeeded." + res=true + else + echo "FAILED: '${{ env.requester }} is not a member of the Flatcar maintainers team." + fi + + $res + + update_sdk: + needs: check_maintainer_membership + if: ( always() && needs.check_maintainer_membership.result == 'success' + && contains(github.event.comment.body, '/update-sdk') ) + name: "Build an updated SDK container" + # SDK build needs access to bincache ssh secret + secrets: inherit + uses: ./.github/workflows/update-sdk.yaml + + build_image: + needs: [ check_maintainer_membership, build_sdk ] + if: ( always() && needs.check_maintainer_membership.result == 'success' + && ( contains(github.event.comment.body, '/build-image') + || needs.build_sdk.result == 'success' ) ) + name: "Build the OS image" + uses: ./.github/workflows/ci.yaml + with: + custom_sdk_version: ${{ needs.update_sdk.outputs.sdk_version }} + image_formats: qemu_uefi diff --git a/.github/workflows/update-sdk.yaml b/.github/workflows/update-sdk.yaml new file mode 100644 index 0000000000..438f6797de --- /dev/null +++ b/.github/workflows/update-sdk.yaml @@ -0,0 +1,145 @@ +name: "Build updated SDK container" +on: + workflow_dispatch: + inputs: + source_sdk_version: + type: string + required: false + description: | + Source SDK container to use. Defaults to version defined in version.txt. + custom_sdk_version: + type: string + required: false + description: | + Custom SDK container version to build. Defaults to source SDK w/ "-github-[DATE]" appended. + + workflow_call: + inputs: + source_sdk_version: + type: string + required: false + description: | + Source SDK container to use. Defaults to version defined in version.txt. + custom_sdk_version: + type: string + required: false + description: | + Custom SDK container version to build. Defaults to source SDK w/ "-github-[DATE]" appended, or + '-github-pr-[PRNUM]-[DATE]' if the build was triggered from a PR. + +permissions: + pull-requests: write + +jobs: + update_sdk: + name: "Build an updated SDK container image" + runs-on: + - self-hosted + - debian + - build + - x64 + strategy: + fail-fast: false + outputs: + sdk_version: ${{ steps.step4.outputs.sdk_version }} + defaults: + run: + working-directory: scripts + + steps: + - name: Prepare machine + shell: bash + working-directory: ${{ github.workspace }} + run: | + sudo rm /bin/sh + sudo ln -s /bin/bash /bin/sh + sudo apt-get install -y ca-certificates curl gnupg lsb-release qemu-user-static git jq openssh-client rsync + sudo mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg + echo \ + "deb [signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + + - uses: actions/checkout@v3 + with: + path: scripts + fetch-depth: 0 + + - name: Set environment + shell: bash + run: | + if [ -n "${{ github.event.inputs.source_sdk_version }}" ] ; then + echo "SOURCE_SDK_VERSION=${{ github.event.inputs.source_sdk_version }}" >> $GITHUB_ENV + fi + if [ -n "${{ github.event.inputs.custom_sdk_version }}" ] ; then + echo "CUSTOM_SDK_VERSION=${{ github.event.inputs.custom_sdk_version }}" >> $GITHUB_ENV + fi + + - name: Build an updated SDK container + shell: bash + run: | + exec 2>&1 + set -x + set -euo pipefail + + source ci-automation/ci_automation_common.sh + source sdk_container/.repo/manifests/version.txt + + version="alpha-$FLATCAR_VERSION_ID" + sdk_version="${SOURCE_SDK_VERSION:-$FLATCAR_SDK_VERSION}" + + sdk_name="flatcar-sdk-all" + docker_sdk_vernum="$(vernum_to_docker_image_version "${sdk_version}")" + + docker_image_from_registry_or_buildcache "${sdk_name}" "${docker_sdk_vernum}" + + sdk_image="$(docker_image_fullname "${sdk_name}" "${docker_sdk_vernum}")" + + # Create version file + ( + source sdk_lib/sdk_container_common.sh + create_versionfile "$sdk_version" "$version" + ) + + if [ -z "${CUSTOM_SDK_VERSION:-}" ] ; then + if [ -n "${{ github.event.issue.pull_request }}" ] ; then + target_version="${sdk_version}-github-PR-${{ github.event.issue.number }}-$(date '+%Y_%m_%d__%H_%M_%S')" + else + target_version="${sdk_version}-github-$(date '+%Y_%m_%d__%H_%M_%S')" + fi + else + target_version="${CUSTOM_SDK_VERSION}" + fi + + echo "sdk_version=${target_version}" >> "$GITHUB_OUTPUT" + + # This also updates sdk_container/.repo/manifests/version.txt with the new SDK version. + ./update_sdk_container_image "${target_version}" + + - name: Upload the SDK container and binary packages to bincache + shell: bash + run: | + set -euo pipefail + + source ci-automation/ci_automation_common.sh + + mkdir -p ~/.ssh + trap 'rm -f ~/.ssh/bincache' EXIT + echo "${{ secrets.BINCACHESSH }}" > ~/.ssh/bincache + chmod 600 ~/.ssh/bincache + + echo "Host ${BUILDCACHE_SERVER}" >> ~/.ssh/config + echo " User ${BUILDCACHE_USER}" >> ~/.ssh/config + echo " IdentityFile ~/.ssh/bincache" >> ~/.ssh/config + + source sdk_container/.repo/manifests/version.txt + vernum="${FLATCAR_SDK_VERSION}" + docker_vernum="$(vernum_to_docker_image_version "${vernum}")" + + docker_image_to_buildcache "${CONTAINER_REGISTRY}/flatcar-sdk-all" "${docker_vernum}" + docker_image_to_buildcache "${CONTAINER_REGISTRY}/flatcar-sdk-amd64" "${docker_vernum}" + docker_image_to_buildcache "${CONTAINER_REGISTRY}/flatcar-sdk-arm64" "${docker_vernum}" + + rm -f ~/.ssh/bincache diff --git a/ci-automation/garbage_collect.sh b/ci-automation/garbage_collect.sh index f58ae8e952..3e0d0a8d58 100644 --- a/ci-automation/garbage_collect.sh +++ b/ci-automation/garbage_collect.sh @@ -18,6 +18,8 @@ # Flatcar CI automation garbage collector. # This script removes development (non-official) build artifacts: # - SDK tarballs, build step containers, and vendor images on buildcache +# - SDK containers built via Github actions (e.g. from PRs). +# See https://github.com/flatcar/scripts/blob/main/.github/workflows/update-sdk.yaml # - tags from the scripts repository # # Garbage collection is based on development (non-official) version tags @@ -142,6 +144,12 @@ function _garbage_collect_impl() { fi done + echo + echo "########################################" + echo + echo Running cloud garbage collector + echo + local mantle_ref mantle_ref=$(cat sdk_container/.repo/manifests/mantle-container) docker run --pull always --rm --net host \ @@ -153,5 +161,14 @@ function _garbage_collect_impl() { --env VMWARE_ESX_CREDS \ --env OPENSTACK_CREDS \ -w /work -v "$PWD":/work "${mantle_ref}" /work/ci-automation/garbage_collect_cloud.sh + + echo + echo "#############################################" + echo + echo Running Github CI SDK garbage collector + echo + + source ci-automation/garbage_collect_github_ci_sdk.sh + garbage_collect_github_ci } # -- diff --git a/ci-automation/garbage_collect_github_ci_sdk.sh b/ci-automation/garbage_collect_github_ci_sdk.sh new file mode 100644 index 0000000000..0d12cbe82a --- /dev/null +++ b/ci-automation/garbage_collect_github_ci_sdk.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# +# Copyright (c) 2021 The Flatcar Maintainers. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# >>> This file is supposed to be SOURCED from the repository ROOT. <<< +# +# garbage_collect_github_ci() should be called after sourcing. +# +# OPTIONAL INPUT +# - Number of (recent) Github SDK builds to keep. Defaults to 20. +# - DRY_RUN (Env variable). Set to "y" to just list what would be done but not +# actually purge anything. + +# Flatcar Github CI SDK rebuild automation garbage collector. +# This script removes development (non-official) SDK image builds generated via Github CI. +# +# Garbage collection is based on development (non-official) SDK versions listed on +# https://bincache.flatcar-linux.net/containers/ +# and following the pattern [VERSION_NUMBER]*-github-*. The newest 20 builds will be retained, +# all older builds will be purged (20 is the default, see OPTIONAL INPUT above). + +function garbage_collect_github_ci() { + # Run a subshell, so the traps, environment changes and global + # variables are not spilled into the caller. + ( + set -euo pipefail + + _garbage_collect_github_ci_impl "${@}" + ) +} +# -- + +function _garbage_collect_github_ci_impl() { + local keep="${1:-20}" + local dry_run="${DRY_RUN:-}" + + # Example version string + # + # + local versions_detected="$(curl -s https://bincache.flatcar-linux.net/containers/ \ + | grep -E '\' \ + | sed 's:.*\"./\([^/]\+\)/".*:\1:' )" + + # Sort versions by date. Since version numbers can differ and this would impact sort, we + # 1. insert a "/" between "...-github-[pr-XXX]-" and "[date]..." + # 2. sort with delimiter "/" and sorting key 2 (i.e. the date part) + # 3. remove the "/" + local versions_sorted="$(echo "${versions_detected}" \ + | sed 's/\(-github\(-pr-[0-9]*\)*-\)/\1\//' \ + | sort -k 2 -t / \ + | sed 's:/::')" + + echo "######## Full list of version(s) found ########" + echo "${versions_sorted}" | awk '{printf "%5d %s\n", NR, $0}' + + local tail_keep="$((keep + 1))" # for tail -n+... + local purge_versions + mapfile -t purge_versions < <(tail -n+"${tail_keep}" <<<"${versions_sorted}") + + source ci-automation/ci_automation_common.sh + local sshcmd="$(gen_sshcmd)" + + echo + echo "######## The following version(s) will be purged ########" + if [ "$dry_run" = "y" ] ; then + echo + echo "(NOTE this is just a dry run since DRY_RUN=y)" + echo + fi + printf '%s\n' "${purge_versions[@]}" | awk -v keep="${keep}" '{if ($0 == "") next; printf "%5d %s\n", NR + keep, $0}' + echo + echo + + local version="" + for version in "${purge_versions[@]}"; do + echo "--------------------------------------------" + echo + echo "#### Processing version '${version}' ####" + echo + + local rmpat="${BUILDCACHE_PATH_PREFIX}/containers/${version}/" + + echo "## The following files will be removed ##" + $sshcmd "${BUILDCACHE_USER}@${BUILDCACHE_SERVER}" \ + "ls -la ${rmpat} || true" + + if [ "$dry_run" != "y" ] ; then + set -x + $sshcmd "${BUILDCACHE_USER}@${BUILDCACHE_SERVER}" \ + "rm -rf ${rmpat}" + set +x + else + echo "## (DRY_RUN=y so not doing anything) ##" + fi + done +} +# -- diff --git a/update_sdk_container_image b/update_sdk_container_image index 79dedd6f60..b11f383f57 100755 --- a/update_sdk_container_image +++ b/update_sdk_container_image @@ -14,6 +14,7 @@ source sdk_lib/sdk_container_common.sh os_version="$(get_version_from_versionfile)" base_sdk_version="$(get_sdk_version_from_versionfile)" +base_sdk_version="$(vernum_to_docker_image_version "${base_sdk_version}")" new_sdk_version="" keep="false"