diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a6f82ca --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,210 @@ +name: Release + +# Manually-triggered release workflow. +# +# What it does: +# 1. Resolves the NetBird upstream version to ship (input or 'latest'). +# 2. Pulls the SHA256 of netbird__linux_amd64.tar.gz directly from +# github.com/netbirdio/netbird releases. +# 3. Updates plugin/netbird.plg + plugin/plugin.json with that version + hash. +# 4. Runs scripts/build.sh to produce dist/*.txz and dist/netbird.plg. +# 5. Prepends a CHANGES entry with the new plugin version + NetBird version. +# 6. Commits, tags, pushes, and creates a GitHub Release with the .txz asset. +# +# Prereqs (one-time): +# - Repo Settings → Actions → General → "Workflow permissions": +# Read and write permissions ✓ +# Allow GitHub Actions to create and approve pull requests ✓ +# - No secrets needed; uses the default GITHUB_TOKEN. + +on: + workflow_dispatch: + inputs: + netbird_version: + description: 'NetBird version to pin (e.g. 0.71.3, or "latest")' + required: false + default: 'latest' + force: + description: 'Force a release even if NetBird version is unchanged' + required: false + type: boolean + default: false + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history so the tag check + push work + + - name: Resolve NetBird version + id: nb + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + REQ='${{ inputs.netbird_version }}' + if [ -z "$REQ" ] || [ "$REQ" = "latest" ]; then + TAG=$(gh release view --repo netbirdio/netbird --json tagName --jq .tagName) + else + # Accept "0.71.3" or "v0.71.3" + TAG="${REQ#v}" + TAG="v${TAG}" + fi + VER="${TAG#v}" + ASSET="netbird_${VER}_linux_amd64.tar.gz" + + # Pull the digest GitHub already stores for the asset. + DIGEST=$(gh release view "$TAG" --repo netbirdio/netbird \ + --json assets --jq ".assets[] | select(.name == \"$ASSET\") | .digest") + if [ -z "$DIGEST" ]; then + echo "::error::Asset $ASSET not found in netbirdio/netbird@$TAG" + exit 1 + fi + SHA256="${DIGEST#sha256:}" + + # Defense in depth: download + verify locally so we don't trust the API alone. + URL="https://github.com/netbirdio/netbird/releases/download/${TAG}/${ASSET}" + curl -fsSL -o /tmp/nb.tgz "$URL" + ACTUAL=$(sha256sum /tmp/nb.tgz | awk '{print $1}') + if [ "$ACTUAL" != "$SHA256" ]; then + echo "::error::SHA256 mismatch: API=$SHA256 actual=$ACTUAL" + exit 1 + fi + rm -f /tmp/nb.tgz + + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=$VER" >> "$GITHUB_OUTPUT" + echo "asset=$ASSET" >> "$GITHUB_OUTPUT" + echo "sha256=$SHA256" >> "$GITHUB_OUTPUT" + echo "url=$URL" >> "$GITHUB_OUTPUT" + + - name: Detect unchanged upstream + id: changed + run: | + CUR=$(sed -nE 's/.*> "$GITHUB_OUTPUT" + if [ "$CUR" = "${{ steps.nb.outputs.version }}" ] && [ "${{ inputs.force }}" != "true" ]; then + echo "::notice::NetBird already pinned at $CUR. Re-run with force=true to release anyway." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Bail out (unchanged) + if: steps.changed.outputs.skip == 'true' + run: exit 0 + + - name: Update plugin/netbird.plg + plugin/plugin.json + if: steps.changed.outputs.skip != 'true' + run: | + set -euo pipefail + NB_VER='${{ steps.nb.outputs.version }}' + NB_SHA='${{ steps.nb.outputs.sha256 }}' + + sed -i -E 's|()|\1'"$NB_VER"'\2|' plugin/netbird.plg + sed -i -E 's|()|\1'"$NB_SHA"'\2|' plugin/netbird.plg + + # plugin.json mirror (documentation only) + tmp=$(mktemp) + jq --arg v "$NB_VER" --arg s "$NB_SHA" \ + '.netbirdVersion = $v | .netbirdSHA256 = $s' plugin/plugin.json > "$tmp" + mv "$tmp" plugin/plugin.json + + echo "--- updated plugin/netbird.plg ---" + grep -E 'netbirdVer|netbirdSHA256' plugin/netbird.plg | head -2 + echo "--- updated plugin/plugin.json ---" + jq '{netbirdVersion, netbirdSHA256}' plugin/plugin.json + + - name: Build .txz + finalized .plg + if: steps.changed.outputs.skip != 'true' + run: ./scripts/build.sh + + - name: Read new plugin version + if: steps.changed.outputs.skip != 'true' + id: build + run: | + PV=$(sed -nE 's/.*> "$GITHUB_OUTPUT" + echo "pkg_file=$(basename $PKG)" >> "$GITHUB_OUTPUT" + echo "pkg_path=$PKG" >> "$GITHUB_OUTPUT" + + - name: Prepend CHANGES entry + if: steps.changed.outputs.skip != 'true' + run: | + set -euo pipefail + PV='${{ steps.build.outputs.plugin_version }}' + NV='${{ steps.nb.outputs.version }}' + + # Insert a new "### PV\n- Update NetBird to NV" block right after the + # opening \s*/dev/null + echo "--- new CHANGES head ---" + sed -n '//,/<\/CHANGES>/p' plugin/netbird.plg | head -10 + + - name: Stage plg + json for commit + if: steps.changed.outputs.skip != 'true' + run: | + # The finalized .plg in dist/ already has the new version + new + # package SHA256 substituted in. Copy it over the source one so + # users get accurate pluginURL metadata on update check. + cp dist/netbird.plg plugin/netbird.plg + + - name: Commit + tag + push + if: steps.changed.outputs.skip != 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + PV='${{ steps.build.outputs.plugin_version }}' + NV='${{ steps.nb.outputs.version }}' + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git add plugin/netbird.plg plugin/plugin.json + git commit -m "release ${PV}: NetBird ${NV}" + git tag "${PV}" + git push origin HEAD:main + git push origin "${PV}" + + - name: Create GitHub Release + if: steps.changed.outputs.skip != 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + PV='${{ steps.build.outputs.plugin_version }}' + NV='${{ steps.nb.outputs.version }}' + PKG='${{ steps.build.outputs.pkg_path }}' + + gh release create "${PV}" "${PKG}" \ + --title "${PV}" \ + --notes "NetBird ${NV} · plugin ${PV} + + Install URL: + \`https://raw.githubusercontent.com/${{ github.repository }}/main/plugin/netbird.plg\`"