Repository navigation
188 lines (172 loc) · 7.41 KB
/
Copy pathrelease-binary.yml
File metadata and controls
188 lines (172 loc) · 7.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
name: Release binary
# Builds the single-file 'git-nested' executable for Linux, macOS and Windows
# (see scripts/create-python-exe.sh) and attaches each archive to the release,
# so git-nested can be used on a machine that has no python -- and no pip, no
# uv -- at all.
#
# Separate from publish.yml on purpose. That one is triggered by the tag push
# and uploads to PyPI, while attaching an asset needs the GitHub *release* to
# already exist, which is only guaranteed once it is published.
on:
release:
types: [published]
# on-demand -- builds and verifies without touching any release. Give 'tag'
# to also (re-)attach the asset to an existing release, e.g. after fixing
# the build for one already published.
workflow_dispatch:
inputs:
tag:
description: 'Existing release tag to build from and attach the asset to (empty: build + verify only)'
required: false
default: ''
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag || github.ref }}
cancel-in-progress: true
env:
# The release this builds. Its tag is what setuptools-scm turns into the
# version frozen into the binary, so checking out anything else would
# produce an executable that misreports itself. Empty on a plain
# workflow_dispatch -- then the triggering ref is built as-is.
REF: ${{ github.event.release.tag_name || inputs.tag || github.ref }}
jobs:
# the glibc floor and every other build decision live there, shared with
# test.yml so both exercise the same executable. 'env.REF' is not available
# to a reusable workflow's inputs, so the ref is spelled out again.
build:
strategy:
fail-fast: false
matrix:
include:
# Intel macOS is deliberately absent: those runners are gone, and
# the arm64 binary is what a current Mac needs.
- target: linux
runs-on: ubuntu-26.04
container: almalinux:8
- target: macos
runs-on: macos-latest
container: ''
- target: windows
runs-on: windows-latest
container: ''
uses: ./.github/workflows/build-binary.yml
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}
artifact: git-nested-binary-${{ matrix.target }}
runs-on: ${{ matrix.runs-on }}
container: ${{ matrix.container }}
# The whole pytest suite, driven through the binary that is about to be
# attached -- the check that it is functionally complete, not just startable.
test:
needs: build
uses: ./.github/workflows/test-binary.yml
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}
artifact: git-nested-binary-linux
# Proves the "runs on any x86_64 Linux" claim rather than assuming it. The
# binary is dropped into stock distro images that have no python installed,
# and has to drive a real nested repo end to end there.
verify:
needs: build
runs-on: ubuntu-26.04
strategy:
fail-fast: false
matrix:
include:
# the glibc floor it was built against, and the oldest still-
# supported LTS releases -- the ones a modern-runner build breaks
- image: almalinux:8
install: dnf -y install git
- image: ubuntu:22.04
install: apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git
- image: debian:12
install: apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git
# ... plus the current end of the range, where a binary built too
# old would fail instead
- image: ubuntu:24.04
install: apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git
- image: fedora:latest
install: dnf -y install git
- image: archlinux:latest
install: pacman -Sy --noconfirm git
steps:
- uses: actions/download-artifact@v8
with:
name: git-nested-binary-linux
# Named git-nested-<version>-<arch>-linux.tar.xz -- resolved by glob
# since the version isn't known here.
- name: Unpack
run: tar -xf git-nested-*-linux.tar.xz
# git is the one thing that does have to be installed -- git-nested
# drives it and refuses to start without it. Python deliberately is not:
# if any of these images had to install it, the binary would not be
# doing its job.
- name: Write the verification script
run: |
cat > verify.sh <<'EOF'
set -eu
if command -v python3 >/dev/null; then
echo "note: this image happens to ship python3; the binary does not use it"
else
echo "note: no python3 in this image at all"
fi
git-nested --version
# invoked as `git nested`, the way users run it: git finds the
# subcommand by looking for a 'git-nested' on PATH.
git config --global user.email ci@example.com
git config --global user.name CI
git config --global init.defaultBranch master
git config --global --add safe.directory '*'
mkdir -p /tmp/repo/doc
cd /tmp/repo
git init -q
touch doc/README.md
git add -A
git commit -qm "initial"
git nested init doc
test -f doc/.gitnested
cat doc/.gitnested
git nested status
EOF
# The binary and the script are mounted in, so the distro images need
# neither tar/xz nor anything else to take part beyond git -- the point
# is that nothing else is required. 'git-nested' is now a symlink to the
# versioned binary, so both have to be mounted together (into PATH)
# rather than just the single file -- a lone symlink mount would
# dangle, pointing at a sibling that was never brought along.
- name: Run on ${{ matrix.image }}
run: |
docker run --rm \
-v "$PWD:/work" -w /work \
${{ matrix.image }} \
sh -c '${{ matrix.install }} >/dev/null && export PATH="/work:$PATH" && sh verify.sh'
upload:
# 'build' listed directly (not just transitively via 'test'/'verify')
# because a job's outputs are only visible to jobs that name it in
# 'needs'.
needs: [build, test, verify]
# nothing to attach when workflow_dispatch ran without a tag
if: github.event_name == 'release' || inputs.tag != ''
runs-on: ubuntu-26.04
permissions:
contents: write
steps:
# every target's artifact, unpacked side by side. 'build' is a matrix,
# and a matrix job's outputs are last-one-wins, so the exact file names
# are read off disk here rather than composed from an output.
- uses: actions/download-artifact@v8
with:
pattern: git-nested-binary-*
merge-multiple: true
# --clobber so re-running against an already-attached release replaces
# the assets instead of failing halfway.
- name: Attach the binaries to ${{ env.REF }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
mapfile -t assets < <(ls git-nested-*.tar.xz git-nested-*.zip 2>/dev/null)
test "${#assets[@]}" -gt 0 || { echo "no archives downloaded" >&2; exit 1; }
printf '%s\n' "${assets[@]}"
gh release upload "$REF" "${assets[@]}" --clobber