|
| 1 | +name: Generate baseline |
| 2 | +description: > |
| 3 | + Build a srcML baseline archive for a given (language, project) pair, |
| 4 | + publish it as a GitHub Release, and open a PR updating the per-project |
| 5 | + current_baseline_release pointer in config/<language>.toml. |
| 6 | +
|
| 7 | +inputs: |
| 8 | + language: |
| 9 | + description: "Language key — must match a config/<language>.toml file (e.g. c, cpp, csharp, java, python)." |
| 10 | + required: true |
| 11 | + project: |
| 12 | + description: "Project name — must match a [[projects]].name entry in config/<language>.toml." |
| 13 | + required: true |
| 14 | + srcml_version_override: |
| 15 | + description: "Override srcml release version (default: read from config)." |
| 16 | + required: false |
| 17 | + default: "" |
| 18 | + project_tag_override: |
| 19 | + description: "Override the project's VCS tag (default: read from config)." |
| 20 | + required: false |
| 21 | + default: "" |
| 22 | + dry_run: |
| 23 | + description: "If 'true', build the archive and upload it as a workflow artifact without creating a release or opening a PR." |
| 24 | + required: false |
| 25 | + default: "false" |
| 26 | + github_token: |
| 27 | + description: "Token used to publish the release and open the pointer PR. Defaults to GITHUB_TOKEN of the calling workflow." |
| 28 | + required: false |
| 29 | + default: ${{ github.token }} |
| 30 | + |
| 31 | +runs: |
| 32 | + using: composite |
| 33 | + steps: |
| 34 | + - name: Install host deps |
| 35 | + shell: bash |
| 36 | + run: | |
| 37 | + sudo apt-get update |
| 38 | + sudo apt-get install -y zstd git curl ca-certificates jq |
| 39 | +
|
| 40 | + - name: Install mikefarah yq |
| 41 | + shell: bash |
| 42 | + run: | |
| 43 | + sudo curl -fsSL -o /usr/local/bin/yq \ |
| 44 | + https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 |
| 45 | + sudo chmod +x /usr/local/bin/yq |
| 46 | + yq --version |
| 47 | +
|
| 48 | + - name: Resolve config |
| 49 | + id: cfg |
| 50 | + shell: bash |
| 51 | + env: |
| 52 | + LANGUAGE: ${{ inputs.language }} |
| 53 | + PROJECT: ${{ inputs.project }} |
| 54 | + SV_OVERRIDE: ${{ inputs.srcml_version_override }} |
| 55 | + PT_OVERRIDE: ${{ inputs.project_tag_override }} |
| 56 | + run: | |
| 57 | + set -euo pipefail |
| 58 | + CFG="config/${LANGUAGE}.toml" |
| 59 | + if [ ! -f "$CFG" ]; then |
| 60 | + echo "::error::config file not found: $CFG" |
| 61 | + exit 1 |
| 62 | + fi |
| 63 | +
|
| 64 | + NAMES="$(yq -p toml -r '.projects[].name' "$CFG")" |
| 65 | + if ! grep -Fxq "$PROJECT" <<<"$NAMES"; then |
| 66 | + echo "::error::project '$PROJECT' is not declared under .projects in $CFG" |
| 67 | + echo "available projects:" |
| 68 | + printf ' - %s\n' $NAMES |
| 69 | + exit 1 |
| 70 | + fi |
| 71 | +
|
| 72 | + SV="$(yq -p toml '.srcml.version' "$CFG")" |
| 73 | + OS="$(lsb_release -rs)" |
| 74 | + PR="$(PROJECT="$PROJECT" yq -p toml '.projects[] | select(.name == strenv(PROJECT)) | .repo' "$CFG")" |
| 75 | + PT="$(PROJECT="$PROJECT" yq -p toml '.projects[] | select(.name == strenv(PROJECT)) | .tag' "$CFG")" |
| 76 | + SUBDIR="$(PROJECT="$PROJECT" yq -p toml '.projects[] | select(.name == strenv(PROJECT)) | .subdir // ""' "$CFG")" |
| 77 | +
|
| 78 | + # Flag precedence: language default + per-project append. |
| 79 | + LANG_FLAGS="$(yq -p toml -r '.srcml_flags | join(" ")' "$CFG")" |
| 80 | + PROJ_FLAGS="$(PROJECT="$PROJECT" yq -p toml -r '.projects[] | select(.name == strenv(PROJECT)) | (.srcml_flags_append // []) | join(" ")' "$CFG")" |
| 81 | + FLAGS="$LANG_FLAGS" |
| 82 | + [ -n "$PROJ_FLAGS" ] && FLAGS="$FLAGS $PROJ_FLAGS" |
| 83 | +
|
| 84 | + # dispatch overrides |
| 85 | + [ -n "$SV_OVERRIDE" ] && SV="$SV_OVERRIDE" |
| 86 | + [ -n "$PT_OVERRIDE" ] && PT="$PT_OVERRIDE" |
| 87 | +
|
| 88 | + TAG="baseline-${LANGUAGE}-${PROJECT}-${PT}-srcml-${SV}" |
| 89 | +
|
| 90 | + { |
| 91 | + echo "srcml_version=$SV" |
| 92 | + echo "srcml_os=$OS" |
| 93 | + echo "project_repo=$PR" |
| 94 | + echo "project_tag=$PT" |
| 95 | + echo "project_subdir=$SUBDIR" |
| 96 | + echo "srcml_flags=$FLAGS" |
| 97 | + echo "release_tag=$TAG" |
| 98 | + echo "xml_name=${TAG}.xml" |
| 99 | + echo "zst_name=${TAG}.xml.zst" |
| 100 | + echo "manifest_name=${TAG}.manifest.json" |
| 101 | + echo "log_name=${TAG}.generate.log" |
| 102 | + echo "config_path=$CFG" |
| 103 | + } >> "$GITHUB_OUTPUT" |
| 104 | +
|
| 105 | + - name: Install pinned srcml release |
| 106 | + shell: bash |
| 107 | + env: |
| 108 | + SV: ${{ steps.cfg.outputs.srcml_version }} |
| 109 | + OS: ${{ steps.cfg.outputs.srcml_os }} |
| 110 | + run: | |
| 111 | + set -euxo pipefail |
| 112 | + URL="https://github.com/srcML/srcML/releases/download/v${SV}/srcml_${SV}-1_ubuntu${OS}_amd64.deb" |
| 113 | + curl -fsSL -o srcml.deb "$URL" |
| 114 | + sudo apt-get install -y ./srcml.deb |
| 115 | + srcml --version |
| 116 | +
|
| 117 | + - name: Clone project source |
| 118 | + shell: bash |
| 119 | + env: |
| 120 | + PR: ${{ steps.cfg.outputs.project_repo }} |
| 121 | + PT: ${{ steps.cfg.outputs.project_tag }} |
| 122 | + run: | |
| 123 | + set -euxo pipefail |
| 124 | + git clone --depth 1 --branch "$PT" "$PR" project-src |
| 125 | +
|
| 126 | + - name: Generate baseline archive |
| 127 | + id: gen |
| 128 | + shell: bash |
| 129 | + env: |
| 130 | + SRCML_BIN: srcml |
| 131 | + SRCML_FLAGS: ${{ steps.cfg.outputs.srcml_flags }} |
| 132 | + SUBDIR: ${{ steps.cfg.outputs.project_subdir }} |
| 133 | + OUTPUT_XML: ${{ steps.cfg.outputs.xml_name }} |
| 134 | + OUTPUT_ZST: ${{ steps.cfg.outputs.zst_name }} |
| 135 | + LOG_FILE: ${{ steps.cfg.outputs.log_name }} |
| 136 | + run: | |
| 137 | + set -euo pipefail |
| 138 | + if [ -n "$SUBDIR" ]; then |
| 139 | + INPUT_DIR="project-src/$SUBDIR" |
| 140 | + if [ ! -d "$INPUT_DIR" ]; then |
| 141 | + echo "::error::configured subdir '$SUBDIR' does not exist under the cloned project" |
| 142 | + exit 1 |
| 143 | + fi |
| 144 | + else |
| 145 | + INPUT_DIR="project-src" |
| 146 | + fi |
| 147 | + export INPUT_DIR |
| 148 | + ./scripts/generate_baseline.sh |
| 149 | +
|
| 150 | + - name: Emit manifest |
| 151 | + id: manifest |
| 152 | + shell: bash |
| 153 | + env: |
| 154 | + LANGUAGE: ${{ inputs.language }} |
| 155 | + PROJECT: ${{ inputs.project }} |
| 156 | + SV: ${{ steps.cfg.outputs.srcml_version }} |
| 157 | + PT: ${{ steps.cfg.outputs.project_tag }} |
| 158 | + FLAGS: ${{ steps.cfg.outputs.srcml_flags }} |
| 159 | + RAW: ${{ steps.gen.outputs.raw_bytes }} |
| 160 | + ZST: ${{ steps.gen.outputs.zst_bytes }} |
| 161 | + SHA: ${{ steps.gen.outputs.sha256 }} |
| 162 | + ZST_NAME: ${{ steps.cfg.outputs.zst_name }} |
| 163 | + MANIFEST_NAME: ${{ steps.cfg.outputs.manifest_name }} |
| 164 | + run: | |
| 165 | + jq -n \ |
| 166 | + --arg language "$LANGUAGE" \ |
| 167 | + --arg project "$PROJECT" \ |
| 168 | + --arg srcml_version "$SV" \ |
| 169 | + --arg project_tag "$PT" \ |
| 170 | + --arg flags "$FLAGS" \ |
| 171 | + --arg asset_name "$ZST_NAME" \ |
| 172 | + --arg bytes_raw "$RAW" \ |
| 173 | + --arg bytes_zst "$ZST" \ |
| 174 | + --arg sha256 "$SHA" \ |
| 175 | + --arg generated_at "$(date --iso-8601=seconds)" \ |
| 176 | + --arg runner_os "$(lsb_release -ds 2>/dev/null || uname -a)" \ |
| 177 | + '{ |
| 178 | + language:$language, |
| 179 | + project:$project, |
| 180 | + srcml_version:$srcml_version, |
| 181 | + project_tag:$project_tag, |
| 182 | + flags:$flags, |
| 183 | + asset_name:$asset_name, |
| 184 | + bytes_raw:($bytes_raw|tonumber), |
| 185 | + bytes_compressed:($bytes_zst|tonumber), |
| 186 | + sha256:$sha256, |
| 187 | + generated_at:$generated_at, |
| 188 | + runner_os:$runner_os |
| 189 | + }' > "$MANIFEST_NAME" |
| 190 | + cat "$MANIFEST_NAME" |
| 191 | +
|
| 192 | + - name: Upload scratch artifact (dry_run path) |
| 193 | + if: ${{ inputs.dry_run == 'true' }} |
| 194 | + uses: actions/upload-artifact@v4 |
| 195 | + with: |
| 196 | + name: baseline-dry-run-${{ steps.cfg.outputs.release_tag }} |
| 197 | + path: | |
| 198 | + ${{ steps.cfg.outputs.zst_name }} |
| 199 | + ${{ steps.cfg.outputs.manifest_name }} |
| 200 | + ${{ steps.cfg.outputs.log_name }} |
| 201 | + retention-days: 14 |
| 202 | + |
| 203 | + - name: Publish GitHub Release |
| 204 | + if: ${{ inputs.dry_run != 'true' }} |
| 205 | + shell: bash |
| 206 | + env: |
| 207 | + GH_TOKEN: ${{ inputs.github_token }} |
| 208 | + TAG: ${{ steps.cfg.outputs.release_tag }} |
| 209 | + LANGUAGE: ${{ inputs.language }} |
| 210 | + PROJECT: ${{ inputs.project }} |
| 211 | + SV: ${{ steps.cfg.outputs.srcml_version }} |
| 212 | + PT: ${{ steps.cfg.outputs.project_tag }} |
| 213 | + ZST_NAME: ${{ steps.cfg.outputs.zst_name }} |
| 214 | + MANIFEST_NAME: ${{ steps.cfg.outputs.manifest_name }} |
| 215 | + LOG_NAME: ${{ steps.cfg.outputs.log_name }} |
| 216 | + run: | |
| 217 | + set -euxo pipefail |
| 218 | + if gh release view "$TAG" >/dev/null 2>&1; then |
| 219 | + gh release upload "$TAG" "$ZST_NAME" "$MANIFEST_NAME" "$LOG_NAME" --clobber |
| 220 | + else |
| 221 | + gh release create "$TAG" \ |
| 222 | + --title "Baseline: ${LANGUAGE}/${PROJECT} ${PT} / srcml ${SV}" \ |
| 223 | + --notes "Baseline srcML archive. language=${LANGUAGE}, project=${PROJECT}, project_tag=${PT}, srcml=${SV}. See ${MANIFEST_NAME} for sha256 and flags." \ |
| 224 | + --latest=false \ |
| 225 | + "$ZST_NAME" "$MANIFEST_NAME" "$LOG_NAME" |
| 226 | + fi |
| 227 | +
|
| 228 | + - name: Update per-project pointer |
| 229 | + if: ${{ inputs.dry_run != 'true' }} |
| 230 | + shell: bash |
| 231 | + env: |
| 232 | + CFG: ${{ steps.cfg.outputs.config_path }} |
| 233 | + PROJECT: ${{ inputs.project }} |
| 234 | + TAG: ${{ steps.cfg.outputs.release_tag }} |
| 235 | + run: | |
| 236 | + yq -i -p toml -o toml ' |
| 237 | + (.projects[] | select(.name == strenv(PROJECT)) | .current_baseline_release) = strenv(TAG) |
| 238 | + ' "$CFG" |
| 239 | +
|
| 240 | + - name: Open baseline pointer PR |
| 241 | + if: ${{ inputs.dry_run != 'true' }} |
| 242 | + uses: peter-evans/create-pull-request@v6 |
| 243 | + with: |
| 244 | + token: ${{ inputs.github_token }} |
| 245 | + commit-message: "chore: point ${{ inputs.language }}/${{ inputs.project }} baseline to ${{ steps.cfg.outputs.release_tag }}" |
| 246 | + branch: baseline-pointer/${{ steps.cfg.outputs.release_tag }} |
| 247 | + title: "Update baseline pointer → ${{ steps.cfg.outputs.release_tag }}" |
| 248 | + body: | |
| 249 | + Automated PR to update `${{ steps.cfg.outputs.config_path }}` → the `[[projects]]` entry for `${{ inputs.project }}`'s `current_baseline_release`. |
| 250 | +
|
| 251 | + - language: ${{ inputs.language }} |
| 252 | + - project: ${{ inputs.project }} |
| 253 | + - project tag: ${{ steps.cfg.outputs.project_tag }} |
| 254 | + - srcml: ${{ steps.cfg.outputs.srcml_version }} |
| 255 | + - release tag: ${{ steps.cfg.outputs.release_tag }} |
| 256 | +
|
| 257 | + Release assets are published. Merge to make downstream runs use this baseline. |
| 258 | + add-paths: | |
| 259 | + ${{ steps.cfg.outputs.config_path }} |
0 commit comments