diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a0fa8de --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: Release + +# Publishes a GitHub Release when a v* tag is pushed (the release process in +# CONTRIBUTING.md): validates every manifest, packages each tool folder as a +# ZIP shaped for chrome://extensions → Load unpacked (the archive contains the +# tool folder itself), and attaches checksums. Uses the preinstalled gh CLI +# instead of a third-party release action. There is no Chrome Web Store +# distribution, and extensions loaded unpacked never auto-update. + +on: + push: + tags: ['v*'] + +# Default every job to read-only. The release job grants itself write because +# creating a GitHub Release requires it. +permissions: + contents: read + +jobs: + release: + name: Package and publish + runs-on: ubuntu-latest + permissions: + contents: write + steps: + # Pin third-party actions to a full commit SHA, not a tag, and keep the + # version comment for readability (same policy as ci.yml). + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version-file: ".nvmrc" + + - name: Validate manifests + run: node scripts/validate-manifests.mjs + + - name: Package tools + run: | + set -euo pipefail + mkdir -p dist-release + for tool in ghl-*/; do + tool="${tool%/}" + version=$(node -p "require('./${tool}/manifest.json').version") + zip -r -X "dist-release/${tool}-${version}.zip" "${tool}" + done + (cd dist-release && sha256sum *.zip > checksums.txt && cat checksums.txt) + + - name: Extract release notes + run: | + set -euo pipefail + ver="${GITHUB_REF_NAME#v}" + awk -v sec="## [${ver}]" ' + index($0, sec) == 1 { found = 1; next } + found && (/^## \[/ || /^\[/) { exit } + found { print } + ' CHANGELOG.md | sed '/^[[:space:]]*$/d' > release-notes.md + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + if [ -s release-notes.md ]; then + gh release create "$GITHUB_REF_NAME" dist-release/* --title "$GITHUB_REF_NAME" -F release-notes.md + else + gh release create "$GITHUB_REF_NAME" dist-release/* --title "$GITHUB_REF_NAME" --generate-notes + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7d470..17b7e53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Repository baseline: per-tool layout, CI manifest validation, CODEOWNERS, issue/PR templates, security policy, AGPL-3.0 license. - `ghl-workflow-tools` ("GHL Workflow Backup") — first tool in the set: exports every workflow in the current HighLevel sub-account as re-importable JSON, packaged as a deterministic ZIP for version control, and restores workflows from a backup as new drafts or in-place overwrites (overwriting a published workflow requires an explicit acknowledgement). - Legion Code Inc. branding across the root and tool READMEs, extension manifest, and popup. +- Release pipeline: pushing a `v*` tag validates manifests, packages each tool folder as a ZIP with checksums, and publishes a GitHub Release from the matching CHANGELOG section (no Chrome Web Store; unpacked extensions do not auto-update). [Unreleased]: https://github.com/legioncodeinc/ghl-toolset/compare/v0.1.0...HEAD [0.1.0]: https://github.com/legioncodeinc/ghl-toolset/releases/tag/v0.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3372ef..ebff818 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ Use the [issue templates](./.github/ISSUE_TEMPLATE/). Do not report security vul ## Release process -Releases are cut manually by a maintainer: bump the affected tool's `version` in its `manifest.json`, update [CHANGELOG.md](./CHANGELOG.md) (rename `Unreleased` to a dated version), commit, and tag `v`. There is no publishing pipeline — consumers pin to tags of this repo. +Releases are cut by a maintainer: bump the affected tool's `version` in its `manifest.json`, update [CHANGELOG.md](./CHANGELOG.md), commit, and push a `v` tag. The tag triggers the [Release workflow](./.github/workflows/release.yml), which validates manifests, packages each tool folder as a ZIP with checksums, and publishes the GitHub Release using that version's CHANGELOG section as the release notes. Consumers download a release ZIP and load the tool folder unpacked — there is no Chrome Web Store distribution and no auto-update. ## Questions diff --git a/README.md b/README.md index 9091198..45dba4e 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Passing looks like one `ok` line per tool manifest and exit code 0. This is the ## Deployment -There is no pipeline to ship: tools are loaded unpacked straight from a checkout of this repo. Distributing via the Chrome Web Store is a future decision; until then, pin consumers to a tag of this repo. Exported data never transits any server — it goes from the browser tab to the ZIP on disk. +Releases are published by pushing a `v` tag: the [Release workflow](./.github/workflows/release.yml) validates every manifest, packages each tool folder as a ZIP (with a `checksums.txt`), and publishes a GitHub Release using that version's CHANGELOG section as the notes. To install from a release: download the tool's ZIP, unzip it, and load the resulting folder via `chrome://extensions` → **Load unpacked**. Extensions loaded unpacked never auto-update — a new release means downloading the new ZIP and replacing the folder. Chrome Web Store distribution is out of scope for this project. Exported data never transits any server — it goes from the browser tab to the ZIP on disk. ## Contributing