Skip to content
Merged
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: 2
updates:
# Keep SHA-pinned GitHub Actions current. Dependabot bumps the commit SHA and
# the trailing version comment together, so pinning does not go stale.
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"
groups:
github-actions:
update-types:
- "minor"
- "patch"
31 changes: 27 additions & 4 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,34 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
# Guard against release-version drift: Release Please is the sole version
# writer, so .release-please-manifest.json and package.json must always
# agree and be valid semver. Catches drift on the PR instead of at
# publish time.
- name: Verify release version consistency
run: |
node -e '
const fs = require("fs");
const manifest = JSON.parse(fs.readFileSync(".release-please-manifest.json", "utf8"))["."];
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
const semver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
for (const [name, v] of [[".release-please-manifest.json", manifest], ["package.json", pkg]]) {
if (typeof v !== "string" || !semver.test(v)) {
console.error(name + " version is not valid semver: " + JSON.stringify(v));
process.exit(1);
}
}
if (manifest !== pkg) {
console.error("Release version drift: .release-please-manifest.json is " + manifest + " but package.json is " + pkg + ". Release Please must be the sole version writer.");
process.exit(1);
}
console.log("Release version consistent: " + pkg);
'
- run: make openapi-generated-check
- run: make lint
- run: make test
Expand All @@ -29,8 +52,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
Expand Down
70 changes: 33 additions & 37 deletions .github/workflows/publish-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
name: Validate Release Ref
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0

Expand All @@ -32,10 +32,9 @@ jobs:
fi

STABLE_SEMVER_RE='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'
NIGHTLY_SEMVER_RE='^v0\.0\.[0-9]+-nightly\.[0-9]{8}\.[0-9]+$'
if [[ ! "$GITHUB_REF_NAME" =~ $STABLE_SEMVER_RE ]] && [[ ! "$GITHUB_REF_NAME" =~ $NIGHTLY_SEMVER_RE ]]; then
if [[ ! "$GITHUB_REF_NAME" =~ $STABLE_SEMVER_RE ]]; then
echo "Unsupported release tag: ${GITHUB_REF_NAME}"
echo "Release tags must use stable SemVer form vMAJOR.MINOR.PATCH or nightly form v0.0.N-nightly.YYYYMMDD.NUMBER."
echo "Release tags must use stable SemVer form vMAJOR.MINOR.PATCH."
exit 1
fi

