Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Reusable GitHub Actions for Sourcegraph repositories.
| Action | Description |
|--------|-------------|
| [go-setup](./go-setup) | Setup Go with private Sourcegraph repository access |
| [diff-tour](./diff-tour) | Generate a link to the Diff Tour for a PR |

## Usage

Expand Down
35 changes: 35 additions & 0 deletions diff-tour/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# `diff-tour`

Generates a link to the Diff Tour for a PR and posts it as a PR comment.

## Usage

```yaml
on:
pull_request:
# `closed` fires on merge, so the comment is updated to link the merge commit
types: [opened, synchronize, reopened, closed]

permissions:
pull-requests: write

jobs:
diff-tour:
runs-on: ubuntu-latest
# Diff Tour resolves branch names against the base repo, so skip fork PRs.
# Closed PRs only need an update when they were merged.
if: >
github.event.pull_request.head.repo.full_name == github.repository
&& (github.event.action != 'closed' || github.event.pull_request.merged)
steps:
- uses: sourcegraph/actions/diff-tour@main
```

## Inputs

This action has no inputs.

## What it does

1. Builds a Diff Tour URL for the PR — a commit link if the PR is merged, or a branch compare link if it's still open
2. Creates a PR comment with the link, or updates the existing one if it has already posted a comment
13 changes: 13 additions & 0 deletions diff-tour/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Diff Tour link
description: Generate a link to the Diff Tour based on a PR.

runs:
using: composite
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const script = require(`${{ github.action_path }}/generateTourLink.cjs`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should rather just have the contents of this script right here. There is a lot of fluff around this which I don't think we quite need yet.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be okay with keeping the separate .js file but getting rid of everything else in the project?

I can totally see everything else being too much ceremony when we're only adding a single new action, but I think there's still benefits to having the logic in a separate file, at least for local dev. My worry is that if everything's inlined, we'll basically be recreating the problem with Bash scripts, just in a different language

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an explicit file would be fine yeah!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Just ripped out the other stuff

const result = await script({ github, context })
console.log(result)
108 changes: 108 additions & 0 deletions diff-tour/generateTourLink.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @file This file is written in plain JavaScript to remove an extra build step, but we should use the @ts-check
* directive and JSDoc comments to make sure the logic is still type-safe.
*
* @todo 2026-09-10 - This file recreates local versions of the `AsyncFunctionArguments` type from
* `@actions/github-script`.so that we can keep this file fully self-contained and type-safe without having to bring in
* typical JS/TS tooling. If this repo gets to the point where it needs multiple JS scripts, consider ripping out these
* local types in favor of bringing in the package straight from GitHub.
*/
//@ts-check

/** @type {{log: (...args: unknown[]) => void}} */
const console = /** @type {any} */ (globalThis).console

/**
* @typedef {object} PullRequest
* @property {number} number
* @property {boolean} merged
* @property {string} merge_commit_sha
* @property {{ref: string}} base
* @property {{ref: string}} head
*/

/**
* @typedef {object} Context
* @property {{pull_request?: PullRequest}} payload
* @property {{owner: string, repo: string}} repo
*/

/**
* @typedef {object} Comment
* @property {number} id
* @property {string | null | undefined} body
*/

/**
* @typedef {object} Github
* @property {object} rest
* @property {object} rest.issues
* @property {(params: {owner: string, repo: string, issue_number: number}) => Promise<{data: Comment[]}>} rest.issues.listComments
* @property {(params: {owner: string, repo: string, comment_id: number, body: string}) => Promise<unknown>} rest.issues.updateComment
* @property {(params: {owner: string, repo: string, issue_number: number, body: string}) => Promise<unknown>} rest.issues.createComment
*/

/**
* @typedef {object} GenerateTourLinkArgs
* @property {Github} github
* @property {Context} context
*/

const instance = "https://sourcegraph.sourcegraph.com"
const diffTourCommentMarker = "<!-- difftour-link -->"

/** @param {GenerateTourLinkArgs} args */
async function generateTourLink({ github, context }) {
const pullRequest = context.payload.pull_request
if (pullRequest === undefined) {
return
}

/** @type {string} */
let url
const { repo, owner } = context.repo
const repoLink = `${owner}/${repo}`
if (pullRequest.merged) {
const commit = encodeURIComponent(pullRequest.merge_commit_sha)
url = `${instance}/r/${repoLink}/-/commit/${commit}?mode=Tour`
} else {
const base = encodeURIComponent(pullRequest.base.ref)
const head = encodeURIComponent(pullRequest.head.ref)
url = `${instance}/r/${repoLink}/-/compare/${base}...${head}?mode=Tour`
}

const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number: pullRequest.number,
})

const markedComments = comments.filter(
(c) => c.body?.includes(diffTourCommentMarker) ?? false,
)
if (markedComments.length > 1) {
console.log(
"Found multiple comments with the Diff Tour marker. Updating only the first",
)
}

const commentContent = `${diffTourCommentMarker}\n[Open the Diff Tour for this PR in Sourcegraph](${url})`
const first = markedComments[0]
if (first !== undefined) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: first.id,
body: commentContent,
})
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number: pullRequest.number,
body: commentContent,
})
}
}

module.exports = generateTourLink
14 changes: 4 additions & 10 deletions go-setup/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
# Sourcegraph Actions

Reusable GitHub Actions for Sourcegraph repositories.

## Available Actions

### go-setup
# `go-setup`

Sets up Go with private Sourcegraph repository access.

**Usage:**
## Usage

```yaml
steps:
Expand All @@ -18,14 +12,14 @@ steps:
private-token: ${{ secrets.PRIVATE_SG_ACCESS_TOKEN }}
```

**Inputs:**
## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `private-token` | Yes | - | Token for accessing private Sourcegraph repos |
| `go-version-file` | No | `go.mod` | Path to go.mod or go.work file to determine Go version |

**What it does:**
## What it does

1. Installs Go using the version from your `go.mod`
2. Configures git to access private `github.com/sourcegraph/*` repos
Expand Down