Skip to content

Commit 9d14700

Browse files
Feat/release policy (#68)
* fix: git submodules tips in release-all; uboot compile timestamp * feat: release policy published
1 parent d8825ed commit 9d14700

4 files changed

Lines changed: 682 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
release_type:
7+
description: 'Version segment to bump'
8+
required: true
9+
default: patch
10+
type: choice
11+
options:
12+
- patch
13+
- minor
14+
- major
15+
version_override:
16+
description: 'Optional explicit version, for example v1.1.0'
17+
required: false
18+
type: string
19+
create_github_release:
20+
description: 'Create a GitHub Release'
21+
required: true
22+
default: true
23+
type: boolean
24+
dry_run:
25+
description: 'Preview only; do not create tag or release'
26+
required: true
27+
default: true
28+
type: boolean
29+
30+
permissions:
31+
contents: write
32+
33+
concurrency:
34+
group: release-${{ github.repository }}
35+
cancel-in-progress: false
36+
37+
jobs:
38+
release:
39+
name: Prepare Release
40+
runs-on: ubuntu-latest
41+
42+
steps:
43+
- name: Checkout
44+
uses: actions/checkout@v4
45+
with:
46+
fetch-depth: 0
47+
48+
- name: Setup Node.js
49+
uses: actions/setup-node@v4
50+
with:
51+
node-version: '22'
52+
53+
- name: Setup pnpm
54+
uses: pnpm/action-setup@v4
55+
with:
56+
version: 10
57+
58+
- name: Resolve version
59+
id: version
60+
shell: bash
61+
env:
62+
RELEASE_TYPE: ${{ inputs.release_type }}
63+
VERSION_OVERRIDE: ${{ inputs.version_override }}
64+
run: |
65+
set -euo pipefail
66+
67+
git fetch --tags --force
68+
69+
latest_tag="$(
70+
git tag --list 'v[0-9]*.[0-9]*.[0-9]*' |
71+
grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' |
72+
sort -V |
73+
tail -n 1 || true
74+
)"
75+
latest_tag="${latest_tag:-v0.0.0}"
76+
77+
version_re='^v([0-9]+)\.([0-9]+)\.([0-9]+)$'
78+
if [[ ! "${latest_tag}" =~ ${version_re} ]]; then
79+
echo "Latest stable tag is invalid: ${latest_tag}" >&2
80+
exit 1
81+
fi
82+
83+
major="${BASH_REMATCH[1]}"
84+
minor="${BASH_REMATCH[2]}"
85+
patch="${BASH_REMATCH[3]}"
86+
87+
if [[ -n "${VERSION_OVERRIDE}" ]]; then
88+
new_version="${VERSION_OVERRIDE}"
89+
if [[ ! "${new_version}" =~ ${version_re} ]]; then
90+
echo "version_override must match vX.Y.Z: ${new_version}" >&2
91+
exit 1
92+
fi
93+
else
94+
case "${RELEASE_TYPE}" in
95+
patch)
96+
new_version="v${major}.${minor}.$((patch + 1))"
97+
;;
98+
minor)
99+
new_version="v${major}.$((minor + 1)).0"
100+
;;
101+
major)
102+
new_version="v$((major + 1)).0.0"
103+
;;
104+
*)
105+
echo "Unsupported release_type: ${RELEASE_TYPE}" >&2
106+
exit 1
107+
;;
108+
esac
109+
fi
110+
111+
if git rev-parse -q --verify "refs/tags/${new_version}" >/dev/null; then
112+
echo "Tag already exists: ${new_version}" >&2
113+
exit 1
114+
fi
115+
116+
highest="$(printf '%s\n%s\n' "${latest_tag}" "${new_version}" | sort -V | tail -n 1)"
117+
if [[ "${highest}" != "${new_version}" || "${new_version}" == "${latest_tag}" ]]; then
118+
echo "New version must be greater than latest stable tag (${latest_tag}): ${new_version}" >&2
119+
exit 1
120+
fi
121+
122+
release_doc="document/release/${new_version}.md"
123+
release_doc_status="not-required"
124+
if [[ -f "${release_doc}" ]]; then
125+
release_doc_status="present"
126+
elif [[ "${RELEASE_TYPE}" != "patch" ]]; then
127+
release_doc_status="missing"
128+
fi
129+
130+
{
131+
echo "latest_tag=${latest_tag}"
132+
echo "new_version=${new_version}"
133+
echo "release_doc=${release_doc}"
134+
echo "release_doc_status=${release_doc_status}"
135+
} >> "${GITHUB_OUTPUT}"
136+
137+
{
138+
echo "## Release Preview"
139+
echo ""
140+
echo "- Latest stable tag: \`${latest_tag}\`"
141+
echo "- New version: \`${new_version}\`"
142+
echo "- Release type: \`${RELEASE_TYPE}\`"
143+
echo "- Release document: \`${release_doc_status}\`"
144+
} >> "${GITHUB_STEP_SUMMARY}"
145+
146+
- name: Validate release files
147+
shell: bash
148+
env:
149+
RELEASE_TYPE: ${{ inputs.release_type }}
150+
RELEASE_DOC: ${{ steps.version.outputs.release_doc }}
151+
RELEASE_DOC_STATUS: ${{ steps.version.outputs.release_doc_status }}
152+
DRY_RUN: ${{ inputs.dry_run }}
153+
run: |
154+
set -euo pipefail
155+
156+
test -f changelog/CHANGELOG.md
157+
test -f document/release/versioning-strategy.md
158+
159+
if [[ "${RELEASE_TYPE}" != "patch" && "${RELEASE_DOC_STATUS}" == "missing" ]]; then
160+
if [[ "${DRY_RUN}" == "true" ]]; then
161+
echo "MINOR/MAJOR release document is missing for this dry run: ${RELEASE_DOC}" >&2
162+
else
163+
echo "MINOR/MAJOR releases require a release document: ${RELEASE_DOC}" >&2
164+
exit 1
165+
fi
166+
fi
167+
168+
- name: Build documentation
169+
run: |
170+
pnpm install --frozen-lockfile
171+
pnpm build
172+
173+
- name: Generate release body
174+
id: body
175+
shell: bash
176+
env:
177+
LATEST_TAG: ${{ steps.version.outputs.latest_tag }}
178+
NEW_VERSION: ${{ steps.version.outputs.new_version }}
179+
RELEASE_DOC: ${{ steps.version.outputs.release_doc }}
180+
RELEASE_DOC_STATUS: ${{ steps.version.outputs.release_doc_status }}
181+
REPOSITORY: ${{ github.repository }}
182+
run: |
183+
set -euo pipefail
184+
185+
compare_range="${LATEST_TAG}..HEAD"
186+
if [[ "${LATEST_TAG}" == "v0.0.0" ]]; then
187+
compare_range="HEAD"
188+
fi
189+
190+
{
191+
echo "## Highlights"
192+
echo ""
193+
echo "- Stable release ${NEW_VERSION}."
194+
echo ""
195+
echo "## Changes"
196+
echo ""
197+
commits="$(git log --no-merges --pretty='- %s (%h)' "${compare_range}" | sed -n '1,80p')"
198+
if [[ -n "${commits}" ]]; then
199+
echo "${commits}"
200+
else
201+
echo "- No commit summary available."
202+
fi
203+
echo ""
204+
echo "## Documents"
205+
echo ""
206+
echo "- Changelog: [changelog/CHANGELOG.md](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/changelog/CHANGELOG.md)"
207+
if [[ "${RELEASE_DOC_STATUS}" == "present" ]]; then
208+
echo "- Release note: [${RELEASE_DOC}](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/${RELEASE_DOC})"
209+
else
210+
echo "- Release note: not required for this release."
211+
fi
212+
echo "- Versioning strategy: [document/release/versioning-strategy.md](https://github.com/${REPOSITORY}/blob/${NEW_VERSION}/document/release/versioning-strategy.md)"
213+
echo ""
214+
echo "## Release Metadata"
215+
echo ""
216+
echo "- Previous tag: ${LATEST_TAG}"
217+
echo "- New tag: ${NEW_VERSION}"
218+
} > release-body.md
219+
220+
echo "body_file=release-body.md" >> "${GITHUB_OUTPUT}"
221+
222+
- name: Preview release body
223+
run: sed -n '1,200p' release-body.md
224+
225+
- name: Create and push tag
226+
if: ${{ !inputs.dry_run }}
227+
shell: bash
228+
env:
229+
NEW_VERSION: ${{ steps.version.outputs.new_version }}
230+
run: |
231+
set -euo pipefail
232+
233+
git config user.name "github-actions[bot]"
234+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
235+
236+
git fetch --tags --force
237+
if git rev-parse -q --verify "refs/tags/${NEW_VERSION}" >/dev/null; then
238+
echo "Tag already exists after refetch: ${NEW_VERSION}" >&2
239+
exit 1
240+
fi
241+
242+
git tag -a "${NEW_VERSION}" -m "IMX-Forge ${NEW_VERSION}"
243+
git push origin "refs/tags/${NEW_VERSION}"
244+
245+
- name: Create GitHub Release
246+
if: ${{ !inputs.dry_run && inputs.create_github_release }}
247+
env:
248+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
249+
NEW_VERSION: ${{ steps.version.outputs.new_version }}
250+
run: |
251+
gh release create "${NEW_VERSION}" \
252+
--title "IMX-Forge ${NEW_VERSION}" \
253+
--notes-file release-body.md
254+
255+
- name: Dry run notice
256+
if: ${{ inputs.dry_run }}
257+
run: echo "Dry run enabled. No tag or GitHub Release was created."

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
**面向 NXP i.MX6ULL 的嵌入式 Linux 开发工坊 —— 从工具链到驱动的完整学习路径**
1313

