From f994634765534dddfa8bca4cef3baa73658d8253 Mon Sep 17 00:00:00 2001 From: MalTeeez <41554385+MalTeeez@users.noreply.github.com> Date: Wed, 27 May 2026 22:52:22 +0200 Subject: [PATCH] First clean pass from test repo --- .github/workflows/build-and-test.yml | 30 ++++ .github/workflows/gate-by-required.yml | 23 +++ .../latest-daily-integration-test.yml | 141 ++++++++++++++++++ .../workflows/trigger-rebuild-with-deps.yml | 100 +++++++++++++ scripts/upload-deps-to-localmaven.sh | 96 ++++++++++++ 5 files changed, 390 insertions(+) create mode 100644 .github/workflows/gate-by-required.yml create mode 100644 .github/workflows/latest-daily-integration-test.yml create mode 100644 .github/workflows/trigger-rebuild-with-deps.yml create mode 100644 scripts/upload-deps-to-localmaven.sh diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d9137d2..2ffe291 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -51,6 +51,36 @@ jobs: path: .gtnh-workflows fetch-depth: 0 + - name: Find external-deps artifact for this run + id: find-artifact + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # The artifact is named after this run's ID because try-rebuild-with-deps runs in the + # master context; filtering by head_sha would never match the PR's SHA (I tried). + # Reruns keep the same run_id, so this name is stable across attempts. + RUN_ID=$(gh api \ + "repos/${{ github.repository }}/actions/artifacts?name=external-deps-${{ github.run_id }}" \ + --jq ".artifacts[0].workflow_run.id") + echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT + + - name: Download external deps + if: steps.find-artifact.outputs.run_id != '' + uses: actions/download-artifact@v8 + with: + name: external-deps-${{ github.run_id }} + path: ./.packscripts/ + run-id: ${{ steps.find-artifact.outputs.run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install external deps to local maven + if: hashFiles('./.packscripts/**') != '' + run: | + echo "Aquired build artifacts from rebuild job: " && ls -lahR ./.packscripts/ + bash scripts/upload-deps-to-localmaven.sh ./.packscripts/required_prs.json + echo "Overriding dependencies of project with versions from local maven:" && cat ~/.gradle/init.gradle + echo "Contents of local maven:" && ls -lahR ~/.m2/repository/com/github + - name: Determine JDK versions id: list-jdk-versions shell: bash diff --git a/.github/workflows/gate-by-required.yml b/.github/workflows/gate-by-required.yml new file mode 100644 index 0000000..e23cf46 --- /dev/null +++ b/.github/workflows/gate-by-required.yml @@ -0,0 +1,23 @@ +name: Check state of other required PRs + +on: + pull_request: + branches: [ master, main, release/** ] + +jobs: + check-required-prs: + runs-on: ubuntu-latest + steps: + - name: Check required PRs + uses: MalTeeez/packscripts@1.4.12 + env: + NO_COLOR: 1 + with: + command: > + pr gate ${{ github.event.pull_request.html_url }} + --build_job "Build and test" + --other_allowed_owner "GTNewHorizons" + --allow_all_merged + github_token: ${{ secrets.GITHUB_TOKEN }} + config: https://raw.githubusercontent.com/MalTeeez/packscripts-auto-builds/refs/heads/gtnh-daily/packscripts.json + annotated_file: https://raw.githubusercontent.com/MalTeeez/packscripts-auto-builds/refs/heads/gtnh-daily/annotated_mods.json \ No newline at end of file diff --git a/.github/workflows/latest-daily-integration-test.yml b/.github/workflows/latest-daily-integration-test.yml new file mode 100644 index 0000000..0274a4c --- /dev/null +++ b/.github/workflows/latest-daily-integration-test.yml @@ -0,0 +1,141 @@ +name: Test integration in latest daily + +# Note: this workflow is triggered via workflow_run, which runs in the context of the default branch. +# Therefore we use the statuses api to place it on the source commit manually +on: + workflow_run: + workflows: ["Build and test"] + types: [completed] + +permissions: + actions: read + contents: read + statuses: write + +# Key on the triggering build's head_sha so each PR head is isolated, and a +# repaired rerun (same run_id, same head_sha, new attempt) cancels the now-stale +# in-flight run for that head instead of racing it to post a status. +concurrency: + group: ${{ github.workflow }}-${{ github.event.workflow_run.head_sha }} + cancel-in-progress: true + +env: + STATUS_CONTEXT: "Test in latest daily" + +jobs: + get-pr-url: + runs-on: ubuntu-latest + # Run on ANY pull_request build completion. We branch on conclusion below; + if: github.event.workflow_run.event == 'pull_request' + outputs: + pr_url: ${{ steps.lookup.outputs.url }} + conclusion: ${{ github.event.workflow_run.conclusion }} + steps: + - name: Look up PR url + id: lookup + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_URL=$(gh api \ + "repos/${{ github.repository }}/commits/${{ github.event.workflow_run.head_sha }}/pulls" \ + --jq '.[0].html_url') + echo "url=$PR_URL" >> "$GITHUB_OUTPUT" + + # Pending only on the path where the real test is about to run. + - name: Mark status pending on PR commit + if: github.event.workflow_run.conclusion == 'success' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api -X POST \ + "repos/${{ github.repository }}/statuses/${{ github.event.workflow_run.head_sha }}" \ + -f state="pending" \ + -f context="$STATUS_CONTEXT" \ + -f description="Running integration test in latest daily…" \ + -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + full-pack-test: + needs: get-pr-url + # Real test work runs ONLY on a successful build. + if: needs.get-pr-url.outputs.conclusion == 'success' + name: Test in latest daily + runs-on: ubuntu-latest + container: + image: ghcr.io/malteeez/gtnh-daily:latest + env: + PR_URL: ${{ needs.get-pr-url.outputs.pr_url }} + steps: + - name: Install libc so we can run our binary on alpine + run: apk add --no-cache libstdc++ libgcc + + - name: Check out PR build artifact + uses: MalTeeez/packscripts@1.4.12 + env: + NO_COLOR: 1 + with: + command: > + pr apply $PR_URL + --build_job "Build and test" + --artifact_name "build-libs" + --other_allowed_owner "GTNewHorizons" + --pack_variant "server" + github_token: ${{ secrets.GITHUB_TOKEN }} + working_directory: /home/gtnh + musl: true + + - name: Run pack test + timeout-minutes: 30 + run: bash /entrypoint.sh + + report: + needs: [get-pr-url, full-pack-test] + # Always run so we can post a terminal status on every concluded build, + # including the failed path where full-pack-test was skipped. + if: always() && needs.get-pr-url.result == 'success' + runs-on: ubuntu-latest + steps: + # Backstop for the cancel-in-progress race on a repaired rerun. Attempt 1 and + # attempt 2 of the triggering build share one run_id but differ in run_attempt, + # and both target the SAME commit's status slot. If this run is processing an + # earlier attempt than the build's current one, it's stale — bail so a late + # attempt-1 result can't clobber the attempt-2 result on the context. + - name: Bail if a newer attempt of the triggering build exists + id: stalecheck + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TRIGGER_RUN_ID="${{ github.event.workflow_run.id }}" + MY_ATTEMPT="${{ github.event.workflow_run.run_attempt }}" + LATEST_ATTEMPT=$(gh api \ + "repos/${{ github.repository }}/actions/runs/${TRIGGER_RUN_ID}" \ + --jq '.run_attempt' 2>/dev/null || true) + echo "::notice::trigger run ${TRIGGER_RUN_ID} — mine=${MY_ATTEMPT} latest=${LATEST_ATTEMPT}" + if [ -z "$LATEST_ATTEMPT" ]; then + # Couldn't read the triggering run — fail open, let the post proceed. + echo "stale=false" >> "$GITHUB_OUTPUT" + elif [ "$MY_ATTEMPT" -lt "$LATEST_ATTEMPT" ]; then + echo "::notice::Attempt ${MY_ATTEMPT} superseded by attempt ${LATEST_ATTEMPT}; not posting status." + echo "stale=true" >> "$GITHUB_OUTPUT" + else + echo "stale=false" >> "$GITHUB_OUTPUT" + fi + + - name: Report final status to PR commit + if: steps.stalecheck.outputs.stale == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + CONCL="${{ needs.get-pr-url.outputs.conclusion }}" + if [ "$CONCL" != "success" ]; then + STATE="failure" + DESC="Build failed; integration test not run" + else + STATE="${{ needs.full-pack-test.result == 'success' && 'success' || 'failure' }}" + DESC="Integration test complete" + fi + gh api -X POST \ + "repos/${{ github.repository }}/statuses/${{ github.event.workflow_run.head_sha }}" \ + -f state="$STATE" \ + -f context="$STATUS_CONTEXT" \ + -f description="$DESC" \ + -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ No newline at end of file diff --git a/.github/workflows/trigger-rebuild-with-deps.yml b/.github/workflows/trigger-rebuild-with-deps.yml new file mode 100644 index 0000000..406de98 --- /dev/null +++ b/.github/workflows/trigger-rebuild-with-deps.yml @@ -0,0 +1,100 @@ +name: Trigger a rebuild with externally required PRs + +# Note: this workflow is triggered via workflow_run, which runs in the context of the default branch. +# Therefore we use the statuses api to place it on the source commit manually +on: + workflow_run: + workflows: ["Build and test"] + types: [completed] + +permissions: + actions: write + contents: read + statuses: write + +jobs: + trigger-rebuild-with-deps: + name: Apply required PRs from external repos + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'failure' + && github.event.workflow_run.event == 'pull_request' + && github.event.workflow_run.run_attempt == 1 + steps: + - name: Look up PR url + id: lookup + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_URL=$(gh api \ + "repos/${{ github.repository }}/commits/${{ github.event.workflow_run.head_sha }}/pulls" \ + --jq '.[0].html_url') + echo "url=$PR_URL" >> $GITHUB_OUTPUT + + - name: Collect required PRs + uses: MalTeeez/packscripts@1.4.12 + env: + NO_COLOR: 1 + with: + command: > + pr deps ${{ steps.lookup.outputs.url }} + --target_dir ./.packscripts/ + --jar_suffix -dev.jar + --artifact_name -dev.jar + --artifact_name build-libs + --build_job "Build and test" + --other_allowed_owner "GTNewHorizons" + github_token: ${{ secrets.GITHUB_TOKEN }} + config: https://raw.githubusercontent.com/MalTeeez/packscripts-auto-builds/refs/heads/gtnh-daily/packscripts.json + annotated_file: https://raw.githubusercontent.com/MalTeeez/packscripts-auto-builds/refs/heads/gtnh-daily/annotated_mods.json + + - name: Check if deps were collected + id: check-deps + run: | + if [ -n "$(ls -A ./.packscripts/ 2>/dev/null)" ]; then + echo "found=true" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Mark status pending on PR commit + if: steps.check-deps.outputs.found == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api -X POST \ + repos/${{ github.repository }}/statuses/${{ github.event.workflow_run.head_sha }} \ + -f state="pending" \ + -f context="Trigger rebuild with deps" \ + -f description="Applying external PR deps and rebuilding…" \ + -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + - name: Upload external deps artifact + if: steps.check-deps.outputs.found == 'true' + uses: actions/upload-artifact@v7 + with: + # Named after the triggering run ID so the rerun can find it by its own run_id. + # workflow_run always runs in the default branch context, so artifacts would otherwise + # be filed under master's head_sha instead of the PR's, making them invisible to the rerun. + name: external-deps-${{ github.event.workflow_run.id }} + path: ./.packscripts/ + include-hidden-files: true + + - name: Rerun failed build + if: steps.check-deps.outputs.found == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api -X POST \ + repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/rerun-failed-jobs + + - name: Report final status to PR commit + if: always() && steps.check-deps.outputs.found == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api -X POST \ + repos/${{ github.repository }}/statuses/${{ github.event.workflow_run.head_sha }} \ + -f state="${{ job.status }}" \ + -f context="Trigger rebuild with deps" \ + -f description="External PR deps applied; rebuild triggered" \ + -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ No newline at end of file diff --git a/scripts/upload-deps-to-localmaven.sh b/scripts/upload-deps-to-localmaven.sh new file mode 100644 index 0000000..972c7b2 --- /dev/null +++ b/scripts/upload-deps-to-localmaven.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +set -euo pipefail + +JSON_FILE=$1 +GRADLE_DIR=~/.gradle +INIT_GRADLE=$GRADLE_DIR/init.gradle + +# Exit early if there are no dependencies +DEP_COUNT=$(jq '.dependencies | length' "$JSON_FILE") +if [ "$DEP_COUNT" -eq 0 ]; then + echo "No dependencies found, failure was probably not due to a required dependency. Failing to propagate initial failure." + exit 1 +fi + +# Write init.gradle header to user gradle dir +# (first as a temp file since it will parsed w. just this) +mkdir -p $GRADLE_DIR +cat > $INIT_GRADLE.tmp << 'EOF' +allprojects { + repositories { + mavenLocal() + } + configurations.all { + resolutionStrategy.eachDependency { details -> +EOF + +# Process each dependency +jq -c '.dependencies[]' $JSON_FILE | while read -r dep; do + JAR_PATH=$(echo $dep | jq -r '.jar_path') + REPO_URL=$(echo $dep | jq -r '.repo_url') + COMMIT_SHA=$(echo $dep | jq -r '.commit_sha') + + PREV_DIR=$(pwd) + REPO_NAME=$(basename $REPO_URL .git) + WORK_DIR=$(mktemp -d)/$REPO_NAME + mkdir -p $WORK_DIR + echo "Processing $REPO_URL @ $COMMIT_SHA (in $WORK_DIR)" + cd $WORK_DIR + + # Shallow clone and checkout + git init . + git remote add origin $REPO_URL + git fetch --depth 1 origin $COMMIT_SHA + git fetch --tags --depth 1 + git checkout $COMMIT_SHA + + # Get coordinates + PROPS=$(./gradlew -q :properties 2>/dev/null) + GROUP=$(echo "$PROPS" | grep '^group:' | awk '{print $2}') + # Gradle does not actually seem to have project name, + # it just takes the project folder name (in this case, the repo) + PROJECT=$REPO_NAME + # We don't actually care about the version, we just want to override it (this just returns a short sha rn) + # If we ever do care about it, we need to figure out how to get it with just a shallow clone + VERSION=$(echo "$PROPS" | grep '^version:' | awk '{print $2}')-local + + echo "Coordinates of local maven result: $GROUP:$PROJECT:$VERSION" + + # Generate a pom for local maven (need to do this, so we have deps included) + VERSION=$VERSION ./gradlew generatePomFileForMavenPublication + + # Setup local maven + MAVEN_PATH="${GROUP//.//}/$PROJECT/$VERSION" + mkdir -p ~/.m2/repository/$MAVEN_PATH + + # Copy in pom and create fake normal jar so gradle is happy + mv ./build/publications/maven/pom-default.xml ~/.m2/repository/$MAVEN_PATH/$PROJECT-$VERSION.pom + touch ~/.m2/repository/$MAVEN_PATH/$PROJECT-$VERSION.jar + + # Go back to original dir & clean up workdir + cd $PREV_DIR + rm -rf $WORK_DIR + + # Move actual jar to local maven + cp $JAR_PATH ~/.m2/repository/$MAVEN_PATH/$PROJECT-$VERSION-dev.jar + + # Append override to init.gradle + cat >> $INIT_GRADLE.tmp << EOF + if (details.requested.module.toString() == '${GROUP}:${PROJECT}') { + details.useVersion '${VERSION}' + details.because 'PR dependency override' + } +EOF + + rm -rf $WORK_DIR +done + +# Close init.gradle +cat >> $INIT_GRADLE.tmp << 'EOF' + } + } +} +EOF + +# Rename to actual now that its parseable +mv $INIT_GRADLE.tmp $INIT_GRADLE \ No newline at end of file