From 1d3e9a9e450e34f69afb4e3d842b6ac104c2e7a0 Mon Sep 17 00:00:00 2001 From: Liam Bigelow <40188355+bglw@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:10:07 +1300 Subject: [PATCH] Add publishing workflows --- .backstage/changelog.cjs | 56 +++++++++++ .backstage/get_tag.cjs | 22 +++++ .backstage/version.cjs | 28 ++++++ .github/dependabot.yml | 12 +++ .github/workflows/dependabot.yml | 26 +++++ .github/workflows/release.yml | 157 +++++++++++++++++++++++++++++++ .github/workflows/test.yml | 44 +++++++++ CHANGELOG.md | 2 + Cargo.lock | 2 +- Cargo.toml | 2 +- 10 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 .backstage/changelog.cjs create mode 100644 .backstage/get_tag.cjs create mode 100644 .backstage/version.cjs create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.backstage/changelog.cjs b/.backstage/changelog.cjs new file mode 100644 index 0000000..bccb8b5 --- /dev/null +++ b/.backstage/changelog.cjs @@ -0,0 +1,56 @@ +const fs = require("fs"); +const path = require("path"); + +const version = process.env.GIT_VERSION; +const changelogFile = path.join(__dirname, "../CHANGELOG.md"); +const releaseFile = path.join(__dirname, "../RELEASE.md"); + +const err = (m) => { + console.error(m); + process.exit(1); +} + +const date = () => { + let options = { year: 'numeric', month: 'long', day: 'numeric' }; + return new Date().toLocaleString('en-US', options); +} + +if (!version) err("Script expected a GIT_VERSION environment variable"); + +if (!fs.existsSync(changelogFile)) err(`Script expected a file at ${changelogFile}`); + +let contents = fs.readFileSync(changelogFile, { encoding: "utf-8" }); +let release = [], lines = contents.split(/\n/g); +let it = lines.entries(); + +while (!(entry = it.next()).done) { + let [num, line] = entry.value; + // Read until we reach our unreleased changelog section. + if (/^\s*## Unreleased\s*$/.test(line)) { + let releaseHeader = `## v${version} (${date()})`; + lines[num] = `## Unreleased\n\n${releaseHeader}`; + break; + } +} + + +while (!(entry = it.next()).done) { + let [, line] = entry.value; + // Read until we reach the section for a new version. + if (/^\s*##\s+v/i.test(line)) { + break; + } + release.push(line); +} + +if (!release.some((v => v.trim().length))) { + err([ + `No unreleased changes exist in ${changelogFile}.`, + `Cancelling release — please write release notes!` + ].join('\n')); +} + +if (process.argv[2] === "write") { + fs.writeFileSync(releaseFile, release.join('\n')); + fs.writeFileSync(changelogFile, lines.join('\n')); +} \ No newline at end of file diff --git a/.backstage/get_tag.cjs b/.backstage/get_tag.cjs new file mode 100644 index 0000000..35981c0 --- /dev/null +++ b/.backstage/get_tag.cjs @@ -0,0 +1,22 @@ +const version = process.env.GIT_VERSION; + +if (!version) { + console.error("Script expected a GIT_VERSION environment variable"); + process.exit(1); +} + +// Only allow latest tag if we are releasing a major/minor/patch +if (/^\d+\.\d+\.\d+$/.test(version)) { + console.log("latest"); + process.exit(0); +} + +// Use the suffix as the tag. i.e. `0.11.0-rc5` -> `rc` +const suffix = version.match(/^\d+\.\d+\.\d+-([a-z]+)/i)?.[1]; +if (suffix) { + console.log(suffix.toLowerCase()); + process.exit(0); +} + +// Fall back to an unknown tag for safety +console.log("unknown"); \ No newline at end of file diff --git a/.backstage/version.cjs b/.backstage/version.cjs new file mode 100644 index 0000000..d81e1ed --- /dev/null +++ b/.backstage/version.cjs @@ -0,0 +1,28 @@ +const fs = require("fs"); +const path = require("path"); + +const version = process.env.GIT_VERSION; +const version_re = /"?version"?\s*[=:]\s*"0.0.0"/; + +const err = (m) => { + console.error(m); + process.exit(1); +}; + +if (!version) err("Script expected a GIT_VERSION environment variable"); + +const file = (localPath) => { + localPath = path.join(__dirname, localPath); + if (!fs.existsSync(localPath)) err(`Script expected a file at ${localPath}`); + const contents = fs.readFileSync(localPath, { encoding: "utf-8" }); + if (!version_re.test(contents)) + err(`Expected ${localPath} to contain a version of "0.0.0"`); + return { path: localPath, contents }; +}; + +let matterhornCfg = file("../Cargo.toml"); +matterhornCfg.contents = matterhornCfg.contents.replace( + version_re, + `version = "${version}"`, +); +fs.writeFileSync(matterhornCfg.path, matterhornCfg.contents); diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..56b16c3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + open-pull-requests-limit: 1 + schedule: + interval: "daily" diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..a345086 --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,26 @@ +name: auto-merge + +on: pull_request_target + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - uses: actions/checkout@v4 + - name: Get Token + id: get_workflow_token + uses: peter-murray/workflow-application-token-action@v2 + with: + application_id: ${{ secrets.CC_OSS_BOT_ID }} + application_private_key: ${{ secrets.CC_OSS_BOT_PEM }} + - uses: fastify/github-action-merge-dependabot@v3 + with: + github-token: ${{ steps.get_workflow_token.outputs.token }} + target: minor + approve-only: true + - name: Enable auto-merge for Dependabot PR + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2d12264 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,157 @@ +name: Test + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + publish-crate: + name: Publish Crate + runs-on: ubuntu-20.04 + needs: publish-github-release + steps: + - name: Clone + uses: actions/checkout@v4 + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.rustup + target + key: ${{ runner.os }}-stable-min165 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + default: true + components: rustfmt, clippy + + - name: Get Version + run: echo GIT_VERSION="$(git describe --tags | sed 's/^v\(.*\)$/\1/')" >> $GITHUB_ENV + - name: Prepare Git + run: | + git config user.email "github@github.com" + git config user.name "Github Actions" + git checkout -b main + # Use throw-away branch so we don't push the changes to origin + git checkout -b deploy_branch + - name: Prepare Crates + run: | + # Update cargo version, + node ./.backstage/version.cjs + git add ./Cargo.toml + # Commit changes so cargo doesn't complain about dirty repo + git commit -m "Deploy changes." + + - name: Publish + run: cargo publish --allow-dirty + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + publish-github-release: + name: GitHub Release + runs-on: ubuntu-20.04 + needs: test + steps: + - name: Get Token + id: get_workflow_token + uses: peter-murray/workflow-application-token-action@v2 + with: + application_id: ${{ secrets.CC_OSS_BOT_ID }} + application_private_key: ${{ secrets.CC_OSS_BOT_PEM }} + + - name: Clone + uses: actions/checkout@v4 + with: + token: ${{ steps.get_workflow_token.outputs.token }} + + - name: Swap to main + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 # Full fetch + token: ${{ steps.get_workflow_token.outputs.token }} + + - name: Get Version + run: echo GIT_VERSION="$(git describe --tags | sed 's/^v\(.*\)$/\1/')" >> $GITHUB_ENV + - name: Get Tag + run: echo GIT_TAG="$(node ./.backstage/get_tag.cjs)" >> $GITHUB_ENV + + - name: Build CHANGELOG + if: env.GIT_TAG == 'latest' + run: | + node ./.backstage/changelog.cjs write + echo CHANGELOG=\"$(base64 -w 0 -i CHANGELOG.md)\" >> $GITHUB_ENV + echo SHA=\"$( git rev-parse main:CHANGELOG.md )\" >> $GITHUB_ENV + - name: Build CHANGELOG + if: env.GIT_TAG != 'latest' + run: | + echo "## Prerelease" > RELEASE.md + node ./.backstage/changelog.cjs write || true + + - name: Commit new CHANGELOG + uses: octokit/request-action@v2.x + if: env.GIT_TAG == 'latest' + id: push_changes + with: + route: PUT /repos/{owner}/{repo}/contents/CHANGELOG.md + owner: cloudcannon + repo: matterhorn + branch: main + message: Changelog for ${{ env.GIT_VERSION }} + sha: ${{ env.SHA }} + content: ${{ env.CHANGELOG }} + env: + GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} + + - name: Release + uses: softprops/action-gh-release@v1 + with: + repository: cloudcannon/matterhorn + prerelease: false + body_path: RELEASE.md + env: + GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} + + test: + name: Test + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.rustup + target + key: ${{ runner.os }}-${{ matrix.rust }}-min165 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + default: true + components: rustfmt, clippy + + - name: Test Lib + run: cargo test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..93866a0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,44 @@ +name: Release + +on: + push: + tags: + - v* + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + name: Test + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.rustup + target + key: ${{ runner.os }}-${{ matrix.rust }}-min165 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + default: true + components: rustfmt, clippy + + - name: Test Lib + run: cargo test diff --git a/CHANGELOG.md b/CHANGELOG.md index ad2adac..f5ff630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,6 @@ ## Unreleased +## v0.1.0 (October 11, 2024) + * Initial release diff --git a/Cargo.lock b/Cargo.lock index 05d6891..539d480 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "matterhorn" -version = "0.1.0" +version = "0.0.0" dependencies = [ "saphyr", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 7ab3fec..34c4f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "matterhorn" -version = "0.1.0" +version = "0.0.0" edition = "2021" description = "A lenient front matter parsing crate that supports files prefixed with YAML, JSON, and TOML front matter." license = "MIT"