From ea2548fd896583dc7e58f3b26cfa5589c514d138 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 18 Nov 2024 16:43:30 -0600 Subject: [PATCH] feat: revamp build process - update semantic-release workflow to reflect new assets - remove all previous docker-related build process - add a new auto-generated command reference file --- .eslintignore | 3 + .github/workflows/release.yml | 31 -- .gitignore | 1 + .prettierignore | 8 +- .releaserc.yml | 20 +- Dockerfile | 11 - bin/docker.js | 100 ------- bin/set-action-image.js | 31 -- bin/verify-clis.sh | 10 - documentation/commands.md | 514 ++++++++++++++++++++++++++++++++++ package.json | 2 +- 11 files changed, 535 insertions(+), 196 deletions(-) delete mode 100644 Dockerfile delete mode 100755 bin/docker.js delete mode 100755 bin/set-action-image.js delete mode 100755 bin/verify-clis.sh create mode 100644 documentation/commands.md diff --git a/.eslintignore b/.eslintignore index 195e13e06..e05d97009 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,6 @@ +# test result artifacts coverage/ + +# release build artifacts dist/ dist-gha/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9116cc5e2..818bef5a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,37 +33,6 @@ jobs: - name: Install semantic-release and friends run: npm i --no-save semantic-release@24 @semantic-release/changelog@6 @semantic-release/exec@6 @semantic-release/git@10 - # We do a dry run here to analyze the commits and grab the next version - # for usage in the Docker metadata action - - name: Dry run of semantic-release workflow - run: npx semantic-release --dry-run - id: release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # For every release we add 2-3 tags to the docker build: - # 1. A semver tag (e.g., 8.0.0, 8.0.0-next.0) - # 2. A branch tag (e.g., next, main) - # 3. A `latest` tag (only on the main branch) - tags: | - type=semver,pattern={{version}},value=${{ steps.release.outputs.nextVersion }} - type=ref,event=branch - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} - - name: Run semantic-release workflow env: GH_TOKEN: ${{ secrets.RELEASE_GH_TOKEN }} diff --git a/.gitignore b/.gitignore index 2957f711e..15be06388 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ node_modules/ # required for building with TS + oclif + GitHub Actions dist-gha/package.json +oclif.manifest.json src/package.json diff --git a/.prettierignore b/.prettierignore index 4039ce149..fe7685933 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,11 @@ +# invalid files __tests__/__fixtures__/invalid-json/yikes.json -CHANGELOG.md + +# test result artifacts coverage/ + +# release build artifacts +CHANGELOG.md dist/ +documentation/commands.md dist-gha/ diff --git a/.releaserc.yml b/.releaserc.yml index e27f762ed..68499dafe 100644 --- a/.releaserc.yml +++ b/.releaserc.yml @@ -15,11 +15,6 @@ plugins: - [ '@semantic-release/exec', { - # Runs two commands: - # 1. Updates our action.yml file to reflect current Docker image version. - # This needs to happen before `@semantic-release/git` so we can commit this file. - # 2. Builds and pushes the Docker image - 'prepareCmd': './bin/set-action-image.js && ./bin/docker.js', # Adds a major version git tag (e.g., v8) as a convenience for GitHub Actions users # We need to run this in the publish phase so it can be force-pushed separately from the other tags 'publishCmd': './bin/set-major-version-tag.js push', @@ -28,18 +23,21 @@ plugins: - [ '@semantic-release/git', { - assets: ['action.yml', 'CHANGELOG.md', 'package.json', 'package-lock.json'], + assets: + [ + 'CHANGELOG.md', + 'documentation/commands.md', + 'package.json', + 'package-lock.json', + 'dist-gha/commands.js', + 'dist-gha/run.js', + ], message: "build(release): 🚀 v${nextRelease.version} 🦉\n\n${nextRelease.notes}\n[skip ci]", }, ] - [ '@semantic-release/exec', { - # Verify existence of docker CLI - 'verifyConditionsCmd': './bin/verify-clis.sh', - # Sets the next version as a GitHub Actions output parameter for usage in subsequent workflow steps - # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter - 'verifyReleaseCmd': 'echo nextVersion=${nextRelease.version} >> $GITHUB_OUTPUT', # Adds an additional git tag without the `v` prefix as a convenience for GitHub Actions users 'prepareCmd': 'git tag ${nextRelease.version}', }, diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5019b7afe..000000000 --- a/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM node:18-alpine as builder - -COPY . /rdme - -RUN cd /rdme && npm ci && npm run build:exe - -FROM alpine:3.14 - -COPY --from=builder /rdme/exe /exe - -ENTRYPOINT ["/exe/rdme"] diff --git a/bin/docker.js b/bin/docker.js deleted file mode 100755 index 0f6c67448..000000000 --- a/bin/docker.js +++ /dev/null @@ -1,100 +0,0 @@ -#! /usr/bin/env node -// @ts-check -/* eslint-disable no-console */ -import { execFile as unpromisifiedExecFile } from 'node:child_process'; -import { promisify } from 'node:util'; - -const execFile = promisify(unpromisifiedExecFile); - -/** - * Retrieves and parses the docker image metadata - * @see {@link https://github.com/docker/metadata-action} - * @see {@link https://gist.github.com/kanadgupta/801c8335e7e2e3d80463c34bdd41c7e6} - */ -function getMetadata() { - try { - // See here for an example JSON output: https://gist.github.com/kanadgupta/801c8335e7e2e3d80463c34bdd41c7e6 - const raw = process.env.DOCKER_METADATA_OUTPUT_JSON; - const metadata = JSON.parse(raw); - if (!Object.keys(metadata.labels || {})?.length) { - throw new Error('Invalid shape (missing labels data)'); - } - if (!metadata?.tags?.length) { - throw new Error('Invalid shape (missing tags data)'); - } - return metadata; - } catch (e) { - console.error('Error retrieving docker metadata:', e.message); - return process.exit(1); - } -} - -/** - * Runs command and logs all output - */ -async function runDockerCmd(args) { - // Promise-based approach grabbed from here: https://stackoverflow.com/a/63027900 - const execCmd = execFile('docker', args); - const child = execCmd.child; - - child.stdout?.on('data', chunk => { - console.log(chunk.toString()); - }); - - child.stderr?.on('data', chunk => { - console.error(chunk.toString()); - }); - - const { stdout, stderr } = await execCmd; - - if (stdout) console.log(stdout); - if (stderr) console.error(stdout); -} - -/** - * Constructs and executes `docker build` and `docker push` commands - */ -async function run() { - const { labels, tags } = getMetadata(); - try { - // start constructing build command - const buildArgs = ['build', '--platform', 'linux/amd64']; - // add labels - Object.keys(labels).forEach(label => { - buildArgs.push('--label', `${label}=${labels[label]}`); - }); - // add tags - tags.forEach(tag => { - buildArgs.push('--tag', tag); - }); - // point to local Dockerfile - buildArgs.push('.'); - - // Strips tag from image so we can use the --all-tags flag in the push command - const imageWithoutTag = tags[0]?.split(':')?.[0]; - - if (!imageWithoutTag) { - console.error(`Unable to separate image name from tag: ${tags[0]}`); - return process.exit(1); - } - - const pushArgs = ['push', '--all-tags', imageWithoutTag]; - - console.log(`🐳 🛠️ Running docker build command: docker ${buildArgs.join(' ')}`); - - await runDockerCmd(buildArgs); - - console.log(`🐳 📌 Running docker push command: docker ${pushArgs.join(' ')}`); - - await runDockerCmd(pushArgs); - } catch (e) { - console.error('Error running Docker script!'); - console.error(e); - return process.exit(1); - } - - console.log('🐳 All done!'); - return process.exit(0); -} - -run(); diff --git a/bin/set-action-image.js b/bin/set-action-image.js deleted file mode 100755 index 60d89be85..000000000 --- a/bin/set-action-image.js +++ /dev/null @@ -1,31 +0,0 @@ -#! /usr/bin/env node -// @ts-check -import fs from 'node:fs/promises'; - -// eslint-disable-next-line import/no-extraneous-dependencies -import jsYaml from 'js-yaml'; - -import pkg from '../package.json' with { type: 'json' }; - -/** - * Updates our `action.yml` file so it properly points to - * the correct docker image - * @see {@link https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-docker-container-actions} - */ -async function setActionImage() { - // Grabs Docker image URL from action.yml, updates version value, - // and writes changes back to action.yml file - const actionFile = await fs.readFile('./action.yml', 'utf-8'); - const actionObj = jsYaml.load(actionFile); - // @ts-expect-error it's annoying to type these YAML instances - const imageURL = new URL(actionObj.runs.image); - imageURL.pathname = imageURL.pathname.replace(/:.*/g, `:${pkg.version}`); - // @ts-expect-error it's annoying to type these YAML instances - actionObj.runs.image = imageURL.toString(); - const actionYaml = jsYaml.dump(actionObj, { lineWidth: -1 }); - await fs.writeFile('./action.yml', actionYaml, { encoding: 'utf-8' }); - // eslint-disable-next-line no-console - console.log('action.yml file successfully updated!'); -} - -setActionImage(); diff --git a/bin/verify-clis.sh b/bin/verify-clis.sh deleted file mode 100755 index 4ed7f0126..000000000 --- a/bin/verify-clis.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Verify existence of docker CLI -# https://stackoverflow.com/a/677212 -set -e -if ! command -v docker &> /dev/null -then - echo "docker CLI could not be found" - exit 1 -fi diff --git a/documentation/commands.md b/documentation/commands.md new file mode 100644 index 000000000..63ac3ebb0 --- /dev/null +++ b/documentation/commands.md @@ -0,0 +1,514 @@ +# Table of contents + + +* [Table of contents](#table-of-contents) +* [Example Usage](#example-usage) +* [Command Reference](#command-reference) + + +# Example Usage + + +```sh-session +$ npm install -g rdme +$ rdme COMMAND +running command... +$ rdme (--version) +rdme/9.0.0-next.21 darwin-arm64 node-v22.11.0 +$ rdme --help [COMMAND] +USAGE + $ rdme COMMAND +... +``` + + +# Command Reference + + +* [`rdme autocomplete [SHELL]`](#rdme-autocomplete-shell) +* [`rdme categories`](#rdme-categories) +* [`rdme categories:create TITLE`](#rdme-categoriescreate-title) +* [`rdme changelogs PATH`](#rdme-changelogs-path) +* [`rdme custompages PATH`](#rdme-custompages-path) +* [`rdme docs PATH`](#rdme-docs-path) +* [`rdme docs:prune FOLDER`](#rdme-docsprune-folder) +* [`rdme help [COMMAND]`](#rdme-help-command) +* [`rdme login`](#rdme-login) +* [`rdme logout`](#rdme-logout) +* [`rdme open`](#rdme-open) +* [`rdme openapi [SPEC]`](#rdme-openapi-spec) +* [`rdme openapi:convert [SPEC]`](#rdme-openapiconvert-spec) +* [`rdme openapi:inspect [SPEC]`](#rdme-openapiinspect-spec) +* [`rdme openapi:reduce [SPEC]`](#rdme-openapireduce-spec) +* [`rdme openapi:validate [SPEC]`](#rdme-openapivalidate-spec) +* [`rdme versions`](#rdme-versions) +* [`rdme versions:create VERSION`](#rdme-versionscreate-version) +* [`rdme versions:delete [VERSION]`](#rdme-versionsdelete-version) +* [`rdme versions:update [VERSION]`](#rdme-versionsupdate-version) +* [`rdme whoami`](#rdme-whoami) + +## `rdme autocomplete [SHELL]` + +Display autocomplete installation instructions. + +``` +USAGE + $ rdme autocomplete [SHELL] [-r] + +ARGUMENTS + SHELL (zsh|bash|powershell) Shell type + +FLAGS + -r, --refresh-cache Refresh cache (ignores displaying instructions) + +DESCRIPTION + Display autocomplete installation instructions. + +EXAMPLES + $ rdme autocomplete + + $ rdme autocomplete bash + + $ rdme autocomplete zsh + + $ rdme autocomplete powershell + + $ rdme autocomplete --refresh-cache +``` + +_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.2.8/src/commands/autocomplete/index.ts)_ + +## `rdme categories` + +Get all categories in your ReadMe project. + +``` +USAGE + $ rdme categories --key [--version ] + +FLAGS + --key= (required) Project API key + --version= Project version. If running command in a CI environment and this option is not passed, the main + project version will be used. + +DESCRIPTION + Get all categories in your ReadMe project. +``` + +## `rdme categories:create TITLE` + +Create a category with the specified title and guide in your ReadMe project. + +``` +USAGE + $ rdme categories:create TITLE --categoryType guide|reference --key [--preventDuplicates] [--version ] + +ARGUMENTS + TITLE Title of the category + +FLAGS + --categoryType=