Expand All @@ -58,15 +57,17 @@ jobs:
prerelease: ${{ steps.release.outputs.prerelease }}
latest: ${{ steps.release.outputs.latest }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"

- name: Resolve release version
id: release
run: |
set -euo pipefail
NIGHTLY_SEMVER_RE='^v0\.0\.[0-9]+-nightly\.[0-9]{8}\.[0-9]+$'

if [ "$GITHUB_REF" = "refs/heads/main" ]; then
DATE_PART="$(date -u +%Y%m%d)"
Expand All @@ -87,13 +88,16 @@ jobs:
LATEST="false"
else
RELEASE_TAG="$GITHUB_REF_NAME"
if [[ "$GITHUB_REF_NAME" =~ $NIGHTLY_SEMVER_RE ]]; then
PRERELEASE="true"
LATEST="false"
else
PRERELEASE="false"
LATEST="auto"
NPM_VERSION="$(node -p "require('./package.json').version")"

if [ "v${NPM_VERSION}" != "$RELEASE_TAG" ]; then
echo "Release tag ${RELEASE_TAG} does not match package.json version ${NPM_VERSION}."
echo "Stable releases must be created from the Release Please release PR commit."
exit 1
fi

PRERELEASE="false"
LATEST="auto"
fi

{
Expand Down Expand Up @@ -141,12 +145,12 @@ jobs:
goarch: amd64
ext: ".exe"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
- uses: sigstore/cosign-installer@v4.1.2
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2

- name: Resolve release build environment
env:
Expand Down Expand Up @@ -188,7 +192,7 @@ jobs:
test -s "$BUNDLE_PATH"

- name: Upload signed binary artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: volcano-binaries-${{ matrix.target.id }}
path: dist/${{ matrix.target.id }}
Expand All @@ -207,11 +211,11 @@ jobs:
RELEASE_PRERELEASE: ${{ needs.resolve-release.outputs.prerelease }}
RELEASE_LATEST: ${{ needs.resolve-release.outputs.latest }}
steps:
- uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v4.1.2
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2

- name: Download signed binary artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: volcano-binaries-*
path: _release
Expand Down Expand Up @@ -389,9 +393,9 @@ jobs:
contents: read
id-token: write # REQUIRED for npm trusted publishing (OIDC)
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
Expand All @@ -401,24 +405,20 @@ jobs:
- name: Upgrade npm
run: npm install -g npm@11

- name: Resolve npm version
- name: Verify npm package version
id: npm_version
env:
CLI_VERSION: ${{ needs.resolve-release.outputs.cli_version }}
run: |
set -euo pipefail
VERSION="${CLI_VERSION#v}"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Refusing to publish non-stable version to npm: ${CLI_VERSION}"
VERSION="$(node -p "require('./package.json').version")"
if [ "v${VERSION}" != "$CLI_VERSION" ]; then
echo "Refusing to publish npm version ${VERSION} for release ${CLI_VERSION}."
echo "package.json must match the stable release tag."
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Set package version
run: |
npm version "${{ steps.npm_version.outputs.version }}" \
--no-git-tag-version --allow-same-version

- name: Verify release assets exist
env:
VERSION: ${{ steps.npm_version.outputs.version }}
Expand All @@ -441,13 +441,9 @@ jobs:
# OIDC trusted publishing: npm detects the id-token and authenticates
# automatically; provenance is generated for public repos. No NPM_TOKEN.
# VOLCANO_SKIP_DOWNLOAD stops the postinstall fetching a binary in CI.
#
# BOOTSTRAP: until an npm admin does the first manual publish and sets the
# OIDC trusted publisher (workflow file: publish-cli.yml), this step fails
# with ENEEDAUTH. continue-on-error keeps the release green meanwhile --
# REMOVE it once trusted publishing is live so real failures surface.
# Trusted publishing is live (the package is published on npm), so a
# publish failure is a real failure and must fail the release.
- name: Publish to npm
continue-on-error: true
env:
VOLCANO_SKIP_DOWNLOAD: "1"
run: npm publish
run: npm publish --provenance --access public
65 changes: 65 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Release Please

on:
push:
branches:
- main
workflow_dispatch:

# Serialize release runs so overlapping pushes don't race the release PR/tag.
concurrency:
group: release-please-${{ github.ref }}
cancel-in-progress: false

# Least privilege for the default GITHUB_TOKEN: the release work runs on the
# GitHub App token minted below, so the default token needs no write scope.
permissions:
contents: read

jobs:
release-please:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
# Mint a short-lived GitHub App installation token. Release Please must
# push tags/releases with a non-default token so publish-cli.yml runs:
# refs created with the default GITHUB_TOKEN do not trigger follow-on
# workflows. A GitHub App token replaces the personal access token
# without depending on an individual's credentials (VOL-367). The app id
# is a non-sensitive repo variable; the PEM private key is a secret. The
# app must be installed on this repo with Contents and Pull requests
# read/write.
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
app-id: ${{ vars.VOLCANO_APP_ID }}
private-key: ${{ secrets.VOLCANO_APP_KEY }}
# Down-scope the minted token to only what Release Please needs
# (contents + pull requests to open/merge the release PR and push
# the tag/release; issues for its release labels), rather than the
# app installation's full grant.
permission-contents: write
permission-pull-requests: write
permission-issues: write

- uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
id: release
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# Auto-merge the release PR so it merges as soon as it satisfies branch
# protection (required approval + green checks). Squash is the only
# enabled merge method, so the release lands as one Conventional Commit.
- name: Enable auto-merge for the release PR
if: ${{ steps.release.outputs.prs_created == 'true' }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
RELEASE_PRS: ${{ steps.release.outputs.prs }}
run: |
set -euo pipefail
jq -r '.[].number' <<< "$RELEASE_PRS" | while read -r number; do
echo "Enabling squash auto-merge for release PR #${number}"
gh pr merge "$number" --repo "$GITHUB_REPOSITORY" --auto --squash
done
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
30 changes: 23 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,29 @@ The publish workflow builds signed binaries for `linux-amd64`, `linux-arm64`,
`macos-amd64`, `macos-arm64`, and `windows-amd64`. It publishes stable release
assets from SemVer tags.

To cut a stable release, merge the release commit to `main`, tag that commit
with a SemVer tag such as `v1.2.3`, and push the tag. Prerelease and build
metadata tags are not stable release tags. The publish workflow also rejects
stable release tags that are not reachable from `origin/main`.

Required repository variable for stable release publishing:

Stable releases are managed by Release Please. Normal feature and fix commits
land on `main` using Conventional Commit messages. Release Please opens or
updates a release PR that bumps `package.json`, updates
`.release-please-manifest.json`, and updates `CHANGELOG.md`. Merge that release
PR to cut a stable release. Release Please then creates the SemVer tag and
GitHub Release, such as `v1.2.3`, and the tag-triggered publish workflow
attaches signed binaries and publishes npm.

Do not manually bump `package.json` outside a Release Please release PR. The
publish workflow rejects stable tags when `package.json` does not match the tag,
or when the tag is not reachable from `origin/main`.

Prerelease and build metadata tags are not stable release tags. Nightly builds
remain automated from `main` and publish to the mutable `nightly` GitHub Release;
they are not npm releases and are separate from the stable Release Please flow.

Required repository secrets and variables for stable release publishing:

- `VOLCANO_APP_ID` (variable) and `VOLCANO_APP_KEY` (secret): the GitHub App
used by Release Please to mint a short-lived installation token. A non-default
token is required so the release tag/release triggers the publish workflow.
The app must be installed on this repo with Contents and Pull requests
read/write.
- `VOLCANO_FIRST_PARTY_DEVICE_CLIENT_ID_PRODUCTION`

Release assets include platform binaries, adjacent `.sigstore.json` bundles,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@volcano.dev/cli",
"version": "0.0.5",
"version": "0.1.0",
"description": "CLI for Volcano's hosting platform.",
"keywords": [
"volcano",
Expand Down
26 changes: 26 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"bootstrap-sha": "0d63cdfd0c686a50511216937610b02f6073c0e8",
"include-component-in-tag": false,
"packages": {
".": {
"release-type": "node",
"package-name": "@volcano.dev/cli",
"changelog-path": "CHANGELOG.md",
"pull-request-title-pattern": "chore${scope}: release${component} ${version}",
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance Improvements" },
{ "type": "revert", "section": "Reverts" },
{ "type": "docs", "section": "Documentation" },
{ "type": "refactor", "section": "Code Refactoring" },
{ "type": "build", "section": "Build System" },
{ "type": "ci", "section": "Continuous Integration" },
{ "type": "test", "section": "Tests", "hidden": true },
{ "type": "style", "section": "Styles", "hidden": true },
{ "type": "chore", "section": "Miscellaneous Chores", "hidden": true }
]
}
}
}
Loading