This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 3cc112b2717faca81b50fc76517539ec764680fb
Author: Szymon Janc <[email protected]>
AuthorDate: Tue Oct 13 17:34:24 2026 +0200

    ci: Build newt once per workflow run
    
    Jobs not using project prepared by prepare job (prepare itself and
    Windows/macOS jobs) were building or restoring newt on their own, so
    newt could be built several times in parallel when cache was empty.
    
    Build newt for all platforms used by Build workflow in a single job
    and pass it to other jobs as artifact. Newt is cross-compiled, as it
    doesn't use cgo.
    
    Assisted-by: Claude:claude-opus-5-5
---
 .github/actions/newt-install/action.yml | 112 ++++++++++++++++++++++++++++----
 .github/workflows/build.yml             |  30 +++++++++
 .github/workflows/cache_cleanup.yml     |   7 +-
 3 files changed, 134 insertions(+), 15 deletions(-)

diff --git a/.github/actions/newt-install/action.yml 
b/.github/actions/newt-install/action.yml
index 58e406799..55e203bde 100644
--- a/.github/actions/newt-install/action.yml
+++ b/.github/actions/newt-install/action.yml
@@ -20,13 +20,70 @@
 name: Install newt
 description: >
   Install newt built from master branch of apache/mynewt-newt. Built
-  binary is cached per newt commit, so newt is rebuilt only when newt
-  master branch changes.
+  binaries are cached per newt commit, so newt is rebuilt only when newt
+  master branch changes. Newt can also be taken from artifact uploaded
+  by another job of the same workflow run, so that it is built only once.
+
+inputs:
+  platforms:
+    description: >
+      Space separated list of GOOS/GOARCH platforms to build newt for, in
+      addition to platform of the runner. Binaries are stored in
+      directory provided by dist output.
+    required: false
+    default: ''
+  artifact:
+    description: >
+      Name of artifact with newt binaries (dist directory) uploaded by
+      another job. If set, newt is taken from it instead of being built.
+    required: false
+    default: ''
+
+outputs:
+  dist:
+    description: Directory with newt binaries, one subdirectory per platform
+    value: ${{ steps.platform.outputs.dist }}
 
 runs:
   using: composite
   steps:
+    - name: Get platforms
+      id: platform
+      shell: bash
+      env:
+        RUNNER_OS: ${{ runner.os }}
+        RUNNER_ARCH: ${{ runner.arch }}
+        PLATFORMS: ${{ inputs.platforms }}
+      run: |
+        case "$RUNNER_OS" in
+          Linux) goos=linux ;;
+          Windows) goos=windows ;;
+          macOS) goos=darwin ;;
+        esac
+        case "$RUNNER_ARCH" in
+          X64) goarch=amd64 ;;
+          ARM64) goarch=arm64 ;;
+        esac
+        if [ -z "$goos" ] || [ -z "$goarch" ]; then
+          echo "::error::Unsupported runner $RUNNER_OS $RUNNER_ARCH"
+          exit 1
+        fi
+        host="$goos/$goarch"
+        all=$(echo "$host $PLATFORMS" | tr ' ' '\n' | grep . | sort -u | tr 
'\n' ' ')
+        echo "host=$host" >> "$GITHUB_OUTPUT"
+        echo "all=${all% }" >> "$GITHUB_OUTPUT"
+        # Relative to workspace, to avoid Windows paths in bash steps
+        echo "dist=.newt-dist" >> "$GITHUB_OUTPUT"
+
+    - name: Download newt
+      if: inputs.artifact != ''
+      uses: actions/download-artifact@v8
+      with:
+        name: ${{ inputs.artifact }}
+        path: ${{ steps.platform.outputs.dist }}
+
     - name: Get newt master commit
+      if: inputs.artifact == ''
       id: newt
       shell: bash
       run: |
@@ -37,15 +94,18 @@ runs:
         fi
         echo "newt master is at $sha"
         echo "sha=$sha" >> "$GITHUB_OUTPUT"
+        echo "platforms=$(echo ${{ steps.platform.outputs.all }} | tr '/ ' 
'_-')" >> "$GITHUB_OUTPUT"
 
     - name: Restore newt from cache
+      if: inputs.artifact == ''
       id: cache
       uses: actions/cache/restore@v6
       with:
-        path: ${{ github.workspace }}/.newt-bin
-        key: newt-${{ runner.os }}-${{ runner.arch }}-${{ 
steps.newt.outputs.sha }}
+        path: ${{ steps.platform.outputs.dist }}
+        key: newt-${{ runner.os }}-${{ steps.newt.outputs.sha }}-${{ 
steps.newt.outputs.platforms }}
 
     - name: Report newt cache status
+      if: inputs.artifact == ''
       shell: bash
       env:
         CACHE_HIT: ${{ steps.cache.outputs.cache-hit }}
@@ -58,35 +118,63 @@ runs:
         fi
 
     - name: Set up Go
-      if: steps.cache.outputs.cache-hit != 'true'
+      if: inputs.artifact == '' && steps.cache.outputs.cache-hit != 'true'
       uses: actions/setup-go@v7
       with:
         go-version: 'stable'
         cache: false
 
     - name: Build newt
-      if: steps.cache.outputs.cache-hit != 'true'
+      if: inputs.artifact == '' && steps.cache.outputs.cache-hit != 'true'
       shell: bash
       env:
-        GOBIN: ${{ github.workspace }}/.newt-bin
+        CGO_ENABLED: 0
+        GOPATH: ${{ runner.temp }}/newt-gopath
+        NEWT_SHA: ${{ steps.newt.outputs.sha }}
+        PLATFORMS: ${{ steps.platform.outputs.all }}
+        DIST: ${{ steps.platform.outputs.dist }}
       run: |
         go version
