Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions .github/workflows/publish-fragment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
# Call it once per project file, after the release's images exist. A merge that
# is not released publishes nothing: the caller triggers on the tag.
#
# Every decision is a deploy-kit command, at the version the calling
# repository's own lockfile pins. The scripts beside it only hand the command
# its arguments and move bytes.
# Every decision is a deploy-kit command, at the version tools/deploy-kit in
# this repository pins; a calling repository pins none. The scripts beside it
# only hand the command its arguments and move bytes.
#
# The signature's identity is this workflow, run for the calling repository, so
# composition verifies every fragment against one subject and reads the
Expand Down Expand Up @@ -68,11 +68,6 @@ on:
required: false
type: string
default: ""
toolkit-directory:
description: The directory holding the package.json and package-lock.json that pin @jorisjonkers-dev/deploy-kit.
required: false
type: string
default: "."
node-version:
description: The Node the toolkit runs on.
required: false
Expand Down Expand Up @@ -139,20 +134,18 @@ jobs:
registry-url: https://npm.pkg.github.com
scope: "@jorisjonkers-dev"

- name: Install the toolkit at the version this repository pins
working-directory: ${{ inputs.toolkit-directory }}
- name: Install the toolkit at the version this workflow pins
working-directory: .github-workflows/tools/deploy-kit
env:
NODE_AUTH_TOKEN: ${{ github.token }}
TOOLKIT_DIRECTORY: ${{ inputs.toolkit-directory }}
run: |
# The version lives in one place, the caller's lockfile, which Renovate
# moves. Nothing here names a version, and `npx --no-install` below
# can only run what this step installed.
# The version lives in tools/deploy-kit/package-lock.json, which Renovate moves.
npm ci --ignore-scripts
echo "::notice::deploy-kit $(jq -r .version node_modules/@jorisjonkers-dev/deploy-kit/package.json)"
# Called with nothing to do, the command answers with its usage and
# exits 2. Anything else means the pinned release has no command.
npx --no-install deploy-kit >/dev/null 2>&1 || [ "$?" -eq 2 ] || {
echo "::error::@jorisjonkers-dev/deploy-kit in ${TOOLKIT_DIRECTORY}/package-lock.json ships no deploy-kit command; pin a release that does"
echo "::error::the pinned @jorisjonkers-dev/deploy-kit ships no deploy-kit command; pin a release that does"
exit 1
}

Expand Down Expand Up @@ -233,7 +226,7 @@ jobs:
VERSION: ${{ inputs.version }}
SOURCE_SHA: ${{ inputs.ref || github.sha }}
REPOSITORY: ${{ github.repository }}
TOOLKIT_DIRECTORY: ${{ inputs.toolkit-directory }}
TOOLKIT_DIRECTORY: .github-workflows/tools/deploy-kit
OUT: ${{ runner.temp }}/fragment
run: bash .github-workflows/actions/publish-fragment/pack.sh

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ should call released tags instead of branches.
| `production-canary.yml` | Run caller-owned production smoke checks. |
| `deploy-bundle.yml` | Validate first-party deploy bundles and optionally publish them to GHCR. |
| `deploy-sources-render.yml` | Render deployment sources and expose image tags for downstream tests. |
| `publish-fragment.yml` | Validate, pack, sign and push a project file's Intent Fragment for a release, then start composition (deploy-kit). |
| `publish-fragment.yml` | Validate, pack, sign and push a project file's Intent Fragment for a release, then start composition (deploy-kit). Runs deploy-kit at the version `tools/deploy-kit` pins; the caller pins none. |
| `path-buckets.yml` | The `changes` job of a bucketed pipeline: its `run` output gates each job on the buckets a diff reaches. |
| `repository-hygiene-guard.yml` | Block reintroduction of planning and scratch artifacts. |
| `add-to-project.yml` | Add opened/reopened issues and pull requests to the org Project. |
Expand Down
4 changes: 2 additions & 2 deletions actions/publish-fragment/pack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# version's shape, hands the command its arguments, and writes the per-file
# manifest a consumer verifies the pulled package against.
#
# The toolkit is the one the calling repository's lockfile pins. `npm ci` in
# TOOLKIT_DIRECTORY installed it, and `npx --no-install` can run nothing else.
# The toolkit is the one tools/deploy-kit pins. `npm ci` in TOOLKIT_DIRECTORY
# installed it, and `npx --no-install` can run nothing else.
set -euo pipefail

: "${PROJECT_FILE:?}" "${VERSION:?}" "${SOURCE_SHA:?}" "${REPOSITORY:?}" "${OUT:?}"
Expand Down
14 changes: 14 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
"before 9am on monday"
]
},
{
"description": "The deploy-kit publish-fragment.yml runs: a bump is a release of this repository, merged only once JorisJonkers-dev/estate composes with that version or newer",
"matchFileNames": [
"tools/deploy-kit/**"
],
"matchPackageNames": [
"@jorisjonkers-dev/deploy-kit"
],
"semanticCommitType": "fix",
"automerge": false,
"prBodyNotes": [
"Merge only once `JorisJonkers-dev/estate`'s `package.json` pins this deploy-kit version or newer: composition refuses a fragment whose model minor is newer than its own toolkit's."
]
},
{
"description": "JorisJonkers-dev/github-workflows: track semver tags, do NOT pin to digest",
"matchPackageNames": [
Expand Down
19 changes: 19 additions & 0 deletions tests/test_publish_fragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""
from __future__ import annotations

import json
import os
import re
import stat
Expand Down Expand Up @@ -356,6 +357,24 @@ def test_the_delivery_is_titled_with_the_release_that_started_it(self):
step = self.text[start:]
self.assertEqual(step.count("gh workflow run compose.yml"), 2)

def test_the_toolkit_is_the_one_this_repository_pins(self):
self.assertNotIn("toolkit-directory", self.text)
step = self.text.split("- name: Install the toolkit at the version this workflow pins", 1)[1].split("- name:", 1)[0]
self.assertIn("working-directory: .github-workflows/tools/deploy-kit", step)
self.assertIn("npm ci --ignore-scripts", step)
self.assertIn('echo "::notice::deploy-kit $(jq -r .version node_modules/@jorisjonkers-dev/deploy-kit/package.json)"', step)
self.assertIn("TOOLKIT_DIRECTORY: .github-workflows/tools/deploy-kit", self.text)

def test_the_pin_is_exact_and_locked(self):
toolkit = ROOT / "tools" / "deploy-kit"
pinned = json.loads((toolkit / "package.json").read_text())["devDependencies"]["@jorisjonkers-dev/deploy-kit"]
self.assertRegex(pinned, r"^\d+\.\d+\.\d+$")
lock = json.loads((toolkit / "package-lock.json").read_text())
self.assertEqual(lock["packages"][""]["devDependencies"]["@jorisjonkers-dev/deploy-kit"], pinned)
locked = lock["packages"]["node_modules/@jorisjonkers-dev/deploy-kit"]
self.assertEqual(locked["version"], pinned)
self.assertIn("deploy-kit", locked["bin"])


if __name__ == "__main__":
unittest.main()
56 changes: 56 additions & 0 deletions tools/deploy-kit/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tools/deploy-kit/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "publish-fragment-toolkit",
"private": true,
"description": "Pins the deploy-kit release publish-fragment.yml validates and packs every Intent Fragment with.",
"devDependencies": {
"@jorisjonkers-dev/deploy-kit": "0.14.0"
}
}
Loading