Build and Publish #77
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Publish | |
| on: | |
| schedule: | |
| # At 04:00 on day-of-month 1. | |
| - cron: "0 4 1 * *" | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - "main" | |
| jobs: | |
| prepare-matrix: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.set-matrix.outputs.matrix }} | |
| latest: ${{ steps.latest-version.outputs.output }} | |
| devcontainer_matrix: ${{ steps.set-dev-matrix.outputs.matrix }} | |
| steps: | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@master | |
| - name: Check Docker image platforms | |
| id: check-platforms | |
| run: | | |
| curl -s "https://nodejs.org/download/release/index.json" >node_versions.json | |
| # Filter out versions older than 2 years | |
| two_years_ago=$(date -u -d "2 years ago" +%s) | |
| jq --argjson two_years_ago "$two_years_ago" '[.[] | select((.date | sub("T.*"; "") | strptime("%Y-%m-%d") | mktime) > $two_years_ago)]' node_versions.json >node_versions_recent.json | |
| # Function to check Docker image platforms | |
| check_platforms() { | |
| version=$1 | |
| # Get platforms from manifest | |
| if manifest=$(docker manifest inspect "node:$version-alpine" 2>/dev/null); then | |
| # Extract all valid platforms (excluding unknown) | |
| platforms=$(echo "$manifest" | jq -r '.manifests[] | | |
| select(.platform.os != "unknown" and .platform.architecture != "unknown") | | |
| if .platform.variant then | |
| "linux/" + .platform.architecture + "/" + .platform.variant | |
| else | |
| "linux/" + .platform.architecture | |
| end' | tr '\n' ',' | sed 's/,$//') | |
| if [ -n "$platforms" ]; then | |
| echo "$platforms" | |
| return | |
| fi | |
| fi | |
| # Default platforms if manifest check fails | |
| echo "linux/amd64,linux/arm64/v8" | |
| } | |
| # Process versions and add platform information (using filtered recent versions) | |
| jq -c '[.[] | select(.version | test("v(1[8-9]|[2-9][0-9]).*"))] | group_by(.version | match("v(\\d+).*").captures[0].string | tonumber) | map(max_by(.version | match("v(\\d+\\.\\d+).*").captures[0].string | split(".") as $parts | ($parts[0] | tonumber) * 1000 + ($parts[1] | tonumber))) | map({version: .version, major: (.version | match("v(\\d+).*").captures[0].string | tonumber)})' node_versions_recent.json >versions.json | |
| # Add platform information to each version | |
| platforms_json="[" | |
| first=true | |
| while IFS= read -r version_info; do | |
| version=$(echo "$version_info" | jq -r '.version') | |
| major=$(echo "$version_info" | jq -r '.major') | |
| version_without_v="${version#v}" # Remove 'v' prefix for Docker tag | |
| platforms=$(check_platforms "$version_without_v") | |
| if [ "$first" = true ]; then | |
| first=false | |
| else | |
| platforms_json="$platforms_json," | |
| fi | |
| platforms_json="$platforms_json{\"version\":\"$version\",\"major\":$major,\"platforms\":\"$platforms\"}" | |
| done < <(jq -c '.[]' versions.json) | |
| platforms_json="$platforms_json]" | |
| echo "$platforms_json" >config.json | |
| - uses: cloudposse/github-action-jq@main | |
| id: latest-version | |
| with: | |
| compact: true | |
| raw-output: true | |
| input: config.json | |
| script: 'sort_by(.major) | reverse | group_by(.major) | map(.[0]) | sort_by(.major) | reverse | .[0].version' | |
| - uses: cloudposse/github-action-jq@main | |
| id: reparse-tags | |
| with: | |
| compact: true | |
| input: config.json | |
| script: '{"tag": . }' | |
| - run: echo 'matrix=${{ steps.reparse-tags.outputs.output }}' >> $GITHUB_OUTPUT | |
| id: set-matrix | |
| # Pick the top 3 major versions for devcontainers | |
| - uses: cloudposse/github-action-jq@main | |
| id: reparse-dev-tags | |
| with: | |
| compact: true | |
| input: config.json | |
| script: 'sort_by(.major) | reverse | group_by(.major) | map(.[0]) | sort_by(.major) | reverse | .[:3] | {"tag": . }' | |
| - run: echo 'matrix=${{ steps.reparse-dev-tags.outputs.output }}' >> $GITHUB_OUTPUT | |
| id: set-dev-matrix | |
| build: | |
| needs: prepare-matrix | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: ${{fromJson(needs.prepare-matrix.outputs.matrix)}} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: all | |
| - name: Set up Docker Buildx | |
| id: buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to DockerHub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_HUB_USER }} | |
| password: ${{ secrets.DOCKER_HUB_TOKEN }} | |
| logout: true | |
| - name: Parse version | |
| id: parse_version | |
| run: | | |
| VERSION=${{ matrix.tag.version }} | |
| PARSED_VERSION=${VERSION#v} | |
| echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT | |
| shell: bash | |
| - name: Is Latest | |
| id: is_latest | |
| run: | | |
| if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then | |
| echo "IS_LATEST=true" >> $GITHUB_OUTPUT | |
| echo "This is the latest version" | |
| else | |
| echo "IS_LATEST=false" >> $GITHUB_OUTPUT | |
| echo "This is not the latest version" | |
| fi | |
| - name: Build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| build-args: | | |
| NODE_MAJOR=${{ matrix.tag.major }} | |
| NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }} | |
| context: . | |
| platforms: ${{ matrix.tag.platforms }} | |
| push: true | |
| tags: | | |
| betterweb/node:${{steps.parse_version.outputs.PARSED_VERSION}} | |
| betterweb/node:${{matrix.tag.major}} | |
| ${{ steps.is_latest.outputs.IS_LATEST == 'true' && 'betterweb/node:latest' || '' }} | |
| build-devcontainers: | |
| needs: prepare-matrix | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: ${{fromJson(needs.prepare-matrix.outputs.devcontainer_matrix)}} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to DockerHub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_HUB_USER }} | |
| password: ${{ secrets.DOCKER_HUB_TOKEN }} | |
| logout: true | |
| - name: Parse version | |
| id: parse_version | |
| run: | | |
| VERSION=${{ matrix.tag.version }} | |
| PARSED_VERSION=${VERSION#v} | |
| echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT | |
| shell: bash | |
| - name: Is Latest | |
| id: is_latest | |
| run: | | |
| if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then | |
| echo "IS_LATEST=true" >> $GITHUB_OUTPUT | |
| echo "This is the latest version" | |
| else | |
| echo "IS_LATEST=false" >> $GITHUB_OUTPUT | |
| echo "This is not the latest version" | |
| fi | |
| - name: Build and push devcontainer image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: Dockerfile.devcontainer | |
| push: true | |
| build-args: | | |
| NODE_MAJOR=${{ matrix.tag.major }} | |
| NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }} | |
| tags: | | |
| betterweb/node:devcontainer-${{steps.parse_version.outputs.PARSED_VERSION}} | |
| betterweb/node:devcontainer-${{matrix.tag.major}} | |
| ${{ steps.is_latest.outputs.IS_LATEST == 'true' && 'betterweb/node:devcontainer-latest' || '' }} |