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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
github-actions:
patterns:
- "*"
230 changes: 47 additions & 183 deletions .github/workflows/_publish-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ on:
description: NuGet package ID
required: true
type: string
tagPrefix:
description: Package-specific release tag prefix
required: true
type: string
version:
description: Package calendar version (YYYY.M.MINOR)
required: true
Expand All @@ -18,6 +14,10 @@ on:
description: Minimum published FSharp.ViewEngine version required by Docs
required: false
type: string
markLatest:
description: Whether this package release becomes the repository-wide Latest release
required: true
type: boolean

permissions:
contents: write
Expand All @@ -31,68 +31,21 @@ jobs:
package:
name: Build and verify ${{ inputs.packageId }}
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
outputs:
tag: ${{ steps.release.outputs.tag }}
version: ${{ steps.release.outputs.version }}
commit: ${{ steps.release.outputs.commit }}
previousTag: ${{ steps.previous-tag.outputs.tag }}
previousTag: ${{ steps.release.outputs.previousTag }}
steps:
- name: Require main branch
run: |
if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then
echo "Releases must run from main, not $GITHUB_REF." >&2
exit 1
fi
- name: Validate package inputs
env:
PACKAGE_ID: ${{ inputs.packageId }}
VERSION: ${{ inputs.version }}
MINIMUM_CORE_VERSION: ${{ inputs.minimumCoreVersion }}
run: |
if [[ ! "$VERSION" =~ ^[0-9]{4}\.[0-9]{1,2}\.[0-9]+$ ]]; then
echo "Package releases require an explicit version in YYYY.M.MINOR form." >&2
exit 1
fi

