name: sunnypilot prebuilt action env: BUILD_DIR: "/data/openpilot" OUTPUT_DIR: ${{ github.workspace }}/output CI_DIR: ${{ github.workspace }}/release/ci SCONS_CACHE_DIR: ${{ github.workspace }}/release/ci/scons_cache PUBLIC_REPO_URL: "https://github.com/sunnypilot/sunnypilot" # Branch configurations MASTER_BRANCH: "master" MASTER_NEW_BRANCH: "master-new" DEV_C3_SOURCE_BRANCH: "master-dev-c3-new" # Target branch configurations STAGING_TARGET_BRANCH: "staging-c3-new" DEV_TARGET_BRANCH: "dev-c3-new" RELEASE_TARGET_BRANCH: "release-c3-new" on: push: branches: [ master, master-new, master-dev-c3-new ] tags: [ '*' ] pull_request: branches: [ master, master-new ] workflow_dispatch: inputs: extra_version: description: 'Extra version identifier' required: false default: '' jobs: build: concurrency: group: build-${{ github.head_ref || github.ref_name }} cancel-in-progress: false runs-on: self-hosted outputs: new_branch: ${{ steps.set-env.outputs.new_branch }} version: ${{ steps.set-env.outputs.version }} extra_version_identifier: ${{ steps.set-env.outputs.extra_version_identifier }} steps: - uses: actions/checkout@v4 with: submodules: recursive - run: git lfs pull - name: Cache SCons uses: actions/cache@v4 with: path: ${{env.SCONS_CACHE_DIR}} key: scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }}-${{ github.sha }} # Note: GitHub Actions enforces cache isolation between different build sources (PR builds, workflow dispatches, etc.) # for security. Only caches from the default branch are shared across all builds. This is by design and cannot be overridden. restore-keys: | scons-${{ runner.os }}-${{ runner.arch }}-${{ github.head_ref || github.ref_name }} scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_NEW_BRANCH }} scons-${{ runner.os }}-${{ runner.arch }}-${{ env.MASTER_BRANCH }} scons-${{ runner.os }}-${{ runner.arch }} - name: Set Configuration run: | if [[ "${{ github.ref_name }}" == "${{ env.DEV_C3_SOURCE_BRANCH }}" ]]; then # Dev configuration echo "BRANCH_TYPE=dev" >> $GITHUB_ENV echo "NEW_BRANCH=${{ env.DEV_TARGET_BRANCH }}" >> $GITHUB_ENV echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV echo "EXTRA_VERSION_IDENTIFIER=${{ github.run_number }}" >> $GITHUB_ENV elif [[ "${{ github.ref_name }}" == "${{ env.MASTER_BRANCH }}" || "${{ github.ref_name }}" == "${{ env.MASTER_NEW_BRANCH }}" ]]; then # Master configuration echo "BRANCH_TYPE=master" >> $GITHUB_ENV echo "NEW_BRANCH=${{ env.STAGING_TARGET_BRANCH }}" >> $GITHUB_ENV echo "EXTRA_VERSION_IDENTIFIER=staging" >> $GITHUB_ENV echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-staging" >> $GITHUB_ENV elif [[ "${{ github.ref }}" == refs/tags/* ]]; then # Tag configuration echo "BRANCH_TYPE=tag" >> $GITHUB_ENV echo "NEW_BRANCH=${{ env.RELEASE_TARGET_BRANCH }}" >> $GITHUB_ENV echo "EXTRA_VERSION_IDENTIFIER=release" >> $GITHUB_ENV echo "VERSION=$(cat common/version.h | grep COMMA_VERSION | sed -e 's/[^0-9|.]//g')-release" >> $GITHUB_ENV else # Feature branch configuration echo "BRANCH_TYPE=dispatch" >> $GITHUB_ENV echo "NEW_BRANCH=${{ github.head_ref || github.ref_name }}-prebuilt" >> $GITHUB_ENV echo "VERSION=$(date '+%Y.%m.%d')-${{ github.run_number }}" >> $GITHUB_ENV fi - name: Set environment variables id: set-env run: | # Write to GITHUB_OUTPUT from environment variables echo "new_branch=$NEW_BRANCH" >> $GITHUB_OUTPUT [[ ! -z "$EXTRA_VERSION_IDENTIFIER" ]] && echo "extra_version_identifier=$EXTRA_VERSION_IDENTIFIER" >> $GITHUB_OUTPUT [[ ! -z "$VERSION" ]] && echo "version=$VERSION" >> $GITHUB_OUTPUT # Set up common environment source /etc/profile; export UV_PROJECT_ENVIRONMENT=${HOME}/venv export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT printenv >> $GITHUB_ENV if [[ "${{ runner.debug }}" == "1" ]]; then cat $GITHUB_OUTPUT fi - name: Setup build environment run: | mkdir -p "${BUILD_DIR}/" sudo find $BUILD_DIR/ -mindepth 1 -delete echo "Starting build stage..." echo "BUILD_DIR: ${BUILD_DIR}" echo "CI_DIR: ${CI_DIR}" echo "VERSION: ${{ steps.set-env.outputs.version }}" echo "UV_PROJECT_ENVIRONMENT: ${UV_PROJECT_ENVIRONMENT}" echo "VIRTUAL_ENV: ${VIRTUAL_ENV}" echo "-------" if [[ "${{ runner.debug }}" == "1" ]]; then printenv fi PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable - name: Build Panda run: | scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} ${{ github.workspace }}/panda - name: Build Main Project run: | export PYTHONPATH="$BUILD_DIR" ./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/ cd $BUILD_DIR sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal touch ${BUILD_DIR}/prebuilt if [[ "${{ runner.debug }}" == "1" ]]; then ls -la ${BUILD_DIR} fi - name: Prepare Output run: | sudo rm -rf ${OUTPUT_DIR} mkdir -p ${OUTPUT_DIR} rsync -am${RUNNER_DEBUG:+v} \ --include='**/panda/board/' \ --include='**/panda/board/obj' \ --include='**/panda/board/obj/panda.bin.signed' \ --include='**/panda/board/obj/panda_h7.bin.signed' \ --include='**/panda/board/obj/bootstub.panda.bin' \ --include='**/panda/board/obj/bootstub.panda_h7.bin' \ --exclude='.sconsign.dblite' \ --exclude='*.a' \ --exclude='*.o' \ --exclude='*.os' \ --exclude='*.pyc' \ --exclude='moc_*' \ --exclude='*.cc' \ --exclude='Jenkinsfile' \ --exclude='supercombo.onnx' \ --exclude='**/panda/board/*' \ --exclude='**/panda/board/obj/**' \ --exclude='**/panda/certs/' \ --exclude='**/panda/crypto/' \ --exclude='**/release/' \ --exclude='**/.github/' \ --exclude='**/selfdrive/ui/replay/' \ --exclude='**/__pycache__/' \ --exclude='**/selfdrive/ui/*.h' \ --exclude='**/selfdrive/ui/**/*.h' \ --exclude='**/selfdrive/ui/qt/offroad/sunnypilot/' \ --exclude='${{env.SCONS_CACHE_DIR}}' \ --exclude='**/.git/' \ --exclude='**/SConstruct' \ --exclude='**/SConscript' \ --exclude='**/.venv/' \ --delete-excluded \ --chown=comma:comma \ ${BUILD_DIR}/ ${OUTPUT_DIR}/ - name: 'Tar.gz files' run: | tar czf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }} . ls -la prebuilt.tar.gz - name: 'Upload Artifact' uses: actions/upload-artifact@v4 with: name: prebuilt path: prebuilt.tar.gz - name: Re-enable powersave if: always() run: | PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --enable publish: concurrency: group: publish-${{ github.head_ref || github.ref_name }} cancel-in-progress: true if: ${{ github.event_name != 'pull_request' || github.event_name == 'pull_request' && github.event.pull_request.draft }} needs: build runs-on: ubuntu-24.04 environment: ${{ contains(fromJSON(vars.AUTO_DEPLOY_PREBUILT_BRANCHES), github.head_ref || github.ref_name) && 'auto-deploy' || 'feature-branch' }} steps: - uses: actions/checkout@v4 - name: Download build artifacts uses: actions/download-artifact@v4 with: name: prebuilt - name: Untar prebuilt run: | mkdir -p ${{ env.OUTPUT_DIR }} tar xzf prebuilt.tar.gz -C ${{ env.OUTPUT_DIR }} - name: Configure Git run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - name: Publish to Public Repository env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo '${{ toJSON(needs.build.outputs) }}' ls -la ${{ env.OUTPUT_DIR }} ${{ env.CI_DIR }}/publish.sh \ "${{ github.workspace }}" \ "${{ env.OUTPUT_DIR }}" \ "${{ needs.build.outputs.new_branch }}" \ "${{ needs.build.outputs.version }}" \ "https://x-access-token:${{github.token}}@github.com/sunnypilot/sunnypilot.git" \ "-${{ needs.build.outputs.extra_version_identifier }}" echo "" echo "---- ℹ️ To update the list of branches that auto deploy prebuilts -----" echo "" echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/AUTO_DEPLOY_PREBUILT_BRANCHES" echo "2. Current value: ${{ vars.AUTO_DEPLOY_PREBUILT_BRANCHES }}" echo "3. Update as needed (JSON array with no spaces)" notify: needs: [ build, publish ] runs-on: ubuntu-24.04 if: success() steps: - uses: actions/checkout@v4 - name: Setup Alpine Linux environment uses: jirutka/setup-alpine@v1.2.0 with: packages: 'jq gettext curl' - name: Send Discord Notification env: DISCORD_WEBHOOK: ${{ contains(fromJSON(vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES), github.head_ref || github.ref_name) && secrets.DISCORD_DEV_FEEDBACK_CHANNEL_WEBHOOK || secrets.DISCORD_DEV_PRIVATE_CHANNEL_WEBHOOK }} run: | TEMPLATE='${{ vars.DISCORD_GENERAL_UPDATE_NOTICE }}' export EXTRA_VERSION_IDENTIFIER="${{ needs.build.outputs.extra_version_identifier }}" export VERSION="${{ needs.build.outputs.version }}" export branch_name=${{ github.head_ref || github.ref_name }} export new_branch=${{ needs.build.outputs.new_branch }} export extra_version_identifier=${{ needs.build.outputs.extra_version_identifier || github.run_number}} echo ${TEMPLATE} | envsubst | jq -c '.' | tee payload.json curl -X POST -H "Content-Type: application/json" -d @payload.json $DISCORD_WEBHOOK echo "" echo "---- ℹ️ To update the list of branches that notify to dev-feedback -----" echo "" echo "1. Go to: ${{ github.server_url }}/${{ github.repository }}/settings/variables/actions/DEV_FEEDBACK_NOTIFICATION_BRANCHES" echo "2. Current value: ${{ vars.DEV_FEEDBACK_NOTIFICATION_BRANCHES }}" echo "3. Update as needed (JSON array with no spaces)" shell: alpine.sh {0}