diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a3fb0d8..bd19d24 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -Thank you for your interest in contributing to the Response Verification package for the Internet Computer. +Thank you for your interest in contributing to ICP CLI Recipes for the Internet Computer. By participating in this project, you agree to abide by our [Code of Conduct](./CODE_OF_CONDUCT.md). As a member of the community, you are invited and encouraged to contribute by submitting issues, offering suggestions for improvements, adding review comments to existing pull requests, or creating new pull requests to fix issues. @@ -81,3 +81,66 @@ If you want to submit a pull request to fix an issue or add a feature, here's a ## Development Refer to the documentation in the [Recipe Authoring Guide](../docs/recipe-authoring.md) for more information on how to develop recipes. + +## Releases + +Releases are managed automatically through GitHub Actions. Each recipe is versioned independently. + +### For Maintainers + +To create a new release for a recipe: + +1. **Create and push a version tag** for the specific recipe: + ```bash + # For a Rust recipe release + git tag rust-v4.0.0 + git push origin rust-v4.0.0 + ``` + +2. **The workflow automatically**: + - Creates a GitHub release for `rust-v4.0.0` with changelog and release artifacts + - Updates the `rust-latest` tag to point to the same commit as `rust-v4.0.0` + - Creates/updates the `rust-latest` release with: + - The same changelog as `rust-v4.0.0` + - The same release artifacts (but named with "latest") + - A fresh timestamp showing when it was updated + +### Release Naming Convention + +Tags follow the pattern `-v`: +- `rust-v4.0.0` - Rust recipe version 4.0.0 +- `motoko-v5.0.0` - Motoko recipe version 5.0.0 +- `asset-canister-v3.0.0` - Asset canister recipe version 3.0.0 +- `prebuilt-v2.0.0` - Pre-built recipe version 2.0.0 + +### Release Notes + +Release notes are automatically generated and include: +- All commits affecting the specific recipe directory since the previous version +- A link to the full changelog comparing the two versions +- Release artifacts (tar.gz, zip files, checksums) + +### What is the `-latest` Release? + +Each recipe has two types of releases: + +1. **Versioned releases** (e.g., `rust-v4.0.0`): + - Permanent, immutable releases + - Pin to specific versions for stability + - Example: `rust-v4.0.0`, `rust-v3.0.0`, etc. + +2. **Latest release** (e.g., `rust-latest`): + - Always points to the most recent version + - Updated automatically when a new version is released + - Same content as the newest versioned release, but with: + - Updated timestamp + - "latest" naming for artifacts + - Use this to always get the newest recipe version + +### Viewing Releases + +Each recipe has its own release history: +- [Rust releases](https://github.com/dfinity/icp-cli-recipes/releases?q=rust&expanded=true) +- [Motoko releases](https://github.com/dfinity/icp-cli-recipes/releases?q=motoko&expanded=true) +- [Pre-built releases](https://github.com/dfinity/icp-cli-recipes/releases?q=prebuilt&expanded=true) +- [Asset Canister releases](https://github.com/dfinity/icp-cli-recipes/releases?q=asset-canister&expanded=true) diff --git a/.github/workflows/release-recipe.yml b/.github/workflows/release-recipe.yml index f17bd68..acf3eb1 100644 --- a/.github/workflows/release-recipe.yml +++ b/.github/workflows/release-recipe.yml @@ -21,6 +21,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 # Fetch all history and tags - name: Extract recipe and version from tag id: tag_info @@ -30,14 +32,14 @@ jobs: VERSION=$(echo "$TAG" | sed 's/^.*-//') # Handle latest tags specially - if [[ "$VERSION" == "latest" ]]; then - echo "recipe=$RECIPE" >> $GITHUB_OUTPUT - echo "version=latest" >> $GITHUB_OUTPUT - echo "is_latest=true" >> $GITHUB_OUTPUT + if [ "$VERSION" = "latest" ]; then + echo "recipe=$RECIPE" >> "$GITHUB_OUTPUT" + echo "version=latest" >> "$GITHUB_OUTPUT" + echo "is_latest=true" >> "$GITHUB_OUTPUT" else - echo "recipe=$RECIPE" >> $GITHUB_OUTPUT - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "is_latest=false" >> $GITHUB_OUTPUT + echo "recipe=$RECIPE" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "is_latest=false" >> "$GITHUB_OUTPUT" fi - name: Package Recipe @@ -56,12 +58,105 @@ jobs: cd release sha256sum * > checksums.txt + - name: Find previous recipe version + id: prev_version + run: | + RECIPE="${{ steps.tag_info.outputs.recipe }}" + CURRENT_TAG="${GITHUB_REF#refs/tags/}" + IS_LATEST="${{ steps.tag_info.outputs.is_latest }}" + + if [ "$IS_LATEST" = "true" ]; then + # For -latest tags: find the versioned tag pointing to the same commit + CURRENT_COMMIT=$(git rev-parse HEAD) + CURRENT_VERSION_TAG=$(git tag --sort=-version:refname -l "${RECIPE}-v*" | while read -r tag; do + if [ "$(git rev-parse "$tag")" = "$CURRENT_COMMIT" ]; then + echo "$tag" + break + fi + done) + + if [ -z "$CURRENT_VERSION_TAG" ]; then + echo "::error::No versioned tag found for this commit. A -latest tag must have a corresponding versioned tag (e.g., ${RECIPE}-v1.0.0) on the same commit." + exit 1 + else + echo "Found versioned tag for this commit: ${CURRENT_VERSION_TAG}" + # Find the previous version before the current versioned tag + PREV_TAG=$(git tag --sort=-version:refname -l "${RECIPE}-v*" | grep -v "${CURRENT_VERSION_TAG}" | head -n 1) + echo "previous_tag=${PREV_TAG}" >> "$GITHUB_OUTPUT" + echo "compare_tag=${CURRENT_VERSION_TAG}" >> "$GITHUB_OUTPUT" + fi + else + # For versioned tags: find the previous version tag + PREV_TAG=$(git tag --sort=-version:refname -l "${RECIPE}-v*" | grep -v "${CURRENT_TAG}" | head -n 1) + + if [ -z "$PREV_TAG" ]; then + echo "No previous version found for ${RECIPE}" + echo "previous_tag=" >> "$GITHUB_OUTPUT" + else + echo "Found previous tag: ${PREV_TAG}" + echo "previous_tag=${PREV_TAG}" >> "$GITHUB_OUTPUT" + fi + echo "compare_tag=${CURRENT_TAG}" >> "$GITHUB_OUTPUT" + fi + + - name: Generate Release Notes Body + id: release_notes + run: | + RECIPE="${{ steps.tag_info.outputs.recipe }}" + PREV_TAG="${{ steps.prev_version.outputs.previous_tag }}" + COMPARE_TAG="${{ steps.prev_version.outputs.compare_tag }}" + + if [ -n "$PREV_TAG" ] && [ -n "$COMPARE_TAG" ]; then + echo "## What's Changed" > release_notes.md + echo "" >> release_notes.md + + # Get commits that affect this recipe's directory + COMMITS=$(git log ${PREV_TAG}..${COMPARE_TAG} --pretty=format:"* %s (%h)" --no-merges -- recipes/${RECIPE}/) + + if [ -n "$COMMITS" ]; then + echo "Changes since ${PREV_TAG}:" >> release_notes.md + echo "" >> release_notes.md + echo "$COMMITS" >> release_notes.md + echo "" >> release_notes.md + else + echo "No changes to the ${RECIPE} recipe files." >> release_notes.md + echo "" >> release_notes.md + fi + + echo "" >> release_notes.md + echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${COMPARE_TAG}" >> release_notes.md + else + echo "## What's Changed" > release_notes.md + echo "" >> release_notes.md + echo "Initial release of the ${RECIPE} recipe." >> release_notes.md + fi + - name: Create Release uses: softprops/action-gh-release@aec2ec56f94eb8180ceec724245f64ef008b89f5 # v2.4.0 with: tag_name: ${{ steps.tag_info.outputs.recipe }}-${{ steps.tag_info.outputs.version }} name: ${{ steps.tag_info.outputs.recipe }} ${{ steps.tag_info.outputs.version }} files: release/* - generate_release_notes: true + body_path: release_notes.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Update latest tag + if: steps.tag_info.outputs.is_latest == 'false' + run: | + RECIPE="${{ steps.tag_info.outputs.recipe }}" + CURRENT_TAG="${GITHUB_REF#refs/tags/}" + + echo "Updating ${RECIPE}-latest to point to ${CURRENT_TAG}" + + # Delete the old latest tag locally and remotely + git tag -d ${RECIPE}-latest 2>/dev/null || true + git push origin :refs/tags/${RECIPE}-latest 2>/dev/null || true + + # Create new latest tag pointing to current commit + git tag ${RECIPE}-latest + + # Push the new latest tag + git push origin ${RECIPE}-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}