3.14/msys: port MSYS2 0062 + 0064 + 0067; 0076 returns to verbatim (#21) #9
Workflow file for this run
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: release-src | |
| # Repack-on-bump ONLY (the PROGRESS/23 lesson): the diff-aware plan | |
| # (tools/build_matrix over Tfs::ReleaseDiff/Tfs::BuildPlan) rebuilds exactly | |
| # the (version x scenario) rows whose inputs changed — a changed patch line | |
| # (only the scenarios the changed patches FEED: an _msys patch never | |
| # re-rolls the POSIX tarball), a moved versions.yml entry, a shared tooling | |
| # change (fail closed: everything) — and carries every other row's asset | |
| # forward from the previous release as a sha256-verified copy | |
| # (tools/copy_asset). The ruby factory fans this out into per-line units | |
| # (_release-line.yml) for 30+ versions across 5 lines; at this factory's | |
| # scale one flat matrix does the same job — split per-line when the | |
| # version count grows. | |
| # | |
| # The compile-smoke gate (the v0.2.8 lesson, roadmap 17.0): publish is | |
| # gated on tools/smoke_matrix — one representative leg per (changed patch | |
| # line x affected scenario) compiled by _compile-smoke.yml | |
| # (tools/compile_smoke) on the scenario's native runner (windows-2022 + | |
| # msys2 ucrt64 for windows-msys). A patch set that ships apply-clean but | |
| # uncompilable never reaches the release. The build legs' extract-verify | |
| # (diff -qr against the staged tree) is the byte-integrity gate. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: Tag to publish the source tarballs to | |
| required: true | |
| default: v0.0.0 | |
| jobs: | |
| plan: | |
| name: plan the release (diff-aware) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| build: ${{ steps.emit.outputs.build }} | |
| copies: ${{ steps.emit.outputs.copies }} | |
| smoke: ${{ steps.emit.outputs.smoke }} | |
| previous_tag: ${{ steps.emit.outputs.previous_tag }} | |
| build_count: ${{ steps.emit.outputs.build_count }} | |
| copies_count: ${{ steps.emit.outputs.copies_count }} | |
| smoke_count: ${{ steps.emit.outputs.smoke_count }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: fetch tags (previous-release diff base) | |
| run: git fetch --tags --force | |
| - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 | |
| with: | |
| ruby-version: "3.2" | |
| - id: emit | |
| env: | |
| RELEASE_TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.release_tag }} | |
| run: | | |
| build=$(tools/build_matrix "$RELEASE_TAG" --build) | |
| copies=$(tools/build_matrix "$RELEASE_TAG" --copies) | |
| smoke=$(tools/smoke_matrix "$RELEASE_TAG") | |
| previous_tag=$(tools/build_matrix "$RELEASE_TAG" --previous-tag) | |
| { | |
| echo "build=$build" | |
| echo "copies=$copies" | |
| echo "smoke=$smoke" | |
| echo "previous_tag=$previous_tag" | |
| # Leg counts drive the job-level if: guards (an unguarded | |
| # empty matrix poisons the run conclusion — the ruby factory's | |
| # v0.2.15 lesson). | |
| echo "build_count=$(jq '.include | length' <<< "$build")" | |
| echo "copies_count=$(jq '.include | length' <<< "$copies")" | |
| echo "smoke_count=$(jq '.include | length' <<< "$smoke")" | |
| } >> "$GITHUB_OUTPUT" | |
| # Mirror the plan to the LOG: values written only to | |
| # $GITHUB_OUTPUT leave no diagnosable record when the legs skip | |
| # (the ruby factory's v0.2.24 zero-plan failure). | |
| echo "plan: tag=$RELEASE_TAG previous_tag=${previous_tag:-<none>} build=$(jq '.include | length' <<< "$build") copies=$(jq '.include | length' <<< "$copies") smoke=$(jq '.include | length' <<< "$smoke")" | |
| if [ -n "$previous_tag" ]; then | |
| echo "changed paths ($previous_tag..$RELEASE_TAG): $(git diff --name-only "$previous_tag..$RELEASE_TAG" 2>/dev/null || git diff --name-only "$previous_tag..HEAD" | paste -sd' ' -)" | |
| else | |
| echo "changed paths: <no previous tag — full build>" | |
| fi | |
| # The changed lines' smoke runs FIRST and gates the build legs (a red | |
| # smoke saves the build spend); copies run independently (a copied asset | |
| # is byte-identical with the previous release's, there is nothing to | |
| # smoke). | |
| smoke: | |
| name: smoke ${{ matrix.version }} (${{ matrix.platform }}) | |
| needs: plan | |
| if: needs.plan.outputs.smoke_count != '0' | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.plan.outputs.smoke) }} | |
| uses: ./.github/workflows/_compile-smoke.yml | |
| with: | |
| version: ${{ matrix.version }} | |
| platform: ${{ matrix.platform }} | |
| runs_on: ${{ matrix.platform == 'windows-msys' && 'windows-2022' || 'ubuntu-latest' }} | |
| build: | |
| name: build ${{ matrix.version }}${{ matrix.suffix }} | |
| needs: [plan, smoke] | |
| if: ${{ always() && needs.plan.outputs.build_count != '0' && (needs.plan.outputs.smoke_count == '0' || needs.smoke.result == 'success') }} | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.plan.outputs.build) }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 | |
| with: | |
| ruby-version: "3.2" | |
| bundler-cache: true | |
| - name: validate manifests against schema | |
| run: bundle exec tools/validate_manifests | |
| # Stage the row's scenario tree: linux-gnu applies the unsuffixed | |
| # set (empty — the asset stays byte-identical with the pristine | |
| # upstream tarball), windows-msys the line's _msys series. | |
| - name: stage the source tree | |
| run: tools/apply "${{ matrix.version }}" "$PWD/build" --platform "${{ matrix.platform }}" | |
| - name: package | |
| working-directory: build | |
| # Deterministic roll (README § "Reproducible tarballs"): the | |
| # published asset's sha256 is content-addressed — identical trees | |
| # must package to identical bytes, or downstream caches and re-cut | |
| # provenance break. Clamp all tar metadata; the gzip header is | |
| # already MTIME=0 via the pipe. ubuntu runner ⇒ GNU tar. | |
| run: | | |
| tar --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \ | |
| -czf "${{ matrix.asset }}" "${{ matrix.tree }}" | |
| sha256sum "${{ matrix.asset }}" > "${{ matrix.asset }}.sha256" | |
| - name: extract-verify | |
| working-directory: build | |
| run: | | |
| mkdir verify | |
| tar -xzf "${{ matrix.asset }}" -C verify | |
| diff -qr "${{ matrix.tree }}" "verify/${{ matrix.tree }}" | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: tfs-python-${{ matrix.version }}-src${{ matrix.suffix }} | |
| path: build/${{ matrix.asset }}* | |
| retention-days: 1 | |
| copy: | |
| name: copy ${{ matrix.asset }} | |
| needs: plan | |
| if: needs.plan.outputs.copies_count != '0' | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.plan.outputs.copies) }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 | |
| with: | |
| ruby-version: "3.2" | |
| - name: download + verify against the previous release's SHA256SUMS | |
| run: tools/copy_asset "${{ needs.plan.outputs.previous_tag }}" "${{ matrix.asset }}" "$PWD/dist" | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: tfs-python-${{ matrix.version }}-src${{ matrix.suffix }} | |
| path: dist/${{ matrix.asset }}* | |
| retention-days: 1 | |
| publish: | |
| name: publish release | |
| needs: [plan, smoke, build, copy] | |
| # Every leg succeeded (or there was nothing to do): a failed smoke or | |
| # build blocks the publish rather than shipping its stale copies. | |
| # always() is LOAD-BEARING: a job-level if without a status-check | |
| # function is implicitly ANDed with success(), and a skipped need makes | |
| # success() false (the ruby factory's v0.2.24 zero-leg failure). | |
| if: ${{ always() && needs.plan.result == 'success' && (needs.smoke.result == 'success' || needs.smoke.result == 'skipped') && (needs.build.result == 'success' || needs.build.result == 'skipped') && (needs.copy.result == 'success' || needs.copy.result == 'skipped') }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| # Diagnosability first: needs.<job>.result is evaluated server-side, | |
| # so echo exactly what the gate saw before doing anything else. | |
| - name: needs results (publish gate diagnostic) | |
| run: | | |
| echo "plan.result = ${{ needs.plan.result }}" | |
| echo "smoke.result = ${{ needs.smoke.result }}" | |
| echo "build.result = ${{ needs.build.result }}" | |
| echo "copy.result = ${{ needs.copy.result }}" | |
| echo "needs (full) = ${{ toJSON(needs) }}" | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: tfs-python-*-src* | |
| path: dist | |
| merge-multiple: true | |
| - name: SHA256SUMS | |
| working-directory: dist | |
| run: | | |
| cat *.sha256 > SHA256SUMS | |
| rm -f *.sha256 | |
| - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 | |
| with: | |
| tag_name: ${{ github.event_name == 'push' && github.ref_name || inputs.release_tag }} | |
| files: | | |
| dist/*.tar.gz | |
| dist/SHA256SUMS |