-        go install mynewt.apache.org/newt/newt@${{ steps.newt.outputs.sha }}
+        for platform in $PLATFORMS; do
+          goos=${platform%/*}
+          goarch=${platform#*/}
+          echo "Building newt for $platform"
+          GOOS=$goos GOARCH=$goarch \
+            go install "mynewt.apache.org/newt/newt@$NEWT_SHA"
+          # Cross compiled binaries are installed to GOOS_GOARCH subdirectory
+          src="$GOPATH/bin/${goos}_${goarch}"
+          [ -d "$src" ] || src="$GOPATH/bin"
+          mkdir -p "$DIST/${goos}_${goarch}"
+          cp "$src"/newt* "$DIST/${goos}_${goarch}/"
+        done
+        ls -lR "$DIST"
 
     # Only master saves cache, so that it is shared with all pull
     # requests instead of being duplicated in each pull request scope
     - name: Save newt to cache
       if: >-
-        steps.cache.outputs.cache-hit != 'true' &&
+        inputs.artifact == '' && steps.cache.outputs.cache-hit != 'true' &&
         github.ref == 'refs/heads/master' &&
         github.event_name != 'pull_request'
       uses: actions/cache/save@v6
       with:
-        path: ${{ github.workspace }}/.newt-bin
+        path: ${{ steps.platform.outputs.dist }}
         key: ${{ steps.cache.outputs.cache-primary-key }}
 
     - name: Add newt to PATH
       shell: bash
+      env:
+        HOST: ${{ steps.platform.outputs.host }}
+        DIST: ${{ steps.platform.outputs.dist }}
       run: |
-        echo "${{ github.workspace }}/.newt-bin" >> "$GITHUB_PATH"
-        "${{ github.workspace }}/.newt-bin/newt" version
+        src="$DIST/$(echo "$HOST" | tr / _)"
+        if ! ls "$src"/newt* > /dev/null 2>&1; then
+          echo "::error::newt for $HOST not found"
+          exit 1
+        fi
+        mkdir -p .newt-bin
+        cp "$src"/newt* .newt-bin/
+        # Artifacts don't preserve file permissions
+        chmod +x .newt-bin/newt*
+        echo "$GITHUB_WORKSPACE/.newt-bin" >> "$GITHUB_PATH"
+        .newt-bin/newt version
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2c01cb171..94d72e555 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,15 +33,39 @@ permissions:
   contents: read
 
 jobs:
+  # Builds newt once per run for all platforms used by jobs of this
+  # workflow, other jobs take newt from artifact uploaded by this job.
+  newt:
+    name: Build newt
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v7
+      - name: Build newt
+        id: newt
+        uses: ./.github/actions/newt-install
+        with:
+          platforms: windows/amd64 darwin/arm64
+      - name: Upload newt
+        uses: actions/upload-artifact@v7
+        with:
+          name: newt
+          path: ${{ steps.newt.outputs.dist }}
+          include-hidden-files: true
+          if-no-files-found: error
+          retention-days: 1
+
   # Downloads external repositories once per run, all Linux jobs use
   # project prepared by this job.
   prepare:
     name: Prepare project
+    needs: newt
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v7
       - name: Install newt
         uses: ./.github/actions/newt-install
+        with:
+          artifact: newt
       - name: Set up project
         uses: ./.github/actions/newt-project
         with:
@@ -102,6 +126,7 @@ jobs:
 
   blinky-other-os:
     name: Build blinky
+    needs: newt
     strategy:
       fail-fast: false
       matrix:
@@ -119,6 +144,8 @@ jobs:
              echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
       - name: Install newt
         uses: ./.github/actions/newt-install
+        with:
+          artifact: newt
       - name: Set up project
         uses: ./.github/actions/newt-project
         with:
@@ -252,6 +279,7 @@ jobs:
 
   targets-other-os:
     name: Build all test targets
+    needs: newt
     strategy:
       fail-fast: false
       matrix:
@@ -269,6 +297,8 @@ jobs:
              echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
       - name: Install newt
         uses: ./.github/actions/newt-install
+        with:
+          artifact: newt
       - name: Set up project
         uses: ./.github/actions/newt-project
         with:
diff --git a/.github/workflows/cache_cleanup.yml 
b/.github/workflows/cache_cleanup.yml
index 5e076eeb3..99f2d437c 100644
--- a/.github/workflows/cache_cleanup.yml
+++ b/.github/workflows/cache_cleanup.yml
@@ -50,11 +50,12 @@ jobs:
             // Caches created by master branch builds are saved with
             // unique keys (e.g. ccache-<job>-<run_id>-<attempt>), only
             // newest entry of each kind is used by later runs. Remove
-            // older entries to keep total cache size below limit.
+            // older entries to keep total cache size below limit. Kind
+            // is identified by all capture groups of matching pattern.
             const families = [
               { re: /^(ccache-.+)-\d+-\d+$/ },
               { re: /^(newt-repos-[^-]+)-[0-9a-f]+$/ },
-              { re: /^(newt-[^-]+-[^-]+)-[0-9a-f]{40}$/ },
+              { re: /^(newt-[^-]+)-[0-9a-f]{40}-(.+)$/ },
             ];
 
             const caches = await github.paginate(
@@ -71,7 +72,7 @@ jobs:
               if (!family) {
                 continue;
               }
-              const name = family[1];
+              const name = family.slice(1).join('-');
               const current = newest.get(name);
               if (!current) {
                 newest.set(name, cache);

Reply via email to