Merge branch 'cooker' of https://github.com/RetroDECK/components into… #174
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
| name: "Alchemic Circle: Build RetroDECK Components" | |
| on: | |
| push: | |
| branches: | |
| - cooker | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| workflow_dispatch: | |
| inputs: | |
| force_rebuild: | |
| description: 'Force rebuilding ALL components (true/false)' | |
| required: false | |
| default: false | |
| type: boolean | |
| # schedule: | |
| # - cron: '00 17 * * *' # 2:00 AM JST (JST is UTC+9, so 17:00 UTC) | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| setup-recipes: | |
| runs-on: ubuntu-latest | |
| if: (github.event_name != 'schedule' || github.ref == 'refs/heads/update/components') | |
| outputs: | |
| heavy-matrix: ${{ steps.set-matrix.outputs.heavy }} | |
| light-matrix: ${{ steps.set-matrix.outputs.light }} | |
| reference-tag: ${{ steps.reference-release.outputs.reference_tag }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Init reference versions (for reuse) | |
| run: | | |
| mkdir -p reference | |
| : > reference/reference_tag.txt | |
| : > reference/components_version_list.md | |
| - name: Assignign recipes to different runners | |
| id: set-matrix | |
| run: | | |
| heavy=() | |
| light=() | |
| # define here all the "source_url" that must be built on the "heavy" runner | |
| heavy_list=() | |
| for recipe in */component_recipe.json; do | |
| # Skip if the glob didn't match any real file | |
| [ -f "$recipe" ] || continue | |
| # Extract the first source_url present in the recipe file (if any) | |
| source_url=$(jq -r '..|objects|.source_url? // empty' "$recipe" | head -n1 || echo "") | |
| # If heavy_list is defined and we have a source_url, check if the source_url is in the heavy_list | |
| if [[ ${#heavy_list[@]} -gt 0 && -n "$source_url" ]]; then | |
| matched=0 | |
| for h in "${heavy_list[@]}"; do | |
| if [[ "$source_url" == "$h" ]]; then | |
| # assign directly to heavy and stop searching | |
| heavy+=("$(jq -n --arg r "$recipe" --arg c "$(basename "$(dirname "$recipe")")" '{recipe:$r,component:$c}')") | |
| matched=1 | |
| break | |
| fi | |
| done | |
| # if no match found, go to light | |
| if [[ $matched -eq 0 ]]; then | |
| light+=("$(jq -n --arg r "$recipe" --arg c "$(basename "$(dirname "$recipe")")" '{recipe:$r,component:$c}')") | |
| fi | |
| else | |
| # no heavy_list or no source_url -> default to light | |
| light+=("$(jq -n --arg r "$recipe" --arg c "$(basename "$(dirname "$recipe")")" '{recipe:$r,component:$c}')") | |
| fi | |
| done | |
| # Each entry in heavy/light is a raw JSON object string; combine them into a JSON array | |
| heavy_json=$(printf '%s\n' "${heavy[@]}" | jq -s -c .) | |
| light_json=$(printf '%s\n' "${light[@]}" | jq -s -c .) | |
| echo "heavy=$heavy_json" >> $GITHUB_OUTPUT | |
| echo "light=$light_json" >> $GITHUB_OUTPUT | |
| - name: Fetch newest cooker reference versions (cooker push only) | |
| id: reference-release | |
| if: github.ref == 'refs/heads/cooker' && github.event_name != 'pull_request' && github.event_name != 'pull_request_target' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -e | |
| tag="$(bash automation-tools/find_newest_cooker_release.sh || true)" | |
| echo "reference_tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "$tag" > reference/reference_tag.txt | |
| if [[ -n "$tag" ]]; then | |
| url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${tag}/components_version_list.md" | |
| if curl -fsSL "$url" -o reference/components_version_list.md; then | |
| echo "[INFO] Downloaded reference components_version_list.md from $tag" | |
| else | |
| echo "[WARN] Could not download reference components_version_list.md from $tag; disabling reuse" | |
| : > reference/components_version_list.md | |
| : > reference/reference_tag.txt | |
| echo "reference_tag=" >> "$GITHUB_OUTPUT" | |
| fi | |
| else | |
| echo "[INFO] No prior cooker release found; disabling reuse" | |
| : > reference/components_version_list.md | |
| fi | |
| - name: Upload reference versions (for reuse) | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: reference-versions | |
| path: reference/* | |
| build-light: | |
| name: "Build ${{ matrix.recipe.component }}" | |
| needs: setup-recipes | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'schedule' || github.ref == 'refs/heads/update/components' | |
| strategy: | |
| matrix: | |
| recipe: ${{ fromJson(needs.setup-recipes.outputs.light-matrix) }} | |
| fail-fast: false | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Clone Repository (pull_request_target) | |
| - name: Clone Target Branch | |
| if: github.event_name == 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.base.ref }} | |
| submodules: true | |
| # Clone Repository (normal) | |
| - name: Clone Components repo | |
| if: github.event_name != 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| # Merge PR for validation | |
| - name: Merge and Validate PR Code | |
| if: github.event_name == 'pull_request_target' | |
| run: | | |
| echo "Fetching PR..." | |
| git fetch origin pull/${{ github.event.pull_request.number }}/head:pr | |
| git merge --no-ff pr || { | |
| echo "Merge conflict detected."; | |
| exit 1; | |
| } | |
| git log -1 --oneline | |
| - name: Download reference versions (for reuse) | |
| if: always() | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: reference-versions | |
| path: reference | |
| - name: Decide reuse vs rebuild (cooker only) | |
| id: reuse-check | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| FORCE_REBUILD_INPUT: ${{ github.event.inputs.force_rebuild || 'false' }} | |
| run: | | |
| set -e | |
| echo "artifact_name=${{ matrix.recipe.component }}" >> $GITHUB_ENV | |
| reuse=false | |
| # Honor global force-rebuild (env) or workflow input | |
| if [[ "${FORCE_REBUILD:-}" == "true" || "${FORCE_REBUILD_INPUT:-}" == "true" ]]; then | |
| echo "[INFO] Global force rebuild requested (env/input)" | |
| reuse=false | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Per-component repo-local changes detection: force rebuild for this component if files changed | |
| component="${{ matrix.recipe.component }}" | |
| if automation-tools/detect_component_changes.sh "$component" >/dev/null 2>&1; then | |
| echo "[INFO] Detected local changes in $component; forcing rebuild" | |
| reuse=false | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [[ "$GITHUB_REF" == "refs/heads/cooker" ]] && [[ "$GITHUB_EVENT_NAME" != "pull_request" ]] && [[ "$GITHUB_EVENT_NAME" != "pull_request_target" ]]; then | |
| ref_tag="$(cat reference/reference_tag.txt 2>/dev/null || true)" | |
| ref_file="reference/components_version_list.md" | |
| if [[ -n "$ref_tag" && -s "$ref_file" ]]; then | |
| ref_ver="$(bash automation-tools/get_component_version_from_components_version_list.sh -f "$ref_file" -c "${{ matrix.recipe.component }}" 2>/dev/null || true)" | |
| next_ver="$(bash automation-tools/resolve_component_version.sh -r "${{ matrix.recipe.recipe }}" -v automation-tools/alchemist/desired_versions.sh 2>/dev/null || true)" | |
| if [[ -n "$ref_ver" && -n "$next_ver" && "$ref_ver" == "$next_ver" ]]; then | |
| reuse=true | |
| echo "REF_TAG=$ref_tag" >> $GITHUB_ENV | |
| echo "[INFO] Reusing ${{ matrix.recipe.component }} from $ref_tag (version $ref_ver)" | |
| else | |
| echo "[INFO] Rebuild ${{ matrix.recipe.component }} (ref='$ref_ver' next='$next_ver')" | |
| fi | |
| else | |
| echo "[INFO] No usable reference; rebuilding" | |
| fi | |
| else | |
| echo "[INFO] Reuse disabled for this ref/event; rebuilding" | |
| fi | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| - name: Reuse component artifacts from newest cooker release | |
| if: steps.reuse-check.outputs.reuse == 'true' | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -e | |
| component="${{ matrix.recipe.component }}" | |
| mkdir -p "$component/artifacts" | |
| base="https://github.com/${GITHUB_REPOSITORY}/releases/download/${REF_TAG}" | |
| curl -fsSL "$base/${component}.tar.gz" -o "$component/artifacts/${component}.tar.gz" | |
| # sha may not always exist; best-effort | |
| curl -fsSL "$base/${component}.tar.gz.sha" -o "$component/artifacts/${component}.tar.gz.sha" || true | |
| echo "$REF_TAG" > "$component/artifacts/reused_from_release.txt" | |
| echo "$REF_TAG" > "$component/artifacts/reused_from_release.txt" | |
| # Remove Stuck Mounts | |
| - name: Remove stuck mounts | |
| if: steps.reuse-check.outputs.reuse != 'true' | |
| run: | | |
| if [ -d "/home/ubuntu/actions-runner/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles" ]; then sudo umount -f /home/ubuntu/actions-runner/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles/*; fi | |
| if [ -d "$HOME/actions-run/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles" ]; then sudo umount -f $HOME/actions-run/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles/*; fi | |
| # Install Dependencies | |
| - name: Install dependencies | |
| if: steps.reuse-check.outputs.reuse != 'true' | |
| run: curl "https://raw.githubusercontent.com/RetroDECK/components-template/main/automation_tools/install_dependencies.sh" | bash | |
| # Run Build | |
| - name: 'Alchemist: Run Build Artifacts' | |
| if: steps.reuse-check.outputs.reuse != 'true' | |
| run: | | |
| # Use the component name provided in the matrix entry | |
| echo "artifact_name=${{ matrix.recipe.component }}" >> $GITHUB_ENV | |
| automation-tools/alchemist/alchemist.sh -f "${{ matrix.recipe.recipe }}" | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.artifact_name }} | |
| path: ${{ env.artifact_name }}/artifacts/* | |
| build-heavy: | |
| name: "Build ${{ matrix.recipe.component }}" | |
| needs: setup-recipes | |
| runs-on: retrodeck | |
| if: (github.event_name != 'schedule' || github.ref == 'refs/heads/update/components') && fromJSON(needs.setup-recipes.outputs.heavy-matrix).length > 0 | |
| strategy: | |
| matrix: | |
| recipe: ${{ fromJson(needs.setup-recipes.outputs.heavy-matrix) }} | |
| fail-fast: false | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Clone Repository (pull_request_target) | |
| - name: Clone Target Branch | |
| if: github.event_name == 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.base.ref }} | |
| submodules: true | |
| # Clone Repository (normal) | |
| - name: Clone Components repo | |
| if: github.event_name != 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| # Merge PR for validation | |
| - name: Merge and Validate PR Code | |
| if: github.event_name == 'pull_request_target' | |
| run: | | |
| echo "Fetching PR..." | |
| git fetch origin pull/${{ github.event.pull_request.number }}/head:pr | |
| git merge --no-ff pr || { | |
| echo "Merge conflict detected."; | |
| exit 1; | |
| } | |
| git log -1 --oneline | |
| - name: Download reference versions (for reuse) | |
| if: always() | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: reference-versions | |
| path: reference | |
| - name: Decide reuse vs rebuild (cooker only) | |
| id: reuse-check | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| FORCE_REBUILD_INPUT: ${{ github.event.inputs.force_rebuild || 'false' }} | |
| run: | | |
| set -e | |
| echo "artifact_name=${{ matrix.recipe.component }}" >> $GITHUB_ENV | |
| reuse=false | |
| # Honor global force-rebuild (env) or workflow input | |
| if [[ "${FORCE_REBUILD:-}" == "true" || "${FORCE_REBUILD_INPUT:-}" == "true" ]]; then | |
| echo "[INFO] Global force rebuild requested (env/input)" | |
| reuse=false | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Per-component repo-local changes detection: force rebuild for this component if files changed | |
| component="${{ matrix.recipe.component }}" | |
| if automation-tools/detect_component_changes.sh "$component" >/dev/null 2>&1; then | |
| echo "[INFO] Detected local changes in $component; forcing rebuild" | |
| reuse=false | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| if [[ "$GITHUB_REF" == "refs/heads/cooker" ]] && [[ "$GITHUB_EVENT_NAME" != "pull_request" ]] && [[ "$GITHUB_EVENT_NAME" != "pull_request_target" ]]; then | |
| ref_tag="$(cat reference/reference_tag.txt 2>/dev/null || true)" | |
| ref_file="reference/components_version_list.md" | |
| if [[ -n "$ref_tag" && -s "$ref_file" ]]; then | |
| ref_ver="$(bash automation-tools/get_component_version_from_components_version_list.sh -f "$ref_file" -c "${{ matrix.recipe.component }}" 2>/dev/null || true)" | |
| next_ver="$(bash automation-tools/resolve_component_version.sh -r "${{ matrix.recipe.recipe }}" -v automation-tools/alchemist/desired_versions.sh 2>/dev/null || true)" | |
| if [[ -n "$ref_ver" && -n "$next_ver" && "$ref_ver" == "$next_ver" ]]; then | |
| reuse=true | |
| echo "REF_TAG=$ref_tag" >> $GITHUB_ENV | |
| echo "[INFO] Reusing ${{ matrix.recipe.component }} from $ref_tag (version $ref_ver)" | |
| else | |
| echo "[INFO] Rebuild ${{ matrix.recipe.component }} (ref='$ref_ver' next='$next_ver')" | |
| fi | |
| else | |
| echo "[INFO] No usable reference; rebuilding" | |
| fi | |
| else | |
| echo "[INFO] Reuse disabled for this ref/event; rebuilding" | |
| fi | |
| echo "reuse=$reuse" >> $GITHUB_OUTPUT | |
| - name: Reuse component artifacts from newest cooker release | |
| if: steps.reuse-check.outputs.reuse == 'true' | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -e | |
| component="${{ matrix.recipe.component }}" | |
| mkdir -p "$component/artifacts" | |
| base="https://github.com/${GITHUB_REPOSITORY}/releases/download/${REF_TAG}" | |
| curl -fsSL "$base/${component}.tar.gz" -o "$component/artifacts/${component}.tar.gz" | |
| curl -fsSL "$base/${component}.tar.gz.sha" -o "$component/artifacts/${component}.tar.gz.sha" || true | |
| # Remove Stuck Mounts | |
| - name: Remove stuck mounts | |
| if: steps.reuse-check.outputs.reuse != 'true' | |
| run: | | |
| if [ -d "/home/ubuntu/actions-runner/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles" ]; then sudo umount -f /home/ubuntu/actions-runner/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles/*; fi | |
| if [ -d "$HOME/actions-run/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles" ]; then sudo umount -f $HOME/actions-run/_work/RetroDECK/RetroDECK/.flatpak-builder/rofiles/*; fi | |
| # Run Build | |
| - name: 'Alchemist: Run Build Artifacts' | |
| if: steps.reuse-check.outputs.reuse != 'true' | |
| run: | | |
| # Use the component name provided in the matrix entry | |
| echo "artifact_name=${{ matrix.recipe.component }}" >> $GITHUB_ENV | |
| automation-tools/alchemist/alchemist.sh -f "${{ matrix.recipe.recipe }}" | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.artifact_name }} | |
| path: ${{ env.artifact_name }}/artifacts/* | |
| Release_RetroDECK_Components: | |
| needs: [build-light, build-heavy] | |
| runs-on: ubuntu-latest | |
| if: always() && (github.event_name != 'schedule' || github.ref == 'refs/heads/update/components') | |
| continue-on-error: ${{ github.ref != 'refs/heads/main' }} | |
| steps: | |
| # Clone Repository (pull_request_target) | |
| - name: Clone Target Branch | |
| if: github.event_name == 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.base.ref }} | |
| submodules: true | |
| # Clone Repository (normal) | |
| - name: Clone Components repo | |
| if: github.event_name != 'pull_request_target' | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Download All Artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: downloaded-artifacts | |
| - name: Move Downloaded Artifacts | |
| run: | | |
| for dir in downloaded-artifacts/*; do | |
| if [ -d "$dir" ]; then | |
| component_name=$(basename "$dir") | |
| mkdir -p "$component_name/artifacts" | |
| mv -v "$dir"/* "$component_name/artifacts/" | |
| fi | |
| done | |
| - name: Generate a token for Rekku | |
| id: generate-rekku-token | |
| uses: actions/create-github-app-token@v1 | |
| with: | |
| app-id: ${{ vars.REKKU_APP_ID }} | |
| private-key: ${{ secrets.REKKU_PRIVATE_KEY }} | |
| repositories: "components" | |
| owner: "RetroDECK" | |
| - name: Fallback missing component artifacts from releases (non-main) | |
| if: github.ref != 'refs/heads/main' | |
| env: | |
| GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| MATCH_LABEL: cooker | |
| FALLBACK_COMPONENTS_FILE: fallback_components.txt | |
| MISSING_COMPONENTS_FILE: missing_components.txt | |
| run: | | |
| bash automation-tools/fallback_release_artifacts.sh | |
| - name: Fail on missing artifacts (main only) | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| set -e | |
| MISSING_ARTIFACTS=() | |
| for folder in $(find . -maxdepth 2 -mindepth 2 -type f \( -name 'component_recipe.json' -o -name 'component_manifest.json' \) -printf '%h\n' | sed 's|^\./||' | sort -u); do | |
| if [ ! -d "$folder/artifacts" ] || ! find "$folder/artifacts" -maxdepth 1 -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.gz" -o -name "*.tar" -o -name "*.7z" -o -name "*.appimage" \) | grep -q .; then | |
| MISSING_ARTIFACTS+=("$folder") | |
| fi | |
| done | |
| if [ ${#MISSING_ARTIFACTS[@]} -ne 0 ]; then | |
| echo "[ERROR] Missing artifacts on main; failing build. Missing:" >&2 | |
| printf '%s\n' "${MISSING_ARTIFACTS[@]}" >&2 | |
| exit 1 | |
| fi | |
| - name: Generate components_version_list.md | |
| env: | |
| FALLBACK_COMPONENTS_FILE: fallback_components.txt | |
| run: | | |
| # Loop through each <component_name> folder | |
| for component_dir in */; do | |
| # Skip automation-tools and other non-component directories | |
| if [[ "$component_dir" == "automation-tools/" ]] || [[ "$component_dir" == "downloaded-artifacts/" ]]; then | |
| continue | |
| fi | |
| # Only process real components | |
| if [[ ! -f "${component_dir}component_recipe.json" && ! -f "${component_dir}component_manifest.json" ]]; then | |
| continue | |
| fi | |
| # Path to artifacts directory | |
| artifacts_dir="${component_dir}artifacts" | |
| # Check if artifacts directory exists | |
| if [[ -d "$artifacts_dir" ]]; then | |
| # Look for tar.gz files in artifacts | |
| for archive in "$artifacts_dir"/*.tar.gz; do | |
| if [[ -f "$archive" ]]; then | |
| echo "Checking archive: $archive" | |
| # Check if archive contains component_version file | |
| fullpath=$(tar -tzf "$archive" | grep 'component_version' | head -n 1) | |
| if [[ -n "$fullpath" ]]; then | |
| echo "Extracting $fullpath from $archive" | |
| tar -xzf "$archive" -C "$component_dir" "$fullpath" | |
| # Clean up the ugly path (normalize it) | |
| extracted_path=$(realpath "$component_dir/$fullpath") | |
| normalized_path=$(realpath "$component_dir/component_version") | |
| # Check if $fullpath is exactly 'component_version' (no leading dirs) | |
| if [[ "$extracted_path" != "$normalized_path" ]]; then | |
| mv -f "$extracted_path" "$normalized_path" | |
| fi | |
| else | |
| echo "No component_version found in $archive" | |
| echo "DEBUG: Listing contents of $archive" | |
| tar -tzf "$archive" | |
| fi | |
| fi | |
| done | |
| fi | |
| done | |
| # Source the write function and call it | |
| source automation-tools/write_components_version.sh | |
| write_components_version | |
| # Show the resulting components_version_list.md file | |
| cat components_version_list.md | |
| # This will generate desired_versions.sh pinned to the versions used by the current build | |
| # (it prefers extracted ./<component>/component_version values when available). | |
| - name: Generate desired_versions.sh (snapshot) | |
| env: | |
| GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }} | |
| run: | | |
| set -e | |
| automation-tools/alchemist/generate_desired_versions.sh -f automation-tools/alchemist/desired_versions.sh | |
| latest_generated="$(ls -1t automation-tools/alchemist/desired_versions_*.sh | head -n 1)" | |
| if [ -z "$latest_generated" ] || [ ! -f "$latest_generated" ]; then | |
| echo "[ERROR] desired_versions generator did not produce an output file" >&2 | |
| exit 1 | |
| fi | |
| cp -f "$latest_generated" desired_versions.sh | |
| - name: Get Branch Name | |
| run: | | |
| if [[ "$GITHUB_EVENT_NAME" == "pull_request" || "$GITHUB_EVENT_NAME" == "pull_request_target" ]]; then | |
| branch_name="$GITHUB_HEAD_REF" | |
| else | |
| branch_name="$GITHUB_REF_NAME" | |
| fi | |
| echo "Branch name: $branch_name" | |
| echo "BRANCH_NAME=$branch_name" >> $GITHUB_ENV | |
| - name: Get commits since last published main release | |
| id: get-commits | |
| run: | | |
| # If this is a Pull Request | |
| if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then | |
| echo "[INFO] Pull Request detected." | |
| BASE_REF=${GITHUB_BASE_REF} | |
| echo "[INFO] Base ref: $BASE_REF" | |
| git fetch origin $BASE_REF | |
| git log origin/$BASE_REF..HEAD --pretty=format:"- %s" > commits_list.txt | |
| cp commits_list.txt commits_since_main.txt | |
| else | |
| # Get the latest published release tag | |
| LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "") | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "[INFO] No previous tag found." | |
| echo "- No previous release." > commits_list.txt | |
| else | |
| echo "[INFO] Latest tag: $LATEST_TAG" | |
| git log ${LATEST_TAG}..HEAD --pretty=format:"- %s" > commits_list.txt | |
| fi | |
| # Get the latest tag on the main branch | |
| LATEST_MAIN_REF=$(git tag --merged origin/main --sort=-creatordate | head -n 1 || echo "") | |
| if [ -z "$LATEST_MAIN_REF" ]; then | |
| echo "[INFO] No tag found on main branch." | |
| echo "- No main release found." > commits_since_main.txt | |
| else | |
| echo "[INFO] Latest tag on main: $LATEST_MAIN_REF" | |
| git log ${LATEST_MAIN_REF}..HEAD --pretty=format:"- %s" > commits_since_main.txt | |
| fi | |
| fi | |
| echo "COMMITS_FILE=commits_list.txt" >> $GITHUB_ENV | |
| echo "COMMITS_MAIN_FILE=commits_since_main.txt" >> $GITHUB_ENV | |
| - name: Generate release body text | |
| id: generate-body | |
| run: | | |
| set -e | |
| RELEASE_BODY_FILE="release_body.md" | |
| echo "# Release Notes" > "$RELEASE_BODY_FILE" | |
| echo "This is a RetroDECK Components Artifacts release from [this commit](https://github.com/${{ github.repository }}/commit/${{ github.sha }}), from branch [${{ env.BRANCH_NAME }}](https://github.com/RetroDECK/RetroDECK/tree/feat/${{ env.BRANCH_NAME }})." >> "$RELEASE_BODY_FILE" | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| # Fallback warnings (if any) | |
| if [ -f "fallback_components.txt" ] && [ -s "fallback_components.txt" ]; then | |
| echo "[WARNING] Fallback artifacts were used for the following components:" >> "$RELEASE_BODY_FILE" | |
| while IFS='|' read -r comp tag; do | |
| [ -z "$comp" ] && continue | |
| echo "- $comp (from release $tag)" >> "$RELEASE_BODY_FILE" | |
| done < fallback_components.txt | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| fi | |
| # Append the contents of components_version_list.md to the release body | |
| cat components_version_list.md >> "$RELEASE_BODY_FILE" | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| # Prepare array | |
| MISSING_ARTIFACTS=() | |
| # Iterate through folders in the repo root | |
| for folder in $(find . -maxdepth 2 -mindepth 2 -type f \( -name 'component_recipe.json' -o -name 'component_manifest.json' \) -printf '%h\n' | sed 's|^\./||' | sort -u); do | |
| if [ ! -d "$folder/artifacts" ] || ! find "$folder/artifacts" -maxdepth 1 -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.gz" -o -name "*.tar" -o -name "*.7z" -o -name "*.appimage" \) | grep -q .; then | |
| MISSING_ARTIFACTS+=("$folder") | |
| fi | |
| done | |
| # Add warnings for missing artifacts | |
| if [ ${#MISSING_ARTIFACTS[@]} -ne 0 ]; then | |
| echo "[WARNING] The following components are missing from this release:" >> "$RELEASE_BODY_FILE" | |
| for folder in "${MISSING_ARTIFACTS[@]}"; do | |
| echo "- $folder" >> "$RELEASE_BODY_FILE" | |
| done | |
| else | |
| echo "No missing components detected!" >> "$RELEASE_BODY_FILE" | |
| fi | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| # Fetch latest main to compare against | |
| git fetch origin main | |
| # Build comparison link | |
| COMPARE_URL="https://github.com/${{ github.repository }}/compare/main...${{ github.sha }}" | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| echo "---" >> "$RELEASE_BODY_FILE" | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| echo "[Check changes since latest main release](${COMPARE_URL})" >> "$RELEASE_BODY_FILE" | |
| echo "" >> "$RELEASE_BODY_FILE" | |
| # Output the final body | |
| echo "RELEASE_BODY<<EOF" >> $GITHUB_OUTPUT | |
| cat "$RELEASE_BODY_FILE" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Set Make Latest | |
| run: | | |
| if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| MAKE_LATEST=true | |
| else | |
| MAKE_LATEST=false | |
| fi | |
| echo "MAKE_LATEST=$MAKE_LATEST" >> $GITHUB_ENV | |
| - name: Generate Version Tag | |
| id: version-tag | |
| run: | | |
| # Get the current date and time in GMT | |
| CURRENT_DATE=$(date -u +"%Y%m%d-%H%M") | |
| # Check if the branch is main or not | |
| if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then | |
| TAG="${CURRENT_DATE}" | |
| else | |
| TAG="cooker-${CURRENT_DATE}" | |
| fi | |
| echo "TAG=$TAG" >> $GITHUB_ENV | |
| echo "TAG=$TAG" >> $GITHUB_OUTPUT | |
| - name: Publish release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| tag: "${{ env.TAG }}" | |
| name: "RetroDECK Components ${{ env.TAG }}" | |
| body: ${{ steps.generate-body.outputs.RELEASE_BODY }} | |
| artifacts: "*/artifacts/*,!*/artifacts/component_version,components_version_list.md,desired_versions.sh" | |
| allowUpdates: true | |
| omitBodyDuringUpdate: true | |
| makeLatest: ${{ env.MAKE_LATEST }} | |
| repo: ${{ env.REPO_NAME }} | |
| token: ${{ steps.generate-rekku-token.outputs.token }} | |
| - name: Create diff between target branch and PR components_version_list.md | |
| run: | | |
| if [[ "${GITHUB_BASE_REF}" == "cooker" ]]; then | |
| TARGET_BRANCH="main" | |
| else | |
| TARGET_BRANCH="cooker" | |
| fi | |
| git fetch origin $TARGET_BRANCH | |
| if [ -f "components_version_list.md" ]; then | |
| if git show origin/$TARGET_BRANCH:components_version_list.md > target_components_version_list.md 2>/dev/null; then | |
| echo "Generating diff between $TARGET_BRANCH and PR components_version_list.md..." | |
| diff -u target_components_version_list.md components_version_list.md > components_version_diff.txt || true | |
| echo "Diff saved to components_version_diff.txt" | |
| else | |
| echo "components_version_list.md does not exist on $TARGET_BRANCH branch. Skipping diff." | |
| echo "No components_version_list.md on $TARGET_BRANCH branch." > components_version_diff.txt | |
| fi | |
| else | |
| echo "components_version_list.md not found in PR. Skipping diff." | |
| echo "No components_version_list.md in PR." > components_version_diff.txt | |
| fi | |
| - name: Write PR body | |
| if: github.event_name == 'pull_request_target' || github.event_name == 'pull_request' || github.event_name != 'schedule' || github.ref == 'refs/heads/update/components' | |
| run: | | |
| echo "## RetroDECK Components Artifacts" > pr_body.md | |
| echo "" >> pr_body.md | |
| echo "This pull request updates the RetroDECK components artifacts to version ${{ env.TAG }}." >> pr_body.md | |
| echo "" >> pr_body.md | |
| if [ -f "fallback_components.txt" ] && [ -s "fallback_components.txt" ]; then | |
| echo "### WARNING: Fallback artifacts used" >> pr_body.md | |
| while IFS='|' read -r comp tag; do | |
| [ -z "$comp" ] && continue | |
| echo "- $comp (from release $tag)" >> pr_body.md | |
| done < fallback_components.txt | |
| echo "" >> pr_body.md | |
| fi | |
| echo "## Changes:" >> pr_body.md | |
| echo "$(cat commits_list.txt)" >> pr_body.md | |
| echo "" >> pr_body.md | |
| echo "## Diff between main and PR components_version_list.md:" >> pr_body.md | |
| echo "```diff" >> pr_body.md | |
| echo "$(cat components_version_diff.txt)" >> pr_body.md | |
| echo "```" >> pr_body.md | |
| # Skipped on cooker as you cannot open PRs against the same branch | |
| - name: Open Pull Request | |
| uses: peter-evans/create-pull-request@v7 | |
| if: (github.event_name != 'schedule' || github.ref == 'refs/heads/update/components') && github.head_ref != github.base_ref && env.BRANCH_NAME != 'cooker' | |
| with: | |
| token: ${{ steps.generate-rekku-token.outputs.token }} | |
| commit-message: "Update RetroDECK Components Artifacts" | |
| branch: ${{ env.BRANCH_NAME }} | |
| title: "Update RetroDECK Components Artifacts" | |
| body-path: pr_body.md | |
| base: "cooker" | |
| - name: Post PR comment with artifacts | |
| uses: marocchino/sticky-pull-request-comment@v2 | |
| if: github.event_name == 'pull_request_target' || github.event_name == 'pull_request' || github.event_name != 'schedule' || github.ref == 'refs/heads/update/components' | |
| with: | |
| GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }} | |
| header: "RetroDECK Build Artifacts" | |
| path: pr_body.md | |
| - name: Rewrite Tag | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| git submodule deinit -f --all | |
| git fetch --tags | |
| if git rev-parse --verify "${{ env.TAG }}" >/dev/null 2>&1; then | |
| git tag -d "${{ env.TAG }}" | |
| git push --delete origin "${{ env.TAG }}" | |
| fi | |
| git tag "${{ env.TAG }}" | |
| git push origin "${{ env.TAG }}" | |
| env: | |
| GITHUB_TOKEN: ${{ steps.generate-rekku-token.outputs.token }} |