release.yml 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. name: release
  2. on:
  3. workflow_run:
  4. workflows: [test]
  5. branches: [master]
  6. types: [completed]
  7. defaults:
  8. run:
  9. shell: bash -euv -o pipefail {0}
  10. jobs:
  11. release:
  12. runs-on: ubuntu-latest
  13. # need to manually check for a couple things
  14. # - tests passed?
  15. # - we are the most recent commit on master?
  16. if: ${{github.event.workflow_run.conclusion == 'success' &&
  17. github.event.workflow_run.head_sha == github.sha}}
  18. steps:
  19. - uses: actions/checkout@v4
  20. with:
  21. ref: ${{github.event.workflow_run.head_sha}}
  22. # need workflow access since we push branches
  23. # containing workflows
  24. token: ${{secrets.BOT_TOKEN}}
  25. # need all tags
  26. fetch-depth: 0
  27. # try to get results from tests
  28. - uses: actions/download-artifact@v4
  29. continue-on-error: true
  30. with:
  31. github-token: ${{secrets.GITHUB_TOKEN}}
  32. run-id: ${{github.event.workflow_run.id}}
  33. pattern: '{sizes,sizes-*}'
  34. merge-multiple: true
  35. path: sizes
  36. - uses: actions/download-artifact@v4
  37. continue-on-error: true
  38. with:
  39. github-token: ${{secrets.GITHUB_TOKEN}}
  40. run-id: ${{github.event.workflow_run.id}}
  41. pattern: '{cov,cov-*}'
  42. merge-multiple: true
  43. path: cov
  44. - uses: actions/download-artifact@v4
  45. continue-on-error: true
  46. with:
  47. github-token: ${{secrets.GITHUB_TOKEN}}
  48. run-id: ${{github.event.workflow_run.id}}
  49. pattern: '{bench,bench-*}'
  50. merge-multiple: true
  51. path: bench
  52. - name: find-version
  53. run: |
  54. # rip version from lfs.h
  55. LFS_VERSION="$(grep -o '^#define LFS_VERSION .*$' lfs.h \
  56. | awk '{print $3}')"
  57. LFS_VERSION_MAJOR="$((0xffff & ($LFS_VERSION >> 16)))"
  58. LFS_VERSION_MINOR="$((0xffff & ($LFS_VERSION >> 0)))"
  59. # find a new patch version based on what we find in our tags
  60. LFS_VERSION_PATCH="$( \
  61. ( git describe --tags --abbrev=0 \
  62. --match="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.*" \
  63. || echo 'v0.0.-1' ) \
  64. | awk -F '.' '{print $3+1}')"
  65. # found new version
  66. LFS_VERSION="v$LFS_VERSION_MAJOR`
  67. `.$LFS_VERSION_MINOR`
  68. `.$LFS_VERSION_PATCH"
  69. echo "LFS_VERSION=$LFS_VERSION"
  70. echo "LFS_VERSION=$LFS_VERSION" >> $GITHUB_ENV
  71. echo "LFS_VERSION_MAJOR=$LFS_VERSION_MAJOR" >> $GITHUB_ENV
  72. echo "LFS_VERSION_MINOR=$LFS_VERSION_MINOR" >> $GITHUB_ENV
  73. echo "LFS_VERSION_PATCH=$LFS_VERSION_PATCH" >> $GITHUB_ENV
  74. # try to find previous version?
  75. - name: find-prev-version
  76. continue-on-error: true
  77. run: |
  78. LFS_PREV_VERSION="$( \
  79. git describe --tags --abbrev=0 --match 'v*' \
  80. || true)"
  81. echo "LFS_PREV_VERSION=$LFS_PREV_VERSION"
  82. echo "LFS_PREV_VERSION=$LFS_PREV_VERSION" >> $GITHUB_ENV
  83. # try to find results from tests
  84. - name: create-table
  85. run: |
  86. # previous results to compare against?
  87. [ -n "$LFS_PREV_VERSION" ] && curl -sS \
  88. "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/status/$LFS_PREV_VERSION`
  89. `?per_page=100" \
  90. | jq -re 'select(.sha != env.GITHUB_SHA) | .statuses[]' \
  91. >> prev-status.json \
  92. || true
  93. # build table for GitHub
  94. declare -A table
  95. # sizes table
  96. i=0
  97. j=0
  98. for c in "" readonly threadsafe multiversion migrate error-asserts
  99. do
  100. # per-config results
  101. c_or_default=${c:-default}
  102. c_camel=${c_or_default^}
  103. table[$i,$j]=$c_camel
  104. ((j+=1))
  105. for s in code stack structs
  106. do
  107. f=sizes/thumb${c:+-$c}.$s.csv
  108. [ -e $f ] && table[$i,$j]=$( \
  109. export PREV="$(jq -re '
  110. select(.context == "'"sizes (thumb${c:+, $c}) / $s"'").description
  111. | capture("(?<prev>[0-9∞]+)").prev' \
  112. prev-status.json || echo 0)"
  113. ./scripts/summary.py $f --max=stack_limit -Y \
  114. | awk '
  115. NR==2 {$1=0; printf "%s B",$NF}
  116. NR==2 && ENVIRON["PREV"]+0 != 0 {
  117. printf " (%+.1f%%)",100*($NF-ENVIRON["PREV"])/ENVIRON["PREV"]
  118. }' \
  119. | sed -e 's/ /\&nbsp;/g')
  120. ((j+=1))
  121. done
  122. ((j=0, i+=1))
  123. done
  124. # coverage table
  125. i=0
  126. j=4
  127. for s in lines branches
  128. do
  129. table[$i,$j]=${s^}
  130. ((j+=1))
  131. f=cov/cov.csv
  132. [ -e $f ] && table[$i,$j]=$( \
  133. export PREV="$(jq -re '
  134. select(.context == "'"cov / $s"'").description
  135. | capture("(?<prev_a>[0-9]+)/(?<prev_b>[0-9]+)")
  136. | 100*((.prev_a|tonumber) / (.prev_b|tonumber))' \
  137. prev-status.json || echo 0)"
  138. ./scripts/cov.py -u $f -f$s -Y \
  139. | awk -F '[ /%]+' -v s=$s '
  140. NR==2 {$1=0; printf "%d/%d %s",$2,$3,s}
  141. NR==2 && ENVIRON["PREV"]+0 != 0 {
  142. printf " (%+.1f%%)",$4-ENVIRON["PREV"]
  143. }' \
  144. | sed -e 's/ /\&nbsp;/g')
  145. ((j=4, i+=1))
  146. done
  147. # benchmark table
  148. i=3
  149. j=4
  150. for s in readed proged erased
  151. do
  152. table[$i,$j]=${s^}
  153. ((j+=1))
  154. f=bench/bench.csv
  155. [ -e $f ] && table[$i,$j]=$( \
  156. export PREV="$(jq -re '
  157. select(.context == "'"bench / $s"'").description
  158. | capture("(?<prev>[0-9]+)").prev' \
  159. prev-status.json || echo 0)"
  160. ./scripts/summary.py $f -f$s=bench_$s -Y \
  161. | awk '
  162. NR==2 {$1=0; printf "%s B",$NF}
  163. NR==2 && ENVIRON["PREV"]+0 != 0 {
  164. printf " (%+.1f%%)",100*($NF-ENVIRON["PREV"])/ENVIRON["PREV"]
  165. }' \
  166. | sed -e 's/ /\&nbsp;/g')
  167. ((j=4, i+=1))
  168. done
  169. # build the actual table
  170. echo "| | Code | Stack | Structs | | Coverage |" >> table.txt
  171. echo "|:--|-----:|------:|--------:|:--|---------:|" >> table.txt
  172. for ((i=0; i<6; i++))
  173. do
  174. echo -n "|" >> table.txt
  175. for ((j=0; j<6; j++))
  176. do
  177. echo -n " " >> table.txt
  178. [[ i -eq 2 && j -eq 5 ]] && echo -n "**Benchmarks**" >> table.txt
  179. echo -n "${table[$i,$j]:-}" >> table.txt
  180. echo -n " |" >> table.txt
  181. done
  182. echo >> table.txt
  183. done
  184. cat table.txt
  185. # find changes from history
  186. - name: create-changes
  187. run: |
  188. [ -n "$LFS_PREV_VERSION" ] || exit 0
  189. # use explicit link to github commit so that release notes can
  190. # be copied elsewhere
  191. git log "$LFS_PREV_VERSION.." \
  192. --grep='^Merge' --invert-grep \
  193. --format="format:[\`%h\`](`
  194. `https://github.com/$GITHUB_REPOSITORY/commit/%h) %s" \
  195. > changes.txt
  196. echo "CHANGES:"
  197. cat changes.txt
  198. # create and update major branches (vN and vN-prefix)
  199. - name: create-major-branches
  200. run: |
  201. # create major branch
  202. git branch "v$LFS_VERSION_MAJOR" HEAD
  203. # create major prefix branch
  204. git config user.name ${{secrets.BOT_USER}}
  205. git config user.email ${{secrets.BOT_EMAIL}}
  206. git fetch "https://github.com/$GITHUB_REPOSITORY.git" \
  207. "v$LFS_VERSION_MAJOR-prefix" || true
  208. ./scripts/changeprefix.py --git "lfs" "lfs$LFS_VERSION_MAJOR"
  209. git branch "v$LFS_VERSION_MAJOR-prefix" $( \
  210. git commit-tree $(git write-tree) \
  211. $(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \
  212. -p HEAD \
  213. -m "Generated v$LFS_VERSION_MAJOR prefixes")
  214. git reset --hard
  215. # push!
  216. git push --atomic origin \
  217. "v$LFS_VERSION_MAJOR" \
  218. "v$LFS_VERSION_MAJOR-prefix"
  219. # build release notes
  220. - name: create-release
  221. run: |
  222. # create release and patch version tag (vN.N.N)
  223. # only draft if not a patch release
  224. touch release.txt
  225. [ -e table.txt ] && cat table.txt >> release.txt
  226. echo >> release.txt
  227. [ -e changes.txt ] && cat changes.txt >> release.txt
  228. cat release.txt
  229. curl -sS -X POST -H "authorization: token ${{secrets.BOT_TOKEN}}" \
  230. "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases" \
  231. -d "$(jq -n --rawfile release release.txt '{
  232. tag_name: env.LFS_VERSION,
  233. name: env.LFS_VERSION | rtrimstr(".0"),
  234. target_commitish: "${{github.event.workflow_run.head_sha}}",
  235. draft: env.LFS_VERSION | endswith(".0"),
  236. body: $release,
  237. }' | tee /dev/stderr)"