Skip to content

Instantly share code, notes, and snippets.

@naoyashiga
Created February 26, 2025 04:44
Show Gist options
  • Save naoyashiga/afdd55e37bc1f5e3376487d2d9ff191e to your computer and use it in GitHub Desktop.
Save naoyashiga/afdd55e37bc1f5e3376487d2d9ff191e to your computer and use it in GitHub Desktop.

Revisions

  1. naoyashiga created this gist Feb 26, 2025.
    85 changes: 85 additions & 0 deletions actions-diff-display-action.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    name: "Diff Display and Update Cache"
    description: "Compare current top5 results with previous cache, display new additions, and update the cache file."

    inputs:
    currentResults:
    description: "The JSON string of current top5 results from malware-check action."
    required: true
    orgName:
    description: "The GitHub organization name."
    required: true

    runs:
    using: "composite"
    steps:
    - name: Compare current results with previous cache and update cache
    id: diff-update
    uses: actions/github-script@v7
    env:
    INPUT_ORGNAME: ${{ inputs.orgName }}
    with:
    script: |
    const GREEN = "\x1b[32m";
    const YELLOW = "\x1b[33m";
    const CYAN = "\x1b[36m";
    const RESET = "\x1b[0m";
    const owner = context.repo.owner;
    const repo = context.repo.repo;
    // currentResults を入力から取得
    const currentResults = JSON.parse(core.getInput('currentResults') || "{}");
    let previousResults = {};
    let fileSha = null;
    try {
    const fileResponse = await github.rest.repos.getContent({
    owner,
    repo,
    path: 'top5_cache.json',
    ref: 'main'
    });
    const content = Buffer.from(fileResponse.data.content, 'base64').toString('utf8');
    previousResults = JSON.parse(content);
    fileSha = fileResponse.data.sha;
    } catch (error) {
    if (error.status === 404) {
    core.info("No previous cache found. Starting fresh.");
    previousResults = {};
    } else {
    throw error;
    }
    }
    // 差分算出: 各対象リポジトリごとに、current にあって previous にない項目(新規追加)のみ抽出
    const differences = {};
    for (const repoName in currentResults) {
    const currList = currentResults[repoName] || [];
    const prevList = previousResults[repoName] || [];
    const prevNames = new Set(prevList.map(item => item.full_name));
    const newItems = currList.filter(item => !prevNames.has(item.full_name));
    if (newItems.length > 0) {
    differences[repoName] = newItems;
    }
    }
    if (Object.keys(differences).length === 0) {
    core.info(`${YELLOW}No new additions detected compared to previous state.${RESET}`);
    } else {
    for (const repoName in differences) {
    core.info(`\nFor organization repo: ${repoName}`);
    differences[repoName].forEach(item => {
    core.info(`${GREEN}New addition: ${item.full_name}${RESET} | ${CYAN}Created: ${item.created_at} | Stars: ${item.stargazers_count} | URL: ${item.html_url}`);
    });
    }
    }
    // キャッシュの更新: currentResults を top5_cache.json として保存
    const newContent = Buffer.from(JSON.stringify(currentResults, null, 2)).toString('base64');
    const commitMessage = `Update top5 cache: ${new Date().toISOString()}`;
    await github.rest.repos.createOrUpdateFileContents({
    owner,
    repo,
    path: 'top5_cache.json',
    message: commitMessage,
    content: newContent,
    sha: fileSha || undefined
    });
    91 changes: 91 additions & 0 deletions actions-malware-check-action.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    name: "Malware Repository Checker"
    description: "Search same-named repositories and output current top5 results per repo."

    inputs:
    orgName:
    description: "The GitHub organization name to check."
    required: true

    outputs:
    currentResults:
    description: "The JSON string representing the current top5 results for each repo."

    runs:
    using: "composite"
    steps:
    # -----------------------------------------------------------
    # 1. 対象組織の public リポジトリを、直近1年以内かつ非 archived でフィルタし、更新日時降順の上位20件を取得
    # -----------------------------------------------------------
    - name: Get target repos from organization
    id: get-repos
    uses: actions/github-script@v7
    env:
    INPUT_ORGNAME: ${{ inputs.orgName }}
    with:
    script: |
    const orgName = core.getInput('orgName');
    core.info(`Searching repos for organization: ${orgName}`);
    // 最大50件取得してからフィルタする
    const { data } = await github.rest.repos.listForOrg({
    org: orgName,
    type: 'public',
    sort: 'updated',
    direction: 'desc',
    per_page: 50
    });
    const oneYearAgo = new Date();
    oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
    const filteredRepos = data.filter(r => new Date(r.updated_at) >= oneYearAgo && !r.archived);
    const topRepos = filteredRepos.slice(0, 20);
    const repoNames = topRepos.map(r => r.name);
    core.info(`Target repos: ${repoNames.join(', ')}`);
    core.setOutput("repoNames", JSON.stringify(repoNames));
    # -----------------------------------------------------------
    # 2. 各リポジトリ名ごとに、GitHub全体から同名(完全一致、大文字小文字無視、かつ対象組織外)の上位5件を検索
    # -----------------------------------------------------------
    - name: Search for same-named repositories
    id: search-repos
    uses: actions/github-script@v6
    env:
    INPUT_ORGNAME: ${{ inputs.orgName }}
    REPO_NAMES: ${{ steps.get-repos.outputs.repoNames }}
    with:
    script: |
    // ANSIカラーコード(ログ用)
    const GREEN = "\x1b[32m";
    const YELLOW = "\x1b[33m";
    const CYAN = "\x1b[36m";
    const RESET = "\x1b[0m";
    const orgName = process.env.INPUT_ORGNAME;
    const repoNames = JSON.parse(process.env.REPO_NAMES);
    const currentResults = {};
    for (const repoName of repoNames) {
    core.info(`\n[Checking repo: ${repoName}] ------------------------`);
    // 検索: repoName in:name, 作成日降順、最大50件取得
    const searchResponse = await github.rest.search.repos({
    q: `${repoName} in:name`,
    sort: 'created',
    order: 'desc',
    per_page: 50,
    });
    // 完全一致かつ対象組織外のみ抽出
    const matched = searchResponse.data.items.filter(item =>
    item.name.toLowerCase() === repoName.toLowerCase() &&
    item.owner.login.toLowerCase() !== orgName.toLowerCase()
    );
    const top5 = matched.slice(0, 5);
    if (top5.length === 0) {
    core.info(`${YELLOW}No same-named repos found for ${repoName}.${RESET}`);
    } else {
    top5.forEach(repo => {
    core.info(`${GREEN}Found: ${repo.full_name}${RESET} | ${CYAN}Created: ${repo.created_at} | Stars: ${repo.stargazers_count} | URL: ${repo.html_url}`);
    });
    }
    currentResults[repoName] = top5;
    }
    // 出力として currentResults をセット
    core.setOutput("currentResults", JSON.stringify(currentResults));
    37 changes: 37 additions & 0 deletions workflows-malware-check.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    name: Malware Check
    on:
    pull_request:
    branches:
    - main
    types: [opened, synchronize]
    push:
    branches:
    - main
    # workflow_dispatch: # 手動実行する場合はコメントアウトを外す

    permissions:
    contents: write

    jobs:
    malware-check-job:
    runs-on: ubuntu-latest
    steps:
    - name: Check out repository
    uses: actions/checkout@v4

    - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
    node-version: '20.x'

    - name: Run Malware Check Action
    id: malware-check
    uses: ./.github/actions/malware-check
    with:
    orgName: layerXcom

    - name: Run Diff Display Action
    uses: ./.github/actions/diff-display
    with:
    orgName: layerXcom
    currentResults: ${{ steps.malware-check.outputs.currentResults }}