1414
[![CI](https://github.com/Awesome-Embedded-Learning-Studio/imx-forge/actions/workflows/ci-build.yml/badge.svg)](https://github.com/Awesome-Embedded-Learning-Studio/imx-forge/actions/workflows/ci-build.yml)
15+
[![Release](https://img.shields.io/github/v/release/Awesome-Embedded-Learning-Studio/imx-forge?style=flat-square&label=Release&color=blue)](https://github.com/Awesome-Embedded-Learning-Studio/imx-forge/releases/latest)
1516
[![License](https://img.shields.io/badge/License-MIT-orange?style=flat-square)](LICENSE)
1617
[![Contributors](https://img.shields.io/github/contributors/Awesome-Embedded-Learning-Studio/imx-forge?style=flat-square)](https://github.com/Awesome-Embedded-Learning-Studio/imx-forge/graphs/contributors)
1718
[![Docker](https://img.shields.io/badge/Docker-supported%20%EF%83%8B-blue?style=flat-square)](docker/README.md)

document/release/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
<ChapterLink num="01" href="v1.0.0">v1.0.0 —— 轻量首版</ChapterLink>
77
</ChapterNav>
88

9+
## 发布规范
10+
11+
<ChapterNav>
12+
<ChapterLink num="01" href="versioning-strategy">版本号管理策略</ChapterLink>
13+
</ChapterNav>
14+
915
## 发布口径
1016

1117
IMX-Forge 的版本发布以可复现构建、镜像装配、烧录路径和真实板卡启动验证为核心。路线图中的历史里程碑编号不等同于正式 release tag。

0 commit comments

Comments
 (0)