name: Build dev env: DEFAULT_SOURCE_BRANCH: "master" DEFAULT_TARGET_BRANCH: "master-dev" LFS_URL: 'https://gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git/info/lfs' LFS_PUSH_URL: 'ssh://git@gitlab.com/sunnypilot/public/sunnypilot-new-lfs.git' on: push: branches: - master pull_request_target: types: [ labeled ] branches: - 'master' workflow_dispatch: inputs: source_branch: description: 'Source branch to reset from' required: true default: 'master' type: string target_branch: description: 'Target branch to reset and squash into' required: true default: 'master-dev' type: string cancel_in_progress: description: 'Cancel any in-progress runs of this workflow' required: false default: true type: boolean concurrency: group: ${{ github.workflow }} cancel-in-progress: ${{ inputs.cancel_in_progress || github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: reset-and-squash: runs-on: ubuntu-latest if: ( (github.event_name == 'workflow_dispatch') || (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) || (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL)))) ) steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch all history for all branches token: ${{ secrets.GITHUB_TOKEN }} persist-credentials: false - name: Wait for Tests uses: ./.github/workflows/wait-for-action # Path to where you place the action if: ( (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) || (contains(github.event_name, 'pull_request') && ((github.event.action == 'labeled' && (github.event.label.name == vars.PREBUILT_PR_LABEL || github.event.label.name == 'trust-fork-pr') && contains(github.event.pull_request.labels.*.name, vars.PREBUILT_PR_LABEL)))) ) with: workflow: tests.yaml # The workflow file to monitor github-token: ${{ secrets.GITHUB_TOKEN }} - name: Configure Git run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' - name: Set up SSH uses: webfactory/ssh-agent@v0.9.0 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Add GitLab public keys run: | ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip pip install PyGithub - name: Check branches exist run: | # Check if source branch exists if ! git ls-remote --heads origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} | grep -q "${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}"; then echo "Source branch ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} does not exist!" exit 1 fi # Make sure we have the latest source branch git fetch origin ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} # Check if target branch exists if ! git ls-remote --heads origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} | grep -q "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}"; then echo "Target branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} does not exist, creating it from ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}" git checkout -b ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} git push origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} else # Fetch target branch if it exists git fetch origin ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} fi - name: Reset target branch run: | echo "Resetting ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} to match ${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}" # Delete if exists and recreate pointing to source git branch -D ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} || true git branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} origin/${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }} - name: Get PRs to squash id: get-prs run: | # Use GitHub API to get PRs with specific label, ordered by creation date PR_LIST=$(gh api graphql -f query=' query($search_query:String!) { search(query: $search_query, type:ISSUE, first:40) { nodes { ... on PullRequest { number headRefName title createdAt labels(last:10) { nodes { name } } headRepository { name nameWithOwner url isFork } commits(last: 1) { nodes { commit { statusCheckRollup { state } } } } } } } }' -F search_query="repo:${{ github.repository }} is:pr is:open label:${{ vars.PREBUILT_PR_LABEL }},${{ vars.PREBUILT_PR_LABEL }}-c3 draft:false sort:created-asc") PR_LIST=${PR_LIST//\'/} echo "PR_LIST=${PR_LIST}" >> $GITHUB_OUTPUT env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Process PRs run: | cp ${{ github.workspace }}/release/ci/squash_and_merge.py /tmp/squash_and_merge.py && \ chmod +x /tmp/squash_and_merge.py && \ python3 ${{ github.workspace }}/release/ci/squash_and_merge_prs.py \ --pr-data '${{ steps.get-prs.outputs.PR_LIST }}' \ --target-branch ${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }} \ --squash-script-path '/tmp/squash_and_merge.py' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Update LFS Config run: | echo '[lfs]' > .lfsconfig echo ' url = ${{ env.LFS_URL }}' >> .lfsconfig echo ' pushurl = ${{ env.LFS_PUSH_URL }}' >> .lfsconfig echo ' locksverify = false' >> .lfsconfig - name: Restore workflows from source run: | TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" SOURCE_BRANCH="${{ inputs.source_branch || env.DEFAULT_SOURCE_BRANCH }}" # Ensure we are on the target branch git checkout $TARGET_BRANCH echo "Restoring .github/workflows from $SOURCE_BRANCH" git checkout origin/$SOURCE_BRANCH -- .github/workflows if ! git diff --cached --quiet; then echo "Workflows differ. Committing restoration." git commit -m "chore: restore .github/workflows from $SOURCE_BRANCH" else echo "Workflows match $SOURCE_BRANCH." fi - uses: actions/create-github-app-token@v2 id: ci-token with: app-id: ${{ secrets.CI_GITHUB_ACTIONS_TOKEN_APP_ID }} private-key: ${{ secrets.CI_GITHUB_ACTIONS_TOKEN_APP_PRIVATE_KEY }} - name: Push changes if there are diffs id: push-changes run: | TARGET_BRANCH="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" # Use the App Token to set the remote URL with authentication git remote set-url origin "https://x-access-token:${{ steps.ci-token.outputs.token }}@github.com/${{ github.repository }}.git" # Fetch the latest from remote git fetch origin $TARGET_BRANCH # Check for diffs between local and remote if git diff $TARGET_BRANCH origin/$TARGET_BRANCH --quiet; then echo "No changes to push - local and remote branches are identical" echo "has_changes=false" >> $GITHUB_OUTPUT exit 0 fi # Push with the authenticated origin if ! git push origin $TARGET_BRANCH --force; then echo "Failed to push changes to $TARGET_BRANCH" exit 1 fi echo "Branch $TARGET_BRANCH has been reset and updated with squashed PRs" echo "has_changes=true" >> $GITHUB_OUTPUT - name: Trigger and wait for selfdrive tests if: steps.push-changes.outputs.has_changes == 'true' run: | echo "Triggering selfdrive tests..." gh workflow run tests.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" echo "Sleeping for 120s to give plenty of time for the action to start and then we wait" sleep 120 echo "Getting latest run ID..." RUN_ID=$(gh run list --workflow=tests.yaml --branch="${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" --limit=1 --json databaseId --jq '.[0].databaseId') echo "Watching run ID: $RUN_ID" gh run watch "$RUN_ID" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Trigger prebuilt workflow if: success() && steps.push-changes.outputs.has_changes == 'true' run: | gh workflow run sunnypilot-build-prebuilt.yaml --ref "${{ inputs.target_branch || env.DEFAULT_TARGET_BRANCH }}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}