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
20 changes: 20 additions & 0 deletions .github/scripts/package-release-bundle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash

set -euo pipefail

version="${1:-}"
output_dir="${2:-dist}"

if [[ -z "${version}" ]]; then
echo "Usage: $0 <version> [output-dir]" >&2
exit 1
fi

mkdir -p "${output_dir}"

archive_path="${output_dir}/devcontainers-${version}.tar.gz"

# Archive the tracked workspace as it exists after version stamping and doc generation.
git ls-files -z | tar --null -czf "${archive_path}" -T -

echo "${archive_path}"
35 changes: 35 additions & 0 deletions .github/scripts/set-devcontainer-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

set -euo pipefail

raw_version="${1:-}"

if [[ -z "${raw_version}" ]]; then
echo "Usage: $0 <version>" >&2
exit 1
fi

version="${raw_version#v}"

if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
echo "Version must look like semver, for example 1.2.3 or 1.2.3-rc.1" >&2
exit 1
fi

mapfile -t manifest_files < <(
find features/src templates/src -type f \
\( -name 'devcontainer-feature.json' -o -name 'devcontainer-template.json' \) \
| sort
)

if [[ "${#manifest_files[@]}" -eq 0 ]]; then
echo "No devcontainer manifest files found." >&2
exit 1
fi

for manifest_file in "${manifest_files[@]}"; do
tmp_file="$(mktemp)"
jq --arg version "${version}" '.version = $version' "${manifest_file}" > "${tmp_file}"
mv "${tmp_file}" "${manifest_file}"
echo "Updated ${manifest_file} to version ${version}"
done
112 changes: 111 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,98 @@
name: "Release Dev Container Features And Templates"
on:
workflow_dispatch:
inputs:
version:
description: "Release version to publish, for example 1.2.3"
required: true
type: string
dry_run:
description: "Run the release flow without publishing packages or committing version updates"
required: false
default: true
type: boolean

release:
types:
- published

jobs:
deploy:
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.event_name == 'release' || github.ref == 'refs/heads/main' || github.event.inputs.dry_run == 'true' }}
runs-on: ubuntu-latest
permissions:
packages: write
contents: write
pull-requests: write
steps:
- name: Resolve release settings
id: settings
env:
INPUT_VERSION: ${{ inputs.version }}
INPUT_DRY_RUN: ${{ inputs.dry_run }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
set -euo pipefail

if [[ "${GITHUB_EVENT_NAME}" == "release" ]]; then
raw_version="${RELEASE_TAG}"
dry_run="false"
mode="release"
else
raw_version="${INPUT_VERSION}"
dry_run="${INPUT_DRY_RUN:-true}"

if [[ "${dry_run}" == "true" ]]; then
mode="dry-run"
else
mode="manual-release"
fi
fi

version="${raw_version#v}"

{
echo "version=${version}"
echo "dry_run=${dry_run}"
echo "mode=${mode}"
} >> "$GITHUB_OUTPUT"

- uses: actions/checkout@v6

- name: Set release version
id: bump
run: |
bash .github/scripts/set-devcontainer-version.sh "${{ steps.settings.outputs.version }}"
{
echo 'manifest_files<<EOF'
find features/src templates/src -type f \( -name 'devcontainer-feature.json' -o -name 'devcontainer-template.json' \) | sort
echo 'EOF'
} >> "$GITHUB_OUTPUT"

- name: "Generate Template Docs"
uses: devcontainers/action@v1
with:
base-path-to-templates: "./templates/src"
generate-docs: "true"

- name: "Generate Feature Docs"
uses: devcontainers/action@v1
with:
base-path-to-features: "./features/src"
generate-docs: "true"

- name: Package release source bundle
if: ${{ github.event_name == 'release' }}
id: package
run: |
archive_path="$(bash .github/scripts/package-release-bundle.sh "${{ steps.settings.outputs.version }}")"
{
echo "archive_path=${archive_path}"
echo "archive_name=$(basename "${archive_path}")"
} >> "$GITHUB_OUTPUT"

- name: "Publish Features"
if: ${{ steps.settings.outputs.dry_run != 'true' }}
uses: devcontainers/action@v1
with:
publish-features: "true"
Expand All @@ -23,10 +102,41 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: "Publish Templates"
if: ${{ steps.settings.outputs.dry_run != 'true' }}
uses: devcontainers/action@v1
with:
publish-templates: "true"
base-path-to-templates: "./templates/src"

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload release source bundle
if: ${{ github.event_name == 'release' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ github.event.release.tag_name }}" "${{ steps.package.outputs.archive_path }}" --clobber

- name: Summarize release run
run: |
{
echo "### Release workflow"
echo ""
echo "- Mode: ${{ steps.settings.outputs.mode }}"
echo "- Version: ${{ steps.settings.outputs.version }}"
echo "- Publish packages: ${{ steps.settings.outputs.dry_run != 'true' }}"
echo "- Attach source bundle to release: ${{ github.event_name == 'release' }}"
echo ""
echo "Updated manifest files:"
echo '```text'
printf '%s\n' "${{ steps.bump.outputs.manifest_files }}"
echo '```'
if [[ "${{ github.event_name }}" == "release" ]]; then
echo ""
echo "Release asset:"
echo '```text'
printf '%s\n' "${{ steps.package.outputs.archive_name }}"
echo '```'
fi
} >> "$GITHUB_STEP_SUMMARY"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Devcontainer templates repository focused on robotics and simulation.
## Workflows

- `.github/workflows/test-pr.yaml`: smoke tests template changes, validates features, and refreshes generated docs on pull requests
- `.github/workflows/release.yaml`: publishes features and templates to GHCR on manual dispatch from `main`
- `.github/workflows/release.yaml`: publishes from GitHub release runs, attaches a versioned source bundle to the release, and also supports manual versioned runs with a dry-run mode that only bumps manifests and regenerates docs in the workflow workspace
Loading