|
| 1 | +# Copy release notes from product repositories into midnight-docs (similar to apis.yml). |
| 2 | +# Trigger manually; pick the component and either: |
| 3 | +# - provide `source_url` (recommended, supports GitHub release/tag/blob URLs), or |
| 4 | +# - provide `source_path` + branch for clone-based copy. |
| 5 | +# |
| 6 | +# Extend: add a `component` option, matching `branch_*` input, clone step, and a branch in the case statement |
| 7 | +# in "Map component → paths" and "Update DynamicList*". |
| 8 | + |
| 9 | +name: Copy release notes |
| 10 | + |
| 11 | +on: |
| 12 | + workflow_dispatch: |
| 13 | + inputs: |
| 14 | + branch_merge: |
| 15 | + description: 'Destination branch on midnight-docs' |
| 16 | + default: 'main' |
| 17 | + component: |
| 18 | + description: 'Which product repo / DynamicList to update' |
| 19 | + type: choice |
| 20 | + options: |
| 21 | + - wallet |
| 22 | + - ledger |
| 23 | + - compact |
| 24 | + - midnight-js |
| 25 | + - node |
| 26 | + - midnight-indexer |
| 27 | + - dapp-connector-api |
| 28 | + - compact-js |
| 29 | + branch_wallet: |
| 30 | + description: 'wallet: branch' |
| 31 | + default: 'main' |
| 32 | + branch_ledger: |
| 33 | + description: 'ledger: branch' |
| 34 | + default: 'main' |
| 35 | + branch_compact: |
| 36 | + description: 'compact: branch' |
| 37 | + default: 'release/midnight' |
| 38 | + branch_midnight_js: |
| 39 | + description: 'midnight-js: branch' |
| 40 | + default: 'main' |
| 41 | + branch_node: |
| 42 | + description: 'node: branch' |
| 43 | + default: 'main' |
| 44 | + branch_midnight_indexer: |
| 45 | + description: 'midnight-indexer: branch' |
| 46 | + default: 'main' |
| 47 | + branch_dapp_connector_api: |
| 48 | + description: 'dapp-connector-api: branch' |
| 49 | + default: 'main' |
| 50 | + branch_compact_js: |
| 51 | + description: 'compact-js: branch/tag (e.g. cjs-2.5.0)' |
| 52 | + default: 'cjs-2.5.0' |
| 53 | + source_url: |
| 54 | + description: 'Optional source URL to release notes (supports raw.githubusercontent.com and github.com release/tag/blob/tree URLs)' |
| 55 | + required: false |
| 56 | + default: '' |
| 57 | + type: string |
| 58 | + source_path: |
| 59 | + description: 'Optional path to the release notes .md inside the cloned repo (used when source_url is empty)' |
| 60 | + required: false |
| 61 | + default: '' |
| 62 | + type: string |
| 63 | + version: |
| 64 | + description: 'Version for DynamicList (e.g. 2.0.0 or v2.0.0)' |
| 65 | + required: true |
| 66 | + type: string |
| 67 | + compact_language_version: |
| 68 | + description: "Only when component is compact: language version (e.g. v0.22.0 or 0.22.0). Ignored for other components." |
| 69 | + required: false |
| 70 | + default: '' |
| 71 | + type: string |
| 72 | + pr_title: |
| 73 | + description: 'PR title' |
| 74 | + default: 'Copy release notes from product repository' |
| 75 | + |
| 76 | +permissions: |
| 77 | + contents: write |
| 78 | + pull-requests: write |
| 79 | + |
| 80 | +jobs: |
| 81 | + copy_release_notes: |
| 82 | + runs-on: ubuntu-latest |
| 83 | + steps: |
| 84 | + - name: Checkout midnight-docs |
| 85 | + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 |
| 86 | + with: |
| 87 | + path: ${{ github.workspace }} |
| 88 | + ref: ${{ github.event.inputs.branch_merge }} |
| 89 | + |
| 90 | + - name: Git config |
| 91 | + run: | |
| 92 | + git config --global user.email "midnight-ci@users.noreply.github.com" |
| 93 | + git config --global user.name "Midnight CI GitHub Action" |
| 94 | + git config --global url.https://${{ secrets.GH_TOKEN }}@github.com/.insteadOf https://github.com/ |
| 95 | +
|
| 96 | + - name: Clone wallet |
| 97 | + if: ${{ github.event.inputs.component == 'wallet' }} |
| 98 | + run: | |
| 99 | + mkdir -p ./temp && cd ./temp |
| 100 | + git clone --branch "$WALLET_BRANCH" https://github.com/midnightntwrk/midnight-wallet.git |
| 101 | + env: |
| 102 | + WALLET_BRANCH: ${{ github.event.inputs.branch_wallet }} |
| 103 | + |
| 104 | + - name: Clone ledger |
| 105 | + if: ${{ github.event.inputs.component == 'ledger' }} |
| 106 | + run: | |
| 107 | + mkdir -p ./temp && cd ./temp |
| 108 | + git clone --branch "$LEDGER_BRANCH" https://github.com/midnightntwrk/midnight-ledger.git |
| 109 | + env: |
| 110 | + LEDGER_BRANCH: ${{ github.event.inputs.branch_ledger }} |
| 111 | + |
| 112 | + - name: Clone compact |
| 113 | + if: ${{ github.event.inputs.component == 'compact' }} |
| 114 | + run: | |
| 115 | + mkdir -p ./temp && cd ./temp |
| 116 | + git clone --branch "$COMPACT_BRANCH" https://github.com/LFDT-Minokawa/compact/ |
| 117 | + env: |
| 118 | + COMPACT_BRANCH: ${{ github.event.inputs.branch_compact }} |
| 119 | + |
| 120 | + - name: Clone midnight-js |
| 121 | + if: ${{ github.event.inputs.component == 'midnight-js' }} |
| 122 | + run: | |
| 123 | + mkdir -p ./temp && cd ./temp |
| 124 | + git clone --branch "$MIDNIGHT_JS_BRANCH" https://github.com/midnightntwrk/midnight-js.git |
| 125 | + env: |
| 126 | + MIDNIGHT_JS_BRANCH: ${{ github.event.inputs.branch_midnight_js }} |
| 127 | + |
| 128 | + - name: Clone node |
| 129 | + if: ${{ github.event.inputs.component == 'node' }} |
| 130 | + run: | |
| 131 | + mkdir -p ./temp && cd ./temp |
| 132 | + git clone --branch "$NODE_BRANCH" https://github.com/midnightntwrk/midnight-node.git |
| 133 | + env: |
| 134 | + NODE_BRANCH: ${{ github.event.inputs.branch_node }} |
| 135 | + |
| 136 | + - name: Clone midnight-indexer |
| 137 | + if: ${{ github.event.inputs.component == 'midnight-indexer' }} |
| 138 | + run: | |
| 139 | + mkdir -p ./temp && cd ./temp |
| 140 | + git clone --branch "$MIDNIGHT_INDEXER_BRANCH" https://github.com/midnightntwrk/midnight-indexer.git |
| 141 | + env: |
| 142 | + MIDNIGHT_INDEXER_BRANCH: ${{ github.event.inputs.branch_midnight_indexer }} |
| 143 | + |
| 144 | + - name: Clone dapp-connector-api |
| 145 | + if: ${{ github.event.inputs.component == 'dapp-connector-api' }} |
| 146 | + run: | |
| 147 | + mkdir -p ./temp && cd ./temp |
| 148 | + git clone --branch "$DAPP_CONNECTOR_API_BRANCH" https://github.com/midnightntwrk/midnight-dapp-connector-api.git |
| 149 | + env: |
| 150 | + DAPP_CONNECTOR_API_BRANCH: ${{ github.event.inputs.branch_dapp_connector_api }} |
| 151 | + |
| 152 | + - name: Clone compact-js |
| 153 | + if: ${{ github.event.inputs.component == 'compact-js' }} |
| 154 | + run: | |
| 155 | + mkdir -p ./temp && cd ./temp |
| 156 | + git clone --branch "$COMPACT_JS_BRANCH" https://github.com/midnightntwrk/midnight-sdk.git |
| 157 | + env: |
| 158 | + COMPACT_JS_BRANCH: ${{ github.event.inputs.branch_compact_js }} |
| 159 | + |
| 160 | + - name: Copy release notes into docs |
| 161 | + id: copy |
| 162 | + run: | |
| 163 | + set -euo pipefail |
| 164 | + COMPONENT="$COMPONENT" |
| 165 | + SRC_REL="$SRC_REL" |
| 166 | + SRC_URL="$SRC_URL" |
| 167 | + SRC="" |
| 168 | +
|
| 169 | + # Resolve source: URL (preferred) or clone path. |
| 170 | + if [ -n "$SRC_URL" ]; then |
| 171 | + mkdir -p temp |
| 172 | + TMP_SOURCE="temp/source-release-notes.md" |
| 173 | + RAW_URL="$SRC_URL" |
| 174 | + case "$SRC_URL" in |
| 175 | + https://raw.githubusercontent.com/*) |
| 176 | + RAW_URL="$SRC_URL" |
| 177 | + ;; |
| 178 | + https://github.com/*/releases/tag/*) |
| 179 | + # Fetch release notes body from GitHub Releases API. |
| 180 | + OWNER_REPO=$(echo "$SRC_URL" | sed -E 's|^https://github.com/([^/]+/[^/]+)/releases/tag/.*$|\1|') |
| 181 | + TAG=$(echo "$SRC_URL" | sed -E 's|^https://github.com/[^/]+/[^/]+/releases/tag/(.*)$|\1|') |
| 182 | + gh api "repos/${OWNER_REPO}/releases/tags/${TAG}" --jq '.body' > "$TMP_SOURCE" |
| 183 | + ;; |
| 184 | + https://github.com/*/blob/*) |
| 185 | + RAW_URL=$(echo "$SRC_URL" | sed -E 's|^https://github.com/([^/]+/[^/]+)/blob/([^/]+)/(.*)$|https://raw.githubusercontent.com/\1/\2/\3|') |
| 186 | + ;; |
| 187 | + https://github.com/*/tree/*) |
| 188 | + # Used by compact-js style URLs. Build RELEASE_NOTES.md from repo/tree root. |
| 189 | + TREE_RAW=$(echo "$SRC_URL" | sed -E 's|^https://github.com/([^/]+/[^/]+)/tree/([^/]+)/(.*)$|https://raw.githubusercontent.com/\1/\2/\3|') |
| 190 | + RAW_URL="${TREE_RAW}/RELEASE_NOTES.md" |
| 191 | + ;; |
| 192 | + esac |
| 193 | + curl -fsSL "$RAW_URL" -o "$TMP_SOURCE" |
| 194 | + SRC="$TMP_SOURCE" |
| 195 | + fi |
| 196 | +
|
| 197 | + if [ -z "$SRC" ] && [ -z "$SRC_REL" ]; then |
| 198 | + echo "One of source_url or source_path is required." |
| 199 | + exit 1 |
| 200 | + fi |
| 201 | +
|
| 202 | + if [ -z "$SRC" ]; then |
| 203 | + # Repo folder name after clone (must match clone URLs above) |
| 204 | + case "$COMPONENT" in |
| 205 | + wallet) REPO_DIR="midnight-wallet" ;; |
| 206 | + ledger) REPO_DIR="midnight-ledger" ;; |
| 207 | + compact) REPO_DIR="compact" ;; |
| 208 | + midnight-js) REPO_DIR="midnight-js" ;; |
| 209 | + node) REPO_DIR="midnight-node" ;; |
| 210 | + midnight-indexer) REPO_DIR="midnight-indexer" ;; |
| 211 | + dapp-connector-api) REPO_DIR="midnight-dapp-connector-api" ;; |
| 212 | + compact-js) REPO_DIR="midnight-sdk" ;; |
| 213 | + *) echo "Unknown component: $COMPONENT"; exit 1 ;; |
| 214 | + esac |
| 215 | + SRC="temp/${REPO_DIR}/${SRC_REL}" |
| 216 | + fi |
| 217 | + if [ ! -f "$SRC" ]; then |
| 218 | + echo "Source file not found: $SRC" |
| 219 | + exit 1 |
| 220 | + fi |
| 221 | + BASENAME_INPUT="${SRC_REL:-$SRC_URL}" |
| 222 | + BASENAME=$(basename "$BASENAME_INPUT") |
| 223 | + BASENAME="${BASENAME%.md}" |
| 224 | + BASENAME="${BASENAME%.mdx}" |
| 225 | + BASENAME="${BASENAME%/}" |
| 226 | + BASENAME="${BASENAME//./-}" |
| 227 | + BASENAME="${BASENAME//_/-}" |
| 228 | + BASENAME=$(echo "$BASENAME" | sed -E 's/^-+//; s/-+$//') |
| 229 | + if [ -z "$BASENAME" ] || [ "$BASENAME" = "source-release-notes" ] || [ "$BASENAME" = "release-notes" ] || [ "$BASENAME" = "RELEASE-NOTES" ]; then |
| 230 | + BASENAME="${COMPONENT}-$VERSION" |
| 231 | + BASENAME="${BASENAME#v}" |
| 232 | + BASENAME="${BASENAME//./-}" |
| 233 | + fi |
| 234 | + case "$COMPONENT" in |
| 235 | + wallet) DEST_DIR="docs/relnotes/wallet" ;; |
| 236 | + ledger) DEST_DIR="docs/relnotes/ledger" ;; |
| 237 | + compact) DEST_DIR="docs/relnotes/compact" ;; |
| 238 | + midnight-js) DEST_DIR="docs/relnotes/midnight-js" ;; |
| 239 | + node) DEST_DIR="docs/relnotes/node" ;; |
| 240 | + midnight-indexer) DEST_DIR="docs/relnotes/midnight-indexer" ;; |
| 241 | + dapp-connector-api) DEST_DIR="docs/relnotes/dapp-connector-api" ;; |
| 242 | + compact-js) DEST_DIR="docs/relnotes/compact-js" ;; |
| 243 | + esac |
| 244 | + mkdir -p "$DEST_DIR" |
| 245 | + cp "$SRC" "${DEST_DIR}/${BASENAME}.mdx" |
| 246 | + echo "relnotes_subdir=$(basename "$DEST_DIR")" >> "$GITHUB_OUTPUT" |
| 247 | + echo "slug=$BASENAME" >> "$GITHUB_OUTPUT" |
| 248 | + echo "Copied $SRC -> ${DEST_DIR}/${BASENAME}.mdx" |
| 249 | + env: |
| 250 | + COMPONENT: ${{ github.event.inputs.component }} |
| 251 | + VERSION: ${{ github.event.inputs.version }} |
| 252 | + SRC_REL: ${{ github.event.inputs.source_path }} |
| 253 | + SRC_URL: ${{ github.event.inputs.source_url }} |
| 254 | + |
| 255 | + - name: Update DynamicList component file |
| 256 | + run: | |
| 257 | + set -euo pipefail |
| 258 | + COMPONENT="$COMPONENT" |
| 259 | + VERSION_RAW="$VERSION_RAW" |
| 260 | + VERSION="${VERSION_RAW#v}" |
| 261 | + SLUG="$SLUG" |
| 262 | + SUBDIR="$SUBDIR" |
| 263 | + TODAY=$(date +'%d %B %Y') |
| 264 | +
|
| 265 | + case "$COMPONENT" in |
| 266 | + wallet) DYNAMIC_LIST="src/components/DynamicListWallet.js" ;; |
| 267 | + ledger) DYNAMIC_LIST="src/components/DynamicListLedger.js" ;; |
| 268 | + compact) DYNAMIC_LIST="src/components/DynamicListCompact.js" ;; |
| 269 | + midnight-js) DYNAMIC_LIST="src/components/DynamicListMidnightJS.js" ;; |
| 270 | + node) DYNAMIC_LIST="src/components/DynamicListNode.js" ;; |
| 271 | + midnight-indexer) DYNAMIC_LIST="src/components/DynamicListMidnightIndexer.js" ;; |
| 272 | + dapp-connector-api) DYNAMIC_LIST="src/components/DynamicListDappConnectorAPI.js" ;; |
| 273 | + compact-js) DYNAMIC_LIST="src/components/DynamicListCompactJS.js" ;; |
| 274 | + *) echo "Unknown component"; exit 1 ;; |
| 275 | + esac |
| 276 | +
|
| 277 | + sed -i "s/status: 'LATEST'/status: 'UNSUPPORTED'/" "$DYNAMIC_LIST" |
| 278 | +
|
| 279 | + if [ "$COMPONENT" = "compact" ]; then |
| 280 | + if [ -z "$LANG_RAW" ]; then |
| 281 | + echo "compact_language_version is required when component is compact" |
| 282 | + exit 1 |
| 283 | + fi |
| 284 | + COMPACT_VER="${LANG_RAW#v}" |
| 285 | + NEW_ENTRY=" {\n\ |
| 286 | + version: '${VERSION}',\n\ |
| 287 | + compactVersion: '${COMPACT_VER}',\n\ |
| 288 | + status: 'LATEST',\n\ |
| 289 | + date: '${TODAY}',\n\ |
| 290 | + summary: 'Summary of Release ${VERSION}',\n\ |
| 291 | + details: [],\n\ |
| 292 | + artifacts: [],\n\ |
| 293 | + link: '/relnotes/${SUBDIR}/${SLUG}',\n\ |
| 294 | + }," |
| 295 | + else |
| 296 | + NEW_ENTRY=" {\n\ |
| 297 | + version: '${VERSION}',\n\ |
| 298 | + status: 'LATEST',\n\ |
| 299 | + date: '${TODAY}',\n\ |
| 300 | + summary: 'Summary of Release ${VERSION}',\n\ |
| 301 | + details: [],\n\ |
| 302 | + artifacts: [],\n\ |
| 303 | + link: '/relnotes/${SUBDIR}/${SLUG}',\n\ |
| 304 | + }," |
| 305 | + fi |
| 306 | +
|
| 307 | + sed -i "/^const releases = \[$/a\\${NEW_ENTRY}" "$DYNAMIC_LIST" |
| 308 | + echo "Updated $DYNAMIC_LIST" |
| 309 | + env: |
| 310 | + COMPONENT: ${{ github.event.inputs.component }} |
| 311 | + VERSION_RAW: ${{ github.event.inputs.version }} |
| 312 | + SLUG: ${{ steps.copy.outputs.slug }} |
| 313 | + SUBDIR: ${{ steps.copy.outputs.relnotes_subdir }} |
| 314 | + LANG_RAW: ${{ github.event.inputs.compact_language_version }} |
| 315 | + |
| 316 | + - name: Prepare branch |
| 317 | + id: prep |
| 318 | + run: | |
| 319 | + rm -rf ./temp |
| 320 | + BRANCH="relnotes/$(date +'%Y-%m-%dT%H%M%S')-$COMPONENT" |
| 321 | + git checkout -b "$BRANCH" |
| 322 | + if git diff --quiet && git diff --cached --quiet; then |
| 323 | + echo "No changes (unexpected)" |
| 324 | + exit 1 |
| 325 | + fi |
| 326 | + echo "NEW_BRANCH=$BRANCH" >> "$GITHUB_OUTPUT" |
| 327 | + echo "UPDATE=true" >> "$GITHUB_OUTPUT" |
| 328 | + git add -A |
| 329 | + git push -u origin "$BRANCH" |
| 330 | + env: |
| 331 | + GH_TOKEN: ${{ secrets.GH_TOKEN }} |
| 332 | + COMPONENT: ${{ github.event.inputs.component }} |
| 333 | + |
| 334 | + - uses: planetscale/ghcommit-action@25309d8005ac7c3bcd61d3fe19b69e0fe47dbdde # v0.2.20 |
| 335 | + if: ${{ steps.prep.outputs.UPDATE == 'true' }} |
| 336 | + with: |
| 337 | + commit_message: "Copy release notes for ${{ github.event.inputs.component }}" |
| 338 | + repo: ${{ github.repository }} |
| 339 | + branch: ${{ steps.prep.outputs.NEW_BRANCH }} |
| 340 | + env: |
| 341 | + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} |
| 342 | + |
| 343 | + - name: Create PR |
| 344 | + if: ${{ steps.prep.outputs.UPDATE == 'true' }} |
| 345 | + run: | |
| 346 | + gh pr create \ |
| 347 | + --base "$BRANCH_MERGE" \ |
| 348 | + --head "$NEW_BRANCH" \ |
| 349 | + --title "$PR_TITLE" \ |
| 350 | + --body "Automated release notes copy — component: \`$COMPONENT\`, source_url: \`$SOURCE_URL\`, source_path: \`$SOURCE_PATH\`, version: \`$VERSION\`." |
| 351 | + env: |
| 352 | + GH_TOKEN: ${{ secrets.GH_TOKEN }} |
| 353 | + PR_TITLE: ${{ github.event.inputs.pr_title }} |
| 354 | + BRANCH_MERGE: ${{ github.event.inputs.branch_merge }} |
| 355 | + NEW_BRANCH: ${{ steps.prep.outputs.NEW_BRANCH }} |
| 356 | + COMPONENT: ${{ github.event.inputs.component }} |
| 357 | + SOURCE_URL: ${{ github.event.inputs.source_url }} |
| 358 | + SOURCE_PATH: ${{ github.event.inputs.source_path }} |
| 359 | + VERSION: ${{ github.event.inputs.version }} |
0 commit comments