case "$PACKAGE_ID" in
FSharp.ViewEngine)
if [[ -n "$MINIMUM_CORE_VERSION" ]]; then
echo "Core releases must not specify a minimum Core version." >&2
exit 1
fi
;;
FSharp.ViewEngine.Docs)
if [[ ! "$MINIMUM_CORE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Docs releases require a minimum Core version in YYYY.M.MINOR form." >&2
exit 1
fi
;;
*)
echo "Unsupported package: $PACKAGE_ID" >&2
exit 1
;;
esac
- uses: actions/checkout@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-tags: true
- name: Confirm changelog contains selected package release
env:
PACKAGE_ID: ${{ inputs.packageId }}
VERSION: ${{ inputs.version }}
run: |
changelog="sln/src/Docs/src/Pages/Changelog.fs"
marker="title = \"$PACKAGE_ID $VERSION ·"
matches=$(grep -F -c "$marker" "$changelog" || true)

if [[ "$matches" -ne 1 ]]; then
echo "Expected exactly one released-package changelog entry beginning '$PACKAGE_ID $VERSION ·'; found $matches." >&2
exit 1
fi
- name: Setup dotnet
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: |
8.0.x
Expand All @@ -104,47 +57,21 @@ jobs:
dotnet paket install
dotnet restore FSharp.ViewEngine.slnx
working-directory: ./sln
- name: Confirm minimum Core package is published
if: inputs.packageId == 'FSharp.ViewEngine.Docs'
env:
CORE_VERSION: ${{ inputs.minimumCoreVersion }}
run: |
url="https://api.nuget.org/v3-flatcontainer/fsharp.viewengine/$CORE_VERSION/fsharp.viewengine.$CORE_VERSION.nupkg"
curl --fail --silent --show-error --output /dev/null "$url" || {
echo "FSharp.ViewEngine $CORE_VERSION must be available from NuGet before publishing Docs." >&2
exit 1
}
- name: Prepare package release
env:
PACKAGE_ID: ${{ inputs.packageId }}
PACKAGE_VERSION: ${{ inputs.version }}
DOCS_MINIMUM_CORE_VERSION: ${{ inputs.minimumCoreVersion }}
MARK_LATEST: ${{ inputs.markLatest }}
RELEASE_METADATA_PATH: ${{ runner.temp }}/release-metadata.json
RELEASE_TAG_PREFIX: ${{ inputs.tagPrefix }}
RELEASE_VERSION: ${{ inputs.version }}
run: ./fake.sh PrepareRelease
run: ./fake.sh PrepareRelease --single-target
working-directory: ./sln
- name: Read release metadata
id: release
env:
RELEASE_METADATA_PATH: ${{ runner.temp }}/release-metadata.json
run: |
{
echo "tag=$(jq -r '.tag' "$RELEASE_METADATA_PATH")"
echo "version=$(jq -r '.version' "$RELEASE_METADATA_PATH")"
echo "commit=$(jq -r '.commit' "$RELEASE_METADATA_PATH")"
} >> "$GITHUB_OUTPUT"
- name: Find previous package release tag
id: previous-tag
env:
RELEASE_TAG_PREFIX: ${{ inputs.tagPrefix }}
TAG: ${{ steps.release.outputs.tag }}
run: |
previous=""
while IFS= read -r candidate; do
if [[ "$candidate" == "$TAG" ]]; then
break
fi
previous="$candidate"
done < <({ git tag --list "$RELEASE_TAG_PREFIX*"; echo "$TAG"; } | sort -Vu)
echo "tag=$previous" >> "$GITHUB_OUTPUT"
run: ./fake.sh ReadReleaseMetadata --single-target
working-directory: ./sln
- name: Build, test, pack, and verify selected package
env:
PACKAGE_ID: ${{ inputs.packageId }}
Expand All @@ -153,127 +80,64 @@ jobs:
run: ./fake.sh VerifyPackage
working-directory: ./sln
- name: Record package checksums
run: sha256sum ./*.nupkg ./*.snupkg > SHA256SUMS
working-directory: ./nugets
run: ./fake.sh RecordPackageChecksums --single-target
working-directory: ./sln
- name: Upload verified package
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ inputs.packageId }}-${{ steps.release.outputs.version }}
path: |
nugets/*.nupkg
nugets/*.snupkg
nugets/SHA256SUMS
${{ runner.temp }}/release-metadata.json
if-no-files-found: error

publish:
name: Publish ${{ inputs.packageId }}
needs: package
runs-on: ubuntu-latest
timeout-minutes: 20
environment: release
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.package.outputs.commit }}
fetch-depth: 0
fetch-tags: true
- name: Setup dotnet
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: 10.0.x
- name: Download verified package
uses: actions/download-artifact@v5
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.packageId }}-${{ needs.package.outputs.version }}
path: ${{ runner.temp }}/package
- name: Verify package checksums
run: sha256sum --check SHA256SUMS
working-directory: ${{ runner.temp }}/package
- name: Authenticate Pulumi
uses: pulumi/auth-actions@v1
with:
organization: meiermade
requested-token-type: urn:pulumi:token-type:access_token:personal
scope: user:meiermade
- name: Publish verified package
env:
PACKAGE_DIRECTORY: ${{ runner.temp }}/package
PACKAGE_ID: ${{ inputs.packageId }}
VERSION: ${{ needs.package.outputs.version }}
- name: Restore build tooling
run: |
package="$PACKAGE_DIRECTORY/$PACKAGE_ID.$VERSION.nupkg"
test -f "$package"
pulumi env run fsharpviewengine/nuget -- \
bash -euo pipefail -c '
dotnet nuget push "$1" \
--source https://api.nuget.org/v3/index.json \
--api-key "$NUGET_API_KEY" \
--skip-duplicate
' bash "$package"
- name: Wait for NuGet availability
env:
PACKAGE_ID: ${{ inputs.packageId }}
VERSION: ${{ needs.package.outputs.version }}
run: |
package_slug="${PACKAGE_ID,,}"
package_url="https://api.nuget.org/v3-flatcontainer/$package_slug/$VERSION/$package_slug.$VERSION.nupkg"

for _ in $(seq 1 60); do
if curl --fail --silent --show-error --output /dev/null "$package_url"; then
echo "$PACKAGE_ID $VERSION is available from NuGet."
exit 0
fi
sleep 10
done

echo "$PACKAGE_ID $VERSION was not available from NuGet after 10 minutes." >&2
exit 1
- name: Check out published package commit
uses: actions/checkout@v5
dotnet tool restore
dotnet paket install
dotnet restore src/Build/Build.fsproj
working-directory: ./sln
- name: Authenticate to NuGet with GitHub OIDC
id: nuget-login
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
with:
ref: ${{ needs.package.outputs.commit }}
fetch-depth: 0
fetch-tags: true
- name: Create package release tag
env:
TAG: ${{ needs.package.outputs.tag }}
COMMIT: ${{ needs.package.outputs.commit }}
run: |
existing_commit=$(git rev-list -n 1 "$TAG" 2>/dev/null || true)

if [[ -n "$existing_commit" && "$existing_commit" != "$COMMIT" ]]; then
echo "Release tag $TAG points to $existing_commit, not $COMMIT." >&2
exit 1
fi

if [[ -z "$existing_commit" ]]; then
git tag "$TAG" "$COMMIT"
git push origin "refs/tags/$TAG"
fi
- name: Create package GitHub release
user: ${{ vars.NUGET_USER }}
- name: Publish and reconcile package release
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
PACKAGE_ID: ${{ inputs.packageId }}
TAG: ${{ needs.package.outputs.tag }}
PREVIOUS_TAG: ${{ needs.package.outputs.previousTag }}
VERSION: ${{ needs.package.outputs.version }}
GITHUB_REPOSITORY: ${{ github.repository }}
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
PACKAGE_DIRECTORY: ${{ runner.temp }}/package
run: |
if gh release view "$TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
echo "GitHub Release $TAG already exists."
else
release_args=(
--repo "$REPOSITORY"
--verify-tag
--title "$PACKAGE_ID $VERSION"
)

if [[ -n "$PREVIOUS_TAG" ]]; then
release_args+=(--generate-notes --notes-start-tag "$PREVIOUS_TAG")
else
release_args+=(--notes "Initial $PACKAGE_ID package release. See the package changelog for release details.")
fi

gh release create "$TAG" \
"${release_args[@]}" \
"$PACKAGE_DIRECTORY"/*.nupkg \
"$PACKAGE_DIRECTORY"/*.snupkg \
"$PACKAGE_DIRECTORY/SHA256SUMS"
fi
PACKAGE_ID: ${{ inputs.packageId }}
PACKAGE_VERSION: ${{ needs.package.outputs.version }}
DOCS_MINIMUM_CORE_VERSION: ${{ inputs.minimumCoreVersion }}
MARK_LATEST: ${{ inputs.markLatest }}
RELEASE_METADATA_PATH: ${{ runner.temp }}/package/release-metadata.json
run: ./fake.sh PublishPackageRelease --single-target
working-directory: ./sln
19 changes: 8 additions & 11 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: production
url: https://fsharpviewengine.meiermade.com
Expand All @@ -48,7 +49,7 @@ jobs:
version: ${{ steps.release.outputs.version }}
commit: ${{ steps.release.outputs.commit }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.ref }}
- name: Resolve release metadata
Expand Down Expand Up @@ -81,24 +82,20 @@ jobs:
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "commit=$commit" >> "$GITHUB_OUTPUT"
- name: Setup Node
uses: actions/setup-node@v5
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Setup gcloud
uses: google-github-actions/setup-gcloud@v2
with:
install_components: gke-gcloud-auth-plugin
- name: Install Packages
run: npm ci
working-directory: ./pulumi
- name: Authenticate Pulumi
uses: pulumi/auth-actions@v1
uses: pulumi/auth-actions@141415910c3beb54e03b48e9057c204c97b956f2 # v2.1.0
with:
organization: meiermade
requested-token-type: urn:pulumi:token-type:access_token:personal
scope: user:meiermade
- name: Deploy
uses: pulumi/actions@v6
uses: pulumi/actions@8e5e406f4007fca908480587cb9893c07090f58d # v7.0.0
env:
RELEASE_VERSION: ${{ steps.release.outputs.version }}
RELEASE_COMMIT: ${{ steps.release.outputs.commit }}
Expand All @@ -115,11 +112,11 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.deploy.outputs.commit }}
- name: Setup Node
uses: actions/setup-node@v5
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: npm
Expand Down Expand Up @@ -160,7 +157,7 @@ jobs:
DOCS_EXPECTED_COMMIT: ${{ needs.deploy.outputs.commit }}
- name: Upload E2E diagnostics
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: fsharp-viewengine-deploy-e2e
path: |
Expand Down
Loading
Loading