{"id":50554847,"url":"https://github.com/coroboros/ci","last_synced_at":"2026-06-04T06:02:10.126Z","repository":{"id":357347873,"uuid":"1235342000","full_name":"coroboros/ci","owner":"coroboros","description":"Reusable GitHub Actions CI for the Coroboros stack.","archived":false,"fork":false,"pushed_at":"2026-06-02T03:57:50.000Z","size":145,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T05:23:28.787Z","etag":null,"topics":["ci-cd","composite-actions","coroboros","github-actions","gitleaks","npm-package","oidc","pnpm","reusable-workflows","trusted-publishing"],"latest_commit_sha":null,"homepage":"https://coroboros.com","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coroboros.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"security/.gitleaks.toml","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T08:27:58.000Z","updated_at":"2026-06-01T13:10:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/coroboros/ci","commit_stats":null,"previous_names":["coroboros/ci"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/coroboros/ci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coroboros%2Fci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coroboros%2Fci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coroboros%2Fci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coroboros%2Fci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coroboros","download_url":"https://codeload.github.com/coroboros/ci/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coroboros%2Fci/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33891733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci-cd","composite-actions","coroboros","github-actions","gitleaks","npm-package","oidc","pnpm","reusable-workflows","trusted-publishing"],"created_at":"2026-06-04T06:02:09.215Z","updated_at":"2026-06-04T06:02:10.113Z","avatar_url":"https://github.com/coroboros.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"assets/logo.png\" width=\"288\" height=\"288\" alt=\"Coroboros\"/\u003e\n\n\u003c!-- omit in toc --\u003e\n# coroboros/ci\n\n**Reusable GitHub Actions CI for the Coroboros stack.**\n\nDrop into any `@coroboros/*` repo via `uses: coroboros/ci/.github/workflows/\u003cname\u003e.yml@v0`, or compose around the composite actions under `.github/actions/`.\n\n[![latest](https://img.shields.io/github/v/release/coroboros/ci?style=flat-square\u0026label=latest\u0026color=000000)](https://github.com/coroboros/ci/releases)\n[![ci](https://img.shields.io/github/actions/workflow/status/coroboros/ci/self.yml?branch=main\u0026style=flat-square\u0026label=ci\u0026color=000000)](https://github.com/coroboros/ci/actions/workflows/self.yml)\n[![branch](https://img.shields.io/badge/branch-main-000000?style=flat-square)](https://github.com/coroboros/ci)\n[![license](https://img.shields.io/badge/license-All%20Rights%20Reserved-000000?style=flat-square)](LICENSE.md)\n[![stars](https://img.shields.io/github/stars/coroboros/ci?style=flat-square\u0026label=stars\u0026color=000000)](https://github.com/coroboros/ci)\n[![skills](https://img.shields.io/badge/skills-000000?style=flat-square\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2IiBmaWxsPSJ3aGl0ZSI+PHBvbHlnb24gcG9pbnRzPSI4LDAgMTAsNiAxNiw4IDEwLDEwIDgsMTYgNiwxMCAwLDggNiw2Ii8+PC9zdmc+)](https://github.com/coroboros/agent-skills)\n[![coroboros.com](https://img.shields.io/badge/coroboros.com-000000?style=flat-square\u0026logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiLz48cGF0aCBkPSJNMiAxMmgyME0xMiAyYTE1LjMgMTUuMyAwIDAgMSA0IDEwIDE1LjMgMTUuMyAwIDAgMS00IDEwIDE1LjMgMTUuMyAwIDAgMS00LTEwIDE1LjMgMTUuMyAwIDAgMSA0LTEweiIvPjwvc3ZnPg==)](https://coroboros.com)\n\n\u003c/div\u003e\n\n**Imposed, not proposed.** Pipelines expose zero `inputs:` — same install flags, same publish auth, same security baseline across every Coroboros repo. Consumers wire it in.\n\n- [Pipelines](#pipelines)\n- [Composable actions](#composable-actions)\n- [Development flow](#development-flow)\n- [Environment](#environment)\n- [Security](#security)\n- [Examples](#examples)\n- [License](#license)\n\n---\n\n## Pipelines\n\n### `javascript-npm-packages.yml`\n\nBundled NPM CI.\n\nConsumer requirements:\n- `.node-version`\n- `package.json` with\n  - `packageManager: \"pnpm@X.Y.Z\"`, `scripts.lint`, `scripts.test`.\n  - `scripts.build` is optional (auto-detected).\n- `pnpm-lock.yaml` — required for `--frozen-lockfile`.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003epreflight\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**Trigger**: `branch push`\n\n**Sequence**:\n1. Checkout\n2. Run [`check-docs`](#composable-actions)\n3. Run [`javascript/base`](#composable-actions)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003epublish\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**Trigger**: `tag push`\n\n**Sequence**:\n1. Checkout `main` with full history\n2. Verify `main` HEAD matches the tag SHA\n3. Run [`check-docs`](#composable-actions)\n4. Run [`javascript/base`](#composable-actions)\n5. Pin `package.json` version to the tag\n6. Generate `CHANGELOG.md` section via [`release/generate-changelog`](#composable-actions)\n7. Publish to npm — OIDC + provenance or token-based via `.npmrc` (see [Security](#security))\n8. Create GitHub Release via [`release/github-release`](#composable-actions)\n9. Commit release artifacts back to `main` as `chore: release ${tag}`\n10. Move rolling major tag `vN` to the release commit (skipped on pre-release tags)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003esecurity\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**Trigger**: `every call`\n\nCalls `security.yml` — see [Security](#security).\n\n\u003c/details\u003e\n\n### `security.yml`\n\nReusable sub-workflow with three parallel scans:\n\n- **`gitleaks`** — Installs `v8.30.1` (SHA-256 verified), scans git history with the [`security/.gitleaks.toml`](security/.gitleaks.toml) ruleset, fails on detected leaks. Emits SARIF as the `gitleaks-report` artifact (30-day retention).\n- **`dependency-review`** — PR-only; needs repo's **Dependency graph** enabled. Fails on high-severity CVE introduced by the dep diff. Uses `actions/dependency-review-action@v4`.\n- **`osv-scanner`** — Scans lockfiles recursively against [OSV.dev](https://osv.dev/) via `google/osv-scanner-action@v2`. Fails on any known vulnerability.\n\nImposed on every Coroboros workflow. Standalone wire-up — see [Examples](#examples).\n\n---\n\n**Notes** — pin via `@v0` (rolling major, auto-bumped on each release) or `@x.y.z` (immutable). Pipelines don't chain via `needs:`; the only sub-workflow call is `security` → `security.yml`.\n\n---\n\n## Composable actions\n\n| Action | Type | Purpose |\n| :--- | :--- | :--- |\n| `check-docs` | transverse | Context dump + documentation check. |\n| `javascript/base` | JavaScript | Sets up Node + corepack pnpm, caches the store, writes `.npmrc` from env, then installs, lints, builds (when present), tests. |\n| `release/generate-changelog` | transverse | SemVer-strict tag guard + generates or reuses the `## vX.Y.Z` section in `CHANGELOG.md` from Conventional Commits. Outputs `body`. Idempotent. |\n| `release/github-release` | transverse | Creates the GitHub Release for the current tag. Body typically chained from `release/generate-changelog` (see [Examples](#examples)). |\n\n---\n\n## Development flow\n\nDevelop with Conventional Commits → tag → push. No manual CHANGELOG, no version bump.\n\nTags follow **SemVer strict** — `1.2.3`, never `v1.2.3`.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eBranch models\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**main-only** — feature branch → PR → squash-merge to `main` → tag the merge commit → push.\n\n**develop + main** — PR into `develop` → tag → `release/x.y.z` branch → merge to `main` → `main` reflects production.\n\nNobody pushes directly to protected branches (`main`, `develop`, `release/x.y.z`).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eConventional Commits → CHANGELOG\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n| Commit type | CHANGELOG subsection |\n| :--- | :--- |\n| `feat` | Features |\n| `fix` | Fixes |\n| `refactor` | Refactor |\n| `perf` | Performance |\n| `docs` | Documentation |\n| `chore` / `ci` / `build` | Configuration |\n| `test` | Tests |\n| `style` | Style |\n| Other / non-standard | Others |\n| `!:` or `BREAKING CHANGE:` | Breaking Changes (always first) |\n\nSection format: `## vX.Y.Z - DD/MM/YYYY`. Idempotent. Reuses an existing hand-curated section for the tag if present.\n\n\u003c/details\u003e\n\n---\n\n## Environment\n\nZero inputs on pipelines and on every composite — imposed, not proposed. Configuration flows through the caller's `secrets:` block. Every npm-publish-related value is a **secret** (encrypted at rest, masked in logs); none of them are GitHub `vars`.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eSecrets (caller's \u003ccode\u003esecrets:\u003c/code\u003e block)\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n| name | required | description |\n| :--- | :---: | :--- |\n| `NPM_CONFIG_FILE` | ✔ | `.npmrc` content. Written to repo root by `javascript/base`. `${VAR}` references inside are expanded by npm at install time. |\n| `NPM_EXTRA_CONFIG` |  | Extra `.npmrc` lines appended after `NPM_CONFIG_FILE`. A **secret** — it lands in `.npmrc`, so it can carry auth material and must stay masked. |\n| `NPM_PACKAGE_REGISTRY` | ✔ | npm package registry URL. |\n| `NPM_PACKAGE_PROXY_REGISTRY` |  | Optional npm proxy registry URL. |\n| `NPM_PACKAGE_REGISTRY_TOKEN` |  | npm Granular Access Token, scoped to the publishing organization with create-new-package permission. Required only for the token bootstrap (first publish of a new scoped package, before npm Trusted Publisher is bound). Absent → OIDC. |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003e\u003ccode\u003ejavascript/base\u003c/code\u003e env contract (standalone composition)\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n| env | required | description |\n| :-- | :---: | :--- |\n| `NPM_CONFIG_FILE` | ✔ — fail if missing | `.npmrc` content |\n| `NPM_EXTRA_CONFIG` |  | Appended after `NPM_CONFIG_FILE` |\n\nSet both at the caller's workflow- or job-level `env:`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003e\u003ccode\u003erelease/*\u003c/code\u003e composites I/O\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**`release/generate-changelog`** — no inputs, no secrets. Reads `GITHUB_REF_NAME`. Requires `fetch-depth: 0` for `git describe`. Output:\n\n| output | description |\n| :--- | :--- |\n| `body` | CHANGELOG section body — use as release notes. |\n\n**`release/github-release`** — input:\n\n| input | required | description |\n| :--- | :---: | :--- |\n| `body` | ✔ | Release notes body, typically `steps.\u003cid\u003e.outputs.body` from `release/generate-changelog`. |\n\nCaller job needs `permissions: contents: write`. Uses `${{ github.token }}` internally via `GH_TOKEN`.\n\n\u003c/details\u003e\n\n---\n\n## Security\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eSupply chain — pnpm install flags\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n`pnpm install --frozen-lockfile --ignore-scripts` runs inside `javascript/base`.\n\n- `--frozen-lockfile` — fails on stale or tampered `pnpm-lock.yaml`. Gate against transitive-dependency injection.\n- `--ignore-scripts` — skips lifecycle scripts (`preinstall`, `install`, `postinstall`) of every dependency. Cuts the postinstall supply-chain vector.\n\npnpm CLI resolved via corepack from `packageManager`. No floating version reaches the runner.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eRecommended \u003ccode\u003eNPM_CONFIG_FILE\u003c/code\u003e contents\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nMinimal hardened `.npmrc` for every Coroboros consumer. Stored as a **secret** (encrypted; carries `${VAR}` expansions resolved at install time):\n\n```ini\n@coroboros:registry=https:${NPM_PACKAGE_REGISTRY}\nsave-exact=true\nfund=false\naudit=false\nignore-scripts=true\npackage-lock=false\nlockfile=true\nprefer-online=true\n```\n\n| Line | Why |\n| :--- | :--- |\n| `@coroboros:registry=https:${NPM_PACKAGE_REGISTRY}` | Scope-resolved registry — `${NPM_PACKAGE_REGISTRY}` expands from the same-named secret. |\n| `save-exact=true` | Pin exact versions on `add` / `install`. |\n| `fund=false` | Suppress funding noise in CI logs. |\n| `audit=false` | `osv-scanner` (in `security.yml`) covers vulnerability scans natively. |\n| `ignore-scripts=true` | Belt-and-suspenders against postinstall supply-chain attacks — backs up the `--ignore-scripts` flag already passed by `javascript/base` on every `pnpm install`. |\n| `package-lock=false` | Prevent `npm` from emitting a parasitic `package-lock.json` in pnpm repos. |\n| `lockfile=true` | Explicit `pnpm-lock.yaml` enablement. Required on pnpm `\u003c 11.0.0` consumers, where the preceding `package-lock=false` is interpreted as `lockfile=false` and collides with `pnpm install --frozen-lockfile`. Pnpm `\u003e= 11` already defaults to `true` and ignores `package-lock` for `pnpm-lock.yaml`, so the line is harmless there. |\n| `prefer-online=true` | Re-fetch dep metadata each install — local cache cannot mask a yanked or republished version. |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003ePublish — OIDC vs token bootstrap\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nAuto-detected by `NPM_PACKAGE_REGISTRY_TOKEN` **secret** presence on the consumer repo:\n\n| Token secret | Mode | Command |\n| :--- | :--- | :--- |\n| absent | **OIDC + provenance** (default) | `pnpm publish --provenance --no-git-checks` |\n| present | **Token bootstrap** | `npm publish --ignore-scripts --access public` |\n\n**OIDC + provenance** — no long-lived token in the repo; npm trusts a per-run id-token issued by GitHub Actions for `coroboros/\u003crepo\u003e/ci.yml`. Requires the npm Trusted Publisher form, which only accepts an existing package — so the very first publish has to take the token bootstrap below.\n\n**Token bootstrap** — publishes the first version of a new scoped package. Set two additional **secrets** on the consumer (encrypted; forwarded via the caller's `secrets:` block):\n\n| Secret | Contents |\n| :--- | :--- |\n| `NPM_PACKAGE_REGISTRY_TOKEN` | npm Granular Access Token scoped to the publishing organization with create-new-package permission. Long-lived; revoke after migrating to OIDC. |\n| `NPM_EXTRA_CONFIG` | `${NPM_PACKAGE_REGISTRY}:_authToken=${NPM_PACKAGE_REGISTRY_TOKEN}` — appended to `.npmrc` by `javascript/base`. Stored as a **secret** because it carries auth expansion. |\n\n`npm publish` is used on the bootstrap path (not `pnpm publish`) because pnpm `\u003e= 11.1.3` in CI auto-attempts the OIDC token exchange and does not fall back to the `.npmrc` token if OIDC fails. `--ignore-scripts --access public` skips publish-time lifecycle hooks (`prepublishOnly` excepted — known `npm` behavior). The published tarball is identical to `pnpm publish`'s.\n\nAfter the first publish, configure the npm Trusted Publisher form (Publisher type: GitHub Actions; Organization: the publishing org; Repository: consumer repo; Workflow filename: `ci.yml`; Environment: empty), then open a `chore(ci):` PR dropping `NPM_PACKAGE_REGISTRY_TOKEN` + `NPM_EXTRA_CONFIG` from the caller's `secrets:` block. Revoke the npm token. `1.0.1+` publishes via OIDC + provenance.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eSecret isolation\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nEach `workflow_call.secrets:` block declares ONLY the secrets the job consumes. No `secrets: inherit` anywhere.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eAction pinning + Dependabot\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nThird-party actions across workflows + composites are pinned to a commit SHA with an inline `# vX` comment. Floating refs (`@master`, `@main`, `@vX`) are banned.\n\nSelf-CI binaries pinned by version. `actionlint` and `gitleaks` install from release tarballs with SHA-256 verification; `yamllint` via `pip install` with version pin. No `curl | bash`.\n\n`.github/dependabot.yml` opens weekly grouped auto-PRs to bump pinned SHAs across `.github/workflows/*` and `.github/actions/**/action.yml`. Consumers should add their own ecosystem entries (e.g., `npm`).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eCanonical gitleaks config\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nCanonical ruleset at `security/.gitleaks.toml` in this repo. Stack-specific rules cover Resend, Neon Postgres, PostHog, and GitHub fine-grained PATs on top of the gitleaks defaults.\n\n`security.yml` sparse-checks the file out of `coroboros/ci` at runtime — imposed, no consumer override.\n\n\u003c/details\u003e\n\n---\n\n## Examples\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003e\u003ccode\u003ejavascript-npm-packages.yml\u003c/code\u003e wire-up\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n```yaml\n# consumer-repo/.github/workflows/ci.yml\nname: CI\non:\n  push:\n    branches: [develop, main]\n    tags: ['*']\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  ci:\n    uses: coroboros/ci/.github/workflows/javascript-npm-packages.yml@v0\n    permissions:\n      contents: write   # GitHub Release on tag\n      id-token: write   # npm OIDC publish on tag\n    secrets:\n      NPM_CONFIG_FILE: ${{ secrets.NPM_CONFIG_FILE }}\n      NPM_PACKAGE_REGISTRY: ${{ secrets.NPM_PACKAGE_REGISTRY }}\n      NPM_PACKAGE_PROXY_REGISTRY: ${{ secrets.NPM_PACKAGE_PROXY_REGISTRY }}\n      # Token bootstrap (drop both after npm Trusted Publisher is wired — see Security):\n      NPM_EXTRA_CONFIG: ${{ secrets.NPM_EXTRA_CONFIG }}\n      NPM_PACKAGE_REGISTRY_TOKEN: ${{ secrets.NPM_PACKAGE_REGISTRY_TOKEN }}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003e\u003ccode\u003esecurity.yml\u003c/code\u003e standalone (non-npm repo)\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n```yaml\n# consumer-repo/.github/workflows/security.yml\nname: Security\non:\n  push:\n    branches: [develop, main]\n  pull_request:\n  schedule:\n    - cron: '0 0 * * 0'   # weekly — catches CVEs published after last push\n\npermissions:\n  contents: read\n\njobs:\n  scan:\n    uses: coroboros/ci/.github/workflows/security.yml@v0\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eCompose with \u003ccode\u003ejavascript/base\u003c/code\u003e\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n```yaml\njobs:\n  custom:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    env:\n      NPM_CONFIG_FILE: ${{ secrets.NPM_CONFIG_FILE }}\n      NPM_EXTRA_CONFIG: ${{ secrets.NPM_EXTRA_CONFIG }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: coroboros/ci/.github/actions/check-docs@v0\n      - uses: coroboros/ci/.github/actions/javascript/base@v0\n      - run: pnpm run my-custom-script\n        shell: bash\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003eCompose a custom release pipeline (non-npm artifact)\u003c/em\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\n```yaml\njobs:\n  publish:\n    if: ${{ github.ref_type == 'tag' }}\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          ref: main\n          fetch-depth: 0\n      - id: changelog\n        uses: coroboros/ci/.github/actions/release/generate-changelog@v0\n      # ...your publish step (docker push, gh release upload, etc.)...\n      - uses: coroboros/ci/.github/actions/release/github-release@v0\n        with:\n          body: ${{ steps.changelog.outputs.body }}\n```\n\n\u003c/details\u003e\n\n---\n\n## License\n\nAll Rights Reserved. See [LICENSE.md](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoroboros%2Fci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoroboros%2Fci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoroboros%2Fci/lists"}