From 83db3f41f1f92ff4d720a60b0e176cdbbab77296 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Sun, 1 Feb 2026 23:06:56 -0800 Subject: [PATCH 01/13] context test --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..30861d2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test macOS features + +on: + push: + branches: + - main + paths-ignore: + - 'docs/**' + pull_request: + +jobs: + macOS: + runs-on: macos-latest + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + + - name: Dump GitHub event context + env: + EVENT_CONTEXT: ${{ toJson(github.event) }} + run: echo "$EVENT_CONTEXT" From 2823a77d49c313b513f54a7d0cb5ff68ac37bf11 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 01:34:13 -0800 Subject: [PATCH 02/13] test --- .github/workflows/test_macos.yml | 191 ++++++++++++++++++++++--------- 1 file changed, 134 insertions(+), 57 deletions(-) diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index 6909c6f..94c078a 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -6,71 +6,148 @@ on: - main paths-ignore: - "docs/**" + - "docs/**" pull_request: - schedule: - - cron: "0 2 * * *" jobs: + generate-matrix: + name: Generate matrix + runs-on: ubuntu-latest + outputs: + features: ${{ steps.build-matrix.outputs.features }} + steps: + - name: 🛒 Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 📄 Get list of changed files + id: changed-files + run: | + # Determine base and head commits using GitHub Actions contexts + if [ "${{ github.event_name }}" = "pull_request" ]; then + base="${{ github.event.pull_request.base.sha }}" + head="${{ github.event.pull_request.head.sha }}" + elif [ "${{ github.event_name }}" = "push" ]; then + base="${{ github.event.before }}" + head="${{ github.sha }}" + else + echo "Unsupported event: ${{ github.event_name }}" + base="" + head="" + fi + + if [ -z "$base" ] || [ -z "$head" ]; then + echo "No base/head to diff; leaving paths empty." + echo "paths=" >> "$GITHUB_OUTPUT" + exit 0 + fi + + files=$(git diff --name-only "$base" "$head" || true) + + echo "Changed files:" + echo "$files" + + { + echo 'paths<> "$GITHUB_OUTPUT" + + - name: 🧮 Generate feature matrix + id: build-matrix + run: | + # Extract feature names for any changed file under pkg/some-feature/* + changed_paths="${{ steps.changed-files.outputs.paths }}" + changed_features=$( + printf '%s\n' "$changed_paths" | + sed -n 's|^pkg/\([^/]*\).*|\1|p' | + sort -u + ) + + echo "Changed features from pkg/:" + echo "$changed_features" + + features_json=$( + printf '%s\n' "$changed_features" | + jq -R -s -c 'split("\n") | map(select(length>0))' + ) + + echo "features=$features_json" >> "$GITHUB_OUTPUT" + + add-fixed-features: + runs-on: ubuntu-latest + needs: generate-matrix + outputs: + features: ${{ steps.final-matrix.outputs.features }} + steps: + - name: Add fixed features + id: final-matrix + env: + FIXED_FEATURES: | + ansible + clean + cli-network + core + git + javascript + macos-appcleaner + macos-brew + macos-c-cpp + macos-clean + macos-cli-network + macos-cli-useful + macos-core + macos-docker + macos-duti + macos-file-handler + macos-finder + macos-font + macos-git + macos-hammerspoon + macos-icon + macos-iterm + macos-javascript + macos-markedit + macos-micro + macos-nano + macos-popclip + macos-quicklook + macos-screensaver + macos-service-workflow + macos-shellcheck + macos-stow + macos-sublime + macos-terminal + macos-touch-id-sudo + macos-tower + macos-vscode + macos-zsh + micro + nano + python + stow + zsh + run: | + # Get dynamic features from previous job (may be empty) + dynamic_features='${{ needs.generate-matrix.outputs.features }}' + fixed_features=$(printf '%s\n' \"$FIXED_FEATURES\" | jq -R -s -c 'split(\"\\n\") | map(select(length>0))') + + # Merge and deduplicate features + merged_features=$(jq -s 'add | unique' <(echo "$dynamic_features") <(echo "$fixed_features")) + + echo "Final features matrix:" + echo "$merged_features" + + echo "features=$merged_features" >> "$GITHUB_OUTPUT" + macOS: runs-on: macos-latest + needs: add-fixed-features strategy: fail-fast: false matrix: - features: [ - "ansible", - "clean", - "cli-network", - "core", - "git", - "javascript", - # "macos-alfred", # Script error to be fixed - # "macos-app-dev", # Depends on `git` - # "macos-app-private", # Depends on `git` - # "macos-app-work", # Depends on `git` - "macos-appcleaner", - "macos-brew", - "macos-c-cpp", - "macos-clean", - # "macos-cleanshot", # Script error to be fixed - "macos-cli-network", - "macos-cli-useful", - "macos-core", - "macos-docker", - "macos-dropbox", - "macos-duti", - "macos-file-handler", - "macos-finder", - "macos-font", - "macos-git", - "macos-hammerspoon", - "macos-icon", - "macos-iterm", - "macos-javascript", - "macos-markedit", - "macos-micro", - "macos-nano", - # "macos-one-password", # mas can't run without login on GitHub Actions - # "macos-perl", # Script error to be fixed - "macos-popclip", - "macos-quicklook", - "macos-screensaver", - "macos-service-workflow", - # "macos-settings", # Script error to be fixed - "macos-shellcheck", - "macos-stow", - "macos-sublime", - "macos-terminal", - "macos-touch-id-sudo", - "macos-tower", - "macos-vscode", - # "macos-xcode", # mas can't run without login on GitHub Actions - "macos-zsh", - "micro", - "nano", - "python", - "stow", - "zsh", - ] + features: ${{ fromJson(needs.add-fixed-features.outputs.features) }} steps: - name: 🛒 Checkout uses: actions/checkout@v4 From 50efcbc345bb17b8b1e73cdbd9a5946da8889beb Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 20:31:05 -0800 Subject: [PATCH 03/13] YOLO --- .github/scripts/affected-list.sh | 7 ++ .github/scripts/changed-files.sh | 49 +++++++++++ .github/scripts/feature-list.sh | 32 +++++++ .github/scripts/merge-features.sh | 51 ++++++++++++ .github/workflows/test.yml | 23 ----- .github/workflows/test_macos.yml | 134 ++++-------------------------- .github/workflows/test_ubuntu.yml | 37 ++++----- 7 files changed, 172 insertions(+), 161 deletions(-) create mode 100644 .github/scripts/affected-list.sh create mode 100755 .github/scripts/changed-files.sh create mode 100755 .github/scripts/feature-list.sh create mode 100755 .github/scripts/merge-features.sh delete mode 100644 .github/workflows/test.yml diff --git a/.github/scripts/affected-list.sh b/.github/scripts/affected-list.sh new file mode 100644 index 0000000..ef3c7c2 --- /dev/null +++ b/.github/scripts/affected-list.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# .github/scripts/affected-list.sh +# infer all affected features based on a list of changed features + +set -euo pipefail + +# TODO: For now we just output passed in features diff --git a/.github/scripts/changed-files.sh b/.github/scripts/changed-files.sh new file mode 100755 index 0000000..c679666 --- /dev/null +++ b/.github/scripts/changed-files.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# .github/scripts/changed-files.sh +# Outputs changed files between base and head commits for GitHub Actions + +set -euo pipefail + +# Read GitHub context from first argument +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +GITHUB_CONTEXT="$1" + +# Parse event information using jq +event_name=$(echo "$GITHUB_CONTEXT" | jq -r '.event_name') + +# Determine base and head commits based on event type +if [ "$event_name" = "pull_request" ]; then + base=$(echo "$GITHUB_CONTEXT" | jq -r '.event.pull_request.base.sha') + head=$(echo "$GITHUB_CONTEXT" | jq -r '.event.pull_request.head.sha') +elif [ "$event_name" = "push" ]; then + base=$(echo "$GITHUB_CONTEXT" | jq -r '.event.before') + head=$(echo "$GITHUB_CONTEXT" | jq -r '.sha') +else + echo "Unsupported event: $event_name" >&2 + base="" + head="" +fi + +# Check if we have valid base and head commits +if [ -z "$base" ] || [ -z "$head" ] || [ "$base" = "null" ] || [ "$head" = "null" ]; then + echo "No base/head to diff; leaving paths empty." >&2 + echo "paths=" >> "$GITHUB_OUTPUT" + exit 0 +fi + +# Get changed files using git diff +files=$(git diff --name-only "$base" "$head" || true) + +echo "Changed files:" >&2 +echo "$files" >&2 + +# Output to GitHub Actions +{ + echo 'paths<> "$GITHUB_OUTPUT" diff --git a/.github/scripts/feature-list.sh b/.github/scripts/feature-list.sh new file mode 100755 index 0000000..eed4d23 --- /dev/null +++ b/.github/scripts/feature-list.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# .github/scripts/feature-list.sh +# Generates feature list from changed file paths + +set -euo pipefail + +# Read changed paths from first argument +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +changed_paths="$1" + +# Extract feature names for any changed file under pkg/some-feature/* +changed_features=$( + printf '%s\n' "$changed_paths" | + sed -n 's|^pkg/\([^/]*\).*|\1|p' | + sort -u +) + +echo "Changed features from pkg/:" >&2 +echo "$changed_features" >&2 + +# Convert to JSON array +features_json=$( + printf '%s\n' "$changed_features" | + jq -R -s -c 'split("\n") | map(select(length>0))' +) + +# Output to GitHub Actions +echo "features=$features_json" >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/merge-features.sh b/.github/scripts/merge-features.sh new file mode 100755 index 0000000..d55d1e0 --- /dev/null +++ b/.github/scripts/merge-features.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# .github/scripts/merge-features.sh +# Merge passed in features with a list of fixed features + +set -euo pipefail + +FIXED_FEATURES=( + ansible + clean + cli-network + core + git + javascript + macos-appcleaner + macos-brew + macos-c-cpp + macos-clean + macos-cli-network + macos-cli-useful + macos-core + macos-docker + macos-duti + macos-file-handler + macos-finder + macos-font + macos-git + macos-hammerspoon + macos-icon + macos-iterm + macos-javascript + macos-markedit + macos-micro + macos-nano + macos-popclip + macos-quicklook + macos-screensaver + macos-service-workflow + macos-shellcheck + macos-stow + macos-sublime + macos-terminal + macos-touch-id-sudo + macos-tower + macos-vscode + macos-zsh + micro + nano + python + stow + zsh +) \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 30861d2..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Test macOS features - -on: - push: - branches: - - main - paths-ignore: - - 'docs/**' - pull_request: - -jobs: - macOS: - runs-on: macos-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - - name: Dump GitHub event context - env: - EVENT_CONTEXT: ${{ toJson(github.event) }} - run: echo "$EVENT_CONTEXT" diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index 94c078a..3e9b28b 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -11,143 +11,39 @@ on: jobs: generate-matrix: - name: Generate matrix + name: 🌀 Generate matrix runs-on: ubuntu-latest outputs: - features: ${{ steps.build-matrix.outputs.features }} + features: ${{ steps.merge-features.outputs.features }} steps: - name: 🛒 Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - - name: 📄 Get list of changed files + - name: 📄 List changed files id: changed-files - run: | - # Determine base and head commits using GitHub Actions contexts - if [ "${{ github.event_name }}" = "pull_request" ]; then - base="${{ github.event.pull_request.base.sha }}" - head="${{ github.event.pull_request.head.sha }}" - elif [ "${{ github.event_name }}" = "push" ]; then - base="${{ github.event.before }}" - head="${{ github.sha }}" - else - echo "Unsupported event: ${{ github.event_name }}" - base="" - head="" - fi + run: .github/scripts/changed-files.sh '${{ toJson(github) }}' - if [ -z "$base" ] || [ -z "$head" ]; then - echo "No base/head to diff; leaving paths empty." - echo "paths=" >> "$GITHUB_OUTPUT" - exit 0 - fi + - name: 🧮 Generate feature list + id: feature-list + run: .github/scripts/feature-list.sh '${{ steps.changed-files.outputs.paths }}' - files=$(git diff --name-only "$base" "$head" || true) + - name: 📊 Generate affected feature list + id: affected-list + run: ./github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' - echo "Changed files:" - echo "$files" - - { - echo 'paths<> "$GITHUB_OUTPUT" - - - name: 🧮 Generate feature matrix - id: build-matrix - run: | - # Extract feature names for any changed file under pkg/some-feature/* - changed_paths="${{ steps.changed-files.outputs.paths }}" - changed_features=$( - printf '%s\n' "$changed_paths" | - sed -n 's|^pkg/\([^/]*\).*|\1|p' | - sort -u - ) - - echo "Changed features from pkg/:" - echo "$changed_features" - - features_json=$( - printf '%s\n' "$changed_features" | - jq -R -s -c 'split("\n") | map(select(length>0))' - ) - - echo "features=$features_json" >> "$GITHUB_OUTPUT" - - add-fixed-features: - runs-on: ubuntu-latest - needs: generate-matrix - outputs: - features: ${{ steps.final-matrix.outputs.features }} - steps: - - name: Add fixed features - id: final-matrix - env: - FIXED_FEATURES: | - ansible - clean - cli-network - core - git - javascript - macos-appcleaner - macos-brew - macos-c-cpp - macos-clean - macos-cli-network - macos-cli-useful - macos-core - macos-docker - macos-duti - macos-file-handler - macos-finder - macos-font - macos-git - macos-hammerspoon - macos-icon - macos-iterm - macos-javascript - macos-markedit - macos-micro - macos-nano - macos-popclip - macos-quicklook - macos-screensaver - macos-service-workflow - macos-shellcheck - macos-stow - macos-sublime - macos-terminal - macos-touch-id-sudo - macos-tower - macos-vscode - macos-zsh - micro - nano - python - stow - zsh - run: | - # Get dynamic features from previous job (may be empty) - dynamic_features='${{ needs.generate-matrix.outputs.features }}' - fixed_features=$(printf '%s\n' \"$FIXED_FEATURES\" | jq -R -s -c 'split(\"\\n\") | map(select(length>0))') - - # Merge and deduplicate features - merged_features=$(jq -s 'add | unique' <(echo "$dynamic_features") <(echo "$fixed_features")) - - echo "Final features matrix:" - echo "$merged_features" - - echo "features=$merged_features" >> "$GITHUB_OUTPUT" + - name: 🔀 Merge with fixed features + id: merge-features + run: ./github/scripts/merge-features.sh '${{ steps.affected-list.outputs.features }}' macOS: runs-on: macos-latest - needs: add-fixed-features + needs: generate-matrix strategy: fail-fast: false matrix: - features: ${{ fromJson(needs.add-fixed-features.outputs.features) }} + features: ${{ fromJson(needs.generate-matrix.outputs.features) }} steps: - name: 🛒 Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/test_ubuntu.yml b/.github/workflows/test_ubuntu.yml index 70e5905..2c13232 100644 --- a/.github/workflows/test_ubuntu.yml +++ b/.github/workflows/test_ubuntu.yml @@ -5,10 +5,10 @@ on: branches: - main paths-ignore: - - 'docs/**' + - "docs/**" pull_request: schedule: - - cron: '0 2 * * *' + - cron: "0 2 * * *" jobs: Ubuntu: @@ -17,24 +17,23 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04, ubuntu-24.04, ubuntu-latest] - features: - [ - "ansible", - "clean", - "cli-network", + features: [ + # "ansible", + # "clean", + # "cli-network", "core", - "git", - "javascript", - "micro", - "nano", - "python", - "stow", - "ubuntu-dropbox", - "ubuntu-essential", - "ubuntu-locale-zhtw", - "ubuntu-ssh", - "ubuntu-tz-taipei", - "zsh", + # "git", + # "javascript", + # "micro", + # "nano", + # "python", + # "stow", + # "ubuntu-dropbox", + # "ubuntu-essential", + # "ubuntu-locale-zhtw", + # "ubuntu-ssh", + # "ubuntu-tz-taipei", + # "zsh", ] steps: - name: 🛒 Checkout From cff7bb3122ffb8fbb1631d3a4384937e1537be9e Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 20:55:56 -0800 Subject: [PATCH 04/13] more script --- .github/scripts/affected-list.sh | 64 ++++++++++++++++++++++++++++++- .github/scripts/feature-list.sh | 2 +- .github/scripts/merge-features.sh | 33 +++++++++++++++- 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/.github/scripts/affected-list.sh b/.github/scripts/affected-list.sh index ef3c7c2..cbee9dc 100644 --- a/.github/scripts/affected-list.sh +++ b/.github/scripts/affected-list.sh @@ -4,4 +4,66 @@ set -euo pipefail -# TODO: For now we just output passed in features +# Read changed features JSON array from first argument +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +features_json="$1" + +if [ -z "$features_json" ] || [ "$features_json" = "null" ]; then + features_json="[]" +fi + +if ! echo "$features_json" | jq -e '.' >/dev/null 2>&1; then + echo "Invalid JSON for changed features." >&2 + exit 1 +fi + +# mapfile -t changed_features < <(echo "$features_json" | jq -r '.[]') + +# declare -A seen +# affected_features=() + +# add_feature() { +# local feature="$1" +# if [ -n "$feature" ] && [ -z "${seen[$feature]+x}" ]; then +# affected_features+=("$feature") +# seen["$feature"]=1 +# fi +# } + +# for feature in "${changed_features[@]}"; do +# add_feature "$feature" +# case "$feature" in +# brew) +# add_feature "macos-brew" +# ;; +# shellcheck) +# add_feature "macos-shellcheck" +# ;; +# sublime) +# add_feature "macos-sublime" +# ;; +# vscode) +# add_feature "macos-vscode" +# ;; +# perl) +# add_feature "macos-perl" +# ;; +# macos-services) +# add_feature "macos-service-workflow" +# ;; +# esac +# done + +# echo "Affected features:" >&2 +# printf '%s\n' "${affected_features[@]}" >&2 + +# features_json_out=$( +# printf '%s\n' "${affected_features[@]}" | +# jq -R -s -c 'split("\n") | map(select(length>0))' +# ) + +echo "features=$features_json" >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/feature-list.sh b/.github/scripts/feature-list.sh index eed4d23..cd989cb 100755 --- a/.github/scripts/feature-list.sh +++ b/.github/scripts/feature-list.sh @@ -19,7 +19,7 @@ changed_features=$( sort -u ) -echo "Changed features from pkg/:" >&2 +echo "Changed features:" >&2 echo "$changed_features" >&2 # Convert to JSON array diff --git a/.github/scripts/merge-features.sh b/.github/scripts/merge-features.sh index d55d1e0..c3292de 100755 --- a/.github/scripts/merge-features.sh +++ b/.github/scripts/merge-features.sh @@ -48,4 +48,35 @@ FIXED_FEATURES=( python stow zsh -) \ No newline at end of file +) + +if [ $# -lt 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +features_json="$1" + +if [ -z "$features_json" ] || [ "$features_json" = "null" ]; then + features_json="[]" +fi + +if ! echo "$features_json" | jq -e '.' >/dev/null 2>&1; then + echo "Invalid JSON for features." >&2 + exit 1 +fi + +fixed_json=$( + printf '%s\n' "${FIXED_FEATURES[@]}" | + jq -R -s -c 'split("\n") | map(select(length>0))' +) + +merged_json=$( + echo "$features_json" | + jq -c --argjson fixed "$fixed_json" '. + $fixed | sort | unique' +) + +echo "Merged features:" >&2 +echo "$merged_json" | jq -r '.[]' >&2 + +echo "features=$merged_json" >> "$GITHUB_OUTPUT" From 5fdc7eaf9ff8297d3d621a4cf8a5ba5e2992d67f Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 20:56:59 -0800 Subject: [PATCH 05/13] fix path --- .github/workflows/test_macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index 3e9b28b..8721936 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -31,11 +31,11 @@ jobs: - name: 📊 Generate affected feature list id: affected-list - run: ./github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' + run: .github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' - name: 🔀 Merge with fixed features id: merge-features - run: ./github/scripts/merge-features.sh '${{ steps.affected-list.outputs.features }}' + run: .github/scripts/merge-features.sh '${{ steps.affected-list.outputs.features }}' macOS: runs-on: macos-latest From 7f3eb192fd684e7c62c99cd73a707fcf09a38765 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 20:57:30 -0800 Subject: [PATCH 06/13] +x --- .github/scripts/affected-list.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/affected-list.sh diff --git a/.github/scripts/affected-list.sh b/.github/scripts/affected-list.sh old mode 100644 new mode 100755 From 993dea93efdc90157ab86fd09b28319487862450 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 21:12:27 -0800 Subject: [PATCH 07/13] test python --- .github/workflows/test_macos.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index 8721936..5a29583 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -31,13 +31,19 @@ jobs: - name: 📊 Generate affected feature list id: affected-list - run: .github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' + # run: .github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' + run: | + which python + which python3 + python --version + python3 --version - name: 🔀 Merge with fixed features id: merge-features run: .github/scripts/merge-features.sh '${{ steps.affected-list.outputs.features }}' - macOS: + macos: + name: 🖥️ macOS Test runs-on: macos-latest needs: generate-matrix strategy: From 8167b35bb1539b54b0637a64b0441dc55188b18f Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 22:37:58 -0800 Subject: [PATCH 08/13] let's see..... --- .github/scripts/affected-list.py | 77 ++++++++++++++++++++++++++++++++ .github/scripts/affected-list.sh | 69 ---------------------------- .github/scripts/all-features.sh | 60 +++++++++++++++++++++++++ .github/workflows/test_macos.yml | 11 +++-- 4 files changed, 142 insertions(+), 75 deletions(-) create mode 100755 .github/scripts/affected-list.py delete mode 100755 .github/scripts/affected-list.sh create mode 100755 .github/scripts/all-features.sh diff --git a/.github/scripts/affected-list.py b/.github/scripts/affected-list.py new file mode 100755 index 0000000..821cbee --- /dev/null +++ b/.github/scripts/affected-list.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# .github/scripts/affected-list.py 'changed_features_json' 'all_features_json' +# Infer all affected features based on a list of changed features + +import json +import os +import sys +from collections import defaultdict + + +# A helper function to recursively collect all dependents of a feature +def collect_dependents( + dependency_map: dict[str, set[str]], feature: str, visited: set[str] +) -> set[str]: + for dependent in dependency_map[feature]: + if dependent not in visited: + visited.add(dependent) + collect_dependents(dependency_map, dependent, visited) + return visited + + +# Build a dependency map from all features +# Format: {feature_a: [feature_b_depends_on_a, feature_c_depends_on_a, ...]} +def parse_dependencies(all_features: dict[str, list[str]]) -> dict[str, set[str]]: + dependency_map: dict[str, set[str]] = defaultdict(set) + + # Convert dependencies to a reverse map + for feature, dependencies in all_features.items(): + for dep in dependencies: + dependency_map[dep].add(feature) + + # Flatten trees of dependencies + # E.g., if A -> B and B -> C, then A -> B,C + flattened: dict[str, set[str]] = defaultdict(set) + for feature in all_features: + flattened[feature] = collect_dependents(dependency_map, feature, set()) + + return flattened + + +def infer_dependencies( + changed_features: list[str], dependency_map: dict[str, set[str]] +) -> list[str]: + affected_features: set[str] = set(changed_features) + for feature in changed_features: + affected_features.update(dependency_map[feature]) + return sorted(affected_features) + + +def output_to_github_actions(affected_features: list[str]) -> None: + github_output_path = os.getenv("GITHUB_OUTPUT") + if github_output_path: + with open(github_output_path, "a") as gh_out: + json.dump(affected_features, gh_out) + gh_out.write("\n") + + +def main() -> None: + # Parse input arguments + changed_features = list(json.loads(sys.argv[1])) + all_features = json.loads(sys.argv[2]) + + # Build the dependency map + dependency_map = parse_dependencies(all_features) + + # Infer affected features + affected_features = infer_dependencies(changed_features, dependency_map) + + print("Affected features:") + print(json.dumps(affected_features)) + + # Output to GitHub Actions + output_to_github_actions(affected_features) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/affected-list.sh b/.github/scripts/affected-list.sh deleted file mode 100755 index cbee9dc..0000000 --- a/.github/scripts/affected-list.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# .github/scripts/affected-list.sh -# infer all affected features based on a list of changed features - -set -euo pipefail - -# Read changed features JSON array from first argument -if [ $# -lt 1 ]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -features_json="$1" - -if [ -z "$features_json" ] || [ "$features_json" = "null" ]; then - features_json="[]" -fi - -if ! echo "$features_json" | jq -e '.' >/dev/null 2>&1; then - echo "Invalid JSON for changed features." >&2 - exit 1 -fi - -# mapfile -t changed_features < <(echo "$features_json" | jq -r '.[]') - -# declare -A seen -# affected_features=() - -# add_feature() { -# local feature="$1" -# if [ -n "$feature" ] && [ -z "${seen[$feature]+x}" ]; then -# affected_features+=("$feature") -# seen["$feature"]=1 -# fi -# } - -# for feature in "${changed_features[@]}"; do -# add_feature "$feature" -# case "$feature" in -# brew) -# add_feature "macos-brew" -# ;; -# shellcheck) -# add_feature "macos-shellcheck" -# ;; -# sublime) -# add_feature "macos-sublime" -# ;; -# vscode) -# add_feature "macos-vscode" -# ;; -# perl) -# add_feature "macos-perl" -# ;; -# macos-services) -# add_feature "macos-service-workflow" -# ;; -# esac -# done - -# echo "Affected features:" >&2 -# printf '%s\n' "${affected_features[@]}" >&2 - -# features_json_out=$( -# printf '%s\n' "${affected_features[@]}" | -# jq -R -s -c 'split("\n") | map(select(length>0))' -# ) - -echo "features=$features_json" >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/all-features.sh b/.github/scripts/all-features.sh new file mode 100755 index 0000000..5f4101f --- /dev/null +++ b/.github/scripts/all-features.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# .github/scripts/all-features.sh +# Outputs all features and their dependencies from Makefile + +set -euo pipefail + +# Extract features and their dependencies from Makefile +features_with_deps=$( + { make -prRn -f Makefile : 2>/dev/null || true; } | + awk ' + function trim(s) { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", s) + return s + } + function is_valid(name) { + return !(name == "" || name ~ /^\.|%/ || name ~ /^\//) + } + $0 == "# Files" { in_files = 1; next } + $0 ~ /^# files hash-table stats:/ { in_files = 0 } + !in_files { next } + $0 ~ /^# Not a target:/ { skip_next = 1; next } + /^[^#[:space:]].*:/ { + line = $0 + if (skip_next) { skip_next = 0; next } + split(line, parts, ":") + name = trim(parts[1]) + if (!is_valid(name)) { next } + deps = trim(substr(line, index(line, ":") + 1)) + gsub(/[[:space:]]+/, " ", deps) + if (deps == "") { + print name + next + } + print name " " deps + } + ' | + sort -u +) + +# Convert to JSON +features_json=$( + printf '%s\n' "$features_with_deps" | + jq -R -s -c ' + split("\n") + | map(select(length>0)) + | map( + split(" ") + | { (.[0]): (.[1:] | map(select(length>0))) } + ) + | add + ' +) + +echo "All features and dependencies:" >&2 +echo "$features_with_deps" >&2 + +# Output to GitHub Actions +echo "features=$features_json" >> "$GITHUB_OUTPUT" + +echo $features_json diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index 5a29583..b6a9096 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -21,6 +21,10 @@ jobs: with: fetch-depth: 0 + - name: 🗂️ Get all features + id: all-features + run: .github/scripts/all-features.sh + - name: 📄 List changed files id: changed-files run: .github/scripts/changed-files.sh '${{ toJson(github) }}' @@ -31,12 +35,7 @@ jobs: - name: 📊 Generate affected feature list id: affected-list - # run: .github/scripts/affected-list.sh '${{ steps.feature-list.outputs.features }}' - run: | - which python - which python3 - python --version - python3 --version + run: .github/scripts/affected-list.py '${{ steps.feature-list.outputs.features }}' '${{ steps.all-features.outputs.features }}' - name: 🔀 Merge with fixed features id: merge-features From 1ba48f2fb7a1651f44a696c0773a293c117735df Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 22:47:10 -0800 Subject: [PATCH 09/13] python fix --- .github/scripts/affected-list.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/scripts/affected-list.py b/.github/scripts/affected-list.py index 821cbee..f42913b 100755 --- a/.github/scripts/affected-list.py +++ b/.github/scripts/affected-list.py @@ -51,8 +51,7 @@ def output_to_github_actions(affected_features: list[str]) -> None: github_output_path = os.getenv("GITHUB_OUTPUT") if github_output_path: with open(github_output_path, "a") as gh_out: - json.dump(affected_features, gh_out) - gh_out.write("\n") + gh_out.write(f"affected_features={json.dumps(affected_features)}\n") def main() -> None: From 0e4a48068470b37366bc8334b7159421d15fbec0 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 23:15:37 -0800 Subject: [PATCH 10/13] test lookup --- .github/scripts/affected-list.py | 9 +++- .github/scripts/merge-features.sh | 78 ++++++++++++++----------------- pkg/macos-appcleaner/test | 0 3 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 pkg/macos-appcleaner/test diff --git a/.github/scripts/affected-list.py b/.github/scripts/affected-list.py index f42913b..6c31ff6 100755 --- a/.github/scripts/affected-list.py +++ b/.github/scripts/affected-list.py @@ -1,6 +1,13 @@ #!/usr/bin/env python -# .github/scripts/affected-list.py 'changed_features_json' 'all_features_json' # Infer all affected features based on a list of changed features +# +# Usage in GitHub Actions: +# +# .github/scripts/affected-list.py 'changed_features_json' 'all_features_json' +# +# Usage in terminal: +# +# .github/scripts/affected-list.py '["macos-brew"]' "$(GITHUB_OUTPUT=/dev/null .github/scripts/all-features.sh)" import json import os diff --git a/.github/scripts/merge-features.sh b/.github/scripts/merge-features.sh index c3292de..7271598 100755 --- a/.github/scripts/merge-features.sh +++ b/.github/scripts/merge-features.sh @@ -5,49 +5,43 @@ set -euo pipefail FIXED_FEATURES=( - ansible - clean - cli-network - core + # ansible + # clean + # cli-network + # core git - javascript - macos-appcleaner - macos-brew - macos-c-cpp - macos-clean - macos-cli-network - macos-cli-useful - macos-core - macos-docker - macos-duti - macos-file-handler - macos-finder - macos-font - macos-git - macos-hammerspoon - macos-icon - macos-iterm - macos-javascript - macos-markedit - macos-micro - macos-nano - macos-popclip - macos-quicklook - macos-screensaver - macos-service-workflow - macos-shellcheck - macos-stow - macos-sublime - macos-terminal - macos-touch-id-sudo - macos-tower - macos-vscode - macos-zsh - micro - nano - python - stow - zsh + # javascript + # macos-brew + # macos-c-cpp + # macos-clean + # macos-cli-network + # macos-cli-useful + # macos-core + # macos-docker + # macos-duti + # macos-file-handler + # macos-finder + # macos-git + # macos-icon + # macos-javascript + # macos-markedit + # macos-micro + # macos-nano + # macos-popclip + # macos-quicklook + # macos-screensaver + # macos-shellcheck + # macos-stow + # macos-sublime + # macos-terminal + # macos-touch-id-sudo + # macos-vscode + # macos-zsh + # micro + # nano + # python + # stow + # zsh ) if [ $# -lt 1 ]; then diff --git a/pkg/macos-appcleaner/test b/pkg/macos-appcleaner/test new file mode 100644 index 0000000..e69de29 From 3750ced8f7a416305737e611fae4701700178af4 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Mon, 2 Feb 2026 23:22:37 -0800 Subject: [PATCH 11/13] fix var name --- .github/scripts/affected-list.py | 2 +- .github/workflows/test_macos.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/scripts/affected-list.py b/.github/scripts/affected-list.py index 6c31ff6..2123d61 100755 --- a/.github/scripts/affected-list.py +++ b/.github/scripts/affected-list.py @@ -58,7 +58,7 @@ def output_to_github_actions(affected_features: list[str]) -> None: github_output_path = os.getenv("GITHUB_OUTPUT") if github_output_path: with open(github_output_path, "a") as gh_out: - gh_out.write(f"affected_features={json.dumps(affected_features)}\n") + gh_out.write(f"features={json.dumps(affected_features)}\n") def main() -> None: diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index b6a9096..2b7d2d8 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -12,7 +12,7 @@ on: jobs: generate-matrix: name: 🌀 Generate matrix - runs-on: ubuntu-latest + runs-on: macos-latest outputs: features: ${{ steps.merge-features.outputs.features }} steps: @@ -37,6 +37,7 @@ jobs: id: affected-list run: .github/scripts/affected-list.py '${{ steps.feature-list.outputs.features }}' '${{ steps.all-features.outputs.features }}' + # TODO: Remove this step when all features are moved under pkg/ - name: 🔀 Merge with fixed features id: merge-features run: .github/scripts/merge-features.sh '${{ steps.affected-list.outputs.features }}' From d7589b8f284405c206c050f620ee976a4ed89119 Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Tue, 3 Feb 2026 00:47:20 -0800 Subject: [PATCH 12/13] rename --- Makefile | 1 + makefiles/macos.mk | 2 +- makefiles/ubuntu.mk | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 01b5733..3d00b06 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ git: $(OS)-git micro: $(OS)-micro nano: $(OS)-nano stow: $(OS)-stow +vscode: $(OS)-vscode zsh: $(OS)-zsh ansible: stow diff --git a/makefiles/macos.mk b/makefiles/macos.mk index ce11272..267b357 100644 --- a/makefiles/macos.mk +++ b/makefiles/macos.mk @@ -153,7 +153,7 @@ macos-quicklook: macos-brew macos-screensaver: macos-brew run "pkg/macos-screensaver/macos-screensaver.sh" -macos-service-workflow: macos-stow +macos-services: macos-stow stow --dir 'pkg' --target "${HOME}/Library/Services" 'macos-services' macos-settings: diff --git a/makefiles/ubuntu.mk b/makefiles/ubuntu.mk index 05a892e..90a66cb 100644 --- a/makefiles/ubuntu.mk +++ b/makefiles/ubuntu.mk @@ -32,6 +32,9 @@ ubuntu-cli-network: ubuntu-dropbox: ubuntu-essential run "pkg/dropbox/ubuntu-dropbox.sh" +ubuntu-vscode: + echo "TODO" + ############################################################################### # CLIs # ############################################################################### From d3105ad0c1c1a233b91426c929c6b94b6c2f0b7c Mon Sep 17 00:00:00 2001 From: Tomy Hsieh Date: Fri, 6 Feb 2026 01:56:57 -0800 Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=9B=A0=20fix:=20brewfile=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/macos-xcode/macos-xcode.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/macos-xcode/macos-xcode.sh b/pkg/macos-xcode/macos-xcode.sh index 6647ef5..408028d 100755 --- a/pkg/macos-xcode/macos-xcode.sh +++ b/pkg/macos-xcode/macos-xcode.sh @@ -2,4 +2,4 @@ set -euo pipefail # Install Xcode -brew bundle --file pkg/xcode/macos-xcode.Brewfile +brew bundle --file pkg/macos-xcode/xcode.Brewfile